@0xmonaco/core 1.0.32 → 1.0.33-develop.c485f61
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/base.js +122 -3
- package/dist/api/pitpass/api.d.ts +25 -2
- package/dist/api/pitpass/api.js +44 -2
- package/dist/api/websocket/websocket.js +9 -5
- package/dist/coverage.d.ts +3 -0
- package/dist/coverage.js +3 -0
- package/package.json +3 -3
package/dist/api/base.js
CHANGED
|
@@ -34,6 +34,112 @@ import { APIError } from "../errors";
|
|
|
34
34
|
* only takes effect in server runtimes (Node/undici, Bun, Deno).
|
|
35
35
|
*/
|
|
36
36
|
const SDK_USER_AGENT = "@0xmonaco/core";
|
|
37
|
+
const MONTHS = {
|
|
38
|
+
Jan: 0,
|
|
39
|
+
Feb: 1,
|
|
40
|
+
Mar: 2,
|
|
41
|
+
Apr: 3,
|
|
42
|
+
May: 4,
|
|
43
|
+
Jun: 5,
|
|
44
|
+
Jul: 6,
|
|
45
|
+
Aug: 7,
|
|
46
|
+
Sep: 8,
|
|
47
|
+
Oct: 9,
|
|
48
|
+
Nov: 10,
|
|
49
|
+
Dec: 11,
|
|
50
|
+
};
|
|
51
|
+
// The three RFC 7231 §7.1.1.1 `HTTP-date` grammars a recipient must accept.
|
|
52
|
+
// Each is anchored so trailing junk is rejected, and `Date.parse` is avoided
|
|
53
|
+
// entirely (it is permissive — it reads asctime in local time and rolls
|
|
54
|
+
// impossible dates like 31 Feb forward instead of rejecting them).
|
|
55
|
+
const MONTH = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
|
|
56
|
+
const IMF_FIXDATE = new RegExp(`^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\\d{2}) (${MONTH}) (\\d{4}) (\\d{2}):(\\d{2}):(\\d{2}) GMT$`);
|
|
57
|
+
const RFC850_DATE = new RegExp(`^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\\d{2})-(${MONTH})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2}) GMT$`);
|
|
58
|
+
const ASCTIME_DATE = new RegExp(`^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (${MONTH}) ([ \\d]\\d) (\\d{2}):(\\d{2}):(\\d{2}) (\\d{4})$`);
|
|
59
|
+
/**
|
|
60
|
+
* Expand an RFC 850 two-digit year per RFC 7231 §7.1.1.1: a full timestamp that
|
|
61
|
+
* appears more than 50 years in the future is interpreted as the most recent
|
|
62
|
+
* past year with the same last two digits. The comparison is against the exact
|
|
63
|
+
* receipt instant plus 50 years (not just calendar years), so the boundary can
|
|
64
|
+
* fall mid-year — e.g. received 2026-06-01, `31-Dec-76` is 50y7m ahead and maps
|
|
65
|
+
* to 1976, while `01-Jan-76` is under 50 years ahead and stays 2076.
|
|
66
|
+
*/
|
|
67
|
+
function expandTwoDigitYear(twoDigitYear, month, day, hour, minute, second) {
|
|
68
|
+
const now = new Date(Date.now());
|
|
69
|
+
const candidateYear = Math.floor(now.getUTCFullYear() / 100) * 100 + twoDigitYear;
|
|
70
|
+
const candidate = Date.UTC(candidateYear, month, day, hour, minute, second);
|
|
71
|
+
const fiftyYearsAhead = Date.UTC(now.getUTCFullYear() + 50, now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
|
|
72
|
+
return candidate > fiftyYearsAhead ? candidateYear - 100 : candidateYear;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parse an RFC 7231 `HTTP-date` — in any of the three formats a recipient must
|
|
76
|
+
* accept (IMF-fixdate, the obsolete RFC 850 form, and asctime) — into a UTC
|
|
77
|
+
* epoch in milliseconds, or `undefined` if the value is not a well-formed, real
|
|
78
|
+
* calendar date. All three denote UTC, so timestamps are built with `Date.UTC`
|
|
79
|
+
* and the fields are round-tripped to reject impossible dates such as
|
|
80
|
+
* `31 Feb` or `25:00:00`. RFC 850's 2-digit year follows the RFC 7231 rule
|
|
81
|
+
* (see {@link expandTwoDigitYear}). The redundant day-name is intentionally not
|
|
82
|
+
* validated against the date — RFC 7231 encourages robust parsing, and the
|
|
83
|
+
* date/time fields already determine the instant unambiguously, so a mismatched
|
|
84
|
+
* weekday still yields the intended delay rather than a dropped header and an
|
|
85
|
+
* arbitrary fallback.
|
|
86
|
+
*/
|
|
87
|
+
function parseHttpDate(value) {
|
|
88
|
+
let day;
|
|
89
|
+
let month;
|
|
90
|
+
let year;
|
|
91
|
+
let hour;
|
|
92
|
+
let minute;
|
|
93
|
+
let second;
|
|
94
|
+
const imf = IMF_FIXDATE.exec(value);
|
|
95
|
+
const rfc850 = RFC850_DATE.exec(value);
|
|
96
|
+
const asctime = ASCTIME_DATE.exec(value);
|
|
97
|
+
if (imf) {
|
|
98
|
+
day = Number(imf[1]);
|
|
99
|
+
month = MONTHS[imf[2]];
|
|
100
|
+
year = Number(imf[3]);
|
|
101
|
+
hour = Number(imf[4]);
|
|
102
|
+
minute = Number(imf[5]);
|
|
103
|
+
second = Number(imf[6]);
|
|
104
|
+
}
|
|
105
|
+
else if (rfc850) {
|
|
106
|
+
day = Number(rfc850[1]);
|
|
107
|
+
month = MONTHS[rfc850[2]];
|
|
108
|
+
hour = Number(rfc850[4]);
|
|
109
|
+
minute = Number(rfc850[5]);
|
|
110
|
+
second = Number(rfc850[6]);
|
|
111
|
+
if (month === undefined)
|
|
112
|
+
return undefined;
|
|
113
|
+
year = expandTwoDigitYear(Number(rfc850[3]), month, day, hour, minute, second);
|
|
114
|
+
}
|
|
115
|
+
else if (asctime) {
|
|
116
|
+
month = MONTHS[asctime[1]];
|
|
117
|
+
day = Number((asctime[2] ?? "").trim());
|
|
118
|
+
hour = Number(asctime[3]);
|
|
119
|
+
minute = Number(asctime[4]);
|
|
120
|
+
second = Number(asctime[5]);
|
|
121
|
+
year = Number(asctime[6]);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
if (month === undefined)
|
|
127
|
+
return undefined;
|
|
128
|
+
const timestamp = Date.UTC(year, month, day, hour, minute, second);
|
|
129
|
+
const utc = new Date(timestamp);
|
|
130
|
+
// Reject impossible calendar/clock values that Date.UTC silently rolls over
|
|
131
|
+
// (e.g. 31 Feb, 25:00:00). A `:60` leap second is likewise dropped rather
|
|
132
|
+
// than special-cased — leap seconds never appear in a Retry-After header.
|
|
133
|
+
if (utc.getUTCFullYear() !== year ||
|
|
134
|
+
utc.getUTCMonth() !== month ||
|
|
135
|
+
utc.getUTCDate() !== day ||
|
|
136
|
+
utc.getUTCHours() !== hour ||
|
|
137
|
+
utc.getUTCMinutes() !== minute ||
|
|
138
|
+
utc.getUTCSeconds() !== second) {
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return timestamp;
|
|
142
|
+
}
|
|
37
143
|
export class BaseAPI {
|
|
38
144
|
apiUrl;
|
|
39
145
|
/** Active session keypair (raw bytes) used to sign authenticated requests. */
|
|
@@ -138,12 +244,25 @@ export class BaseAPI {
|
|
|
138
244
|
if (!headers)
|
|
139
245
|
return {};
|
|
140
246
|
const requestId = headers.get("x-request-id") || headers.get("request-id") || headers.get("x-correlation-id") || undefined;
|
|
141
|
-
// Parse Retry-After header
|
|
247
|
+
// Parse the Retry-After header (RFC 7231). It is either a non-negative
|
|
248
|
+
// number of seconds (delay-seconds) or an HTTP-date. Both forms are
|
|
249
|
+
// normalized to a delay in SECONDS, so callers multiply by 1000 for
|
|
250
|
+
// setTimeout. An HTTP-date is converted to the remaining seconds from now
|
|
251
|
+
// (past dates clamp to 0). Values matching neither grammar are dropped so
|
|
252
|
+
// a malformed header can never masquerade as a 0s "retry immediately".
|
|
142
253
|
const retryAfterHeader = headers.get("retry-after");
|
|
143
254
|
let retryAfter;
|
|
144
255
|
if (retryAfterHeader !== null) {
|
|
145
|
-
const
|
|
146
|
-
|
|
256
|
+
const value = retryAfterHeader.trim();
|
|
257
|
+
if (/^\d+$/.test(value)) {
|
|
258
|
+
retryAfter = Number.parseInt(value, 10);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
const dateMs = parseHttpDate(value);
|
|
262
|
+
if (dateMs !== undefined) {
|
|
263
|
+
retryAfter = Math.max(0, Math.ceil((dateMs - Date.now()) / 1000));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
147
266
|
}
|
|
148
267
|
return { requestId, retryAfter };
|
|
149
268
|
}
|
|
@@ -33,13 +33,36 @@
|
|
|
33
33
|
* console.log(`Rewards remaining: ${result.rewardsBalance}`);
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
import type { GetRewardsBalanceResponse, PitpassAPI, TraderCodeInfoResponse, TraderCodeResponse, TransferRewardsParams, TransferRewardsResponse } from "@0xmonaco/types";
|
|
36
|
+
import type { GetRewardsBalanceResponse, PitpassAPI, TraderCodeAvailabilityResponse, TraderCodeInfoResponse, TraderCodeResponse, TransferRewardsParams, TransferRewardsResponse } from "@0xmonaco/types";
|
|
37
37
|
import { BaseAPI } from "../base";
|
|
38
38
|
export declare class PitpassAPIImpl extends BaseAPI implements PitpassAPI {
|
|
39
39
|
/**
|
|
40
|
-
* Return the authenticated caller's TraderCode
|
|
40
|
+
* Return the authenticated caller's TraderCode.
|
|
41
|
+
*
|
|
42
|
+
* The response carries both the permanent wallet-derived `code` and the
|
|
43
|
+
* custom-handle fields (`customCode`, `isCustom`, `display`).
|
|
41
44
|
*/
|
|
42
45
|
getMyTraderCode(): Promise<TraderCodeResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* Claim or edit the caller's custom (vanity) TraderCode handle.
|
|
48
|
+
*
|
|
49
|
+
* The handle is validated client-side (letter-first `[A-Za-z0-9_]`, length
|
|
50
|
+
* 5–20) and sent exactly as entered — casing is preserved. The server is the
|
|
51
|
+
* authoritative gate (409 taken, 422 invalid, 429 rate-limited, 403 disabled).
|
|
52
|
+
*/
|
|
53
|
+
setTraderCode(code: string): Promise<TraderCodeResponse>;
|
|
54
|
+
/**
|
|
55
|
+
* Clear the caller's custom TraderCode handle, reverting to the wallet-derived
|
|
56
|
+
* default. The freed handle enters a cooldown before it can be reclaimed.
|
|
57
|
+
*/
|
|
58
|
+
clearTraderCode(): Promise<TraderCodeResponse>;
|
|
59
|
+
/**
|
|
60
|
+
* Public advisory check of whether a custom TraderCode handle is available.
|
|
61
|
+
*
|
|
62
|
+
* As-you-type UX helper only; the authoritative gate is the atomic claim in
|
|
63
|
+
* {@link setTraderCode}. Returns `available` and, when unavailable, a `reason`.
|
|
64
|
+
*/
|
|
65
|
+
checkTraderCodeAvailability(code: string): Promise<TraderCodeAvailabilityResponse>;
|
|
43
66
|
/**
|
|
44
67
|
* Public lookup of a TraderCode by wallet address.
|
|
45
68
|
* Accepts the bare address or the `Monaco - <address>` display form.
|
package/dist/api/pitpass/api.js
CHANGED
|
@@ -33,15 +33,57 @@
|
|
|
33
33
|
* console.log(`Rewards remaining: ${result.rewardsBalance}`);
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
import { TransferRewardsParamsSchema, validate } from "@0xmonaco/types";
|
|
36
|
+
import { SetTraderCodeParamsSchema, TransferRewardsParamsSchema, validate } from "@0xmonaco/types";
|
|
37
37
|
import { BaseAPI } from "../base";
|
|
38
38
|
export class PitpassAPIImpl extends BaseAPI {
|
|
39
39
|
/**
|
|
40
|
-
* Return the authenticated caller's TraderCode
|
|
40
|
+
* Return the authenticated caller's TraderCode.
|
|
41
|
+
*
|
|
42
|
+
* The response carries both the permanent wallet-derived `code` and the
|
|
43
|
+
* custom-handle fields (`customCode`, `isCustom`, `display`).
|
|
41
44
|
*/
|
|
42
45
|
async getMyTraderCode() {
|
|
43
46
|
return await this.makeAuthenticatedRequest("/api/v1/pitpass/codes/me", { method: "GET" });
|
|
44
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Claim or edit the caller's custom (vanity) TraderCode handle.
|
|
50
|
+
*
|
|
51
|
+
* The handle is validated client-side (letter-first `[A-Za-z0-9_]`, length
|
|
52
|
+
* 5–20) and sent exactly as entered — casing is preserved. The server is the
|
|
53
|
+
* authoritative gate (409 taken, 422 invalid, 429 rate-limited, 403 disabled).
|
|
54
|
+
*/
|
|
55
|
+
async setTraderCode(code) {
|
|
56
|
+
const validated = validate(SetTraderCodeParamsSchema, { code });
|
|
57
|
+
return await this.makeAuthenticatedRequest("/api/v1/pitpass/codes/me/custom", {
|
|
58
|
+
method: "PUT",
|
|
59
|
+
body: JSON.stringify({ code: validated.code }),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Clear the caller's custom TraderCode handle, reverting to the wallet-derived
|
|
64
|
+
* default. The freed handle enters a cooldown before it can be reclaimed.
|
|
65
|
+
*/
|
|
66
|
+
async clearTraderCode() {
|
|
67
|
+
return await this.makeAuthenticatedRequest("/api/v1/pitpass/codes/me/custom", { method: "DELETE" });
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Public advisory check of whether a custom TraderCode handle is available.
|
|
71
|
+
*
|
|
72
|
+
* As-you-type UX helper only; the authoritative gate is the atomic claim in
|
|
73
|
+
* {@link setTraderCode}. Returns `available` and, when unavailable, a `reason`.
|
|
74
|
+
*/
|
|
75
|
+
async checkTraderCodeAvailability(code) {
|
|
76
|
+
// Trim to mirror the claim path (setTraderCode validates a trimmed handle),
|
|
77
|
+
// so the advisory check and the eventual claim agree on padded input.
|
|
78
|
+
const query = new URLSearchParams({ code: code.trim() }).toString();
|
|
79
|
+
const raw = await this.makePublicRequest(`/api/v1/pitpass/codes/available?${query}`, {
|
|
80
|
+
method: "GET",
|
|
81
|
+
});
|
|
82
|
+
// The API always serializes `reason`, sending "" (proto3 string default)
|
|
83
|
+
// when the handle is available; surface that as an omitted `reason` so
|
|
84
|
+
// callers can rely on the documented ergonomic shape.
|
|
85
|
+
return { available: raw.available, reason: raw.reason ? raw.reason : undefined };
|
|
86
|
+
}
|
|
45
87
|
/**
|
|
46
88
|
* Public lookup of a TraderCode by wallet address.
|
|
47
89
|
* Accepts the bare address or the `Monaco - <address>` display form.
|
|
@@ -150,6 +150,10 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
150
150
|
* opens. The client signs `WS-AUTH\n<pubkey>\n<ts>` with the session private
|
|
151
151
|
* key; the server binds the connection to the session. No-op if no session
|
|
152
152
|
* is set (public channels work unauthenticated).
|
|
153
|
+
*
|
|
154
|
+
* `timestamp` is the current epoch-ms and is bound into the signature; the
|
|
155
|
+
* server rejects it outside a ±30s skew window, so the handshake is freshly
|
|
156
|
+
* signed on every (re)connect rather than cached and replayed.
|
|
153
157
|
*/
|
|
154
158
|
const sendAuthenticate = () => {
|
|
155
159
|
if (!session || ws?.readyState !== WebSocket.OPEN)
|
|
@@ -504,7 +508,7 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
504
508
|
});
|
|
505
509
|
};
|
|
506
510
|
const subscribeMovements = (handler) => {
|
|
507
|
-
// User is identified from the
|
|
511
|
+
// User is identified from the authenticated session, so channel is just "movements"
|
|
508
512
|
const channel = "movements";
|
|
509
513
|
return subscribe(channel, (rawData) => {
|
|
510
514
|
try {
|
|
@@ -541,10 +545,10 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
541
545
|
};
|
|
542
546
|
/**
|
|
543
547
|
* Subscribe to authenticated user's order events (all trading pairs)
|
|
544
|
-
* Requires authentication - user is identified from the
|
|
548
|
+
* Requires authentication - user is identified from the signed session-key handshake
|
|
545
549
|
*/
|
|
546
550
|
const subscribeUserOrders = (handler) => {
|
|
547
|
-
// User is identified from the
|
|
551
|
+
// User is identified from the authenticated session, so channel is just "orders"
|
|
548
552
|
const channel = "orders";
|
|
549
553
|
return subscribe(channel, (rawData) => {
|
|
550
554
|
try {
|
|
@@ -564,10 +568,10 @@ export function createMonacoWebSocket(baseUrl, options = {}) {
|
|
|
564
568
|
};
|
|
565
569
|
/**
|
|
566
570
|
* Subscribe to authenticated user's balance events
|
|
567
|
-
* Requires authentication - user is identified from the
|
|
571
|
+
* Requires authentication - user is identified from the signed session-key handshake
|
|
568
572
|
*/
|
|
569
573
|
const subscribeBalances = (handler) => {
|
|
570
|
-
// User is identified from the
|
|
574
|
+
// User is identified from the authenticated session, so channel is just "balances"
|
|
571
575
|
const channel = "balances";
|
|
572
576
|
return subscribe(channel, (rawData) => {
|
|
573
577
|
try {
|
package/dist/coverage.d.ts
CHANGED
|
@@ -34,6 +34,9 @@ export declare const COVERED: {
|
|
|
34
34
|
get_my_trader_code: string;
|
|
35
35
|
get_rewards_balance: string;
|
|
36
36
|
get_trader_code_info: string;
|
|
37
|
+
set_trader_code: string;
|
|
38
|
+
clear_trader_code: string;
|
|
39
|
+
check_trader_code_availability: string;
|
|
37
40
|
get_open_interest: string;
|
|
38
41
|
get_order_by_id: string;
|
|
39
42
|
get_orderbook_snapshot: string;
|
package/dist/coverage.js
CHANGED
|
@@ -34,6 +34,9 @@ export const COVERED = {
|
|
|
34
34
|
get_my_trader_code: "pitpass.getMyTraderCode",
|
|
35
35
|
get_rewards_balance: "pitpass.getRewardsBalance",
|
|
36
36
|
get_trader_code_info: "pitpass.getTraderCodeInfo",
|
|
37
|
+
set_trader_code: "pitpass.setTraderCode",
|
|
38
|
+
clear_trader_code: "pitpass.clearTraderCode",
|
|
39
|
+
check_trader_code_availability: "pitpass.checkTraderCodeAvailability",
|
|
37
40
|
get_open_interest: "getOpenInterest",
|
|
38
41
|
get_order_by_id: "getOrder",
|
|
39
42
|
get_orderbook_snapshot: "getOrderbook",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@0xmonaco/core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33-develop.c485f61",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"viem": "^2.45.2"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@0xmonaco/contracts": "1.0.
|
|
27
|
-
"@0xmonaco/types": "1.0.
|
|
26
|
+
"@0xmonaco/contracts": "1.0.33-develop.c485f61",
|
|
27
|
+
"@0xmonaco/types": "1.0.33-develop.c485f61",
|
|
28
28
|
"@noble/curves": "^1.9.1",
|
|
29
29
|
"@noble/hashes": "^1.8.0",
|
|
30
30
|
"http-status-codes": "^2.3.0"
|