perps-sdk-ts 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +11 -0
- package/CONTRACT_METHOD_FIXES.md +189 -0
- package/INTEGRATION_SUMMARY.md +219 -0
- package/OPTIMIZATION_GUIDE.md +238 -0
- package/README.md +384 -0
- package/SNAPSHOT_FIX_SUMMARY.md +161 -0
- package/SNAPSHOT_OPTIMIZATION_SUMMARY.md +199 -0
- package/dist/abis/Referral.d.ts +36 -0
- package/dist/abis/Referral.js +4 -0
- package/dist/abis/Trading.d.ts +57 -0
- package/dist/abis/Trading.js +742 -0
- package/dist/abis/erc20.d.ts +51 -0
- package/dist/abis/erc20.js +4 -0
- package/dist/abis/index.d.ts +8 -0
- package/dist/abis/index.js +24 -0
- package/dist/abis/multicall.d.ts +85 -0
- package/dist/abis/multicall.js +4 -0
- package/dist/abis/pairInfos.d.ts +77 -0
- package/dist/abis/pairInfos.js +4 -0
- package/dist/abis/pairStorage.d.ts +124 -0
- package/dist/abis/pairStorage.js +4 -0
- package/dist/abis/priceAggregator.d.ts +77 -0
- package/dist/abis/priceAggregator.js +4 -0
- package/dist/abis/tardingStorage.d.ts +97 -0
- package/dist/abis/tardingStorage.js +1295 -0
- package/dist/abis.d.ts +623 -0
- package/dist/abis.js +49 -0
- package/dist/client.d.ts +118 -0
- package/dist/client.js +224 -0
- package/dist/config.d.ts +43 -0
- package/dist/config.js +42 -0
- package/dist/crypto/spki.d.ts +55 -0
- package/dist/crypto/spki.js +160 -0
- package/dist/feed/feed_client.d.ts +68 -0
- package/dist/feed/feed_client.js +239 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +87 -0
- package/dist/rpc/asset_parameters.d.ts +62 -0
- package/dist/rpc/asset_parameters.js +169 -0
- package/dist/rpc/blended.d.ts +23 -0
- package/dist/rpc/blended.js +55 -0
- package/dist/rpc/category_parameters.d.ts +34 -0
- package/dist/rpc/category_parameters.js +105 -0
- package/dist/rpc/delegation.d.ts +81 -0
- package/dist/rpc/delegation.js +180 -0
- package/dist/rpc/fee_parameters.d.ts +46 -0
- package/dist/rpc/fee_parameters.js +113 -0
- package/dist/rpc/multicall.d.ts +83 -0
- package/dist/rpc/multicall.js +117 -0
- package/dist/rpc/pair_info_queries.d.ts +101 -0
- package/dist/rpc/pair_info_queries.js +161 -0
- package/dist/rpc/pairs_cache.d.ts +62 -0
- package/dist/rpc/pairs_cache.js +240 -0
- package/dist/rpc/referral_operations.d.ts +67 -0
- package/dist/rpc/referral_operations.js +143 -0
- package/dist/rpc/snapshot.d.ts +49 -0
- package/dist/rpc/snapshot.js +162 -0
- package/dist/rpc/trade.d.ts +84 -0
- package/dist/rpc/trade.js +249 -0
- package/dist/rpc/trading_operations.d.ts +103 -0
- package/dist/rpc/trading_operations.js +295 -0
- package/dist/rpc/trading_parameters.d.ts +49 -0
- package/dist/rpc/trading_parameters.js +94 -0
- package/dist/signers/base.d.ts +24 -0
- package/dist/signers/base.js +10 -0
- package/dist/signers/kms.d.ts +47 -0
- package/dist/signers/kms.js +172 -0
- package/dist/signers/local.d.ts +43 -0
- package/dist/signers/local.js +64 -0
- package/dist/types.d.ts +1419 -0
- package/dist/types.js +245 -0
- package/dist/utils.d.ts +52 -0
- package/dist/utils.js +134 -0
- package/examples/advanced-queries.ts +181 -0
- package/examples/basic-usage.ts +78 -0
- package/examples/delegation-and-referrals.ts +130 -0
- package/examples/get-pyth-ids.ts +61 -0
- package/examples/kms-signer.ts +31 -0
- package/examples/optimized-snapshot.ts +153 -0
- package/examples/price-feed-with-sdk-ids.ts +97 -0
- package/examples/price-feed.ts +36 -0
- package/examples/trading-operations.ts +149 -0
- package/package.json +41 -0
- package/src/abis/Referral.ts +3 -0
- package/src/abis/Trading.ts +741 -0
- package/src/abis/erc20.ts +3 -0
- package/src/abis/index.ts +8 -0
- package/src/abis/multicall.ts +3 -0
- package/src/abis/pairInfos.ts +3 -0
- package/src/abis/pairStorage.ts +3 -0
- package/src/abis/priceAggregator.ts +3 -0
- package/src/abis/tardingStorage.ts +1294 -0
- package/src/abis.ts +56 -0
- package/src/client.ts +373 -0
- package/src/config.ts +62 -0
- package/src/crypto/spki.ts +197 -0
- package/src/feed/feed_client.ts +288 -0
- package/src/index.ts +114 -0
- package/src/rpc/asset_parameters.ts +217 -0
- package/src/rpc/blended.ts +77 -0
- package/src/rpc/category_parameters.ts +128 -0
- package/src/rpc/delegation.ts +225 -0
- package/src/rpc/fee_parameters.ts +150 -0
- package/src/rpc/multicall.ts +164 -0
- package/src/rpc/pair_info_queries.ts +208 -0
- package/src/rpc/pairs_cache.ts +268 -0
- package/src/rpc/referral_operations.ts +164 -0
- package/src/rpc/snapshot.ts +210 -0
- package/src/rpc/trade.ts +306 -0
- package/src/rpc/trading_operations.ts +378 -0
- package/src/rpc/trading_parameters.ts +127 -0
- package/src/signers/base.ts +27 -0
- package/src/signers/kms.ts +212 -0
- package/src/signers/local.ts +70 -0
- package/src/types.ts +410 -0
- package/src/utils.ts +155 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { KMSClient, GetPublicKeyCommand, SignCommand } from '@aws-sdk/client-kms';
|
|
2
|
+
import { Provider, TransactionRequest, keccak256, getBytes, Transaction, resolveAddress } from 'ethers';
|
|
3
|
+
import { BaseSigner } from './base';
|
|
4
|
+
import {
|
|
5
|
+
derEncodedPublicKeyToEthAddress,
|
|
6
|
+
getSigRSV,
|
|
7
|
+
signatureToHex,
|
|
8
|
+
} from '../crypto/spki';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* AWS KMS signer for secure transaction signing without exposing private keys
|
|
12
|
+
* Private key never leaves AWS KMS hardware security modules
|
|
13
|
+
*/
|
|
14
|
+
export class KMSSigner extends BaseSigner {
|
|
15
|
+
private kmsClient: KMSClient;
|
|
16
|
+
private kmsKeyId: string;
|
|
17
|
+
private provider: Provider;
|
|
18
|
+
private addressCache?: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create a KMS signer
|
|
22
|
+
* @param kmsKeyId - AWS KMS key ID
|
|
23
|
+
* @param provider - Ethereum provider
|
|
24
|
+
* @param region - AWS region (default: us-east-1)
|
|
25
|
+
*/
|
|
26
|
+
constructor(kmsKeyId: string, provider: Provider, region: string = 'us-east-1') {
|
|
27
|
+
super();
|
|
28
|
+
this.kmsKeyId = kmsKeyId;
|
|
29
|
+
this.provider = provider;
|
|
30
|
+
this.kmsClient = new KMSClient({ region });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get the public key from KMS
|
|
35
|
+
* @returns DER-encoded public key
|
|
36
|
+
*/
|
|
37
|
+
private async getPublicKey(): Promise<Uint8Array> {
|
|
38
|
+
const command = new GetPublicKeyCommand({
|
|
39
|
+
KeyId: this.kmsKeyId,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const response = await this.kmsClient.send(command);
|
|
43
|
+
|
|
44
|
+
if (!response.PublicKey) {
|
|
45
|
+
throw new Error('Failed to get public key from KMS');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return response.PublicKey;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get the Ethereum address derived from the KMS key
|
|
53
|
+
* @returns Ethereum address
|
|
54
|
+
*/
|
|
55
|
+
async getAddress(): Promise<string> {
|
|
56
|
+
if (this.addressCache) {
|
|
57
|
+
return this.addressCache;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const publicKey = await this.getPublicKey();
|
|
61
|
+
this.addressCache = derEncodedPublicKeyToEthAddress(publicKey);
|
|
62
|
+
return this.addressCache;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Sign a message hash using KMS
|
|
67
|
+
* @param msgHash - Message hash to sign
|
|
68
|
+
* @returns DER-encoded signature
|
|
69
|
+
*/
|
|
70
|
+
private async signMsgHash(msgHash: Uint8Array): Promise<Uint8Array> {
|
|
71
|
+
const command = new SignCommand({
|
|
72
|
+
KeyId: this.kmsKeyId,
|
|
73
|
+
Message: msgHash,
|
|
74
|
+
MessageType: 'DIGEST',
|
|
75
|
+
SigningAlgorithm: 'ECDSA_SHA_256',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const response = await this.kmsClient.send(command);
|
|
79
|
+
|
|
80
|
+
if (!response.Signature) {
|
|
81
|
+
throw new Error('Failed to sign message with KMS');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return response.Signature;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Sign a transaction using KMS
|
|
89
|
+
* @param transaction - Transaction to sign
|
|
90
|
+
* @returns Signed transaction as hex string
|
|
91
|
+
*/
|
|
92
|
+
async signTransaction(transaction: TransactionRequest): Promise<string> {
|
|
93
|
+
// Get the address to fill in the 'from' field if not set
|
|
94
|
+
const address = await this.getAddress();
|
|
95
|
+
|
|
96
|
+
// Fill in missing fields
|
|
97
|
+
const tx: TransactionRequest = {
|
|
98
|
+
...transaction,
|
|
99
|
+
from: address,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Get chain ID if not provided
|
|
103
|
+
if (!tx.chainId) {
|
|
104
|
+
const network = await this.provider.getNetwork();
|
|
105
|
+
tx.chainId = network.chainId;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Get nonce if not provided
|
|
109
|
+
if (tx.nonce === undefined) {
|
|
110
|
+
tx.nonce = await this.provider.getTransactionCount(address);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Estimate gas if not provided
|
|
114
|
+
if (!tx.gasLimit) {
|
|
115
|
+
tx.gasLimit = await this.provider.estimateGas(tx);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Get gas price if not provided (for legacy transactions)
|
|
119
|
+
if (!tx.maxFeePerGas && !tx.gasPrice) {
|
|
120
|
+
const feeData = await this.provider.getFeeData();
|
|
121
|
+
if (feeData.maxFeePerGas) {
|
|
122
|
+
tx.maxFeePerGas = feeData.maxFeePerGas;
|
|
123
|
+
tx.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas || feeData.maxFeePerGas;
|
|
124
|
+
} else {
|
|
125
|
+
tx.gasPrice = feeData.gasPrice || undefined;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Resolve address fields to strings
|
|
130
|
+
const resolvedTo = tx.to ? await resolveAddress(tx.to, this.provider) : null;
|
|
131
|
+
const resolvedFrom = tx.from ? await resolveAddress(tx.from, this.provider) : address;
|
|
132
|
+
|
|
133
|
+
// Create a transaction object with resolved fields
|
|
134
|
+
const resolvedTx = {
|
|
135
|
+
type: tx.type,
|
|
136
|
+
to: resolvedTo,
|
|
137
|
+
from: resolvedFrom,
|
|
138
|
+
nonce: tx.nonce,
|
|
139
|
+
gasLimit: tx.gasLimit,
|
|
140
|
+
gasPrice: tx.gasPrice,
|
|
141
|
+
maxFeePerGas: tx.maxFeePerGas,
|
|
142
|
+
maxPriorityFeePerGas: tx.maxPriorityFeePerGas,
|
|
143
|
+
data: tx.data,
|
|
144
|
+
value: tx.value,
|
|
145
|
+
chainId: tx.chainId,
|
|
146
|
+
accessList: tx.accessList,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Create unsigned transaction
|
|
150
|
+
const unsignedTx = Transaction.from(resolvedTx);
|
|
151
|
+
const unsignedSerialized = unsignedTx.unsignedSerialized;
|
|
152
|
+
|
|
153
|
+
// Hash the unsigned transaction
|
|
154
|
+
const msgHash = keccak256(unsignedSerialized);
|
|
155
|
+
const msgHashBytes = getBytes(msgHash);
|
|
156
|
+
|
|
157
|
+
// Sign with KMS
|
|
158
|
+
const kmsSignature = await this.signMsgHash(msgHashBytes);
|
|
159
|
+
|
|
160
|
+
// Parse signature and recover v
|
|
161
|
+
const { r, s, v } = getSigRSV(kmsSignature, msgHash, address);
|
|
162
|
+
|
|
163
|
+
// Create signed transaction
|
|
164
|
+
unsignedTx.signature = { r, s, v };
|
|
165
|
+
|
|
166
|
+
return unsignedTx.serialized;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Sign a message using KMS
|
|
171
|
+
* @param message - Message to sign
|
|
172
|
+
* @returns Signature as hex string
|
|
173
|
+
*/
|
|
174
|
+
async signMessage(message: string | Uint8Array): Promise<string> {
|
|
175
|
+
const address = await this.getAddress();
|
|
176
|
+
|
|
177
|
+
// Convert message to bytes
|
|
178
|
+
const messageBytes =
|
|
179
|
+
typeof message === 'string' ? Buffer.from(message, 'utf8') : message;
|
|
180
|
+
|
|
181
|
+
// Create Ethereum signed message hash
|
|
182
|
+
const messageHash = keccak256(
|
|
183
|
+
concat([
|
|
184
|
+
Buffer.from('\x19Ethereum Signed Message:\n', 'utf8'),
|
|
185
|
+
Buffer.from(String(messageBytes.length), 'utf8'),
|
|
186
|
+
messageBytes,
|
|
187
|
+
])
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
const msgHashBytes = getBytes(messageHash);
|
|
191
|
+
|
|
192
|
+
// Sign with KMS
|
|
193
|
+
const kmsSignature = await this.signMsgHash(msgHashBytes);
|
|
194
|
+
|
|
195
|
+
// Parse signature and recover v
|
|
196
|
+
const { r, s, v } = getSigRSV(kmsSignature, messageHash, address);
|
|
197
|
+
|
|
198
|
+
return signatureToHex(r, s, v);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Helper function for concat
|
|
203
|
+
function concat(arrays: (Uint8Array | Buffer)[]): Uint8Array {
|
|
204
|
+
const totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0);
|
|
205
|
+
const result = new Uint8Array(totalLength);
|
|
206
|
+
let offset = 0;
|
|
207
|
+
for (const arr of arrays) {
|
|
208
|
+
result.set(arr, offset);
|
|
209
|
+
offset += arr.length;
|
|
210
|
+
}
|
|
211
|
+
return result;
|
|
212
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Wallet, Provider, TransactionRequest } from 'ethers';
|
|
2
|
+
import { BaseSigner } from './base';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Local signer using a private key stored in memory
|
|
6
|
+
* WARNING: Use with caution in production environments
|
|
7
|
+
*/
|
|
8
|
+
export class LocalSigner extends BaseSigner {
|
|
9
|
+
private wallet: Wallet;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Create a local signer from a private key
|
|
13
|
+
* @param privateKey - Private key (with or without 0x prefix)
|
|
14
|
+
* @param provider - Ethereum provider
|
|
15
|
+
*/
|
|
16
|
+
constructor(privateKey: string, provider: Provider) {
|
|
17
|
+
super();
|
|
18
|
+
|
|
19
|
+
// Ensure private key has 0x prefix
|
|
20
|
+
if (!privateKey.startsWith('0x')) {
|
|
21
|
+
privateKey = '0x' + privateKey;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.wallet = new Wallet(privateKey, provider);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Sign a transaction using the private key
|
|
29
|
+
* @param transaction - Transaction to sign
|
|
30
|
+
* @returns Signed transaction as hex string
|
|
31
|
+
*/
|
|
32
|
+
async signTransaction(transaction: TransactionRequest): Promise<string> {
|
|
33
|
+
return await this.wallet.signTransaction(transaction);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the Ethereum address for this signer
|
|
38
|
+
* @returns Ethereum address
|
|
39
|
+
*/
|
|
40
|
+
async getAddress(): Promise<string> {
|
|
41
|
+
return this.wallet.address;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sign a message using the private key
|
|
46
|
+
* @param message - Message to sign
|
|
47
|
+
* @returns Signature
|
|
48
|
+
*/
|
|
49
|
+
async signMessage(message: string | Uint8Array): Promise<string> {
|
|
50
|
+
return await this.wallet.signMessage(message);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Get the wallet instance (for advanced usage)
|
|
55
|
+
* @returns Wallet instance
|
|
56
|
+
*/
|
|
57
|
+
getWallet(): Wallet {
|
|
58
|
+
return this.wallet;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Create a random wallet
|
|
63
|
+
* @param provider - Ethereum provider
|
|
64
|
+
* @returns LocalSigner with random wallet
|
|
65
|
+
*/
|
|
66
|
+
static createRandom(provider: Provider): LocalSigner {
|
|
67
|
+
const wallet = Wallet.createRandom();
|
|
68
|
+
return new LocalSigner(wallet.privateKey, provider);
|
|
69
|
+
}
|
|
70
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// ========== ENUMS ==========
|
|
4
|
+
|
|
5
|
+
export enum TradeInputOrderType {
|
|
6
|
+
MARKET = 'market',
|
|
7
|
+
STOP_LIMIT = 'stop_limit',
|
|
8
|
+
LIMIT = 'limit',
|
|
9
|
+
MARKET_ZERO_FEE = 'market_zero_fee',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export enum MarginUpdateType {
|
|
13
|
+
DEPOSIT = 0,
|
|
14
|
+
WITHDRAW = 1,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ========== ZOD SCHEMAS ==========
|
|
18
|
+
|
|
19
|
+
// Address validation
|
|
20
|
+
const addressSchema = z.string().regex(/^0x[a-fA-F0-9]{40}$/);
|
|
21
|
+
|
|
22
|
+
// Spread schema
|
|
23
|
+
export const SpreadSchema = z.object({
|
|
24
|
+
min: z.number(),
|
|
25
|
+
max: z.number(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// PairInfo schema
|
|
29
|
+
export const PairInfoSchema = z.object({
|
|
30
|
+
from: z.string(),
|
|
31
|
+
to: z.string(),
|
|
32
|
+
spread: SpreadSchema,
|
|
33
|
+
groupIndex: z.number(),
|
|
34
|
+
feeIndex: z.number(),
|
|
35
|
+
maxLeverage: z.number(),
|
|
36
|
+
maxLongOiP: z.number(),
|
|
37
|
+
maxShortOiP: z.number(),
|
|
38
|
+
|
|
39
|
+
// maxOpenInterestUsdc: z.number(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// TradeInput schema
|
|
43
|
+
export const TradeInputSchema = z.object({
|
|
44
|
+
pair: z.string(),
|
|
45
|
+
isLong: z.boolean(),
|
|
46
|
+
collateralInTrade: z.number(),
|
|
47
|
+
leverage: z.number(),
|
|
48
|
+
openPrice: z.number(),
|
|
49
|
+
tp: z.number(),
|
|
50
|
+
sl: z.number(),
|
|
51
|
+
referrer: addressSchema.optional().default('0x0000000000000000000000000000000000000000'),
|
|
52
|
+
orderType: z.nativeEnum(TradeInputOrderType),
|
|
53
|
+
maxSlippageP: z.number(),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// TradeResponse schema
|
|
57
|
+
export const TradeResponseSchema = z.object({
|
|
58
|
+
trader: addressSchema,
|
|
59
|
+
pairIndex: z.number(),
|
|
60
|
+
index: z.number(),
|
|
61
|
+
initialPosUsdc: z.number(),
|
|
62
|
+
openPrice: z.number(),
|
|
63
|
+
buy: z.boolean(),
|
|
64
|
+
leverage: z.number(),
|
|
65
|
+
tp: z.number(),
|
|
66
|
+
sl: z.number(),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Price schema (from Pyth)
|
|
70
|
+
export const PriceSchema = z.object({
|
|
71
|
+
price: z.string(),
|
|
72
|
+
conf: z.string(),
|
|
73
|
+
expo: z.number(),
|
|
74
|
+
publishTime: z.number(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// EmaPrice schema
|
|
78
|
+
export const EmaPriceSchema = z.object({
|
|
79
|
+
price: z.string(),
|
|
80
|
+
conf: z.string(),
|
|
81
|
+
expo: z.number(),
|
|
82
|
+
publishTime: z.number(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// PriceFeedResponse schema
|
|
86
|
+
export const PriceFeedResponseSchema = z.object({
|
|
87
|
+
id: z.string(),
|
|
88
|
+
price: PriceSchema,
|
|
89
|
+
emaPrice: EmaPriceSchema,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// OpenInterest schema
|
|
93
|
+
export const OpenInterestSchema = z.object({
|
|
94
|
+
long: z.number(),
|
|
95
|
+
short: z.number(),
|
|
96
|
+
max: z.number(),
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// OpenInterestLimits schema
|
|
100
|
+
export const OpenInterestLimitsSchema = z.object({
|
|
101
|
+
pairIndex: z.number(),
|
|
102
|
+
maxLong: z.number(),
|
|
103
|
+
maxShort: z.number(),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Utilization schema
|
|
107
|
+
export const UtilizationSchema = z.object({
|
|
108
|
+
utilizationLong: z.number(),
|
|
109
|
+
utilizationShort: z.number(),
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Skew schema
|
|
113
|
+
export const SkewSchema = z.object({
|
|
114
|
+
skew: z.number(),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Fee schema
|
|
118
|
+
export const FeeSchema = z.object({
|
|
119
|
+
feeP: z.number(),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Depth schema
|
|
123
|
+
export const DepthSchema = z.object({
|
|
124
|
+
onePercentDepthAboveUsdc: z.number(),
|
|
125
|
+
onePercentDepthBelowUsdc: z.number(),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// LossProtectionInfo schema
|
|
129
|
+
export const LossProtectionInfoSchema = z.object({
|
|
130
|
+
tier: z.number(),
|
|
131
|
+
percentage: z.number(),
|
|
132
|
+
amount: z.number(),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// PairData schema (for snapshot)
|
|
136
|
+
export const PairDataSchema = z.object({
|
|
137
|
+
pairInfo: PairInfoSchema,
|
|
138
|
+
openInterest: OpenInterestSchema.optional(),
|
|
139
|
+
utilization: UtilizationSchema.optional(),
|
|
140
|
+
skew: SkewSchema.optional(),
|
|
141
|
+
fee: FeeSchema.optional(),
|
|
142
|
+
depth: DepthSchema.optional(),
|
|
143
|
+
spread: z.number().optional(),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Group schema (for snapshot)
|
|
147
|
+
export const GroupSchema = z.object({
|
|
148
|
+
groupIndex: z.number(),
|
|
149
|
+
pairs: z.record(z.string(), PairDataSchema),
|
|
150
|
+
openInterest: OpenInterestSchema.optional(),
|
|
151
|
+
utilization: UtilizationSchema.optional(),
|
|
152
|
+
skew: SkewSchema.optional(),
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Snapshot schema
|
|
156
|
+
export const SnapshotSchema = z.object({
|
|
157
|
+
groups: z.record(z.string(), GroupSchema),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Trade schema (from smart contract)
|
|
161
|
+
export const TradeSchema = z.object({
|
|
162
|
+
trader: addressSchema,
|
|
163
|
+
pairIndex: z.number(),
|
|
164
|
+
index: z.number(),
|
|
165
|
+
initialPosToken: z.number(), // 6 decimals
|
|
166
|
+
positionSizeUSDC: z.number(), // 6 decimals
|
|
167
|
+
openPrice: z.number(), // 10 decimals
|
|
168
|
+
buy: z.boolean(),
|
|
169
|
+
leverage: z.number(), // 10 decimals
|
|
170
|
+
tp: z.number(), // 10 decimals
|
|
171
|
+
sl: z.number(), // 10 decimals
|
|
172
|
+
timestamp: z.number(),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// TradeInfo schema (from smart contract)
|
|
176
|
+
export const TradeInfoSchema = z.object({
|
|
177
|
+
openInterestUSDC: z.number(), // 6 decimals
|
|
178
|
+
tpLastUpdated: z.number(),
|
|
179
|
+
slLastUpdated: z.number(),
|
|
180
|
+
beingMarketClosed: z.boolean(),
|
|
181
|
+
lossProtection: z.number(),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// OpenLimitOrder schema (from smart contract)
|
|
185
|
+
export const OpenLimitOrderSchema = z.object({
|
|
186
|
+
trader: addressSchema,
|
|
187
|
+
pairIndex: z.number(),
|
|
188
|
+
index: z.number(),
|
|
189
|
+
positionSize: z.number(), // 6 decimals
|
|
190
|
+
buy: z.boolean(),
|
|
191
|
+
leverage: z.number(), // 10 decimals
|
|
192
|
+
tp: z.number(), // 10 decimals
|
|
193
|
+
sl: z.number(), // 10 decimals
|
|
194
|
+
price: z.number(), // 10 decimals
|
|
195
|
+
slippageP: z.number(), // 10 decimals
|
|
196
|
+
block: z.number(),
|
|
197
|
+
executionFee: z.number(), // 18 decimals
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ReferralTier schema
|
|
201
|
+
export const ReferralTierSchema = z.object({
|
|
202
|
+
feeDiscountPct: z.number(),
|
|
203
|
+
refRebatePct: z.number(),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// ReferralDiscount schema
|
|
207
|
+
export const ReferralDiscountSchema = z.object({
|
|
208
|
+
traderDiscount: z.number(),
|
|
209
|
+
referrer: addressSchema,
|
|
210
|
+
rebateShare: z.number(),
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// ========== TYPESCRIPT TYPES ==========
|
|
214
|
+
|
|
215
|
+
export type Spread = z.infer<typeof SpreadSchema>;
|
|
216
|
+
export type PairInfo = z.infer<typeof PairInfoSchema>;
|
|
217
|
+
export type TradeInput = z.infer<typeof TradeInputSchema>;
|
|
218
|
+
export type TradeResponse = z.infer<typeof TradeResponseSchema>;
|
|
219
|
+
export type Price = z.infer<typeof PriceSchema>;
|
|
220
|
+
export type EmaPrice = z.infer<typeof EmaPriceSchema>;
|
|
221
|
+
export type PriceFeedResponse = z.infer<typeof PriceFeedResponseSchema>;
|
|
222
|
+
export type OpenInterest = z.infer<typeof OpenInterestSchema>;
|
|
223
|
+
export type OpenInterestLimits = z.infer<typeof OpenInterestLimitsSchema>;
|
|
224
|
+
export type Utilization = z.infer<typeof UtilizationSchema>;
|
|
225
|
+
export type Skew = z.infer<typeof SkewSchema>;
|
|
226
|
+
export type Fee = z.infer<typeof FeeSchema>;
|
|
227
|
+
export type Depth = z.infer<typeof DepthSchema>;
|
|
228
|
+
export type LossProtectionInfo = z.infer<typeof LossProtectionInfoSchema>;
|
|
229
|
+
export type PairData = z.infer<typeof PairDataSchema>;
|
|
230
|
+
export type Group = z.infer<typeof GroupSchema>;
|
|
231
|
+
export type Snapshot = z.infer<typeof SnapshotSchema>;
|
|
232
|
+
export type Trade = z.infer<typeof TradeSchema>;
|
|
233
|
+
export type TradeInfo = z.infer<typeof TradeInfoSchema>;
|
|
234
|
+
export type OpenLimitOrder = z.infer<typeof OpenLimitOrderSchema>;
|
|
235
|
+
export type ReferralTier = z.infer<typeof ReferralTierSchema>;
|
|
236
|
+
export type ReferralDiscount = z.infer<typeof ReferralDiscountSchema>;
|
|
237
|
+
|
|
238
|
+
// ========== UTILITY TYPES ==========
|
|
239
|
+
|
|
240
|
+
export interface ContractCallOptions {
|
|
241
|
+
value?: bigint;
|
|
242
|
+
gasLimit?: bigint;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export interface TransactionReceipt {
|
|
246
|
+
transactionHash: string;
|
|
247
|
+
blockNumber: number;
|
|
248
|
+
status: number;
|
|
249
|
+
gasUsed: bigint;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ========== CONVERSION HELPERS ==========
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Convert blockchain integer to decimal (10^10 precision)
|
|
256
|
+
*/
|
|
257
|
+
export function fromBlockchain10(value: bigint | number | string): number {
|
|
258
|
+
return Number(BigInt(value)) / 1e10;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Convert blockchain integer to decimal (10^6 precision for USDC)
|
|
263
|
+
*/
|
|
264
|
+
export function fromBlockchain6(value: bigint | number | string): number {
|
|
265
|
+
return Number(BigInt(value)) / 1e6;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Convert decimal to blockchain integer (10^10 precision)
|
|
270
|
+
*/
|
|
271
|
+
export function toBlockchain10(value: number): bigint {
|
|
272
|
+
return BigInt(Math.floor(value * 1e10));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Convert decimal to blockchain integer (10^6 precision for USDC)
|
|
277
|
+
*/
|
|
278
|
+
export function toBlockchain6(value: number): bigint {
|
|
279
|
+
return BigInt(Math.floor(value * 1e6));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Convert blockchain integer to decimal (10^12 precision for fees)
|
|
284
|
+
*/
|
|
285
|
+
export function fromBlockchain12(value: bigint | number | string): number {
|
|
286
|
+
return Number(BigInt(value)) / 1e12;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Convert decimal to blockchain integer (10^12 precision for fees)
|
|
291
|
+
*/
|
|
292
|
+
export function toBlockchain12(value: number): bigint {
|
|
293
|
+
return BigInt(Math.floor(value * 1e12));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Convert blockchain integer to decimal (10^18 precision for ETH/execution fees)
|
|
298
|
+
*/
|
|
299
|
+
export function fromBlockchain18(value: bigint | number | string): number {
|
|
300
|
+
return Number(BigInt(value)) / 1e18;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Convert decimal to blockchain integer (10^18 precision for ETH/execution fees)
|
|
305
|
+
*/
|
|
306
|
+
export function toBlockchain18(value: number): bigint {
|
|
307
|
+
return BigInt(Math.floor(value * 1e18));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
export interface ContractPairInfo {
|
|
312
|
+
feed: {
|
|
313
|
+
maxOpenDeviationP: bigint;
|
|
314
|
+
maxCloseDeviationP: bigint;
|
|
315
|
+
feedId: string; // bytes32
|
|
316
|
+
};
|
|
317
|
+
backupFeed: {
|
|
318
|
+
maxDeviationP: bigint;
|
|
319
|
+
feedId: string; // address
|
|
320
|
+
};
|
|
321
|
+
spreadP: bigint;
|
|
322
|
+
pnlSpreadP: bigint;
|
|
323
|
+
leverages: {
|
|
324
|
+
minLeverage: bigint;
|
|
325
|
+
maxLeverage: bigint;
|
|
326
|
+
pnlMinLeverage: bigint;
|
|
327
|
+
pnlMaxLeverage: bigint;
|
|
328
|
+
};
|
|
329
|
+
priceImpactMultiplier: bigint;
|
|
330
|
+
skewImpactMultiplier: bigint; // int256
|
|
331
|
+
groupIndex: bigint;
|
|
332
|
+
feeIndex: bigint;
|
|
333
|
+
values: {
|
|
334
|
+
maxGainP: bigint; // int256
|
|
335
|
+
maxSlP: bigint; // int256
|
|
336
|
+
maxLongOiP: bigint;
|
|
337
|
+
maxShortOiP: bigint;
|
|
338
|
+
groupOpenInterestPercentageP: bigint;
|
|
339
|
+
maxWalletOIP: bigint;
|
|
340
|
+
isUSDCAligned: boolean;
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
export interface FeedStruct {
|
|
346
|
+
maxOpenDeviationP: bigint;
|
|
347
|
+
maxCloseDeviationP: bigint;
|
|
348
|
+
feedId: string; // bytes32
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export interface BackupFeedStruct {
|
|
352
|
+
maxDeviationP: bigint;
|
|
353
|
+
feedId: string; // address
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface LeverageStruct {
|
|
357
|
+
minLeverage: bigint;
|
|
358
|
+
maxLeverage: bigint;
|
|
359
|
+
pnlMinLeverage: bigint;
|
|
360
|
+
pnlMaxLeverage: bigint;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export interface ValuesStruct {
|
|
364
|
+
maxGainP: bigint;
|
|
365
|
+
maxSlP: bigint;
|
|
366
|
+
maxLongOiP: bigint;
|
|
367
|
+
maxShortOiP: bigint;
|
|
368
|
+
groupOpenInterestPercentageP: bigint;
|
|
369
|
+
maxWalletOIP: bigint;
|
|
370
|
+
isUSDCAligned: boolean;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export interface PairStruct {
|
|
374
|
+
feed: FeedStruct;
|
|
375
|
+
backupFeed: BackupFeedStruct;
|
|
376
|
+
spreadP: bigint;
|
|
377
|
+
pnlSpreadP: bigint;
|
|
378
|
+
leverages: LeverageStruct;
|
|
379
|
+
priceImpactMultiplier: bigint;
|
|
380
|
+
skewImpactMultiplier: bigint;
|
|
381
|
+
groupIndex: bigint;
|
|
382
|
+
feeIndex: bigint;
|
|
383
|
+
values: ValuesStruct;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export interface GroupStruct {
|
|
387
|
+
name: string;
|
|
388
|
+
maxOpenInterestP: bigint;
|
|
389
|
+
isSpreadDynamic: boolean;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export interface PnlFeesStruct {
|
|
393
|
+
numTiers: bigint;
|
|
394
|
+
tierP: bigint[];
|
|
395
|
+
feesP: bigint[];
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export interface FeeStruct {
|
|
399
|
+
openFeeP: bigint;
|
|
400
|
+
closeFeeP: bigint;
|
|
401
|
+
limitOrderFeeP: bigint;
|
|
402
|
+
minLevPosUSDC: bigint;
|
|
403
|
+
pnlFees: PnlFeesStruct;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export type PairsBackendReturn = {
|
|
407
|
+
pair: PairStruct;
|
|
408
|
+
group: GroupStruct;
|
|
409
|
+
fee: FeeStruct;
|
|
410
|
+
};
|