pacifica-js-sdk 1.0.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 +52 -0
- package/dist/client.d.ts +117 -0
- package/dist/client.js +284 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +5 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.js +48 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
# Pacifica JS SDK
|
|
3
|
+
|
|
4
|
+
Official JavaScript/TypeScript SDK for [Pacifica](https://pacifica.fi).
|
|
5
|
+
Converted from the official [Python SDK](https://github.com/pacifica-fi/python-sdk).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install pacifica-js-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
Create a `.env` file with your private key (optional for public data):
|
|
16
|
+
```
|
|
17
|
+
PRIVATE_KEY=your_base58_private_key
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Run the example:
|
|
21
|
+
```bash
|
|
22
|
+
npx tsx examples/usage.ts
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Code Example
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { PacificaClient } from './src';
|
|
29
|
+
|
|
30
|
+
const client = new PacificaClient({
|
|
31
|
+
privateKey: "YOUR_BASE58_PRIVATE_KEY",
|
|
32
|
+
network: "testnet"
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// REST API
|
|
36
|
+
const subaccounts = await client.listSubaccounts();
|
|
37
|
+
|
|
38
|
+
// WebSocket API
|
|
39
|
+
await client.connect();
|
|
40
|
+
await client.createMarketOrderWs({
|
|
41
|
+
symbol: "BTC",
|
|
42
|
+
side: "bid",
|
|
43
|
+
amount: "0.1"
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
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
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
export interface PacificaClientConfig {
|
|
3
|
+
privateKey?: string;
|
|
4
|
+
network?: 'mainnet' | 'testnet';
|
|
5
|
+
}
|
|
6
|
+
export declare class PacificaClient extends EventEmitter {
|
|
7
|
+
private keypair?;
|
|
8
|
+
private restUrl;
|
|
9
|
+
private wsUrl;
|
|
10
|
+
private ws;
|
|
11
|
+
private wsConnected;
|
|
12
|
+
private pendingRequests;
|
|
13
|
+
private axiosInstance;
|
|
14
|
+
constructor(config: PacificaClientConfig);
|
|
15
|
+
connect(): Promise<void>;
|
|
16
|
+
close(): void;
|
|
17
|
+
private handleWsMessage;
|
|
18
|
+
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
|
+
/**
|
|
99
|
+
* Cancel all orders via REST
|
|
100
|
+
*/
|
|
101
|
+
cancelAllOrdersRest(params: {
|
|
102
|
+
all_symbols?: boolean;
|
|
103
|
+
exclude_reduce_only?: boolean;
|
|
104
|
+
}): Promise<any>;
|
|
105
|
+
/**
|
|
106
|
+
* List subaccounts via REST
|
|
107
|
+
*/
|
|
108
|
+
listSubaccounts(): Promise<any>;
|
|
109
|
+
/**
|
|
110
|
+
* Get exchange information including all available markets
|
|
111
|
+
*/
|
|
112
|
+
getMarkets(): Promise<any>;
|
|
113
|
+
/**
|
|
114
|
+
* Get current prices for all markets
|
|
115
|
+
*/
|
|
116
|
+
getPrices(): Promise<any>;
|
|
117
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
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 {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
super();
|
|
12
|
+
this.ws = null;
|
|
13
|
+
this.wsConnected = null;
|
|
14
|
+
this.pendingRequests = new Map();
|
|
15
|
+
if (config.privateKey) {
|
|
16
|
+
this.keypair = Keypair.fromSecretKey(bs58.decode(config.privateKey));
|
|
17
|
+
}
|
|
18
|
+
if (config.network === 'testnet') {
|
|
19
|
+
this.restUrl = TESTNET_REST_URL;
|
|
20
|
+
this.wsUrl = TESTNET_WS_URL;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this.restUrl = MAINNET_REST_URL;
|
|
24
|
+
this.wsUrl = MAINNET_WS_URL;
|
|
25
|
+
}
|
|
26
|
+
this.axiosInstance = axios.create({
|
|
27
|
+
baseURL: this.restUrl,
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
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');
|
|
43
|
+
};
|
|
44
|
+
const onError = (err) => {
|
|
45
|
+
this.ws?.removeListener('open', onOpen);
|
|
46
|
+
reject(err);
|
|
47
|
+
};
|
|
48
|
+
this.ws?.once('open', onOpen);
|
|
49
|
+
this.ws?.once('error', onError);
|
|
50
|
+
});
|
|
51
|
+
this.ws.on('message', (data) => {
|
|
52
|
+
try {
|
|
53
|
+
const message = JSON.parse(data.toString());
|
|
54
|
+
this.handleWsMessage(message);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error('Failed to parse WS message:', err);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
this.ws.on('close', () => {
|
|
61
|
+
this.ws = null;
|
|
62
|
+
this.wsConnected = null;
|
|
63
|
+
this.emit('disconnected');
|
|
64
|
+
});
|
|
65
|
+
await this.wsConnected;
|
|
66
|
+
}
|
|
67
|
+
close() {
|
|
68
|
+
if (this.ws) {
|
|
69
|
+
this.ws.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
handleWsMessage(message) {
|
|
73
|
+
if (message.id && this.pendingRequests.has(message.id)) {
|
|
74
|
+
const { resolve, reject } = this.pendingRequests.get(message.id);
|
|
75
|
+
if (message.error) {
|
|
76
|
+
reject(new Error(message.error.message || JSON.stringify(message.error)));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
resolve(message.result || message);
|
|
80
|
+
}
|
|
81
|
+
this.pendingRequests.delete(message.id);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Handle subscriptions or unsolicited messages
|
|
85
|
+
this.emit('message', message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async sendWsRequest(method, payload) {
|
|
89
|
+
await this.connect();
|
|
90
|
+
const id = uuidv4();
|
|
91
|
+
const message = {
|
|
92
|
+
id,
|
|
93
|
+
params: {
|
|
94
|
+
[method]: payload
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
99
|
+
this.ws.send(JSON.stringify(message));
|
|
100
|
+
// Timeout after 30 seconds
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
if (this.pendingRequests.has(id)) {
|
|
103
|
+
this.pendingRequests.delete(id);
|
|
104
|
+
reject(new Error('Request timed out'));
|
|
105
|
+
}
|
|
106
|
+
}, 30000);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
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
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Subscribe to a data source
|
|
131
|
+
*/
|
|
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));
|
|
142
|
+
}
|
|
143
|
+
// WebSocket Trading Operations
|
|
144
|
+
/**
|
|
145
|
+
* Create a market order via WebSocket
|
|
146
|
+
*/
|
|
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);
|
|
158
|
+
}
|
|
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);
|
|
175
|
+
}
|
|
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);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Cancel all orders via WebSocket
|
|
192
|
+
*/
|
|
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);
|
|
201
|
+
}
|
|
202
|
+
// REST API Methods
|
|
203
|
+
/**
|
|
204
|
+
* Helper to send signed REST requests
|
|
205
|
+
*/
|
|
206
|
+
async sendRestRequest(endpoint, type, payload) {
|
|
207
|
+
const signedPayload = this.signAndPreparePayload(type, payload);
|
|
208
|
+
const response = await this.axiosInstance.post(endpoint, signedPayload);
|
|
209
|
+
return response.data;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Create a market order via REST
|
|
213
|
+
*/
|
|
214
|
+
async createMarketOrderRest(params) {
|
|
215
|
+
const payload = {
|
|
216
|
+
symbol: params.symbol,
|
|
217
|
+
side: params.side,
|
|
218
|
+
amount: params.amount,
|
|
219
|
+
reduce_only: params.reduce_only ?? false,
|
|
220
|
+
slippage_percent: params.slippage_percent ?? "0.5",
|
|
221
|
+
client_order_id: params.client_order_id ?? uuidv4()
|
|
222
|
+
};
|
|
223
|
+
return this.sendRestRequest('/orders/create_market', "create_market_order", payload);
|
|
224
|
+
}
|
|
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);
|
|
239
|
+
}
|
|
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);
|
|
252
|
+
}
|
|
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
|
|
260
|
+
};
|
|
261
|
+
return this.sendRestRequest('/orders/cancel_all', "cancel_all_orders", payload);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* List subaccounts via REST
|
|
265
|
+
*/
|
|
266
|
+
async listSubaccounts() {
|
|
267
|
+
return this.sendRestRequest('/account/subaccount/list', "list_subaccounts", {});
|
|
268
|
+
}
|
|
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;
|
|
276
|
+
}
|
|
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;
|
|
283
|
+
}
|
|
284
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const MAINNET_REST_URL = "https://api.pacifica.fi/api/v1";
|
|
2
|
+
export declare const MAINNET_WS_URL = "wss://ws.pacifica.fi/ws";
|
|
3
|
+
export declare const TESTNET_REST_URL = "https://test-api.pacifica.fi/api/v1";
|
|
4
|
+
export declare const TESTNET_WS_URL = "wss://test-ws.pacifica.fi/ws";
|
|
5
|
+
export declare const DEFAULT_EXPIRY_WINDOW = 5000;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
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;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SignatureHeader {
|
|
2
|
+
timestamp: number;
|
|
3
|
+
expiry_window: number;
|
|
4
|
+
type: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SignaturePayload {
|
|
7
|
+
[key: string]: any;
|
|
8
|
+
}
|
|
9
|
+
export interface RequestHeader {
|
|
10
|
+
account: string;
|
|
11
|
+
signature: string;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
expiry_window: number;
|
|
14
|
+
}
|
|
15
|
+
export interface SignedRequest extends RequestHeader, SignaturePayload {
|
|
16
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Keypair } from "@solana/web3.js";
|
|
2
|
+
import { SignatureHeader, SignaturePayload } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Sorts object keys recursively to match Python's behavior
|
|
5
|
+
*/
|
|
6
|
+
export declare function sortJsonKeys(value: any): any;
|
|
7
|
+
/**
|
|
8
|
+
* Prepares the message string for signing
|
|
9
|
+
*/
|
|
10
|
+
export declare function prepareMessage(header: SignatureHeader, payload: SignaturePayload): string;
|
|
11
|
+
/**
|
|
12
|
+
* Signs the message using the provided keypair
|
|
13
|
+
*/
|
|
14
|
+
export declare function signMessage(header: SignatureHeader, payload: SignaturePayload, keypair: Keypair): {
|
|
15
|
+
message: string;
|
|
16
|
+
signature: string;
|
|
17
|
+
};
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import bs58 from "bs58";
|
|
2
|
+
import nacl from "tweetnacl";
|
|
3
|
+
/**
|
|
4
|
+
* Sorts object keys recursively to match Python's behavior
|
|
5
|
+
*/
|
|
6
|
+
export function sortJsonKeys(value) {
|
|
7
|
+
if (value === null || value === undefined) {
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
return value.map(sortJsonKeys);
|
|
12
|
+
}
|
|
13
|
+
if (typeof value === 'object') {
|
|
14
|
+
const sorted = {};
|
|
15
|
+
Object.keys(value)
|
|
16
|
+
.sort()
|
|
17
|
+
.forEach(key => {
|
|
18
|
+
sorted[key] = sortJsonKeys(value[key]);
|
|
19
|
+
});
|
|
20
|
+
return sorted;
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Prepares the message string for signing
|
|
26
|
+
*/
|
|
27
|
+
export function prepareMessage(header, payload) {
|
|
28
|
+
const data = {
|
|
29
|
+
...header,
|
|
30
|
+
data: payload
|
|
31
|
+
};
|
|
32
|
+
const sortedData = sortJsonKeys(data);
|
|
33
|
+
// JSON.stringify without arguments produces compact JSON (no spaces)
|
|
34
|
+
// which matches Python's separators=(",", ":")
|
|
35
|
+
return JSON.stringify(sortedData);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Signs the message using the provided keypair
|
|
39
|
+
*/
|
|
40
|
+
export function signMessage(header, payload, keypair) {
|
|
41
|
+
const message = prepareMessage(header, payload);
|
|
42
|
+
const messageBytes = new TextEncoder().encode(message);
|
|
43
|
+
const signatureBytes = nacl.sign.detached(messageBytes, keypair.secretKey);
|
|
44
|
+
return {
|
|
45
|
+
message,
|
|
46
|
+
signature: bs58.encode(signatureBytes)
|
|
47
|
+
};
|
|
48
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pacifica-js-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official JavaScript/TypeScript SDK for Pacifica",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"pacifica",
|
|
18
|
+
"sdk",
|
|
19
|
+
"crypto",
|
|
20
|
+
"trading",
|
|
21
|
+
"solana",
|
|
22
|
+
"defi"
|
|
23
|
+
],
|
|
24
|
+
"author": "Pacifica",
|
|
25
|
+
"license": "ISC",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@solana/web3.js": "^1.98.4",
|
|
28
|
+
"axios": "^1.13.6",
|
|
29
|
+
"bs58": "^6.0.0",
|
|
30
|
+
"tweetnacl": "^1.0.3",
|
|
31
|
+
"uuid": "^13.0.0",
|
|
32
|
+
"ws": "^8.19.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^25.3.2",
|
|
36
|
+
"@types/uuid": "^10.0.0",
|
|
37
|
+
"@types/ws": "^8.18.1",
|
|
38
|
+
"dotenv": "^17.3.1",
|
|
39
|
+
"ts-node": "^10.9.2",
|
|
40
|
+
"typescript": "^5.9.3"
|
|
41
|
+
}
|
|
42
|
+
}
|