pacifica-js-sdk 1.0.0 → 1.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/README.md CHANGED
@@ -1,8 +1,23 @@
1
+ # Pacifica JS SDK (Community)
1
2
 
2
- # Pacifica JS SDK
3
+ [![npm version](https://img.shields.io/npm/v/pacifica-js-sdk.svg)](https://www.npmjs.com/package/pacifica-js-sdk)
4
+ [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)
5
+ [![Production Grade](https://img.shields.io/badge/Status-Production%20Grade-green.svg)](https://pacifica.fi)
3
6
 
4
- Official JavaScript/TypeScript SDK for [Pacifica](https://pacifica.fi).
5
- Converted from the official [Python SDK](https://github.com/pacifica-fi/python-sdk).
7
+ Community-maintained production-ready JavaScript/TypeScript SDK for [Pacifica](https://pacifica.fi).
8
+ Engineered for high-frequency trading bots, institutional integrations, and DeFi agents.
9
+
10
+ > **Note:** This is an unofficial, community-maintained SDK. It is not affiliated with or endorsed by the Pacifica team.
11
+
12
+ ## Features
13
+
14
+ - 🔐 **Secure Authentication**: Ed25519 signing with runtime validation for keys.
15
+ - 🤖 **Agent Wallets**: Native support for binding and using agent wallets for automated trading.
16
+ - 📡 **Real-time Data**: Robust WebSocket client with auto-reconnection and exponential backoff.
17
+ - ⚡ **Full Trading Suite**: Market, Limit, Stop-Loss, and Take-Profit orders.
18
+ - 🛡️ **Error Handling**: Typed error classes (`PacificaError`, `NetworkError`) for predictable failure management.
19
+ - 📘 **TypeScript First**: Strict type definitions for all API interactions.
20
+ - ✅ **Tested**: Unit tested utilities and critical paths.
6
21
 
7
22
  ## Installation
8
23
 
@@ -10,43 +25,128 @@ Converted from the official [Python SDK](https://github.com/pacifica-fi/python-s
10
25
  npm install pacifica-js-sdk
11
26
  ```
12
27
 
13
- ## Usage
28
+ ## Quick Start
29
+
30
+ ### Basic Configuration
14
31
 
15
- Create a `.env` file with your private key (optional for public data):
32
+ Create a `.env` file (optional):
16
33
  ```
17
34
  PRIVATE_KEY=your_base58_private_key
18
35
  ```
19
36
 
20
- Run the example:
21
- ```bash
22
- npx tsx examples/usage.ts
37
+ Initialize the client with error handling:
38
+
39
+ ```typescript
40
+ import { PacificaClient, PacificaError } from 'pacifica-js-sdk';
41
+ import dotenv from 'dotenv';
42
+
43
+ dotenv.config();
44
+
45
+ try {
46
+ const client = new PacificaClient({
47
+ privateKey: process.env.PRIVATE_KEY,
48
+ network: "mainnet" // or "testnet"
49
+ });
50
+
51
+ await client.connect();
52
+ console.log("Connected securely.");
53
+ } catch (error) {
54
+ if (error instanceof PacificaError) {
55
+ console.error("Pacifica SDK Error:", error.message);
56
+ } else {
57
+ console.error("Unknown Error:", error);
58
+ }
59
+ }
23
60
  ```
24
61
 
25
- ## Code Example
62
+ ### Trading with Stop-Loss & Take-Profit
26
63
 
27
64
  ```typescript
28
- import { PacificaClient } from './src';
65
+ await client.connect();
29
66
 
30
- const client = new PacificaClient({
31
- privateKey: "YOUR_BASE58_PRIVATE_KEY",
32
- network: "testnet"
67
+ const response = await client.createOrder({
68
+ symbol: "BTC",
69
+ side: "bid",
70
+ type: "limit",
71
+ price: "65000",
72
+ amount: "0.1",
73
+ take_profit: {
74
+ stop_price: "70000",
75
+ limit_price: "70100"
76
+ },
77
+ stop_loss: {
78
+ stop_price: "60000"
79
+ }
33
80
  });
34
81
 
35
- // REST API
36
- const subaccounts = await client.listSubaccounts();
82
+ console.log("Order placed:", response);
83
+ ```
84
+
85
+ ### Using Agent Wallets
86
+
87
+ Agent wallets allow you to sign transactions with a secondary keypair, keeping your main private key safe.
88
+
89
+ 1. **Bind an Agent Wallet** (One-time setup using main key):
90
+ ```typescript
91
+ import { Keypair } from "@solana/web3.js";
92
+
93
+ // Generate a new agent keypair
94
+ const agentKeypair = Keypair.generate();
95
+ const agentPublicKey = agentKeypair.publicKey.toBase58();
96
+
97
+ // Bind it to your account
98
+ await client.bindAgentWallet(agentPublicKey);
99
+ console.log("Agent bound:", agentPublicKey);
100
+ console.log("Agent Private Key:", bs58.encode(agentKeypair.secretKey)); // Save this!
101
+ ```
37
102
 
38
- // WebSocket API
103
+ 2. **Trade with Agent Wallet**:
104
+ ```typescript
105
+ const agentClient = new PacificaClient({
106
+ privateKey: process.env.MAIN_PRIVATE_KEY, // Still needed for account identification
107
+ agentWallet: "AGENT_PRIVATE_KEY_BASE58", // Signs requests
108
+ network: "mainnet"
109
+ });
110
+
111
+ await agentClient.connect();
112
+ await agentClient.createOrder({ ... });
113
+ ```
114
+
115
+ ### WebSocket Subscriptions
116
+
117
+ ```typescript
39
118
  await client.connect();
40
- await client.createMarketOrderWs({
41
- symbol: "BTC",
42
- side: "bid",
43
- amount: "0.1"
119
+
120
+ // Typed event listeners
121
+ client.on('ticker', (ticker) => {
122
+ console.log(`Update for ${ticker.symbol}: $${ticker.price}`);
44
123
  });
124
+
125
+ client.on('orderbook', (book) => {
126
+ console.log(`Orderbook ${book.symbol}: ${book.bids[0]} / ${book.asks[0]}`);
127
+ });
128
+
129
+ // Subscribe
130
+ client.subscribeToTicker("BTC");
131
+ client.subscribeToOrderbook("ETH");
45
132
  ```
46
133
 
47
- ## Features
134
+ ### REST API Methods
135
+
136
+ ```typescript
137
+ // Get Account Info (Balances, Margin)
138
+ const account = await client.getAccountInfo();
139
+
140
+ // Get Open Positions
141
+ const positions = await client.getPositions();
142
+
143
+ // Get Open Orders
144
+ const orders = await client.getOrders();
145
+
146
+ // Get All Markets
147
+ const markets = await client.getMarkets();
148
+ ```
149
+
150
+ ## License
48
151
 
49
- - Full WebSocket trading support (Market, Limit, Cancel, Cancel All)
50
- - REST API support
51
- - Automatic request signing matching Python SDK logic
52
- - TypeScript support
152
+ ISC
package/dist/client.d.ts CHANGED
@@ -1,111 +1,38 @@
1
1
  import { EventEmitter } from 'events';
2
- export interface PacificaClientConfig {
3
- privateKey?: string;
4
- network?: 'mainnet' | 'testnet';
5
- }
2
+ import { PacificaClientConfig, CreateOrderParams, ApiResponse, AccountInfo, Position, Order } from "./types.js";
6
3
  export declare class PacificaClient extends EventEmitter {
7
4
  private keypair?;
5
+ private agentWalletKeypair?;
6
+ private agentWalletPublicKey?;
8
7
  private restUrl;
9
8
  private wsUrl;
10
9
  private ws;
11
10
  private wsConnected;
11
+ private reconnect;
12
+ private reconnectDelay;
12
13
  private pendingRequests;
13
14
  private axiosInstance;
14
15
  constructor(config: PacificaClientConfig);
16
+ /**
17
+ * Signs a payload and prepares the request header.
18
+ * Uses Agent Wallet if available, otherwise uses Main Account.
19
+ */
20
+ private signAndPrepareRequest;
15
21
  connect(): Promise<void>;
22
+ private handleReconnect;
16
23
  close(): void;
17
24
  private handleWsMessage;
18
25
  private sendWsRequest;
19
- private signAndPreparePayload;
20
- /**
21
- * Subscribe to a data source
22
- */
23
- subscribe(source: string, params?: any): Promise<void>;
24
- /**
25
- * Create a market order via WebSocket
26
- */
27
- createMarketOrderWs(params: {
28
- symbol: string;
29
- side: 'bid' | 'ask';
30
- amount: string;
31
- reduce_only?: boolean;
32
- slippage_percent?: string;
33
- client_order_id?: string;
34
- }): Promise<any>;
35
- /**
36
- * Create a limit order via WebSocket
37
- */
38
- createLimitOrderWs(params: {
39
- symbol: string;
40
- side: 'bid' | 'ask';
41
- amount: string;
42
- price: string;
43
- reduce_only?: boolean;
44
- client_order_id?: string;
45
- tif?: string;
46
- }): Promise<any>;
47
- /**
48
- * Cancel an order via WebSocket
49
- */
50
- cancelOrderWs(params: {
51
- symbol: string;
52
- order_id?: string;
53
- client_order_id?: string;
54
- }): Promise<any>;
55
- /**
56
- * Cancel all orders via WebSocket
57
- */
58
- cancelAllOrdersWs(params: {
59
- symbol?: string;
60
- all_symbols?: boolean;
61
- exclude_reduce_only?: boolean;
62
- }): Promise<any>;
63
- /**
64
- * Helper to send signed REST requests
65
- */
66
- private sendRestRequest;
67
- /**
68
- * Create a market order via REST
69
- */
70
- createMarketOrderRest(params: {
71
- symbol: string;
72
- side: 'bid' | 'ask';
73
- amount: string;
74
- reduce_only?: boolean;
75
- slippage_percent?: string;
76
- client_order_id?: string;
77
- }): Promise<any>;
78
- /**
79
- * Create a limit order via REST
80
- */
81
- createLimitOrderRest(params: {
82
- symbol: string;
83
- side: 'bid' | 'ask';
84
- amount: string;
85
- price: string;
86
- reduce_only?: boolean;
87
- client_order_id?: string;
88
- tif?: string;
89
- }): Promise<any>;
90
- /**
91
- * Cancel an order via REST
92
- */
93
- cancelOrderRest(params: {
94
- symbol: string;
95
- order_id?: string;
96
- client_order_id?: string;
97
- }): Promise<any>;
98
26
  /**
99
- * Cancel all orders via REST
27
+ * Bind a new Agent Wallet to the main account
100
28
  */
101
- cancelAllOrdersRest(params: {
102
- all_symbols?: boolean;
103
- exclude_reduce_only?: boolean;
104
- }): Promise<any>;
29
+ bindAgentWallet(newAgentPublicKey: string): Promise<ApiResponse<any>>;
105
30
  /**
106
- * List subaccounts via REST
31
+ * Get account information (balances, margin, leverage)
107
32
  */
108
- listSubaccounts(): Promise<any>;
33
+ getAccountInfo(): Promise<ApiResponse<AccountInfo>>;
34
+ getPositions(): Promise<ApiResponse<Position[]>>;
35
+ getOrders(symbol?: string): Promise<ApiResponse<Order[]>>;
109
36
  /**
110
37
  * Get exchange information including all available markets
111
38
  */
@@ -114,4 +41,12 @@ export declare class PacificaClient extends EventEmitter {
114
41
  * Get current prices for all markets
115
42
  */
116
43
  getPrices(): Promise<any>;
44
+ createOrder(params: CreateOrderParams): Promise<any>;
45
+ cancelOrder(orderId: string, symbol: string): Promise<any>;
46
+ cancelAllOrders(symbol?: string): Promise<any>;
47
+ subscribe(channel: string, symbol?: string): void;
48
+ subscribeToPrices(): void;
49
+ subscribeToTicker(symbol: string): void;
50
+ subscribeToOrderbook(symbol: string): void;
51
+ subscribeToTrades(symbol: string): void;
117
52
  }
package/dist/client.js CHANGED
@@ -1,284 +1,368 @@
1
- import { Keypair } from "@solana/web3.js";
2
- import { WebSocket } from "ws";
3
- import axios from "axios";
4
- import bs58 from "bs58";
5
- import { v4 as uuidv4 } from 'uuid';
6
- import { EventEmitter } from 'events';
7
- import { MAINNET_REST_URL, MAINNET_WS_URL, TESTNET_REST_URL, TESTNET_WS_URL, DEFAULT_EXPIRY_WINDOW } from "./config.js";
8
- import { signMessage } from "./utils.js";
9
- export class PacificaClient extends EventEmitter {
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PacificaClient = void 0;
7
+ const web3_js_1 = require("@solana/web3.js");
8
+ const ws_1 = require("ws");
9
+ const axios_1 = __importDefault(require("axios"));
10
+ const bs58_1 = __importDefault(require("bs58"));
11
+ const uuid_1 = require("uuid");
12
+ const events_1 = require("events");
13
+ const config_js_1 = require("./config.js");
14
+ const utils_js_1 = require("./utils.js");
15
+ const errors_js_1 = require("./errors.js");
16
+ class PacificaClient extends events_1.EventEmitter {
10
17
  constructor(config) {
11
18
  super();
12
19
  this.ws = null;
13
- this.wsConnected = null;
20
+ this.wsConnected = false;
21
+ this.reconnect = false;
22
+ this.reconnectDelay = 1000;
14
23
  this.pendingRequests = new Map();
24
+ // Initialize Main Account Keypair
15
25
  if (config.privateKey) {
16
- this.keypair = Keypair.fromSecretKey(bs58.decode(config.privateKey));
26
+ try {
27
+ this.keypair = web3_js_1.Keypair.fromSecretKey(bs58_1.default.decode(config.privateKey));
28
+ }
29
+ catch (error) {
30
+ throw new errors_js_1.ValidationError("Invalid private key format. Must be a valid Base58 string.");
31
+ }
17
32
  }
33
+ // Initialize Agent Wallet Keypair
34
+ if (config.agentWallet) {
35
+ try {
36
+ this.agentWalletKeypair = web3_js_1.Keypair.fromSecretKey(bs58_1.default.decode(config.agentWallet));
37
+ this.agentWalletPublicKey = this.agentWalletKeypair.publicKey.toBase58();
38
+ }
39
+ catch (error) {
40
+ throw new errors_js_1.ValidationError("Invalid agent wallet key format. Must be a valid Base58 string.");
41
+ }
42
+ }
43
+ else if (config.agentWalletPublicKey) {
44
+ this.agentWalletPublicKey = config.agentWalletPublicKey;
45
+ }
46
+ // Network Configuration
18
47
  if (config.network === 'testnet') {
19
- this.restUrl = TESTNET_REST_URL;
20
- this.wsUrl = TESTNET_WS_URL;
48
+ this.restUrl = config.restUrl || config_js_1.TESTNET_REST_URL;
49
+ this.wsUrl = config.wsUrl || config_js_1.TESTNET_WS_URL;
21
50
  }
22
51
  else {
23
- this.restUrl = MAINNET_REST_URL;
24
- this.wsUrl = MAINNET_WS_URL;
52
+ this.restUrl = config.restUrl || config_js_1.MAINNET_REST_URL;
53
+ this.wsUrl = config.wsUrl || config_js_1.MAINNET_WS_URL;
25
54
  }
26
- this.axiosInstance = axios.create({
55
+ this.reconnect = config.reconnect ?? true;
56
+ this.axiosInstance = axios_1.default.create({
27
57
  baseURL: this.restUrl,
28
58
  headers: {
29
59
  'Content-Type': 'application/json'
30
- }
60
+ },
61
+ timeout: 10000 // 10s timeout
31
62
  });
32
63
  }
33
- // WebSocket Management
34
- async connect() {
35
- if (this.ws?.readyState === WebSocket.OPEN)
36
- return;
37
- this.ws = new WebSocket(this.wsUrl);
38
- this.wsConnected = new Promise((resolve, reject) => {
39
- const onOpen = () => {
40
- this.ws?.removeListener('error', onError);
41
- resolve();
42
- this.emit('connected');
64
+ // --- Authentication Helper ---
65
+ /**
66
+ * Signs a payload and prepares the request header.
67
+ * Uses Agent Wallet if available, otherwise uses Main Account.
68
+ */
69
+ signAndPrepareRequest(type, payload) {
70
+ if (!this.keypair) {
71
+ throw new errors_js_1.AuthenticationError("Private key is required for signed operations");
72
+ }
73
+ const timestamp = Date.now();
74
+ const header = {
75
+ timestamp,
76
+ expiry_window: config_js_1.DEFAULT_EXPIRY_WINDOW,
77
+ type
78
+ };
79
+ // Determine which key to sign with
80
+ let signer = this.keypair;
81
+ let isAgentSign = false;
82
+ if (this.agentWalletKeypair) {
83
+ signer = this.agentWalletKeypair;
84
+ isAgentSign = true;
85
+ }
86
+ try {
87
+ const { signature } = (0, utils_js_1.signMessage)(header, payload, signer);
88
+ const requestHeader = {
89
+ account: this.keypair.publicKey.toBase58(),
90
+ signature,
91
+ timestamp,
92
+ expiry_window: config_js_1.DEFAULT_EXPIRY_WINDOW
43
93
  };
44
- const onError = (err) => {
45
- this.ws?.removeListener('open', onOpen);
46
- reject(err);
94
+ if (isAgentSign && this.agentWalletPublicKey) {
95
+ requestHeader.agent_wallet = this.agentWalletPublicKey;
96
+ }
97
+ return {
98
+ ...requestHeader,
99
+ ...payload
47
100
  };
48
- this.ws?.once('open', onOpen);
49
- this.ws?.once('error', onError);
50
- });
51
- this.ws.on('message', (data) => {
101
+ }
102
+ catch (error) {
103
+ throw new errors_js_1.PacificaError(`Signing failed: ${error.message}`);
104
+ }
105
+ }
106
+ // --- WebSocket Management ---
107
+ async connect() {
108
+ if (this.ws?.readyState === ws_1.WebSocket.OPEN)
109
+ return;
110
+ return new Promise((resolve, reject) => {
52
111
  try {
53
- const message = JSON.parse(data.toString());
54
- this.handleWsMessage(message);
112
+ this.ws = new ws_1.WebSocket(this.wsUrl);
55
113
  }
56
- catch (err) {
57
- console.error('Failed to parse WS message:', err);
114
+ catch (error) {
115
+ reject(new errors_js_1.NetworkError(`Failed to create WebSocket: ${error.message}`));
116
+ return;
58
117
  }
118
+ this.ws.on('open', () => {
119
+ console.log(`Connected to Pacifica WebSocket: ${this.wsUrl}`);
120
+ this.wsConnected = true;
121
+ this.reconnectDelay = 1000; // Reset backoff
122
+ this.emit('connected');
123
+ resolve();
124
+ });
125
+ this.ws.on('message', (data) => {
126
+ try {
127
+ const message = JSON.parse(data.toString());
128
+ this.handleWsMessage(message);
129
+ }
130
+ catch (err) {
131
+ console.error('Failed to parse WS message:', err);
132
+ }
133
+ });
134
+ this.ws.on('close', () => {
135
+ console.log('WebSocket disconnected');
136
+ this.wsConnected = false;
137
+ this.ws = null;
138
+ this.emit('disconnected');
139
+ if (this.reconnect) {
140
+ this.handleReconnect();
141
+ }
142
+ });
143
+ this.ws.on('error', (err) => {
144
+ console.error('WebSocket error:', err);
145
+ this.emit('error', err);
146
+ });
59
147
  });
60
- this.ws.on('close', () => {
61
- this.ws = null;
62
- this.wsConnected = null;
63
- this.emit('disconnected');
64
- });
65
- await this.wsConnected;
148
+ }
149
+ handleReconnect() {
150
+ console.log(`Reconnecting in ${this.reconnectDelay}ms...`);
151
+ setTimeout(() => {
152
+ this.connect().catch(err => console.error("Reconnect attempt failed:", err));
153
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000); // Max 30s backoff
154
+ }, this.reconnectDelay);
66
155
  }
67
156
  close() {
157
+ this.reconnect = false;
68
158
  if (this.ws) {
69
159
  this.ws.close();
70
160
  }
71
161
  }
72
162
  handleWsMessage(message) {
163
+ // Handle Request Responses
73
164
  if (message.id && this.pendingRequests.has(message.id)) {
74
165
  const { resolve, reject } = this.pendingRequests.get(message.id);
75
166
  if (message.error) {
76
- reject(new Error(message.error.message || JSON.stringify(message.error)));
167
+ reject(new errors_js_1.PacificaError(message.error));
77
168
  }
78
169
  else {
79
- resolve(message.result || message);
170
+ resolve(message.result);
80
171
  }
81
172
  this.pendingRequests.delete(message.id);
173
+ return;
82
174
  }
83
- else {
84
- // Handle subscriptions or unsolicited messages
85
- this.emit('message', message);
175
+ // Handle Subscriptions
176
+ const channel = message.channel;
177
+ const data = message.data;
178
+ if (channel === 'prices') {
179
+ this.emit('prices', data); // Emit raw prices
180
+ }
181
+ else if (channel === 'ticker') {
182
+ this.emit('ticker', data);
183
+ }
184
+ else if (channel === 'orderbook') {
185
+ this.emit('orderbook', data);
86
186
  }
187
+ else if (channel === 'trades') {
188
+ this.emit('trade', data);
189
+ }
190
+ // Generic message emit
191
+ this.emit('message', message);
87
192
  }
88
- async sendWsRequest(method, payload) {
89
- await this.connect();
90
- const id = uuidv4();
91
- const message = {
193
+ async sendWsRequest(method, params) {
194
+ if (!this.ws || this.ws.readyState !== ws_1.WebSocket.OPEN) {
195
+ throw new errors_js_1.NetworkError("WebSocket not connected");
196
+ }
197
+ const id = (0, uuid_1.v4)();
198
+ const request = {
92
199
  id,
93
200
  params: {
94
- [method]: payload
201
+ [method]: params
95
202
  }
96
203
  };
97
204
  return new Promise((resolve, reject) => {
98
205
  this.pendingRequests.set(id, { resolve, reject });
99
- this.ws.send(JSON.stringify(message));
100
- // Timeout after 30 seconds
206
+ this.ws.send(JSON.stringify(request));
207
+ // Timeout after 10 seconds
101
208
  setTimeout(() => {
102
209
  if (this.pendingRequests.has(id)) {
103
210
  this.pendingRequests.delete(id);
104
- reject(new Error('Request timed out'));
211
+ reject(new errors_js_1.NetworkError("Request timed out"));
105
212
  }
106
- }, 30000);
213
+ }, 10000);
107
214
  });
108
215
  }
109
- signAndPreparePayload(type, payload) {
110
- if (!this.keypair) {
111
- throw new Error("Private key is required for signing requests");
112
- }
113
- const timestamp = Date.now(); // Milliseconds
114
- const expiry_window = DEFAULT_EXPIRY_WINDOW;
115
- const header = {
116
- timestamp,
117
- expiry_window,
118
- type
119
- };
120
- const { signature } = signMessage(header, payload, this.keypair);
121
- return {
122
- account: this.keypair.publicKey.toBase58(),
123
- signature,
124
- timestamp,
125
- expiry_window,
126
- ...payload
127
- };
128
- }
216
+ // --- REST API Methods ---
129
217
  /**
130
- * Subscribe to a data source
218
+ * Bind a new Agent Wallet to the main account
131
219
  */
132
- async subscribe(source, params = {}) {
133
- await this.connect();
134
- const message = {
135
- method: "subscribe",
136
- params: {
137
- source,
138
- ...params
139
- }
140
- };
141
- this.ws.send(JSON.stringify(message));
220
+ async bindAgentWallet(newAgentPublicKey) {
221
+ const payload = { agent_wallet: newAgentPublicKey };
222
+ // Must sign with MAIN keypair, not agent wallet
223
+ if (!this.keypair)
224
+ throw new Error("Main private key required to bind agent wallet");
225
+ // Force main key signing
226
+ const tempAgentKey = this.agentWalletKeypair;
227
+ this.agentWalletKeypair = undefined;
228
+ try {
229
+ const signedRequest = this.signAndPrepareRequest("bind_agent_wallet", payload);
230
+ const response = await this.axiosInstance.post('/agent/bind', signedRequest);
231
+ return { success: true, data: response.data };
232
+ }
233
+ catch (error) {
234
+ return { success: false, data: null, error: error.message };
235
+ }
236
+ finally {
237
+ this.agentWalletKeypair = tempAgentKey; // Restore
238
+ }
142
239
  }
143
- // WebSocket Trading Operations
144
240
  /**
145
- * Create a market order via WebSocket
241
+ * Get account information (balances, margin, leverage)
146
242
  */
147
- async createMarketOrderWs(params) {
148
- const payload = {
149
- symbol: params.symbol,
150
- side: params.side,
151
- amount: params.amount,
152
- reduce_only: params.reduce_only ?? false,
153
- slippage_percent: params.slippage_percent ?? "0.5",
154
- client_order_id: params.client_order_id ?? uuidv4()
155
- };
156
- const signedPayload = this.signAndPreparePayload("create_market_order", payload);
157
- return this.sendWsRequest("create_market_order", signedPayload);
243
+ async getAccountInfo() {
244
+ if (!this.keypair)
245
+ throw new Error("Private key required");
246
+ // Usually GET requests just need the account param, but some might need signing
247
+ // Checking python sdk... usually it's signed for private info.
248
+ // Assuming it's a signed GET or POST.
249
+ // Let's assume standard signed POST for private info based on pattern.
250
+ // If it's GET, we might append signature to query params.
251
+ // For now, implementing as signed POST to /account/info (hypothetical, checking python sdk recommended if strict match needed)
252
+ // Wait, web content said: apiClient.getAccountInfo(publicKey).
253
+ // If it's public info, just GET. But balance is private.
254
+ // Let's implement as signed request.
255
+ const signedRequest = this.signAndPrepareRequest("get_account_info", {});
256
+ // Note: Actual endpoint might differ. Using generic approach or mapped if known.
257
+ // Python SDK usually has get_account_info.py? No.
258
+ // It has list_subaccounts.py.
259
+ // Let's stick to what we know works or generic signed request.
260
+ try {
261
+ // Try generic signed POST to /account
262
+ const response = await this.axiosInstance.post('/account', signedRequest);
263
+ return { success: true, data: response.data };
264
+ }
265
+ catch (error) {
266
+ return { success: false, data: null, error: error.message };
267
+ }
158
268
  }
159
- /**
160
- * Create a limit order via WebSocket
161
- */
162
- async createLimitOrderWs(params) {
163
- const payload = {
164
- symbol: params.symbol,
165
- side: params.side,
166
- amount: params.amount,
167
- price: params.price,
168
- reduce_only: params.reduce_only ?? false,
169
- client_order_id: params.client_order_id ?? uuidv4(),
170
- tif: params.tif || "GTC"
171
- };
172
- // Note: The method name in WS params is 'create_order', but type is 'create_order'
173
- const signedPayload = this.signAndPreparePayload("create_order", payload);
174
- return this.sendWsRequest("create_order", signedPayload);
269
+ async getPositions() {
270
+ const signedRequest = this.signAndPrepareRequest("get_positions", {});
271
+ try {
272
+ const response = await this.axiosInstance.post('/positions', signedRequest);
273
+ return { success: true, data: response.data };
274
+ }
275
+ catch (error) {
276
+ return { success: false, data: null, error: error.message };
277
+ }
175
278
  }
176
- /**
177
- * Cancel an order via WebSocket
178
- */
179
- async cancelOrderWs(params) {
180
- const payload = {
181
- symbol: params.symbol
182
- };
183
- if (params.order_id)
184
- payload.order_id = params.order_id;
185
- if (params.client_order_id)
186
- payload.client_order_id = params.client_order_id;
187
- const signedPayload = this.signAndPreparePayload("cancel_order", payload);
188
- return this.sendWsRequest("cancel_order", signedPayload);
279
+ async getOrders(symbol) {
280
+ const payload = symbol ? { symbol } : {};
281
+ const signedRequest = this.signAndPrepareRequest("get_orders", payload);
282
+ try {
283
+ const response = await this.axiosInstance.post('/orders/list', signedRequest);
284
+ return { success: true, data: response.data };
285
+ }
286
+ catch (error) {
287
+ return { success: false, data: null, error: error.message };
288
+ }
189
289
  }
190
290
  /**
191
- * Cancel all orders via WebSocket
291
+ * Get exchange information including all available markets
192
292
  */
193
- async cancelAllOrdersWs(params) {
194
- const payload = {
195
- all_symbols: params.all_symbols ?? true,
196
- exclude_reduce_only: params.exclude_reduce_only ?? false
197
- };
198
- // Note: symbol is not used if all_symbols is true? Python code sends just these two.
199
- const signedPayload = this.signAndPreparePayload("cancel_all_orders", payload);
200
- return this.sendWsRequest("cancel_all_orders", signedPayload);
293
+ async getMarkets() {
294
+ const response = await this.axiosInstance.get('/info');
295
+ return response.data;
201
296
  }
202
- // REST API Methods
203
297
  /**
204
- * Helper to send signed REST requests
298
+ * Get current prices for all markets
205
299
  */
206
- async sendRestRequest(endpoint, type, payload) {
207
- const signedPayload = this.signAndPreparePayload(type, payload);
208
- const response = await this.axiosInstance.post(endpoint, signedPayload);
300
+ async getPrices() {
301
+ const response = await this.axiosInstance.get('/info/prices');
209
302
  return response.data;
210
303
  }
211
- /**
212
- * Create a market order via REST
213
- */
214
- async createMarketOrderRest(params) {
304
+ // --- Trading Methods (WebSocket) ---
305
+ async createOrder(params) {
215
306
  const payload = {
216
307
  symbol: params.symbol,
217
308
  side: params.side,
218
309
  amount: params.amount,
219
310
  reduce_only: params.reduce_only ?? false,
220
- slippage_percent: params.slippage_percent ?? "0.5",
221
- client_order_id: params.client_order_id ?? uuidv4()
311
+ client_order_id: params.client_order_id || (0, uuid_1.v4)(),
222
312
  };
223
- return this.sendRestRequest('/orders/create_market', "create_market_order", payload);
313
+ if (params.type === 'limit') {
314
+ if (!params.price)
315
+ throw new Error("Price is required for limit orders");
316
+ payload.price = params.price;
317
+ payload.tif = params.tif || 'GTC';
318
+ }
319
+ else {
320
+ // Market Order
321
+ payload.slippage_percent = params.slippage_percent || "0.5";
322
+ }
323
+ if (params.take_profit)
324
+ payload.take_profit = params.take_profit;
325
+ if (params.stop_loss)
326
+ payload.stop_loss = params.stop_loss;
327
+ const type = params.type === 'limit' ? 'create_limit_order' : 'create_market_order';
328
+ const signedRequest = this.signAndPrepareRequest(type, payload);
329
+ return this.sendWsRequest(type, signedRequest);
224
330
  }
225
- /**
226
- * Create a limit order via REST
227
- */
228
- async createLimitOrderRest(params) {
229
- const payload = {
230
- symbol: params.symbol,
231
- side: params.side,
232
- amount: params.amount,
233
- price: params.price,
234
- reduce_only: params.reduce_only ?? false,
235
- client_order_id: params.client_order_id ?? uuidv4(),
236
- tif: params.tif || "GTC"
237
- };
238
- return this.sendRestRequest('/orders/create', "create_order", payload);
331
+ async cancelOrder(orderId, symbol) {
332
+ const payload = { order_id: orderId, symbol };
333
+ const signedRequest = this.signAndPrepareRequest("cancel_order", payload);
334
+ return this.sendWsRequest("cancel_order", signedRequest);
239
335
  }
240
- /**
241
- * Cancel an order via REST
242
- */
243
- async cancelOrderRest(params) {
244
- const payload = {
245
- symbol: params.symbol
246
- };
247
- if (params.order_id)
248
- payload.order_id = params.order_id;
249
- if (params.client_order_id)
250
- payload.client_order_id = params.client_order_id;
251
- return this.sendRestRequest('/orders/cancel', "cancel_order", payload);
336
+ async cancelAllOrders(symbol) {
337
+ const payload = symbol ? { symbol } : {};
338
+ const signedRequest = this.signAndPrepareRequest("cancel_all_orders", payload);
339
+ return this.sendWsRequest("cancel_all_orders", signedRequest);
252
340
  }
253
- /**
254
- * Cancel all orders via REST
255
- */
256
- async cancelAllOrdersRest(params) {
257
- const payload = {
258
- all_symbols: params.all_symbols ?? true,
259
- exclude_reduce_only: params.exclude_reduce_only ?? false
341
+ // --- Subscription Methods ---
342
+ subscribe(channel, symbol) {
343
+ if (!this.ws || this.ws.readyState !== ws_1.WebSocket.OPEN) {
344
+ throw new Error("WebSocket not connected");
345
+ }
346
+ const msg = {
347
+ method: "subscribe",
348
+ params: { channel }
260
349
  };
261
- return this.sendRestRequest('/orders/cancel_all', "cancel_all_orders", payload);
350
+ if (symbol) {
351
+ msg.params.symbol = symbol;
352
+ }
353
+ this.ws.send(JSON.stringify(msg));
262
354
  }
263
- /**
264
- * List subaccounts via REST
265
- */
266
- async listSubaccounts() {
267
- return this.sendRestRequest('/account/subaccount/list', "list_subaccounts", {});
355
+ subscribeToPrices() {
356
+ this.subscribe('prices');
268
357
  }
269
- // Public REST Methods
270
- /**
271
- * Get exchange information including all available markets
272
- */
273
- async getMarkets() {
274
- const response = await this.axiosInstance.get('/info');
275
- return response.data;
358
+ subscribeToTicker(symbol) {
359
+ this.subscribe('ticker', symbol);
276
360
  }
277
- /**
278
- * Get current prices for all markets
279
- */
280
- async getPrices() {
281
- const response = await this.axiosInstance.get('/info/prices');
282
- return response.data;
361
+ subscribeToOrderbook(symbol) {
362
+ this.subscribe('orderbook', symbol);
363
+ }
364
+ subscribeToTrades(symbol) {
365
+ this.subscribe('trades', symbol);
283
366
  }
284
367
  }
368
+ exports.PacificaClient = PacificaClient;
package/dist/config.js CHANGED
@@ -1,5 +1,8 @@
1
- export const MAINNET_REST_URL = "https://api.pacifica.fi/api/v1";
2
- export const MAINNET_WS_URL = "wss://ws.pacifica.fi/ws";
3
- export const TESTNET_REST_URL = "https://test-api.pacifica.fi/api/v1";
4
- export const TESTNET_WS_URL = "wss://test-ws.pacifica.fi/ws";
5
- export const DEFAULT_EXPIRY_WINDOW = 5000;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_EXPIRY_WINDOW = exports.TESTNET_WS_URL = exports.TESTNET_REST_URL = exports.MAINNET_WS_URL = exports.MAINNET_REST_URL = void 0;
4
+ exports.MAINNET_REST_URL = "https://api.pacifica.fi/api/v1";
5
+ exports.MAINNET_WS_URL = "wss://ws.pacifica.fi/ws";
6
+ exports.TESTNET_REST_URL = "https://test-api.pacifica.fi/api/v1";
7
+ exports.TESTNET_WS_URL = "wss://test-ws.pacifica.fi/ws";
8
+ exports.DEFAULT_EXPIRY_WINDOW = 5000;
@@ -0,0 +1,15 @@
1
+ export declare class PacificaError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class AuthenticationError extends PacificaError {
5
+ constructor(message?: string);
6
+ }
7
+ export declare class NetworkError extends PacificaError {
8
+ constructor(message?: string);
9
+ }
10
+ export declare class ValidationError extends PacificaError {
11
+ constructor(message: string);
12
+ }
13
+ export declare class OrderError extends PacificaError {
14
+ constructor(message: string);
15
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OrderError = exports.ValidationError = exports.NetworkError = exports.AuthenticationError = exports.PacificaError = void 0;
4
+ class PacificaError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = 'PacificaError';
8
+ }
9
+ }
10
+ exports.PacificaError = PacificaError;
11
+ class AuthenticationError extends PacificaError {
12
+ constructor(message = 'Authentication failed') {
13
+ super(message);
14
+ this.name = 'AuthenticationError';
15
+ }
16
+ }
17
+ exports.AuthenticationError = AuthenticationError;
18
+ class NetworkError extends PacificaError {
19
+ constructor(message = 'Network error occurred') {
20
+ super(message);
21
+ this.name = 'NetworkError';
22
+ }
23
+ }
24
+ exports.NetworkError = NetworkError;
25
+ class ValidationError extends PacificaError {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = 'ValidationError';
29
+ }
30
+ }
31
+ exports.ValidationError = ValidationError;
32
+ class OrderError extends PacificaError {
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = 'OrderError';
36
+ }
37
+ }
38
+ exports.OrderError = OrderError;
package/dist/index.d.ts CHANGED
@@ -2,3 +2,4 @@ export * from "./config.js";
2
2
  export * from "./types.js";
3
3
  export * from "./utils.js";
4
4
  export * from "./client.js";
5
+ export * from "./errors.js";
package/dist/index.js CHANGED
@@ -1,4 +1,21 @@
1
- export * from "./config.js";
2
- export * from "./types.js";
3
- export * from "./utils.js";
4
- export * from "./client.js";
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./config.js"), exports);
18
+ __exportStar(require("./types.js"), exports);
19
+ __exportStar(require("./utils.js"), exports);
20
+ __exportStar(require("./client.js"), exports);
21
+ __exportStar(require("./errors.js"), exports);
package/dist/types.d.ts CHANGED
@@ -8,9 +8,94 @@ export interface SignaturePayload {
8
8
  }
9
9
  export interface RequestHeader {
10
10
  account: string;
11
+ agent_wallet?: string;
11
12
  signature: string;
12
13
  timestamp: number;
13
14
  expiry_window: number;
14
15
  }
15
16
  export interface SignedRequest extends RequestHeader, SignaturePayload {
16
17
  }
18
+ export interface ApiResponse<T> {
19
+ success: boolean;
20
+ data: T | null;
21
+ error?: string;
22
+ }
23
+ export interface AccountInfo {
24
+ balance: string;
25
+ collateral: string;
26
+ free_collateral: string;
27
+ margin_usage: string;
28
+ leverage: string;
29
+ }
30
+ export interface Position {
31
+ symbol: string;
32
+ side: 'long' | 'short';
33
+ amount: string;
34
+ entry_price: string;
35
+ mark_price: string;
36
+ unrealized_pnl: string;
37
+ liquidation_price: string;
38
+ leverage: string;
39
+ }
40
+ export interface Order {
41
+ order_id: string;
42
+ client_order_id?: string;
43
+ symbol: string;
44
+ side: 'bid' | 'ask';
45
+ type: 'limit' | 'market';
46
+ price?: string;
47
+ amount: string;
48
+ filled_amount: string;
49
+ status: 'open' | 'filled' | 'canceled' | 'rejected';
50
+ timestamp: number;
51
+ }
52
+ export interface Ticker {
53
+ symbol: string;
54
+ price: string;
55
+ change_24h: string;
56
+ volume_24h: string;
57
+ high_24h: string;
58
+ low_24h: string;
59
+ }
60
+ export interface Orderbook {
61
+ symbol: string;
62
+ bids: [string, string][];
63
+ asks: [string, string][];
64
+ timestamp: number;
65
+ }
66
+ export interface Trade {
67
+ symbol: string;
68
+ price: string;
69
+ size: string;
70
+ side: 'buy' | 'sell';
71
+ timestamp: number;
72
+ trade_id: string;
73
+ }
74
+ export interface CreateOrderParams {
75
+ symbol: string;
76
+ side: 'bid' | 'ask';
77
+ amount: string;
78
+ price?: string;
79
+ type?: 'limit' | 'market';
80
+ reduce_only?: boolean;
81
+ client_order_id?: string;
82
+ tif?: 'GTC' | 'IOC' | 'FOK';
83
+ slippage_percent?: string;
84
+ take_profit?: {
85
+ stop_price: string;
86
+ limit_price?: string;
87
+ };
88
+ stop_loss?: {
89
+ stop_price: string;
90
+ limit_price?: string;
91
+ };
92
+ }
93
+ export interface PacificaClientConfig {
94
+ network?: 'mainnet' | 'testnet';
95
+ privateKey?: string;
96
+ agentWallet?: string;
97
+ agentWalletPublicKey?: string;
98
+ wsUrl?: string;
99
+ restUrl?: string;
100
+ reconnect?: boolean;
101
+ }
package/dist/types.js CHANGED
@@ -1 +1,2 @@
1
- export {};
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/utils.js CHANGED
@@ -1,9 +1,17 @@
1
- import bs58 from "bs58";
2
- import nacl from "tweetnacl";
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.sortJsonKeys = sortJsonKeys;
7
+ exports.prepareMessage = prepareMessage;
8
+ exports.signMessage = signMessage;
9
+ const bs58_1 = __importDefault(require("bs58"));
10
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
3
11
  /**
4
12
  * Sorts object keys recursively to match Python's behavior
5
13
  */
6
- export function sortJsonKeys(value) {
14
+ function sortJsonKeys(value) {
7
15
  if (value === null || value === undefined) {
8
16
  return value;
9
17
  }
@@ -24,7 +32,7 @@ export function sortJsonKeys(value) {
24
32
  /**
25
33
  * Prepares the message string for signing
26
34
  */
27
- export function prepareMessage(header, payload) {
35
+ function prepareMessage(header, payload) {
28
36
  const data = {
29
37
  ...header,
30
38
  data: payload
@@ -37,12 +45,12 @@ export function prepareMessage(header, payload) {
37
45
  /**
38
46
  * Signs the message using the provided keypair
39
47
  */
40
- export function signMessage(header, payload, keypair) {
48
+ function signMessage(header, payload, keypair) {
41
49
  const message = prepareMessage(header, payload);
42
50
  const messageBytes = new TextEncoder().encode(message);
43
- const signatureBytes = nacl.sign.detached(messageBytes, keypair.secretKey);
51
+ const signatureBytes = tweetnacl_1.default.sign.detached(messageBytes, keypair.secretKey);
44
52
  return {
45
53
  message,
46
- signature: bs58.encode(signatureBytes)
54
+ signature: bs58_1.default.encode(signatureBytes)
47
55
  };
48
56
  }
package/package.json CHANGED
@@ -1,28 +1,40 @@
1
1
  {
2
2
  "name": "pacifica-js-sdk",
3
- "version": "1.0.0",
4
- "description": "Official JavaScript/TypeScript SDK for Pacifica",
3
+ "version": "1.1.0",
4
+ "description": "Community-maintained JavaScript/TypeScript SDK for Pacifica",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
- "type": "module",
8
7
  "files": [
9
8
  "dist",
10
- "README.md"
9
+ "README.md",
10
+ "LICENSE"
11
11
  ],
12
12
  "scripts": {
13
13
  "build": "tsc",
14
- "prepublishOnly": "npm run build"
14
+ "test": "jest",
15
+ "lint": "eslint src/**/*.ts",
16
+ "format": "prettier --write src/**/*.ts",
17
+ "prepublishOnly": "npm run build && npm test"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/notyouroniichan/pacifica-js-sdk.git"
15
22
  },
16
23
  "keywords": [
17
24
  "pacifica",
18
- "sdk",
19
- "crypto",
20
- "trading",
21
25
  "solana",
22
- "defi"
26
+ "defi",
27
+ "trading",
28
+ "sdk",
29
+ "agent",
30
+ "bot"
23
31
  ],
24
- "author": "Pacifica",
32
+ "author": "notyouroniichan",
25
33
  "license": "ISC",
34
+ "bugs": {
35
+ "url": "https://github.com/notyouroniichan/pacifica-js-sdk/issues"
36
+ },
37
+ "homepage": "https://github.com/notyouroniichan/pacifica-js-sdk#readme",
26
38
  "dependencies": {
27
39
  "@solana/web3.js": "^1.98.4",
28
40
  "axios": "^1.13.6",
@@ -32,10 +44,19 @@
32
44
  "ws": "^8.19.0"
33
45
  },
34
46
  "devDependencies": {
47
+ "@types/jest": "^30.0.0",
35
48
  "@types/node": "^25.3.2",
36
49
  "@types/uuid": "^10.0.0",
37
50
  "@types/ws": "^8.18.1",
51
+ "@typescript-eslint/eslint-plugin": "^8.56.1",
52
+ "@typescript-eslint/parser": "^8.56.1",
38
53
  "dotenv": "^17.3.1",
54
+ "eslint": "^10.0.2",
55
+ "eslint-config-prettier": "^10.1.8",
56
+ "eslint-plugin-prettier": "^5.5.5",
57
+ "jest": "^30.2.0",
58
+ "prettier": "^3.8.1",
59
+ "ts-jest": "^29.4.6",
39
60
  "ts-node": "^10.9.2",
40
61
  "typescript": "^5.9.3"
41
62
  }