txflow-sdk 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 andy-vibecoding
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,312 @@
1
+ # txflow-sdk
2
+
3
+ TypeScript SDK for [TxFlow](https://txflow-testnet.xyz) perpetual DEX with EIP-712 signing and MCP (Model Context Protocol) server.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install txflow-sdk
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ TxFlow uses an **agent wallet pattern**: a user wallet authorizes a temporary agent wallet via EIP-712 signature, and all subsequent trading actions are signed by the agent wallet.
14
+
15
+ ```typescript
16
+ import { TxFlowClient } from "txflow-sdk";
17
+ import { ethers } from "ethers";
18
+
19
+ const userWallet = new ethers.Wallet("0xUSER_PRIVATE_KEY");
20
+ const agentWallet = new ethers.Wallet("0xAGENT_PRIVATE_KEY");
21
+
22
+ const client = new TxFlowClient({
23
+ privateKey: agentWallet.privateKey,
24
+ });
25
+
26
+ // Step 1: Authorize the agent wallet (one-time)
27
+ await client.approveAgent(userWallet);
28
+
29
+ // Step 2: Place orders using the agent wallet
30
+ const result = await client.placeOrder({
31
+ asset: 1, // Asset index (use getPerpMeta to look up)
32
+ isBuy: true,
33
+ price: "50000",
34
+ size: "0.01",
35
+ reduceOnly: false,
36
+ timeInForce: "gtc",
37
+ });
38
+ ```
39
+
40
+ ## Configuration
41
+
42
+ ```typescript
43
+ const client = new TxFlowClient({
44
+ privateKey: "0x...", // Agent wallet private key (required)
45
+ baseUrl: "https://testnet-api.txflow.net", // API base URL (optional)
46
+ isMainnet: true, // Source identifier for signing (optional, default: true)
47
+ vaultAddress: null, // Vault address if applicable (optional)
48
+ chainId: 1337, // L1 signing domain chain ID (optional)
49
+ });
50
+ ```
51
+
52
+ ### Mainnet Migration
53
+
54
+ When TxFlow launches mainnet, update the config:
55
+
56
+ ```typescript
57
+ import { TxFlowClient, MAINNET_API_URL } from "txflow-sdk";
58
+
59
+ const client = new TxFlowClient({
60
+ privateKey: "0x...",
61
+ baseUrl: MAINNET_API_URL, // or the actual mainnet URL
62
+ chainId: 1337, // update if mainnet uses a different chain ID
63
+ });
64
+ ```
65
+
66
+ All signing logic, EIP-712 domains, and API calls adapt automatically based on the config.
67
+
68
+ ## API Reference
69
+
70
+ ### Trading
71
+
72
+ #### `placeOrder(params)`
73
+
74
+ ```typescript
75
+ await client.placeOrder({
76
+ asset: 1,
77
+ isBuy: true,
78
+ price: "50000",
79
+ size: "0.01",
80
+ reduceOnly: false,
81
+ timeInForce: "gtc", // "gtc" | "ioc" | "alo"
82
+ });
83
+ ```
84
+
85
+ #### `placeBatchOrders(params[])`
86
+
87
+ ```typescript
88
+ await client.placeBatchOrders([
89
+ { asset: 1, isBuy: true, price: "50000", size: "0.01" },
90
+ { asset: 1, isBuy: false, price: "60000", size: "0.01" },
91
+ ]);
92
+ ```
93
+
94
+ #### `cancelOrder(params)`
95
+
96
+ ```typescript
97
+ await client.cancelOrder({ asset: 1, orderId: "123456" });
98
+ ```
99
+
100
+ #### `cancelOrders(params[])`
101
+
102
+ ```typescript
103
+ await client.cancelOrders([
104
+ { asset: 1, orderId: "123456" },
105
+ { asset: 1, orderId: "789012" },
106
+ ]);
107
+ ```
108
+
109
+ #### `modifyOrder(params)`
110
+
111
+ ```typescript
112
+ await client.modifyOrder({
113
+ orderId: "123456",
114
+ asset: 1,
115
+ isBuy: true,
116
+ price: "51000",
117
+ size: "0.02",
118
+ });
119
+ ```
120
+
121
+ ### Account Settings
122
+
123
+ #### `updateLeverage(params)`
124
+
125
+ ```typescript
126
+ await client.updateLeverage({ asset: 1, leverage: 10 });
127
+ ```
128
+
129
+ #### `updateMarginMode(params)`
130
+
131
+ ```typescript
132
+ await client.updateMarginMode({ asset: 1, isCross: true });
133
+ ```
134
+
135
+ ### User Actions
136
+
137
+ These methods require the **user wallet** (not the agent wallet).
138
+
139
+ #### `approveAgent(userWallet, params?)`
140
+
141
+ ```typescript
142
+ await client.approveAgent(userWallet, {
143
+ agentAddress: "0x...", // defaults to SDK agent address
144
+ agentName: "MyBot", // defaults to "TradeAgent"
145
+ });
146
+ ```
147
+
148
+ #### `withdraw(userWallet, params)`
149
+
150
+ ```typescript
151
+ await client.withdraw(userWallet, {
152
+ destination: "0x...",
153
+ amount: "100",
154
+ });
155
+ ```
156
+
157
+ #### `transferToken(userWallet, params)`
158
+
159
+ ```typescript
160
+ await client.transferToken(userWallet, {
161
+ amount: "100",
162
+ fromAccountType: "perp",
163
+ toAccountType: "spot",
164
+ });
165
+ ```
166
+
167
+ #### `sendToken(userWallet, params)`
168
+
169
+ ```typescript
170
+ await client.sendToken(userWallet, {
171
+ toAddress: "0x...",
172
+ amount: "50",
173
+ });
174
+ ```
175
+
176
+ ### Queries
177
+
178
+ ```typescript
179
+ const meta = await client.getPerpMeta();
180
+ const state = await client.getUserState("0xUSER_ADDRESS");
181
+ const orders = await client.getOpenOrders("0xUSER_ADDRESS");
182
+ const book = await client.getL2Book(1);
183
+ const assetData = await client.getActiveAssetData("0xUSER_ADDRESS", "1");
184
+ const candles = await client.getCandleSnapshot({
185
+ coin: "BTC",
186
+ interval: "1h",
187
+ startTime: Date.now() - 86400000,
188
+ endTime: Date.now(),
189
+ });
190
+ const fees = await client.getUserFees("0xUSER_ADDRESS");
191
+ const funding = await client.getFundingHistory({
192
+ user: "0xUSER_ADDRESS",
193
+ startTime: Date.now() - 86400000,
194
+ });
195
+ ```
196
+
197
+ ### Testnet Faucet
198
+
199
+ ```typescript
200
+ await client.requestFund("0xUSER_ADDRESS");
201
+ ```
202
+
203
+ ## Advanced: Low-Level Signing
204
+
205
+ For custom integrations, the signing primitives are exported directly:
206
+
207
+ ```typescript
208
+ import {
209
+ actionHash,
210
+ signL1Action,
211
+ createSign,
212
+ signUserAction,
213
+ getL1ActionDomain,
214
+ L1_ACTION_TYPES,
215
+ USER_ACTION_DOMAIN,
216
+ APPROVE_AGENT_TYPES,
217
+ } from "txflow-sdk";
218
+
219
+ // Compute action hash (MessagePack + nonce + vault encoding + keccak256)
220
+ const hash = actionHash(action, vaultAddress, nonce);
221
+
222
+ // Sign an L1 action (order, cancel, modify, leverage, margin mode)
223
+ const sig = await signL1Action(wallet, action, vaultAddress, nonce, isMainnet, chainId);
224
+
225
+ // Sign a user action (approve, withdraw, transfer)
226
+ const sig = await signUserAction(wallet, domain, types, message);
227
+ ```
228
+
229
+ ### Signing Algorithm
230
+
231
+ 1. Remove trailing zeros from `p` and `s` string fields recursively
232
+ 2. MessagePack encode the action with BigInt64 support
233
+ 3. Append nonce as 8-byte big-endian uint64
234
+ 4. Append vault flag: `0x00` if null (9 extra bytes), or `0x01` + 20-byte address (29 extra bytes)
235
+ 5. Keccak256 hash the buffer
236
+ 6. Sign via EIP-712 typed data with the L1 action domain
237
+
238
+ ## MCP Server
239
+
240
+ The SDK includes an MCP server for AI agent integration (e.g., Claude Desktop, Cursor, OpenClaw).
241
+
242
+ ### Setup
243
+
244
+ Build the project first:
245
+
246
+ ```bash
247
+ npm install && npm run build
248
+ ```
249
+
250
+ ### Configuration
251
+
252
+ Add to your MCP client config (e.g., `claude_desktop_config.json`):
253
+
254
+ ```json
255
+ {
256
+ "mcpServers": {
257
+ "txflow": {
258
+ "command": "node",
259
+ "args": ["/path/to/txflow-sdk/dist/mcp.js"],
260
+ "env": {
261
+ "TXFLOW_AGENT_PRIVATE_KEY": "0xAGENT_PRIVATE_KEY",
262
+ "TXFLOW_USER_PRIVATE_KEY": "0xUSER_PRIVATE_KEY",
263
+ "TXFLOW_BASE_URL": "https://testnet-api.txflow.net"
264
+ }
265
+ }
266
+ }
267
+ }
268
+ ```
269
+
270
+ Or if installed globally via npm:
271
+
272
+ ```json
273
+ {
274
+ "mcpServers": {
275
+ "txflow": {
276
+ "command": "txflow-mcp",
277
+ "env": {
278
+ "TXFLOW_AGENT_PRIVATE_KEY": "0xAGENT_PRIVATE_KEY",
279
+ "TXFLOW_USER_PRIVATE_KEY": "0xUSER_PRIVATE_KEY"
280
+ }
281
+ }
282
+ }
283
+ }
284
+ ```
285
+
286
+ ### Environment Variables
287
+
288
+ | Variable | Required | Description |
289
+ |---|---|---|
290
+ | `TXFLOW_AGENT_PRIVATE_KEY` | Yes | Agent wallet private key for signing trades |
291
+ | `TXFLOW_USER_PRIVATE_KEY` | No | User wallet private key (only for `approve_agent`) |
292
+ | `TXFLOW_BASE_URL` | No | API URL (default: `https://testnet-api.txflow.net`) |
293
+
294
+ ### Available Tools
295
+
296
+ | Tool | Description |
297
+ |---|---|
298
+ | `place_order` | Place a perpetual futures order |
299
+ | `cancel_order` | Cancel an open order |
300
+ | `cancel_orders` | Cancel multiple orders |
301
+ | `modify_order` | Modify an existing order |
302
+ | `update_leverage` | Update leverage for an asset |
303
+ | `update_margin_mode` | Switch cross/isolated margin |
304
+ | `approve_agent` | Authorize agent wallet (requires user key) |
305
+ | `get_open_orders` | List open orders for a user |
306
+ | `get_user_state` | Get account state, positions, balances |
307
+ | `get_perp_meta` | Get available assets and their indices |
308
+ | `get_l2_book` | Get order book depth for a coin |
309
+
310
+ ## License
311
+
312
+ MIT
@@ -0,0 +1,64 @@
1
+ import { ethers } from "ethers";
2
+ import type { TxFlowConfig, PlaceOrderParams, CancelOrderParams, ModifyOrderParams, UpdateLeverageParams, UpdateMarginModeParams, ApproveAgentParams, WithdrawParams, TransferTokenParams, SendTokenParams, ApiResponse, InfoResponse } from "./types.js";
3
+ export declare class TxFlowClient {
4
+ private wallet;
5
+ private baseUrl;
6
+ private isMainnet;
7
+ private vaultAddress;
8
+ private chainId?;
9
+ constructor(config: TxFlowConfig);
10
+ get address(): string;
11
+ private postExchange;
12
+ private postInfo;
13
+ private buildOrderWire;
14
+ placeOrder(params: PlaceOrderParams): Promise<ApiResponse>;
15
+ placeBatchOrders(orderParams: PlaceOrderParams[]): Promise<ApiResponse>;
16
+ cancelOrder(params: CancelOrderParams): Promise<ApiResponse>;
17
+ cancelOrders(params: CancelOrderParams[]): Promise<ApiResponse>;
18
+ modifyOrder(params: ModifyOrderParams): Promise<ApiResponse>;
19
+ updateLeverage(params: UpdateLeverageParams): Promise<ApiResponse>;
20
+ updateMarginMode(params: UpdateMarginModeParams): Promise<ApiResponse>;
21
+ approveAgent(userWallet: ethers.Wallet, params?: Partial<ApproveAgentParams>): Promise<ApiResponse>;
22
+ withdraw(userWallet: ethers.Wallet, params: WithdrawParams): Promise<ApiResponse>;
23
+ transferToken(userWallet: ethers.Wallet, params: TransferTokenParams): Promise<ApiResponse>;
24
+ sendToken(userWallet: ethers.Wallet, params: SendTokenParams): Promise<ApiResponse>;
25
+ getOpenOrders(user: string): Promise<InfoResponse>;
26
+ getMultiUserOpenOrders(users: string[]): Promise<InfoResponse>;
27
+ getMaxPositions(params: {
28
+ user: string;
29
+ asset: string;
30
+ orderType: string;
31
+ buyPrice: string;
32
+ sellPrice: string;
33
+ reduceOnly: boolean;
34
+ }): Promise<InfoResponse>;
35
+ getLiquidationPrices(params: {
36
+ user: string;
37
+ asset: string;
38
+ orderType: string;
39
+ buyPrice: string;
40
+ sellPrice: string;
41
+ buyQuantity: string;
42
+ sellQuantity: string;
43
+ reduceOnly: boolean;
44
+ }): Promise<InfoResponse>;
45
+ getUserState(user: string): Promise<InfoResponse>;
46
+ getFundingHistory(params: {
47
+ user: string;
48
+ startTime: number;
49
+ endTime?: number;
50
+ }): Promise<InfoResponse>;
51
+ getUserFees(user: string): Promise<InfoResponse>;
52
+ getReferral(user: string): Promise<InfoResponse>;
53
+ getPerpMeta(dex?: string): Promise<InfoResponse>;
54
+ getSpotMeta(dex?: string): Promise<InfoResponse>;
55
+ getActiveAssetData(user: string, coin: string): Promise<InfoResponse>;
56
+ getL2Book(coin: string | number): Promise<InfoResponse>;
57
+ getCandleSnapshot(params: {
58
+ coin: string;
59
+ interval: string;
60
+ startTime: number;
61
+ endTime: number;
62
+ }): Promise<InfoResponse>;
63
+ requestFund(address?: string): Promise<ApiResponse>;
64
+ }
package/dist/client.js ADDED
@@ -0,0 +1,283 @@
1
+ import { ethers } from "ethers";
2
+ import { createSign, signUserAction } from "./signing.js";
3
+ import { TESTNET_API_URL, USDC_TOKEN_ADDRESS, USER_ACTION_DOMAIN, APPROVE_AGENT_TYPES, WITHDRAW_TYPES, TRANSFER_TOKEN_TYPES, SEND_TOKEN_TYPES, } from "./constants.js";
4
+ export class TxFlowClient {
5
+ wallet;
6
+ baseUrl;
7
+ isMainnet;
8
+ vaultAddress;
9
+ chainId;
10
+ constructor(config) {
11
+ this.wallet = new ethers.Wallet(config.privateKey);
12
+ this.baseUrl = config.baseUrl ?? TESTNET_API_URL;
13
+ this.isMainnet = config.isMainnet ?? true;
14
+ this.vaultAddress = config.vaultAddress ?? null;
15
+ this.chainId = config.chainId;
16
+ }
17
+ get address() {
18
+ return this.wallet.address;
19
+ }
20
+ async postExchange(body) {
21
+ const resp = await fetch(`${this.baseUrl}/exchange`, {
22
+ method: "POST",
23
+ headers: { "Content-Type": "application/json" },
24
+ body: JSON.stringify(body, (_key, value) => typeof value === "bigint" ? Number(value) : value),
25
+ });
26
+ return resp.json();
27
+ }
28
+ async postInfo(body) {
29
+ const resp = await fetch(`${this.baseUrl}/info`, {
30
+ method: "POST",
31
+ headers: { "Content-Type": "application/json" },
32
+ body: JSON.stringify(body),
33
+ });
34
+ return resp.json();
35
+ }
36
+ buildOrderWire(params) {
37
+ return {
38
+ a: params.asset,
39
+ b: params.isBuy,
40
+ p: params.price,
41
+ s: params.size,
42
+ r: params.reduceOnly ?? false,
43
+ t: { limit: { tif: params.timeInForce ?? "gtc" } },
44
+ };
45
+ }
46
+ async placeOrder(params) {
47
+ const order = this.buildOrderWire(params);
48
+ const action = { grouping: "na", orders: [order] };
49
+ const { signature, nonce } = await createSign(this.wallet, action, this.isMainnet, this.vaultAddress, this.chainId);
50
+ return this.postExchange({
51
+ action: { type: "order", grouping: "na", orders: [order], nonce },
52
+ signature: { r: signature.r, s: signature.s, v: signature.v },
53
+ nonce,
54
+ });
55
+ }
56
+ async placeBatchOrders(orderParams) {
57
+ const orders = orderParams.map((p) => this.buildOrderWire(p));
58
+ const action = { grouping: "na", orders };
59
+ const { signature, nonce } = await createSign(this.wallet, action, this.isMainnet, this.vaultAddress, this.chainId);
60
+ return this.postExchange({
61
+ action: { type: "order", grouping: "na", orders, nonce },
62
+ signature: { r: signature.r, s: signature.s, v: signature.v },
63
+ nonce,
64
+ });
65
+ }
66
+ async cancelOrder(params) {
67
+ const cancelAction = {
68
+ cancels: [{ a: params.asset, o: BigInt(params.orderId) }],
69
+ };
70
+ const { signature, nonce } = await createSign(this.wallet, cancelAction, this.isMainnet, this.vaultAddress, this.chainId);
71
+ return this.postExchange({
72
+ action: { type: "cancel", cancels: [{ a: params.asset, o: params.orderId }] },
73
+ signature: { r: signature.r, s: signature.s, v: signature.v },
74
+ nonce,
75
+ vaultAddress: this.vaultAddress,
76
+ });
77
+ }
78
+ async cancelOrders(params) {
79
+ const cancelAction = {
80
+ cancels: params.map((p) => ({ a: p.asset, o: BigInt(p.orderId) })),
81
+ };
82
+ const { signature, nonce } = await createSign(this.wallet, cancelAction, this.isMainnet, this.vaultAddress, this.chainId);
83
+ return this.postExchange({
84
+ action: {
85
+ type: "cancel",
86
+ cancels: params.map((p) => ({ a: p.asset, o: p.orderId })),
87
+ },
88
+ signature: { r: signature.r, s: signature.s, v: signature.v },
89
+ nonce,
90
+ vaultAddress: this.vaultAddress,
91
+ });
92
+ }
93
+ async modifyOrder(params) {
94
+ const order = this.buildOrderWire({
95
+ asset: params.asset,
96
+ isBuy: params.isBuy,
97
+ price: params.price,
98
+ size: params.size,
99
+ reduceOnly: params.reduceOnly,
100
+ timeInForce: params.timeInForce,
101
+ });
102
+ const modifyAction = { oid: BigInt(params.orderId), order };
103
+ const { signature, nonce } = await createSign(this.wallet, modifyAction, this.isMainnet, this.vaultAddress, this.chainId);
104
+ return this.postExchange({
105
+ action: { type: "modify", oid: params.orderId, order },
106
+ signature: { r: signature.r, s: signature.s, v: signature.v },
107
+ nonce,
108
+ vaultAddress: this.vaultAddress,
109
+ });
110
+ }
111
+ async updateLeverage(params) {
112
+ const action = { asset: params.asset, leverage: params.leverage };
113
+ const { signature, nonce } = await createSign(this.wallet, action, this.isMainnet, this.vaultAddress, this.chainId);
114
+ return this.postExchange({
115
+ action: { type: "updateLeverage", ...action },
116
+ isFrontend: true,
117
+ vaultAddress: this.vaultAddress,
118
+ signature: { r: signature.r, s: signature.s, v: signature.v },
119
+ nonce,
120
+ });
121
+ }
122
+ async updateMarginMode(params) {
123
+ const action = { asset: params.asset, isCross: params.isCross };
124
+ const { signature, nonce } = await createSign(this.wallet, action, this.isMainnet, this.vaultAddress, this.chainId);
125
+ return this.postExchange({
126
+ action: { type: "updateMarginMode", ...action },
127
+ isFrontend: true,
128
+ vaultAddress: this.vaultAddress,
129
+ signature: { r: signature.r, s: signature.s, v: signature.v },
130
+ nonce,
131
+ });
132
+ }
133
+ async approveAgent(userWallet, params) {
134
+ const agentAddress = params?.agentAddress ?? this.wallet.address;
135
+ const agentName = params?.agentName ?? "TradeAgent";
136
+ const nonce = params?.nonce ?? Date.now();
137
+ const message = {
138
+ ofinChain: "ofin",
139
+ agentAddress,
140
+ agentName,
141
+ nonce,
142
+ };
143
+ const signature = await signUserAction(userWallet, USER_ACTION_DOMAIN, APPROVE_AGENT_TYPES, message);
144
+ return this.postExchange({
145
+ action: {
146
+ type: "approveAgent",
147
+ agentAddress,
148
+ agentName,
149
+ nonce,
150
+ ofinChain: "ofin",
151
+ },
152
+ nonce,
153
+ signature: { r: signature.r, s: signature.s, v: signature.v },
154
+ });
155
+ }
156
+ async withdraw(userWallet, params) {
157
+ const nonce = Date.now();
158
+ const signatureChainId = params.signatureChainId ?? "0x66eee";
159
+ const message = {
160
+ ofinChain: "ofin",
161
+ destination: params.destination,
162
+ signatureChainId,
163
+ tokenAddress: USDC_TOKEN_ADDRESS,
164
+ amount: params.amount,
165
+ nonce,
166
+ };
167
+ const signature = await signUserAction(userWallet, USER_ACTION_DOMAIN, WITHDRAW_TYPES, message);
168
+ return this.postExchange({
169
+ action: {
170
+ type: "withdraw3",
171
+ ofinChain: "ofin",
172
+ signatureChainId,
173
+ destination: params.destination,
174
+ tokenAddress: USDC_TOKEN_ADDRESS,
175
+ amount: params.amount,
176
+ nonce,
177
+ },
178
+ nonce,
179
+ signature: { r: signature.r, s: signature.s, v: signature.v },
180
+ });
181
+ }
182
+ async transferToken(userWallet, params) {
183
+ const nonce = Date.now();
184
+ const token = `USDC:${USDC_TOKEN_ADDRESS}`;
185
+ const message = {
186
+ ofinChain: "ofin",
187
+ amount: params.amount,
188
+ fromAccountType: params.fromAccountType,
189
+ toAccountType: params.toAccountType,
190
+ token,
191
+ nonce,
192
+ };
193
+ const signature = await signUserAction(userWallet, USER_ACTION_DOMAIN, TRANSFER_TOKEN_TYPES, message);
194
+ return this.postExchange({
195
+ action: {
196
+ type: "transferToken",
197
+ ofinChain: "ofin",
198
+ amount: params.amount,
199
+ fromAccountType: params.fromAccountType,
200
+ toAccountType: params.toAccountType,
201
+ token,
202
+ nonce,
203
+ },
204
+ nonce,
205
+ signature: { r: signature.r, s: signature.s, v: signature.v },
206
+ });
207
+ }
208
+ async sendToken(userWallet, params) {
209
+ const nonce = Date.now();
210
+ const token = `USDC:${USDC_TOKEN_ADDRESS}`;
211
+ const message = {
212
+ ofinChain: "ofin",
213
+ toAddress: params.toAddress,
214
+ amount: params.amount,
215
+ fromAccountType: params.fromAccountType ?? "perp",
216
+ toAccountType: params.toAccountType ?? "perp",
217
+ token,
218
+ nonce,
219
+ };
220
+ const signature = await signUserAction(userWallet, USER_ACTION_DOMAIN, SEND_TOKEN_TYPES, message);
221
+ return this.postExchange({
222
+ action: {
223
+ type: "sendToken",
224
+ ofinChain: "ofin",
225
+ toAddress: params.toAddress,
226
+ amount: params.amount,
227
+ fromAccountType: params.fromAccountType ?? "perp",
228
+ toAccountType: params.toAccountType ?? "perp",
229
+ token,
230
+ nonce,
231
+ },
232
+ nonce,
233
+ signature: { r: signature.r, s: signature.s, v: signature.v },
234
+ });
235
+ }
236
+ async getOpenOrders(user) {
237
+ return this.postInfo({ type: "openorders", user });
238
+ }
239
+ async getMultiUserOpenOrders(users) {
240
+ return this.postInfo({ type: "multiuseropenorders", users, limit: "" });
241
+ }
242
+ async getMaxPositions(params) {
243
+ return this.postInfo({ type: "getmaxpositions", ...params });
244
+ }
245
+ async getLiquidationPrices(params) {
246
+ return this.postInfo({ type: "getliquidationprices", ...params });
247
+ }
248
+ async getUserState(user) {
249
+ return this.postInfo({ type: "clearinghouseState", user });
250
+ }
251
+ async getFundingHistory(params) {
252
+ return this.postInfo({ type: "userfunding", ...params });
253
+ }
254
+ async getUserFees(user) {
255
+ return this.postInfo({ type: "userfees", user });
256
+ }
257
+ async getReferral(user) {
258
+ return this.postInfo({ type: "referral", user });
259
+ }
260
+ async getPerpMeta(dex) {
261
+ return this.postInfo({ type: "perpmeta", dex: dex ?? "" });
262
+ }
263
+ async getSpotMeta(dex) {
264
+ return this.postInfo({ type: "spotmeta", dex: dex ?? "" });
265
+ }
266
+ async getActiveAssetData(user, coin) {
267
+ return this.postInfo({ type: "activeassetdata", user, coin });
268
+ }
269
+ async getL2Book(coin) {
270
+ return this.postInfo({ type: "l2book", coin: String(coin) });
271
+ }
272
+ async getCandleSnapshot(params) {
273
+ return this.postInfo({ type: "candleSnapshot", ...params });
274
+ }
275
+ async requestFund(address) {
276
+ const resp = await fetch(`${this.baseUrl}/request-fund`, {
277
+ method: "POST",
278
+ headers: { "Content-Type": "application/json" },
279
+ body: JSON.stringify({ address: address ?? this.wallet.address }),
280
+ });
281
+ return resp.json();
282
+ }
283
+ }
@@ -0,0 +1,47 @@
1
+ export declare const TESTNET_API_URL = "https://testnet-api.txflow.net";
2
+ export declare const MAINNET_API_URL = "https://api.txflow.net";
3
+ export declare const USDC_TOKEN_ADDRESS = "0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464";
4
+ export declare const TESTNET_L1_CHAIN_ID = 1337;
5
+ export declare const MAINNET_L1_CHAIN_ID = 1337;
6
+ export declare function getL1ActionDomain(chainId?: number): {
7
+ name: "Exchange";
8
+ version: "1";
9
+ chainId: number;
10
+ verifyingContract: `0x${string}`;
11
+ };
12
+ export declare const L1_ACTION_TYPES: {
13
+ Agent: {
14
+ name: string;
15
+ type: string;
16
+ }[];
17
+ };
18
+ export declare const USER_ACTION_DOMAIN: {
19
+ name: string;
20
+ version: string;
21
+ chainId: number;
22
+ verifyingContract: `0x${string}`;
23
+ };
24
+ export declare const APPROVE_AGENT_TYPES: {
25
+ ApproveAgent: {
26
+ name: string;
27
+ type: string;
28
+ }[];
29
+ };
30
+ export declare const WITHDRAW_TYPES: {
31
+ Withdraw3: {
32
+ name: string;
33
+ type: string;
34
+ }[];
35
+ };
36
+ export declare const TRANSFER_TOKEN_TYPES: {
37
+ TransferToken: {
38
+ name: string;
39
+ type: string;
40
+ }[];
41
+ };
42
+ export declare const SEND_TOKEN_TYPES: {
43
+ SendToken: {
44
+ name: string;
45
+ type: string;
46
+ }[];
47
+ };
@@ -0,0 +1,64 @@
1
+ export const TESTNET_API_URL = "https://testnet-api.txflow.net";
2
+ export const MAINNET_API_URL = "https://api.txflow.net";
3
+ export const USDC_TOKEN_ADDRESS = "0xd04f0d19E8FbA49cfA541E272F9C60a2ad720464";
4
+ export const TESTNET_L1_CHAIN_ID = 1337;
5
+ export const MAINNET_L1_CHAIN_ID = 1337;
6
+ export function getL1ActionDomain(chainId = TESTNET_L1_CHAIN_ID) {
7
+ return {
8
+ name: "Exchange",
9
+ version: "1",
10
+ chainId,
11
+ verifyingContract: "0x0000000000000000000000000000000000000000",
12
+ };
13
+ }
14
+ export const L1_ACTION_TYPES = {
15
+ Agent: [
16
+ { name: "source", type: "string" },
17
+ { name: "connectionId", type: "bytes32" },
18
+ ],
19
+ };
20
+ export const USER_ACTION_DOMAIN = {
21
+ name: "Ofin",
22
+ version: "1",
23
+ chainId: 1,
24
+ verifyingContract: "0x0000000000000000000000000000000000000000",
25
+ };
26
+ export const APPROVE_AGENT_TYPES = {
27
+ ApproveAgent: [
28
+ { name: "ofinChain", type: "string" },
29
+ { name: "agentAddress", type: "address" },
30
+ { name: "agentName", type: "string" },
31
+ { name: "nonce", type: "uint64" },
32
+ ],
33
+ };
34
+ export const WITHDRAW_TYPES = {
35
+ Withdraw3: [
36
+ { name: "ofinChain", type: "string" },
37
+ { name: "destination", type: "address" },
38
+ { name: "signatureChainId", type: "string" },
39
+ { name: "tokenAddress", type: "address" },
40
+ { name: "amount", type: "string" },
41
+ { name: "nonce", type: "uint64" },
42
+ ],
43
+ };
44
+ export const TRANSFER_TOKEN_TYPES = {
45
+ TransferToken: [
46
+ { name: "ofinChain", type: "string" },
47
+ { name: "amount", type: "string" },
48
+ { name: "fromAccountType", type: "string" },
49
+ { name: "toAccountType", type: "string" },
50
+ { name: "token", type: "string" },
51
+ { name: "nonce", type: "uint64" },
52
+ ],
53
+ };
54
+ export const SEND_TOKEN_TYPES = {
55
+ SendToken: [
56
+ { name: "ofinChain", type: "string" },
57
+ { name: "toAddress", type: "address" },
58
+ { name: "amount", type: "string" },
59
+ { name: "fromAccountType", type: "string" },
60
+ { name: "toAccountType", type: "string" },
61
+ { name: "token", type: "string" },
62
+ { name: "nonce", type: "uint64" },
63
+ ],
64
+ };
@@ -0,0 +1,4 @@
1
+ export { TxFlowClient } from "./client.js";
2
+ export { actionHash, signL1Action, createSign, signUserAction, generateNonce } from "./signing.js";
3
+ export { TESTNET_API_URL, MAINNET_API_URL, USDC_TOKEN_ADDRESS, TESTNET_L1_CHAIN_ID, MAINNET_L1_CHAIN_ID, getL1ActionDomain, L1_ACTION_TYPES, USER_ACTION_DOMAIN, APPROVE_AGENT_TYPES, WITHDRAW_TYPES, TRANSFER_TOKEN_TYPES, SEND_TOKEN_TYPES, } from "./constants.js";
4
+ export type { TxFlowConfig, OrderWire, OrderGrouping, CancelWire, CancelAction, ModifyWire, Signature, SignedRequest, PlaceOrderParams, CancelOrderParams, ModifyOrderParams, UpdateLeverageParams, UpdateMarginModeParams, ApproveAgentParams, WithdrawParams, TransferTokenParams, SendTokenParams, ApiResponse, InfoResponse, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { TxFlowClient } from "./client.js";
2
+ export { actionHash, signL1Action, createSign, signUserAction, generateNonce } from "./signing.js";
3
+ export { TESTNET_API_URL, MAINNET_API_URL, USDC_TOKEN_ADDRESS, TESTNET_L1_CHAIN_ID, MAINNET_L1_CHAIN_ID, getL1ActionDomain, L1_ACTION_TYPES, USER_ACTION_DOMAIN, APPROVE_AGENT_TYPES, WITHDRAW_TYPES, TRANSFER_TOKEN_TYPES, SEND_TOKEN_TYPES, } from "./constants.js";
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/mcp.js ADDED
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { TxFlowClient } from "./client.js";
6
+ import { ethers } from "ethers";
7
+ function getEnvOrThrow(name) {
8
+ const val = process.env[name];
9
+ if (!val)
10
+ throw new Error(`Missing required env var: ${name}`);
11
+ return val;
12
+ }
13
+ function fmt(result) {
14
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
15
+ }
16
+ async function main() {
17
+ const agentPrivateKey = getEnvOrThrow("TXFLOW_AGENT_PRIVATE_KEY");
18
+ const baseUrl = process.env.TXFLOW_BASE_URL || "https://testnet-api.txflow.net";
19
+ const userPrivateKey = process.env.TXFLOW_USER_PRIVATE_KEY;
20
+ const client = new TxFlowClient({
21
+ privateKey: agentPrivateKey,
22
+ baseUrl,
23
+ isMainnet: true,
24
+ });
25
+ const userWallet = userPrivateKey ? new ethers.Wallet(userPrivateKey) : null;
26
+ const server = new McpServer({
27
+ name: "txflow-mcp",
28
+ version: "0.1.0",
29
+ });
30
+ server.tool("place_order", "Place a perpetual futures order on TxFlow DEX.", {
31
+ asset: z.number().describe("Asset index (e.g. 0 for BTC)"),
32
+ isBuy: z.boolean().describe("True for buy/long, false for sell/short"),
33
+ price: z.string().describe("Order price"),
34
+ size: z.string().describe("Order size"),
35
+ reduceOnly: z.boolean().optional().describe("Reduce-only order"),
36
+ timeInForce: z.string().optional().describe("Time in force: gtc, ioc, alo"),
37
+ }, async (params) => {
38
+ const result = await client.placeOrder(params);
39
+ return fmt(result);
40
+ });
41
+ server.tool("cancel_order", "Cancel an open order on TxFlow DEX.", {
42
+ asset: z.number().describe("Asset index"),
43
+ orderId: z.string().describe("Order ID to cancel"),
44
+ }, async (params) => {
45
+ const result = await client.cancelOrder(params);
46
+ return fmt(result);
47
+ });
48
+ server.tool("cancel_orders", "Cancel multiple open orders on TxFlow DEX.", {
49
+ orders: z
50
+ .string()
51
+ .describe('JSON array of {asset: number, orderId: string} objects'),
52
+ }, async ({ orders }) => {
53
+ const parsed = JSON.parse(orders);
54
+ const result = await client.cancelOrders(parsed);
55
+ return fmt(result);
56
+ });
57
+ server.tool("modify_order", "Modify an existing order on TxFlow DEX.", {
58
+ orderId: z.string().describe("Order ID to modify"),
59
+ asset: z.number().describe("Asset index"),
60
+ isBuy: z.boolean().describe("True for buy, false for sell"),
61
+ price: z.string().describe("New price"),
62
+ size: z.string().describe("New size"),
63
+ reduceOnly: z.boolean().optional().describe("Reduce-only"),
64
+ timeInForce: z.string().optional().describe("Time in force"),
65
+ }, async (params) => {
66
+ const result = await client.modifyOrder(params);
67
+ return fmt(result);
68
+ });
69
+ server.tool("update_leverage", "Update leverage for an asset on TxFlow DEX.", {
70
+ asset: z.number().describe("Asset index"),
71
+ leverage: z.number().describe("New leverage value"),
72
+ }, async (params) => {
73
+ const result = await client.updateLeverage(params);
74
+ return fmt(result);
75
+ });
76
+ server.tool("update_margin_mode", "Update margin mode (cross/isolated) for an asset.", {
77
+ asset: z.number().describe("Asset index"),
78
+ isCross: z.boolean().describe("True for cross margin, false for isolated"),
79
+ }, async (params) => {
80
+ const result = await client.updateMarginMode(params);
81
+ return fmt(result);
82
+ });
83
+ server.tool("get_open_orders", "Get open orders for a user on TxFlow DEX.", {
84
+ user: z.string().describe("User address"),
85
+ }, async ({ user }) => {
86
+ const result = await client.getOpenOrders(user);
87
+ return fmt(result);
88
+ });
89
+ server.tool("get_user_state", "Get user account state including positions and balances.", {
90
+ user: z.string().describe("User address"),
91
+ }, async ({ user }) => {
92
+ const result = await client.getUserState(user);
93
+ return fmt(result);
94
+ });
95
+ server.tool("get_perp_meta", "Get perpetual futures metadata including available assets and their indices.", {
96
+ dex: z.string().optional().describe("DEX identifier (optional)"),
97
+ }, async ({ dex }) => {
98
+ const result = await client.getPerpMeta(dex);
99
+ return fmt(result);
100
+ });
101
+ server.tool("get_l2_book", "Get L2 order book for a coin.", {
102
+ coin: z.string().describe("Coin symbol, e.g. BTC"),
103
+ }, async ({ coin }) => {
104
+ const result = await client.getL2Book(coin);
105
+ return fmt(result);
106
+ });
107
+ server.tool("approve_agent", "Approve agent wallet for trading. Requires TXFLOW_USER_PRIVATE_KEY.", {
108
+ agentAddress: z.string().optional().describe("Agent address (defaults to SDK agent)"),
109
+ agentName: z.string().optional().describe("Agent name (defaults to TradeAgent)"),
110
+ }, async (params) => {
111
+ if (!userWallet) {
112
+ return fmt({ error: "TXFLOW_USER_PRIVATE_KEY env var is required for approve_agent" });
113
+ }
114
+ const result = await client.approveAgent(userWallet, params);
115
+ return fmt(result);
116
+ });
117
+ const transport = new StdioServerTransport();
118
+ await server.connect(transport);
119
+ }
120
+ main().catch((err) => {
121
+ console.error("Fatal error:", err);
122
+ process.exit(1);
123
+ });
@@ -0,0 +1,10 @@
1
+ import { ethers } from "ethers";
2
+ import type { Signature } from "./types.js";
3
+ export declare function actionHash(action: unknown, vaultAddress: string | null, nonce: number): string;
4
+ export declare function signL1Action(wallet: ethers.Wallet, action: unknown, vaultAddress: string | null, nonce: number, isMainnet: boolean, chainId?: number): Promise<Signature>;
5
+ export declare function generateNonce(): number;
6
+ export declare function createSign(wallet: ethers.Wallet, action: unknown, isMainnet: boolean, vaultAddress?: string | null, chainId?: number): Promise<{
7
+ signature: Signature;
8
+ nonce: number;
9
+ }>;
10
+ export declare function signUserAction(wallet: ethers.Wallet, domain: ethers.TypedDataDomain, types: Record<string, ethers.TypedDataField[]>, message: Record<string, unknown>): Promise<Signature>;
@@ -0,0 +1,81 @@
1
+ import { ethers } from "ethers";
2
+ import { encode } from "@msgpack/msgpack";
3
+ import { getL1ActionDomain, L1_ACTION_TYPES } from "./constants.js";
4
+ function removeTrailingZeros(value) {
5
+ if (!value.includes("."))
6
+ return value;
7
+ const result = value.replace(/\.?0+$/, "");
8
+ return result === "-0" ? "0" : result;
9
+ }
10
+ function cleanAction(obj) {
11
+ if (!obj || typeof obj !== "object")
12
+ return obj;
13
+ if (Array.isArray(obj))
14
+ return obj.map((item) => cleanAction(item));
15
+ const cleaned = { ...obj };
16
+ for (const key in cleaned) {
17
+ if (Object.prototype.hasOwnProperty.call(cleaned, key)) {
18
+ const val = cleaned[key];
19
+ if (val && typeof val === "object") {
20
+ cleaned[key] = cleanAction(val);
21
+ }
22
+ else if ((key === "p" || key === "s") && typeof val === "string") {
23
+ cleaned[key] = removeTrailingZeros(val);
24
+ }
25
+ }
26
+ }
27
+ return cleaned;
28
+ }
29
+ function msgpackEncode(action) {
30
+ return encode(action, { useBigInt64: true });
31
+ }
32
+ export function actionHash(action, vaultAddress, nonce) {
33
+ const cleaned = cleanAction(action);
34
+ const encoded = msgpackEncode(cleaned);
35
+ const extraBytes = vaultAddress === null ? 9 : 29;
36
+ const buffer = new Uint8Array(encoded.length + extraBytes);
37
+ buffer.set(encoded);
38
+ const view = new DataView(buffer.buffer);
39
+ view.setBigUint64(encoded.length, BigInt(nonce), false);
40
+ if (vaultAddress === null) {
41
+ buffer[encoded.length + 8] = 0;
42
+ }
43
+ else {
44
+ buffer[encoded.length + 8] = 1;
45
+ const addrBytes = ethers.getBytes(vaultAddress);
46
+ buffer.set(addrBytes, encoded.length + 9);
47
+ }
48
+ return ethers.keccak256(buffer);
49
+ }
50
+ export async function signL1Action(wallet, action, vaultAddress, nonce, isMainnet, chainId) {
51
+ const hash = actionHash(action, vaultAddress, nonce);
52
+ const message = {
53
+ source: isMainnet ? "a" : "b",
54
+ connectionId: hash,
55
+ };
56
+ const domain = getL1ActionDomain(chainId);
57
+ const sig = await wallet.signTypedData(domain, L1_ACTION_TYPES, message);
58
+ const split = ethers.Signature.from(sig);
59
+ return {
60
+ r: split.r,
61
+ s: split.s,
62
+ v: split.v,
63
+ };
64
+ }
65
+ export function generateNonce() {
66
+ return Date.now();
67
+ }
68
+ export async function createSign(wallet, action, isMainnet, vaultAddress = null, chainId) {
69
+ const nonce = generateNonce();
70
+ const signature = await signL1Action(wallet, action, vaultAddress, nonce, isMainnet, chainId);
71
+ return { signature, nonce };
72
+ }
73
+ export async function signUserAction(wallet, domain, types, message) {
74
+ const sig = await wallet.signTypedData(domain, types, message);
75
+ const split = ethers.Signature.from(sig);
76
+ return {
77
+ r: split.r,
78
+ s: split.s,
79
+ v: split.v,
80
+ };
81
+ }
@@ -0,0 +1,105 @@
1
+ export interface TxFlowConfig {
2
+ privateKey: string;
3
+ baseUrl?: string;
4
+ isMainnet?: boolean;
5
+ vaultAddress?: string | null;
6
+ chainId?: number;
7
+ }
8
+ export interface OrderWire {
9
+ a: number;
10
+ b: boolean;
11
+ p: string;
12
+ s: string;
13
+ r: boolean;
14
+ t: {
15
+ limit: {
16
+ tif: string;
17
+ };
18
+ };
19
+ }
20
+ export interface OrderGrouping {
21
+ grouping: string;
22
+ orders: OrderWire[];
23
+ }
24
+ export interface CancelWire {
25
+ a: number;
26
+ o: bigint;
27
+ }
28
+ export interface CancelAction {
29
+ cancels: CancelWire[];
30
+ }
31
+ export interface ModifyWire {
32
+ oid: bigint;
33
+ order: OrderWire;
34
+ }
35
+ export interface Signature {
36
+ r: string;
37
+ s: string;
38
+ v: number;
39
+ }
40
+ export interface SignedRequest {
41
+ action: Record<string, unknown>;
42
+ signature: Signature;
43
+ nonce: number;
44
+ vaultAddress?: string | null;
45
+ }
46
+ export interface PlaceOrderParams {
47
+ asset: number;
48
+ isBuy: boolean;
49
+ price: string;
50
+ size: string;
51
+ reduceOnly?: boolean;
52
+ timeInForce?: string;
53
+ }
54
+ export interface CancelOrderParams {
55
+ asset: number;
56
+ orderId: string;
57
+ }
58
+ export interface ModifyOrderParams {
59
+ orderId: string;
60
+ asset: number;
61
+ isBuy: boolean;
62
+ price: string;
63
+ size: string;
64
+ reduceOnly?: boolean;
65
+ timeInForce?: string;
66
+ }
67
+ export interface UpdateLeverageParams {
68
+ asset: number;
69
+ leverage: number;
70
+ }
71
+ export interface UpdateMarginModeParams {
72
+ asset: number;
73
+ isCross: boolean;
74
+ }
75
+ export interface ApproveAgentParams {
76
+ agentAddress: string;
77
+ agentName?: string;
78
+ nonce?: number;
79
+ }
80
+ export interface WithdrawParams {
81
+ destination: string;
82
+ amount: string;
83
+ signatureChainId?: string;
84
+ }
85
+ export interface TransferTokenParams {
86
+ amount: string;
87
+ fromAccountType: string;
88
+ toAccountType: string;
89
+ }
90
+ export interface SendTokenParams {
91
+ toAddress: string;
92
+ amount: string;
93
+ fromAccountType?: string;
94
+ toAccountType?: string;
95
+ }
96
+ export interface ApiResponse<T = unknown> {
97
+ status: string;
98
+ response?: string;
99
+ data?: T;
100
+ }
101
+ export interface InfoResponse<T = unknown> {
102
+ code: number;
103
+ success: boolean;
104
+ data?: T;
105
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "txflow-sdk",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for TxFlow perpetual DEX with EIP-712 signing and MCP server",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "txflow-mcp": "dist/mcp.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "typecheck": "tsc --noEmit",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "keywords": [
28
+ "txflow",
29
+ "perp",
30
+ "dex",
31
+ "eip712",
32
+ "trading",
33
+ "sdk",
34
+ "mcp",
35
+ "perpetual",
36
+ "futures"
37
+ ],
38
+ "license": "MIT",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://gitlab.com/andy-vibecoding/txflow-sdk.git"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.26.0",
45
+ "@msgpack/msgpack": "^3.0.0-beta2",
46
+ "ethers": "^6.13.0",
47
+ "zod": "^3.24.0"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^22.0.0",
51
+ "tsx": "^4.19.0",
52
+ "typescript": "^5.7.0"
53
+ }
54
+ }