@n1xyz/nord-ts 0.0.18-8121ed05.0 → 0.0.19
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/.claude/settings.local.json +11 -0
- package/.local/qa.ts +77 -0
- package/.local/test-atomic.ts +112 -0
- package/check.sh +4 -0
- package/default.nix +47 -0
- package/package.json +20 -27
- package/src/index.ts +0 -16
- package/src/nord/api/actions.ts +131 -9
- package/src/nord/api/core.ts +0 -71
- package/src/nord/client/Nord.ts +142 -76
- package/src/nord/client/NordUser.ts +171 -50
- package/src/nord/index.ts +0 -2
- package/src/nord/models/Subscriber.ts +2 -2
- package/src/types.ts +55 -216
- package/src/utils.ts +6 -42
- package/src/websocket/NordWebSocketClient.ts +23 -15
- package/src/websocket/index.ts +1 -1
- package/tests/utils.spec.ts +1 -34
- package/dist/bridge/client.d.ts +0 -151
- package/dist/bridge/client.js +0 -434
- package/dist/bridge/const.d.ts +0 -23
- package/dist/bridge/const.js +0 -47
- package/dist/bridge/index.d.ts +0 -4
- package/dist/bridge/index.js +0 -23
- package/dist/bridge/types.d.ts +0 -120
- package/dist/bridge/types.js +0 -18
- package/dist/bridge/utils.d.ts +0 -64
- package/dist/bridge/utils.js +0 -131
- package/dist/const.d.ts +0 -8
- package/dist/const.js +0 -30
- package/dist/gen/common.d.ts +0 -68
- package/dist/gen/common.js +0 -215
- package/dist/gen/nord.d.ts +0 -853
- package/dist/gen/nord.js +0 -6368
- package/dist/idl/bridge.d.ts +0 -569
- package/dist/idl/bridge.js +0 -8
- package/dist/idl/bridge.json +0 -1506
- package/dist/idl/index.d.ts +0 -607
- package/dist/idl/index.js +0 -8
- package/dist/index.d.ts +0 -6
- package/dist/index.js +0 -30
- package/dist/nord/api/actions.d.ts +0 -106
- package/dist/nord/api/actions.js +0 -256
- package/dist/nord/api/core.d.ts +0 -49
- package/dist/nord/api/core.js +0 -164
- package/dist/nord/api/market.d.ts +0 -36
- package/dist/nord/api/market.js +0 -96
- package/dist/nord/api/metrics.d.ts +0 -67
- package/dist/nord/api/metrics.js +0 -229
- package/dist/nord/api/queries.d.ts +0 -46
- package/dist/nord/api/queries.js +0 -109
- package/dist/nord/client/Nord.d.ts +0 -284
- package/dist/nord/client/Nord.js +0 -491
- package/dist/nord/client/NordUser.d.ts +0 -287
- package/dist/nord/client/NordUser.js +0 -595
- package/dist/nord/index.d.ts +0 -9
- package/dist/nord/index.js +0 -33
- package/dist/nord/models/Subscriber.d.ts +0 -37
- package/dist/nord/models/Subscriber.js +0 -25
- package/dist/nord/utils/NordError.d.ts +0 -35
- package/dist/nord/utils/NordError.js +0 -49
- package/dist/types.d.ts +0 -407
- package/dist/types.js +0 -103
- package/dist/utils.d.ts +0 -116
- package/dist/utils.js +0 -271
- package/dist/websocket/NordWebSocketClient.d.ts +0 -68
- package/dist/websocket/NordWebSocketClient.js +0 -338
- package/dist/websocket/events.d.ts +0 -19
- package/dist/websocket/events.js +0 -2
- package/dist/websocket/index.d.ts +0 -2
- package/dist/websocket/index.js +0 -5
- package/jest.config.ts +0 -9
- package/nodemon.json +0 -4
- package/protoc-generate.sh +0 -23
- package/src/idl/bridge.json +0 -1506
- package/src/nord/api/market.ts +0 -122
- package/src/nord/api/queries.ts +0 -135
package/.local/qa.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// quick script i use to qa sdk against deployed instance. this
|
|
2
|
+
// will no longer be needed once we have proper integration tests.
|
|
3
|
+
|
|
4
|
+
import { Nord, NordUser } from "../src";
|
|
5
|
+
|
|
6
|
+
const nord = await Nord.initNord({
|
|
7
|
+
bridgeVk: "rbrpQMMPaVLSG3hTPZj2GLKxGLbPM2q7gDxc7BTync6",
|
|
8
|
+
initWebSockets: false,
|
|
9
|
+
solanaUrl: "https://api.devnet.solana.com",
|
|
10
|
+
webServerUrl: "https://zo-devnet-rc.n1.xyz",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const poky = await nord.getUser({
|
|
14
|
+
pubkey: "GSPX6qX4bs8oTU3n5A1Xx8q9MPiGFyxWhMtuVvpZNK52",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
console.log("user", poky);
|
|
18
|
+
|
|
19
|
+
if (!poky) {
|
|
20
|
+
throw new Error("No user found");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log("account", await nord.getAccount(poky.accountIds[0]));
|
|
24
|
+
console.log("timestamp", await nord.getTimestamp());
|
|
25
|
+
console.log("actionNonce", await nord.getActionNonce());
|
|
26
|
+
console.log("lastActionId", await nord.getLastActionId());
|
|
27
|
+
console.log("info", await nord.getInfo());
|
|
28
|
+
|
|
29
|
+
console.log(
|
|
30
|
+
"marketStats",
|
|
31
|
+
await nord.getMarketStats({
|
|
32
|
+
marketId: 1,
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
console.log(
|
|
37
|
+
"orderbook",
|
|
38
|
+
await nord.getOrderbook({
|
|
39
|
+
market_id: 1,
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const since = new Date();
|
|
44
|
+
since.setMinutes(0);
|
|
45
|
+
|
|
46
|
+
console.log(
|
|
47
|
+
"trades",
|
|
48
|
+
await nord.getTrades({
|
|
49
|
+
takerId: poky.accountIds[0],
|
|
50
|
+
sinceRcf3339: since.toISOString(),
|
|
51
|
+
untilRfc3339: new Date().toISOString(),
|
|
52
|
+
pageId: "0",
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
console.log(
|
|
57
|
+
"recentActions",
|
|
58
|
+
await nord.queryRecentActions({
|
|
59
|
+
from: 6,
|
|
60
|
+
to: 6,
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
console.log("action", await nord.queryAction({ action_id: 0 }));
|
|
65
|
+
console.log("action", await nord.queryAction({ action_id: 1 }));
|
|
66
|
+
console.log("action", await nord.queryAction({ action_id: 2 }));
|
|
67
|
+
console.log("action", await nord.queryAction({ action_id: 5 }));
|
|
68
|
+
console.log("action", await nord.queryAction({ action_id: 100 }));
|
|
69
|
+
|
|
70
|
+
const user = NordUser.fromPrivateKey(
|
|
71
|
+
nord,
|
|
72
|
+
new Uint8Array(JSON.parse(process.env.PRIVATE_KEY!)),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
console.log("deposit", await user.deposit({ amount: 2.0, tokenId: 0 }));
|
|
76
|
+
await user.refreshSession();
|
|
77
|
+
console.log("withdraw", await user.withdraw({ amount: 2.0, tokenId: 0 }));
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { NordWebSocketClient } from "../src/websocket/NordWebSocketClient";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* All event types that we track in the counters object. Keeping this
|
|
5
|
+
* union type explicit lets TypeScript validate that we use the same
|
|
6
|
+
* keys everywhere and prevents typos.
|
|
7
|
+
*/
|
|
8
|
+
export type CounterKeys =
|
|
9
|
+
| "trade"
|
|
10
|
+
| "delta"
|
|
11
|
+
| "account"
|
|
12
|
+
| "error"
|
|
13
|
+
| "disconnected";
|
|
14
|
+
|
|
15
|
+
interface Endpoint {
|
|
16
|
+
/** WebSocket endpoint URL */
|
|
17
|
+
url: string;
|
|
18
|
+
/** Streams we want to subscribe to once connected (can be empty) */
|
|
19
|
+
streams: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Web‑socket endpoints to test. Keep this list short & focused so the smoke
|
|
24
|
+
* test finishes quickly inside CI.
|
|
25
|
+
*/
|
|
26
|
+
const endpoints: Endpoint[] = [
|
|
27
|
+
// {
|
|
28
|
+
// url: "wss://staging.n1.xyz/ws/",
|
|
29
|
+
// streams: [], // no runtime subscribe call needed
|
|
30
|
+
// },
|
|
31
|
+
|
|
32
|
+
// {
|
|
33
|
+
// url: `wss://staging.n1.xyz/ws/account@${process.env.NORD_ACCOUNT_ID ?? "3"}`,
|
|
34
|
+
// streams: [],
|
|
35
|
+
// },
|
|
36
|
+
|
|
37
|
+
{
|
|
38
|
+
url: `wss://zo-devnet-rc.n1.xyz/ws/trades@ETHPERP`,
|
|
39
|
+
streams: ["trades@ETHPERP"],
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
/** How long each endpoint should be observed for (default: 15 s) */
|
|
44
|
+
// const TEST_DURATION_MS = Number(process.env.TEST_DURATION_MS ?? 15_000);
|
|
45
|
+
const TEST_DURATION_MS = 15_000;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Simple message counters for every event type the client can emit.
|
|
49
|
+
* This is mutated by the event handlers during the test run.
|
|
50
|
+
*/
|
|
51
|
+
const counters: Record<CounterKeys, number> = {
|
|
52
|
+
trade: 0,
|
|
53
|
+
delta: 0,
|
|
54
|
+
account: 0,
|
|
55
|
+
error: 0,
|
|
56
|
+
disconnected: 0,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Connect to a single endpoint, wait for activity, then verify we received the
|
|
61
|
+
* expected events.
|
|
62
|
+
*/
|
|
63
|
+
async function smokeTest(endpoint: Endpoint): Promise<void> {
|
|
64
|
+
console.log(`🛰️ Connecting to ${endpoint.url} …`);
|
|
65
|
+
const client = new NordWebSocketClient(endpoint.url);
|
|
66
|
+
client.connect();
|
|
67
|
+
|
|
68
|
+
client.on("connected", () => {
|
|
69
|
+
console.log("✅ connected");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
client.on("disconnected", () => {
|
|
73
|
+
console.log("⚠️ disconnected");
|
|
74
|
+
counters.disconnected++;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
client.on("error", (err: unknown) => {
|
|
78
|
+
console.error("❌ error:", err);
|
|
79
|
+
counters.error++;
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
client.on("trades", (m) => {
|
|
83
|
+
console.log({ m });
|
|
84
|
+
counters.trade++;
|
|
85
|
+
});
|
|
86
|
+
client.on("deltas", (m) => {
|
|
87
|
+
console.log({ m });
|
|
88
|
+
counters.delta++;
|
|
89
|
+
});
|
|
90
|
+
client.on("accounts", (m) => {
|
|
91
|
+
console.log({ m });
|
|
92
|
+
counters.account++;
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Wait a bit, then close and report
|
|
96
|
+
await new Promise((resolve) => setTimeout(resolve, TEST_DURATION_MS));
|
|
97
|
+
client.close();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
(async () => {
|
|
101
|
+
try {
|
|
102
|
+
for (const ep of endpoints) {
|
|
103
|
+
await smokeTest(ep);
|
|
104
|
+
}
|
|
105
|
+
console.log("\n🎉 All websocket endpoints responded as expected:");
|
|
106
|
+
console.table(counters);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
console.error("\n🔴 Smoke test failed:", (e as Error).message);
|
|
109
|
+
console.table(counters);
|
|
110
|
+
process.exitCode = 1;
|
|
111
|
+
}
|
|
112
|
+
})();
|
package/check.sh
ADDED
package/default.nix
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{ inputs, ... }:
|
|
2
|
+
{
|
|
3
|
+
perSystem =
|
|
4
|
+
{
|
|
5
|
+
pkgs,
|
|
6
|
+
self',
|
|
7
|
+
system,
|
|
8
|
+
...
|
|
9
|
+
}:
|
|
10
|
+
{
|
|
11
|
+
packages = {
|
|
12
|
+
nord-ts-proto =
|
|
13
|
+
let
|
|
14
|
+
ts-proto = inputs.n1.packages.${system}.ts-proto;
|
|
15
|
+
in
|
|
16
|
+
pkgs.writeShellApplication {
|
|
17
|
+
name = "nord-ts-proto";
|
|
18
|
+
runtimeInputs = [ pkgs.protobuf ];
|
|
19
|
+
text = ''
|
|
20
|
+
if [ $# -ne 2 ]; then
|
|
21
|
+
echo >&2 "usage: $(basename "$0") <proto-file> <out-dir>"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
readonly PROTO_FILE="$1"
|
|
25
|
+
readonly OUT_DIR="$2"
|
|
26
|
+
protoc \
|
|
27
|
+
--plugin="${ts-proto}/bin/protoc-gen-ts_proto" \
|
|
28
|
+
--ts_proto_opt=forceLong=bigint \
|
|
29
|
+
--ts_proto_opt=esModuleInterop=true \
|
|
30
|
+
--ts_proto_opt=oneof=unions-value \
|
|
31
|
+
--ts_proto_opt=unrecognizedEnum=false \
|
|
32
|
+
--ts_proto_out="$OUT_DIR" \
|
|
33
|
+
--proto_path="$(dirname "$PROTO_FILE")" \
|
|
34
|
+
"$PROTO_FILE"
|
|
35
|
+
'';
|
|
36
|
+
};
|
|
37
|
+
nord-ts-check = pkgs.writeShellApplication {
|
|
38
|
+
name = "ts-check";
|
|
39
|
+
runtimeInputs = [
|
|
40
|
+
pkgs.protobuf
|
|
41
|
+
pkgs.pnpm
|
|
42
|
+
];
|
|
43
|
+
text = builtins.readFile ./check.sh;
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@n1xyz/nord-ts",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"description": "Typescript for Nord",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"types": "dist/index.d.ts",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/n1xyz/nord.git"
|
|
10
|
-
},
|
|
11
5
|
"keywords": [],
|
|
12
6
|
"author": "",
|
|
13
7
|
"license": "ISC",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"gen:proto": "nix run ..#nord-ts-proto ../engine/nord.proto src/gen/",
|
|
12
|
+
"gen:api": "nix run ..#nord-openapi | bunx openapi-typescript > src/gen/openapi.ts",
|
|
13
|
+
"gen": "bun run gen:proto && bun run gen:api",
|
|
14
|
+
"build": "bun run gen && bunx tsc && bun run docs",
|
|
15
|
+
"docs": "bunx typedoc --out ./docs ./src",
|
|
16
|
+
"compile": "tsc",
|
|
17
|
+
"clean": "rm -rf ./src/gen ./dist ./docs",
|
|
18
|
+
"fmt": "prettier --write src tests",
|
|
19
|
+
"ci": "bunx eslint && prettier --check src tests"
|
|
20
|
+
},
|
|
14
21
|
"devDependencies": {
|
|
15
|
-
"@
|
|
16
|
-
"@jest/types": "^29.6.3",
|
|
22
|
+
"@types/bun": "latest",
|
|
17
23
|
"@types/google-protobuf": "^3.15.10",
|
|
18
|
-
"@types/jest": "^29.5.12",
|
|
19
24
|
"@types/node": "^22.15.29",
|
|
20
25
|
"@types/node-fetch": "^2.6.9",
|
|
21
26
|
"@types/ws": "^8.5.10",
|
|
@@ -23,16 +28,15 @@
|
|
|
23
28
|
"@typescript-eslint/parser": "^8.26.1",
|
|
24
29
|
"eslint": "^9.22.0",
|
|
25
30
|
"globals": "^16.0.0",
|
|
26
|
-
"
|
|
31
|
+
"openapi-typescript": "^7.8.0",
|
|
27
32
|
"prettier": "^3.5.3",
|
|
28
|
-
"ts-jest": "^29.1.4",
|
|
29
33
|
"ts-node": "^10.9.2",
|
|
30
|
-
"ts-proto": "^2.7.0",
|
|
31
34
|
"typedoc": "^0.27.9",
|
|
32
35
|
"typescript": "*"
|
|
33
36
|
},
|
|
34
37
|
"dependencies": {
|
|
35
38
|
"@bufbuild/protobuf": "^2.0.0",
|
|
39
|
+
"@n1xyz/proton": "0.0.0",
|
|
36
40
|
"@noble/curves": "^1.3.0",
|
|
37
41
|
"@noble/ed25519": "^2.2.3",
|
|
38
42
|
"@noble/hashes": "^1.3.2",
|
|
@@ -42,19 +46,8 @@
|
|
|
42
46
|
"decimal.js": "^10.4.3",
|
|
43
47
|
"ethers": "^6.11.1",
|
|
44
48
|
"node-fetch": "2.6.13",
|
|
49
|
+
"openapi-fetch": "^0.14.0",
|
|
45
50
|
"tweetnacl": "^1.0.3",
|
|
46
|
-
"ws": "^8.16.0"
|
|
47
|
-
"@n1xyz/proton": "0.0.1-8121ed05.0"
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"dev": "nodemon --exec npx tsc",
|
|
51
|
-
"build:proto": "./protoc-generate.sh",
|
|
52
|
-
"build": "pnpm run build:proto && pnpm exec tsc && pnpm run docs",
|
|
53
|
-
"test": "pnpm exec jest",
|
|
54
|
-
"docs": "pnpm exec typedoc --out ./docs ./src",
|
|
55
|
-
"compile": "tsc",
|
|
56
|
-
"clean": "rm -rf ./src/gen ./dist ./docs",
|
|
57
|
-
"fmt": "prettier --write src tests",
|
|
58
|
-
"ci": "eslint && prettier --check src tests"
|
|
51
|
+
"ws": "^8.16.0"
|
|
59
52
|
}
|
|
60
|
-
}
|
|
53
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,21 +1,5 @@
|
|
|
1
|
-
// Export types
|
|
2
1
|
export * from "./types";
|
|
3
2
|
export * from "./utils";
|
|
4
3
|
export * from "./const";
|
|
5
|
-
|
|
6
|
-
// Export Nord modules
|
|
7
4
|
export * from "./nord";
|
|
8
|
-
|
|
9
|
-
// Export WebSocket modules
|
|
10
5
|
export * from "./websocket/index";
|
|
11
|
-
|
|
12
|
-
// Re-export proton bridge functionality for backward compatibility
|
|
13
|
-
export {
|
|
14
|
-
ProtonClient,
|
|
15
|
-
buildDepositTx,
|
|
16
|
-
createTokenAccountIfNeeded,
|
|
17
|
-
type DepositSplParams,
|
|
18
|
-
type WithdrawalParams,
|
|
19
|
-
type WithdrawalClaim,
|
|
20
|
-
type SPLTokenInfo,
|
|
21
|
-
} from "@n1xyz/proton";
|
package/src/nord/api/actions.ts
CHANGED
|
@@ -3,15 +3,12 @@ import * as proto from "../../gen/nord";
|
|
|
3
3
|
import { FillMode, fillModeToProtoFillMode, KeyType, Side } from "../../types";
|
|
4
4
|
import {
|
|
5
5
|
assert,
|
|
6
|
-
bigIntToProtoU128,
|
|
7
6
|
BigIntValue,
|
|
8
7
|
checkedFetch,
|
|
9
8
|
checkPubKeyLength,
|
|
10
9
|
decodeLengthDelimited,
|
|
11
10
|
encodeLengthDelimited,
|
|
12
|
-
optMap,
|
|
13
11
|
SESSION_TTL,
|
|
14
|
-
toScaledU128,
|
|
15
12
|
toScaledU64,
|
|
16
13
|
} from "../../utils";
|
|
17
14
|
|
|
@@ -35,6 +32,7 @@ function makeSendHttp(
|
|
|
35
32
|
serverUrl: string,
|
|
36
33
|
): (encoded: Uint8Array) => Promise<Uint8Array> {
|
|
37
34
|
return async (body) => {
|
|
35
|
+
// TODO: this should be changed to use openapi
|
|
38
36
|
const response = await checkedFetch(`${serverUrl}/action`, {
|
|
39
37
|
method: "POST",
|
|
40
38
|
headers: {
|
|
@@ -271,15 +269,17 @@ async function placeOrderImpl(
|
|
|
271
269
|
// NOTE: if `size` equals 1.0, it will sell whole unit, for example 1.0 BTC
|
|
272
270
|
size?: Decimal.Value;
|
|
273
271
|
price?: Decimal.Value;
|
|
274
|
-
|
|
272
|
+
quoteSizeSize?: Decimal.Value;
|
|
273
|
+
quoteSizePrice?: Decimal.Value;
|
|
275
274
|
clientOrderId?: BigIntValue;
|
|
276
275
|
},
|
|
277
276
|
): Promise<bigint | undefined> {
|
|
278
277
|
const price = toScaledU64(params.price ?? 0, params.priceDecimals);
|
|
279
278
|
const size = toScaledU64(params.size ?? 0, params.sizeDecimals);
|
|
280
|
-
const quoteSize =
|
|
281
|
-
|
|
282
|
-
params.
|
|
279
|
+
const quoteSize = toScaledU64(params.quoteSizeSize ?? 0, params.sizeDecimals);
|
|
280
|
+
const quotePrice = toScaledU64(
|
|
281
|
+
params.quoteSizePrice ?? 0,
|
|
282
|
+
params.priceDecimals,
|
|
283
283
|
);
|
|
284
284
|
|
|
285
285
|
// Compose action object
|
|
@@ -297,8 +297,11 @@ async function placeOrderImpl(
|
|
|
297
297
|
isReduceOnly: params.isReduceOnly,
|
|
298
298
|
price,
|
|
299
299
|
size,
|
|
300
|
-
quoteSize:
|
|
301
|
-
clientOrderId:
|
|
300
|
+
quoteSize: { size: quoteSize, price: quotePrice },
|
|
301
|
+
clientOrderId:
|
|
302
|
+
params.clientOrderId === undefined
|
|
303
|
+
? undefined
|
|
304
|
+
: BigInt(params.clientOrderId),
|
|
302
305
|
delegatorAccountId: params.liquidateeId,
|
|
303
306
|
},
|
|
304
307
|
},
|
|
@@ -478,6 +481,124 @@ export async function transfer(
|
|
|
478
481
|
params,
|
|
479
482
|
);
|
|
480
483
|
}
|
|
484
|
+
|
|
485
|
+
export type AtomicSubaction =
|
|
486
|
+
| {
|
|
487
|
+
kind: "place";
|
|
488
|
+
// Market and order parameters – identical semantics to placeOrder()
|
|
489
|
+
marketId: number;
|
|
490
|
+
side: Side;
|
|
491
|
+
fillMode: FillMode;
|
|
492
|
+
isReduceOnly: boolean;
|
|
493
|
+
// decimals for scaling
|
|
494
|
+
sizeDecimals: number;
|
|
495
|
+
priceDecimals: number;
|
|
496
|
+
// at least one of the three has to be specified; 0 treated as "not set"
|
|
497
|
+
size?: Decimal.Value;
|
|
498
|
+
price?: Decimal.Value;
|
|
499
|
+
quoteSizeSize?: Decimal.Value;
|
|
500
|
+
quoteSizePrice?: Decimal.Value;
|
|
501
|
+
clientOrderId?: BigIntValue;
|
|
502
|
+
}
|
|
503
|
+
| {
|
|
504
|
+
kind: "cancel";
|
|
505
|
+
orderId: BigIntValue;
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
async function atomicImpl(
|
|
509
|
+
sendFn: (encoded: Uint8Array) => Promise<Uint8Array>,
|
|
510
|
+
signFn: (message: Uint8Array) => Promise<Uint8Array>,
|
|
511
|
+
currentTimestamp: bigint,
|
|
512
|
+
nonce: number,
|
|
513
|
+
params: {
|
|
514
|
+
sessionId: BigIntValue;
|
|
515
|
+
accountId?: number;
|
|
516
|
+
actions: AtomicSubaction[];
|
|
517
|
+
},
|
|
518
|
+
): Promise<proto.Receipt_AtomicResult> {
|
|
519
|
+
assert(
|
|
520
|
+
params.actions.length > 0 && params.actions.length <= 4,
|
|
521
|
+
"Atomic action must contain between 1 and 4 sub-actions",
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
const subactions: proto.AtomicSubactionKind[] = params.actions.map((a) => {
|
|
525
|
+
if (a.kind === "place") {
|
|
526
|
+
const price = toScaledU64(a.price ?? 0, a.priceDecimals);
|
|
527
|
+
const size = toScaledU64(a.size ?? 0, a.sizeDecimals);
|
|
528
|
+
const quoteSizeSize = toScaledU64(a.quoteSizeSize ?? 0, a.sizeDecimals);
|
|
529
|
+
const quoteSizePrice = toScaledU64(
|
|
530
|
+
a.quoteSizePrice ?? 0,
|
|
531
|
+
a.priceDecimals,
|
|
532
|
+
);
|
|
533
|
+
const tradeOrPlace: proto.TradeOrPlace = {
|
|
534
|
+
marketId: a.marketId,
|
|
535
|
+
orderType: {
|
|
536
|
+
side: a.side === Side.Bid ? proto.Side.BID : proto.Side.ASK,
|
|
537
|
+
fillMode: fillModeToProtoFillMode(a.fillMode),
|
|
538
|
+
isReduceOnly: a.isReduceOnly,
|
|
539
|
+
},
|
|
540
|
+
limit: {
|
|
541
|
+
price,
|
|
542
|
+
size,
|
|
543
|
+
quoteSize: { size: quoteSizeSize, price: quoteSizePrice },
|
|
544
|
+
},
|
|
545
|
+
clientOrderId:
|
|
546
|
+
a.clientOrderId === undefined ? undefined : BigInt(a.clientOrderId),
|
|
547
|
+
};
|
|
548
|
+
return {
|
|
549
|
+
inner: { $case: "tradeOrPlace", value: tradeOrPlace },
|
|
550
|
+
} as proto.AtomicSubactionKind;
|
|
551
|
+
}
|
|
552
|
+
return {
|
|
553
|
+
inner: { $case: "cancelOrder", value: { orderId: BigInt(a.orderId) } },
|
|
554
|
+
} as proto.AtomicSubactionKind;
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const action: proto.Action = {
|
|
558
|
+
currentTimestamp,
|
|
559
|
+
nonce,
|
|
560
|
+
kind: {
|
|
561
|
+
$case: "atomic",
|
|
562
|
+
value: {
|
|
563
|
+
sessionId: BigInt(params.sessionId),
|
|
564
|
+
accountId: params.accountId, // optional
|
|
565
|
+
actions: subactions,
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
const resp = await sendAction(
|
|
571
|
+
sendFn,
|
|
572
|
+
(m) => sessionSign(signFn, m),
|
|
573
|
+
action,
|
|
574
|
+
"execute atomic action",
|
|
575
|
+
);
|
|
576
|
+
if (resp.kind?.$case === "atomic") {
|
|
577
|
+
return resp.kind.value;
|
|
578
|
+
}
|
|
579
|
+
throw new Error(`Unexpected receipt kind ${resp.kind?.$case}`);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export async function atomic(
|
|
583
|
+
serverUrl: string,
|
|
584
|
+
signFn: (message: Uint8Array) => Promise<Uint8Array>,
|
|
585
|
+
currentTimestamp: bigint,
|
|
586
|
+
nonce: number,
|
|
587
|
+
params: {
|
|
588
|
+
sessionId: BigIntValue;
|
|
589
|
+
accountId?: number;
|
|
590
|
+
actions: AtomicSubaction[];
|
|
591
|
+
},
|
|
592
|
+
): Promise<proto.Receipt_AtomicResult> {
|
|
593
|
+
return atomicImpl(
|
|
594
|
+
makeSendHttp(serverUrl),
|
|
595
|
+
signFn,
|
|
596
|
+
currentTimestamp,
|
|
597
|
+
nonce,
|
|
598
|
+
params,
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
|
|
481
602
|
/**
|
|
482
603
|
* For testing purposes
|
|
483
604
|
*/
|
|
@@ -488,4 +609,5 @@ export const _private = {
|
|
|
488
609
|
placeOrderImpl,
|
|
489
610
|
cancelOrderImpl,
|
|
490
611
|
transferImpl,
|
|
612
|
+
atomicImpl,
|
|
491
613
|
};
|
package/src/nord/api/core.ts
CHANGED
|
@@ -3,77 +3,6 @@ import { checkedFetch } from "../../utils";
|
|
|
3
3
|
import { NordWebSocketClient } from "../../websocket/index";
|
|
4
4
|
import { NordError } from "../utils/NordError";
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* Get the current timestamp from the Nord server
|
|
8
|
-
*
|
|
9
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
10
|
-
* @returns Current timestamp as a bigint
|
|
11
|
-
* @throws {NordError} If the request fails
|
|
12
|
-
*/
|
|
13
|
-
export async function getTimestamp(webServerUrl: string): Promise<bigint> {
|
|
14
|
-
try {
|
|
15
|
-
const response = await checkedFetch(`${webServerUrl}/timestamp`);
|
|
16
|
-
return BigInt(await response.json());
|
|
17
|
-
} catch (error) {
|
|
18
|
-
throw new NordError("Failed to get timestamp", { cause: error });
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get the next action nonce from the Nord server
|
|
24
|
-
*
|
|
25
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
26
|
-
* @returns Next action nonce
|
|
27
|
-
* @throws {NordError} If the request fails
|
|
28
|
-
*/
|
|
29
|
-
export async function getLastEventNonce(webServerUrl: string): Promise<number> {
|
|
30
|
-
try {
|
|
31
|
-
const response = await checkedFetch(
|
|
32
|
-
`${webServerUrl}/event/last-acked-nonce`,
|
|
33
|
-
);
|
|
34
|
-
const data = await response.json();
|
|
35
|
-
return data.nonce;
|
|
36
|
-
} catch (error) {
|
|
37
|
-
throw new NordError("Failed to get action nonce", { cause: error });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Get information about the Nord server
|
|
43
|
-
*
|
|
44
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
45
|
-
* @returns Information about markets and tokens
|
|
46
|
-
* @throws {NordError} If the request fails
|
|
47
|
-
*/
|
|
48
|
-
export async function getInfo(webServerUrl: string): Promise<Info> {
|
|
49
|
-
try {
|
|
50
|
-
const response = await checkedFetch(`${webServerUrl}/info`);
|
|
51
|
-
return await response.json();
|
|
52
|
-
} catch (error) {
|
|
53
|
-
throw new NordError("Failed to get info", { cause: error });
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Get account information
|
|
59
|
-
*
|
|
60
|
-
* @param webServerUrl - Base URL for the Nord web server
|
|
61
|
-
* @param accountId - Account ID to get information for
|
|
62
|
-
* @returns Account information
|
|
63
|
-
* @throws {NordError} If the request fails
|
|
64
|
-
*/
|
|
65
|
-
export async function getAccount(
|
|
66
|
-
webServerUrl: string,
|
|
67
|
-
accountId: number,
|
|
68
|
-
): Promise<Account> {
|
|
69
|
-
try {
|
|
70
|
-
const response = await checkedFetch(`${webServerUrl}/account/${accountId}`);
|
|
71
|
-
return await response.json();
|
|
72
|
-
} catch (error) {
|
|
73
|
-
throw new NordError(`Failed to get account ${accountId}`, { cause: error });
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
6
|
/**
|
|
78
7
|
* Initialize a WebSocket client for Nord
|
|
79
8
|
*
|