@0xmonaco/react 1.0.69 → 1.0.70

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.
@@ -9,6 +9,19 @@ export declare const COVERED: {
9
9
  cancel_conditional_order: string;
10
10
  get_conditional_order: string;
11
11
  cancel_order: string;
12
+ upsert_lead_trader: string;
13
+ get_my_lead_trader: string;
14
+ list_my_followers: string;
15
+ list_lead_traders: string;
16
+ get_lead_trader: string;
17
+ list_lead_trader_closed_trades: string;
18
+ list_lead_trader_open_positions: string;
19
+ upsert_follow: string;
20
+ stop_follow: string;
21
+ list_my_follows: string;
22
+ get_follow: string;
23
+ list_follow_leader_positions: string;
24
+ preview_follow: string;
12
25
  close_position: string;
13
26
  batch_close_all_positions: string;
14
27
  create_challenge: string;
package/dist/coverage.js CHANGED
@@ -9,6 +9,19 @@ export const COVERED = {
9
9
  cancel_conditional_order: "useTrade",
10
10
  get_conditional_order: "useTrade",
11
11
  cancel_order: "useTrade",
12
+ upsert_lead_trader: "useCopyTrading",
13
+ get_my_lead_trader: "useCopyTrading",
14
+ list_my_followers: "useCopyTrading",
15
+ list_lead_traders: "useCopyTrading",
16
+ get_lead_trader: "useCopyTrading",
17
+ list_lead_trader_closed_trades: "useCopyTrading",
18
+ list_lead_trader_open_positions: "useCopyTrading",
19
+ upsert_follow: "useCopyTrading",
20
+ stop_follow: "useCopyTrading",
21
+ list_my_follows: "useCopyTrading",
22
+ get_follow: "useCopyTrading",
23
+ list_follow_leader_positions: "useCopyTrading",
24
+ preview_follow: "useCopyTrading",
12
25
  close_position: "usePositions",
13
26
  batch_close_all_positions: "usePositions",
14
27
  create_challenge: "useAuth",
@@ -1,4 +1,6 @@
1
1
  export * from "./useAuth";
2
+ export * from "./useCopyEvents";
3
+ export * from "./useCopyTrading";
2
4
  export * from "./useFees";
3
5
  export * from "./useInstruments";
4
6
  export * from "./useMarket";
@@ -1,4 +1,6 @@
1
1
  export * from "./useAuth";
2
+ export * from "./useCopyEvents";
3
+ export * from "./useCopyTrading";
2
4
  export * from "./useFees";
3
5
  export * from "./useInstruments";
4
6
  export * from "./useMarket";
@@ -0,0 +1,2 @@
1
+ export { MAX_RECENT_COPY_SKIPS, type UseCopyEventsReturn } from "./types";
2
+ export { useCopyEvents } from "./useCopyEvents";
@@ -0,0 +1,2 @@
1
+ export { MAX_RECENT_COPY_SKIPS } from "./types";
2
+ export { useCopyEvents } from "./useCopyEvents";
@@ -0,0 +1,37 @@
1
+ import type { CopyRelationshipEventData, CopySkipEventData } from "@0xmonaco/types";
2
+ /**
3
+ * Return type of {@link useCopyEvents}.
4
+ */
5
+ export interface UseCopyEventsReturn {
6
+ /**
7
+ * Your follows as last reported on the `copy` channel, newest first (by
8
+ * `startedAt`, then `relationshipId`). A follow stopped while the socket was
9
+ * up keeps its entry with status `STOPPED` until the next snapshot, which
10
+ * carries live follows only.
11
+ */
12
+ relationships: CopyRelationshipEventData[];
13
+ /** The same entries keyed by `relationshipId` (the follow id). */
14
+ relationshipsById: Record<string, CopyRelationshipEventData>;
15
+ /**
16
+ * Leader fills your follows did not mirror, newest first, capped at
17
+ * {@link MAX_RECENT_COPY_SKIPS}. Only skips received while subscribed; the
18
+ * snapshot carries none. Read `getFollow` for a follow's persisted history.
19
+ */
20
+ recentSkips: CopySkipEventData[];
21
+ /**
22
+ * Whether a subscription to the `copy` channel is currently registered. Not
23
+ * a connection indicator: the client accepts subscriptions while the socket
24
+ * is down and sends them on the next open.
25
+ */
26
+ subscribed: boolean;
27
+ /**
28
+ * A rejected subscription request (the core helper's synchronous throw). Not
29
+ * a transport signal: connection loss and malformed frames never appear
30
+ * here.
31
+ */
32
+ error: Error | null;
33
+ /** Clear the current error state. */
34
+ clearError: () => void;
35
+ }
36
+ /** How many skips {@link UseCopyEventsReturn.recentSkips} keeps. */
37
+ export declare const MAX_RECENT_COPY_SKIPS = 50;
@@ -0,0 +1,2 @@
1
+ /** How many skips {@link UseCopyEventsReturn.recentSkips} keeps. */
2
+ export const MAX_RECENT_COPY_SKIPS = 50;
@@ -0,0 +1,20 @@
1
+ import { type UseCopyEventsReturn } from "./types";
2
+ /**
3
+ * Stream your copy trading follows over the authenticated `copy` channel.
4
+ * The subscription runs only while a user is signed in: it starts on login,
5
+ * and logout (or an account switch) clears the follows and skips.
6
+ *
7
+ * The subscribe-time snapshot seeds your live follows (`ACTIVE` or
8
+ * `CLOSE_ONLY`); each `copy_relationship_update` frame then carries a follow's
9
+ * FULL state and replaces its entry when its `version` is not older than the
10
+ * stored one. A later snapshot (after a reconnect) REPLACES the set
11
+ * wholesale, so a follow that stopped while the socket was down leaves it.
12
+ *
13
+ * `copy_skip` frames (leader fills a follow could not mirror) collect in
14
+ * `recentSkips`.
15
+ *
16
+ * `cumNetRealized` and `hwm` on each follow are its profit-share basis, not
17
+ * the copy account's balance; read the copy risk bucket (`copyRiskBucketId`)
18
+ * for that.
19
+ */
20
+ export declare function useCopyEvents(): UseCopyEventsReturn;
@@ -0,0 +1,121 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { AuthenticationStatus, useMonacoContext } from "../../provider";
3
+ import { useMonacoSDK } from "../useMonaco";
4
+ import { MAX_RECENT_COPY_SKIPS } from "./types";
5
+ const IDLE = { source: undefined, userId: undefined, byId: {}, skips: [], subscribed: false, error: null };
6
+ /** Shared empties, so an out-of-scope render keeps a stable identity. */
7
+ const NO_RELATIONSHIPS = {};
8
+ const NO_SKIPS = [];
9
+ /**
10
+ * Whether a live frame supersedes the stored row. `version` is the durable-log
11
+ * sequence that produced the row: an older one is a late frame and is ignored.
12
+ * Equal is applied (the same step, re-sent). When either side has no version
13
+ * the frame is applied, since there is nothing to order it by.
14
+ */
15
+ function supersedes(incoming, stored) {
16
+ if (!stored || stored.version === undefined || incoming.version === undefined)
17
+ return true;
18
+ return incoming.version >= stored.version;
19
+ }
20
+ /**
21
+ * Newest follow first. `startedAt` is compared as a parsed instant, not as a
22
+ * string: the server's RFC 3339 stamps carry 0, 3 or 6 fractional digits, so
23
+ * `…05Z` would string-sort after `…05.500Z` although it is earlier. A stamp
24
+ * that does not parse falls back to plain string order against its peer. The
25
+ * id breaks ties with a codepoint compare, so the order never shifts with the
26
+ * runtime locale.
27
+ */
28
+ function newestFirst(left, right) {
29
+ const leftAt = Date.parse(left.startedAt);
30
+ const rightAt = Date.parse(right.startedAt);
31
+ if (!Number.isNaN(leftAt) && !Number.isNaN(rightAt)) {
32
+ if (leftAt !== rightAt)
33
+ return rightAt - leftAt;
34
+ }
35
+ else if (left.startedAt !== right.startedAt) {
36
+ return left.startedAt > right.startedAt ? -1 : 1;
37
+ }
38
+ if (left.relationshipId === right.relationshipId)
39
+ return 0;
40
+ return left.relationshipId < right.relationshipId ? -1 : 1;
41
+ }
42
+ /**
43
+ * Stream your copy trading follows over the authenticated `copy` channel.
44
+ * The subscription runs only while a user is signed in: it starts on login,
45
+ * and logout (or an account switch) clears the follows and skips.
46
+ *
47
+ * The subscribe-time snapshot seeds your live follows (`ACTIVE` or
48
+ * `CLOSE_ONLY`); each `copy_relationship_update` frame then carries a follow's
49
+ * FULL state and replaces its entry when its `version` is not older than the
50
+ * stored one. A later snapshot (after a reconnect) REPLACES the set
51
+ * wholesale, so a follow that stopped while the socket was down leaves it.
52
+ *
53
+ * `copy_skip` frames (leader fills a follow could not mirror) collect in
54
+ * `recentSkips`.
55
+ *
56
+ * `cumNetRealized` and `hwm` on each follow are its profit-share basis, not
57
+ * the copy account's balance; read the copy risk bucket (`copyRiskBucketId`)
58
+ * for that.
59
+ */
60
+ export function useCopyEvents() {
61
+ const { sdk } = useMonacoSDK();
62
+ const { authenticationStatus } = useMonacoContext();
63
+ const ws = sdk?.ws;
64
+ // The channel is private, and login/logout leave `sdk.ws` the same object:
65
+ // a subscribe sent before login is refused and never resent, and logout
66
+ // tears down the client's handlers. Scope the subscription to the signed-in
67
+ // user, derived reactively from the provider's status, so it starts on
68
+ // login, stops (and clears) on logout, and restarts on an account switch.
69
+ const userId = authenticationStatus === AuthenticationStatus.AUTHENTICATED ? sdk?.getAuthState()?.user.id : undefined;
70
+ const [subscription, setSubscription] = useState(IDLE);
71
+ const inScope = subscription.source === ws && subscription.userId === userId && userId !== undefined;
72
+ const clearError = useCallback(() => {
73
+ setSubscription((prev) => (prev.error === null ? prev : { ...prev, error: null }));
74
+ }, []);
75
+ useEffect(() => {
76
+ if (!ws || !userId) {
77
+ setSubscription(IDLE);
78
+ return;
79
+ }
80
+ const belongsToThisSubscription = (prev) => prev.source === ws && prev.userId === userId;
81
+ setSubscription({ source: ws, userId, byId: {}, skips: [], subscribed: false, error: null });
82
+ let unsubscribe;
83
+ try {
84
+ unsubscribe = ws.copy((event) => {
85
+ setSubscription((prev) => {
86
+ if (!belongsToThisSubscription(prev))
87
+ return prev;
88
+ if (event.eventType === "copy_skip") {
89
+ return { ...prev, skips: [event.data, ...prev.skips].slice(0, MAX_RECENT_COPY_SKIPS) };
90
+ }
91
+ const id = event.data.relationshipId;
92
+ if (!supersedes(event.data, prev.byId[id]))
93
+ return prev;
94
+ return { ...prev, byId: { ...prev.byId, [id]: event.data } };
95
+ });
96
+ }, (items) => {
97
+ const byId = Object.fromEntries(items.map((item) => [item.relationshipId, item]));
98
+ setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, byId } : prev));
99
+ });
100
+ setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, subscribed: true } : prev));
101
+ }
102
+ catch (err) {
103
+ const error = err instanceof Error ? err : new Error(String(err));
104
+ setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, subscribed: false, error } : prev));
105
+ }
106
+ return () => {
107
+ unsubscribe?.();
108
+ setSubscription((prev) => (belongsToThisSubscription(prev) ? { ...prev, subscribed: false } : prev));
109
+ };
110
+ }, [ws, userId]);
111
+ const relationshipsById = inScope ? subscription.byId : NO_RELATIONSHIPS;
112
+ const relationships = useMemo(() => Object.values(relationshipsById).sort(newestFirst), [relationshipsById]);
113
+ return {
114
+ relationships,
115
+ relationshipsById,
116
+ recentSkips: inScope ? subscription.skips : NO_SKIPS,
117
+ subscribed: inScope && subscription.subscribed,
118
+ error: inScope ? subscription.error : null,
119
+ clearError,
120
+ };
121
+ }
@@ -0,0 +1,2 @@
1
+ export type { UseCopyTradingReturn } from "./types";
2
+ export { useCopyTrading } from "./useCopyTrading";
@@ -0,0 +1 @@
1
+ export { useCopyTrading } from "./useCopyTrading";
@@ -0,0 +1,6 @@
1
+ import type { CopyTradingAPI } from "@0xmonaco/types";
2
+ /**
3
+ * Return type of {@link useCopyTrading}: the copy trading client methods, each
4
+ * with the same signature and documentation as `sdk.copyTrading`.
5
+ */
6
+ export type UseCopyTradingReturn = Pick<CopyTradingAPI, "listLeadTraders" | "getLeadTrader" | "listLeadTraderClosedTrades" | "listLeadTraderOpenPositions" | "upsertLeadTrader" | "getMyLeadTrader" | "listMyFollowers" | "upsertFollow" | "stopFollow" | "listMyFollows" | "getFollow" | "listFollowLeaderPositions" | "previewFollow">;
File without changes
@@ -0,0 +1,22 @@
1
+ import type { UseCopyTradingReturn } from "./types";
2
+ /**
3
+ * Copy trading: the public leaderboard and lead trader profiles, your own lead
4
+ * trader profile and followers, and your follows.
5
+ *
6
+ * Every method delegates to `sdk.copyTrading` (see `@0xmonaco/core` for the
7
+ * request contract). The leaderboard, `getLeadTrader` and
8
+ * `listLeadTraderClosedTrades` need no session; everything else is signed and
9
+ * scoped to the authenticated caller. Each method throws when the SDK is not
10
+ * available yet.
11
+ *
12
+ * For live follow state (status changes and skipped mirrors), use
13
+ * {@link useCopyEvents}.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * const { listLeadTraders, upsertFollow } = useCopyTrading();
18
+ * const { leaders } = await listLeadTraders({ sort: "roi", window: "30d" });
19
+ * await upsertFollow({ leader: "alpha_trader", allocation: "1000", slPct: "0.25" });
20
+ * ```
21
+ */
22
+ export declare const useCopyTrading: () => UseCopyTradingReturn;
@@ -0,0 +1,58 @@
1
+ import { useCallback } from "react";
2
+ import { useMonacoSDK } from "../useMonaco";
3
+ /**
4
+ * Copy trading: the public leaderboard and lead trader profiles, your own lead
5
+ * trader profile and followers, and your follows.
6
+ *
7
+ * Every method delegates to `sdk.copyTrading` (see `@0xmonaco/core` for the
8
+ * request contract). The leaderboard, `getLeadTrader` and
9
+ * `listLeadTraderClosedTrades` need no session; everything else is signed and
10
+ * scoped to the authenticated caller. Each method throws when the SDK is not
11
+ * available yet.
12
+ *
13
+ * For live follow state (status changes and skipped mirrors), use
14
+ * {@link useCopyEvents}.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const { listLeadTraders, upsertFollow } = useCopyTrading();
19
+ * const { leaders } = await listLeadTraders({ sort: "roi", window: "30d" });
20
+ * await upsertFollow({ leader: "alpha_trader", allocation: "1000", slPct: "0.25" });
21
+ * ```
22
+ */
23
+ export const useCopyTrading = () => {
24
+ const { sdk } = useMonacoSDK();
25
+ const client = useCallback(() => {
26
+ if (!sdk)
27
+ throw new Error("SDK not available");
28
+ return sdk.copyTrading;
29
+ }, [sdk]);
30
+ const listLeadTraders = useCallback(async (params) => await client().listLeadTraders(params), [client]);
31
+ const getLeadTrader = useCallback(async (handle) => await client().getLeadTrader(handle), [client]);
32
+ const listLeadTraderClosedTrades = useCallback(async (handle, params) => await client().listLeadTraderClosedTrades(handle, params), [client]);
33
+ const listLeadTraderOpenPositions = useCallback(async (handle) => await client().listLeadTraderOpenPositions(handle), [client]);
34
+ const upsertLeadTrader = useCallback(async (params) => await client().upsertLeadTrader(params), [client]);
35
+ const getMyLeadTrader = useCallback(async () => await client().getMyLeadTrader(), [client]);
36
+ const listMyFollowers = useCallback(async (params) => await client().listMyFollowers(params), [client]);
37
+ const upsertFollow = useCallback(async (params) => await client().upsertFollow(params), [client]);
38
+ const stopFollow = useCallback(async (followId) => await client().stopFollow(followId), [client]);
39
+ const listMyFollows = useCallback(async (params) => await client().listMyFollows(params), [client]);
40
+ const getFollow = useCallback(async (followId) => await client().getFollow(followId), [client]);
41
+ const previewFollow = useCallback(async (params) => await client().previewFollow(params), [client]);
42
+ const listFollowLeaderPositions = useCallback(async (followId) => await client().listFollowLeaderPositions(followId), [client]);
43
+ return {
44
+ listLeadTraders,
45
+ getLeadTrader,
46
+ listLeadTraderClosedTrades,
47
+ listLeadTraderOpenPositions,
48
+ upsertLeadTrader,
49
+ getMyLeadTrader,
50
+ listMyFollowers,
51
+ upsertFollow,
52
+ stopFollow,
53
+ listMyFollows,
54
+ getFollow,
55
+ listFollowLeaderPositions,
56
+ previewFollow,
57
+ };
58
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xmonaco/react",
3
- "version": "1.0.69",
3
+ "version": "1.0.70",
4
4
  "type": "module",
5
5
  "homepage": "https://docs.0xmonaco.com/sdk/typescript",
6
6
  "main": "./dist/index.js",
@@ -16,8 +16,8 @@
16
16
  "lint": "biome lint ."
17
17
  },
18
18
  "dependencies": {
19
- "@0xmonaco/core": "1.0.69",
20
- "@0xmonaco/types": "1.0.69"
19
+ "@0xmonaco/core": "1.0.70",
20
+ "@0xmonaco/types": "1.0.70"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/react": "^19.2.18",