@megatao/sdk 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/.env.example +37 -0
- package/CHANGELOG.md +19 -0
- package/README.md +199 -0
- package/bin/alf +4 -0
- package/cli/README.md +198 -0
- package/cli/TEST_MANUAL.md +577 -0
- package/cli/commands/account.ts +545 -0
- package/cli/commands/funding.ts +481 -0
- package/cli/commands/liquidation.ts +523 -0
- package/cli/commands/market.ts +590 -0
- package/cli/commands/orders.ts +395 -0
- package/cli/commands/position.ts +1085 -0
- package/cli/commands/shared/positionUtils.ts +239 -0
- package/cli/commands/trading.ts +483 -0
- package/cli/commands/utils.ts +281 -0
- package/cli/commands/vault.ts +522 -0
- package/cli/index.ts +169 -0
- package/cli/interactive.ts +530 -0
- package/cli/utils/client.ts +457 -0
- package/cli/utils/config.ts +226 -0
- package/cli/utils/display.ts +258 -0
- package/cli/utils/index.ts +10 -0
- package/cli/utils/prompts.ts +364 -0
- package/config.example.json +23 -0
- package/dist/AlphaFuturesClient.d.ts +36 -0
- package/dist/AlphaFuturesClient.d.ts.map +1 -0
- package/dist/AlphaFuturesClient.js +116 -0
- package/dist/AlphaFuturesClient.js.map +1 -0
- package/dist/abi/Alpha.json +5987 -0
- package/dist/abi/abis.d.ts +319 -0
- package/dist/abi/abis.d.ts.map +1 -0
- package/dist/abi/abis.js +128 -0
- package/dist/abi/abis.js.map +1 -0
- package/dist/abi/index.d.ts +11 -0
- package/dist/abi/index.d.ts.map +1 -0
- package/dist/abi/index.js +15 -0
- package/dist/abi/index.js.map +1 -0
- package/dist/config/contracts.config.d.ts +70 -0
- package/dist/config/contracts.config.d.ts.map +1 -0
- package/dist/config/contracts.config.js +137 -0
- package/dist/config/contracts.config.js.map +1 -0
- package/dist/config/environments/alpha.config.d.ts +17 -0
- package/dist/config/environments/alpha.config.d.ts.map +1 -0
- package/dist/config/environments/alpha.config.js +140 -0
- package/dist/config/environments/alpha.config.js.map +1 -0
- package/dist/config/environments/beta.config.d.ts +16 -0
- package/dist/config/environments/beta.config.d.ts.map +1 -0
- package/dist/config/environments/beta.config.js +131 -0
- package/dist/config/environments/beta.config.js.map +1 -0
- package/dist/config/environments/dev.config.d.ts +13 -0
- package/dist/config/environments/dev.config.d.ts.map +1 -0
- package/dist/config/environments/dev.config.js +123 -0
- package/dist/config/environments/dev.config.js.map +1 -0
- package/dist/config/environments/index.d.ts +48 -0
- package/dist/config/environments/index.d.ts.map +1 -0
- package/dist/config/environments/index.js +81 -0
- package/dist/config/environments/index.js.map +1 -0
- package/dist/config/environments/localhost.config.d.ts +16 -0
- package/dist/config/environments/localhost.config.d.ts.map +1 -0
- package/dist/config/environments/localhost.config.js +152 -0
- package/dist/config/environments/localhost.config.js.map +1 -0
- package/dist/config/environments/prod.config.d.ts +20 -0
- package/dist/config/environments/prod.config.d.ts.map +1 -0
- package/dist/config/environments/prod.config.js +143 -0
- package/dist/config/environments/prod.config.js.map +1 -0
- package/dist/config/index.d.ts +7 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +41 -0
- package/dist/config/index.js.map +1 -0
- package/dist/constants/assets.d.ts +76 -0
- package/dist/constants/assets.d.ts.map +1 -0
- package/dist/constants/assets.js +277 -0
- package/dist/constants/assets.js.map +1 -0
- package/dist/constants/contracts.d.ts +41 -0
- package/dist/constants/contracts.d.ts.map +1 -0
- package/dist/constants/contracts.js +57 -0
- package/dist/constants/contracts.js.map +1 -0
- package/dist/constants/index.d.ts +36 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +75 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/networks.d.ts +32 -0
- package/dist/constants/networks.d.ts.map +1 -0
- package/dist/constants/networks.js +174 -0
- package/dist/constants/networks.js.map +1 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +21 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/viem/AlphaViem.d.ts +518 -0
- package/dist/contracts/viem/AlphaViem.d.ts.map +1 -0
- package/dist/contracts/viem/AlphaViem.js +1287 -0
- package/dist/contracts/viem/AlphaViem.js.map +1 -0
- package/dist/contracts/viem/PriceOracleViem.d.ts +71 -0
- package/dist/contracts/viem/PriceOracleViem.d.ts.map +1 -0
- package/dist/contracts/viem/PriceOracleViem.js +212 -0
- package/dist/contracts/viem/PriceOracleViem.js.map +1 -0
- package/dist/contracts/viem/index.d.ts +9 -0
- package/dist/contracts/viem/index.d.ts.map +1 -0
- package/dist/contracts/viem/index.js +17 -0
- package/dist/contracts/viem/index.js.map +1 -0
- package/dist/errors/index.d.ts +44 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +83 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/types/alpha.d.ts +299 -0
- package/dist/types/alpha.d.ts.map +1 -0
- package/dist/types/alpha.js +6 -0
- package/dist/types/alpha.js.map +1 -0
- package/dist/types/client.d.ts +24 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/client.js +13 -0
- package/dist/types/client.js.map +1 -0
- package/dist/types/contracts.d.ts +48 -0
- package/dist/types/contracts.d.ts.map +1 -0
- package/dist/types/contracts.js +6 -0
- package/dist/types/contracts.js.map +1 -0
- package/dist/types/funding.d.ts +27 -0
- package/dist/types/funding.d.ts.map +1 -0
- package/dist/types/funding.js +6 -0
- package/dist/types/funding.js.map +1 -0
- package/dist/types/index.d.ts +92 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +47 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/liquidation.d.ts +20 -0
- package/dist/types/liquidation.d.ts.map +1 -0
- package/dist/types/liquidation.js +6 -0
- package/dist/types/liquidation.js.map +1 -0
- package/dist/types/margin.d.ts +29 -0
- package/dist/types/margin.d.ts.map +1 -0
- package/dist/types/margin.js +6 -0
- package/dist/types/margin.js.map +1 -0
- package/dist/types/oracle.d.ts +21 -0
- package/dist/types/oracle.d.ts.map +1 -0
- package/dist/types/oracle.js +6 -0
- package/dist/types/oracle.js.map +1 -0
- package/dist/types/positions.d.ts +43 -0
- package/dist/types/positions.d.ts.map +1 -0
- package/dist/types/positions.js +13 -0
- package/dist/types/positions.js.map +1 -0
- package/dist/utils/calculations.d.ts +84 -0
- package/dist/utils/calculations.d.ts.map +1 -0
- package/dist/utils/calculations.js +155 -0
- package/dist/utils/calculations.js.map +1 -0
- package/dist/utils/errors.d.ts +24 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +129 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/events.d.ts +40 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +73 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/format.d.ts +40 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +86 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +26 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/network.d.ts +52 -0
- package/dist/utils/network.d.ts.map +1 -0
- package/dist/utils/network.js +192 -0
- package/dist/utils/network.js.map +1 -0
- package/dist/utils/positionCalculations.d.ts +145 -0
- package/dist/utils/positionCalculations.d.ts.map +1 -0
- package/dist/utils/positionCalculations.js +278 -0
- package/dist/utils/positionCalculations.js.map +1 -0
- package/dist/utils/validation.d.ts +28 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +68 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/README.md +40 -0
- package/docs/api/API.md +831 -0
- package/docs/guides/GETTING_STARTED.md +316 -0
- package/docs/guides/TRADING_GUIDE.md +677 -0
- package/docs/integration/INTEGRATION_GUIDE.md +1679 -0
- package/docs/integration/VIEM_INTEGRATION.md +294 -0
- package/docs/reference/CLI_QUICK_REFERENCE.md +197 -0
- package/docs/reference/TROUBLESHOOTING.md +922 -0
- package/package.json +113 -0
- package/src/AlphaFuturesClient.ts +158 -0
- package/src/abi/.gitkeep +1 -0
- package/src/abi/Alpha.json +5987 -0
- package/src/abi/README.md +99 -0
- package/src/abi/abis.ts +131 -0
- package/src/abi/index.ts +13 -0
- package/src/config/contracts.config.ts +186 -0
- package/src/config/environments/alpha.config.ts +139 -0
- package/src/config/environments/beta.config.ts +130 -0
- package/src/config/environments/dev.config.ts +122 -0
- package/src/config/environments/index.ts +87 -0
- package/src/config/environments/localhost.config.ts +153 -0
- package/src/config/environments/prod.config.ts +142 -0
- package/src/config/index.ts +29 -0
- package/src/constants/assets.ts +299 -0
- package/src/constants/contracts.ts +64 -0
- package/src/constants/index.ts +69 -0
- package/src/constants/networks.ts +182 -0
- package/src/contracts/index.ts +5 -0
- package/src/contracts/viem/AlphaViem.ts +1615 -0
- package/src/contracts/viem/PriceOracleViem.ts +272 -0
- package/src/contracts/viem/index.ts +11 -0
- package/src/errors/index.ts +87 -0
- package/src/index.ts +59 -0
- package/src/types/VIEM_TYPES_README.md +70 -0
- package/src/types/alpha.ts +358 -0
- package/src/types/client.ts +27 -0
- package/src/types/contracts.ts +74 -0
- package/src/types/funding.ts +31 -0
- package/src/types/index.ts +108 -0
- package/src/types/liquidation.ts +23 -0
- package/src/types/margin.ts +34 -0
- package/src/types/oracle.ts +24 -0
- package/src/types/positions.ts +48 -0
- package/src/utils/calculations.ts +175 -0
- package/src/utils/errors.ts +147 -0
- package/src/utils/events.ts +98 -0
- package/src/utils/format.ts +84 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/network.ts +212 -0
- package/src/utils/positionCalculations.ts +317 -0
- package/src/utils/validation.ts +76 -0
|
@@ -0,0 +1,1615 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alpha contract wrapper using Viem
|
|
3
|
+
* Main implementation of ALPHA perpetual futures protocol
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
Address,
|
|
8
|
+
PublicClient,
|
|
9
|
+
WalletClient,
|
|
10
|
+
Hash,
|
|
11
|
+
TransactionReceipt,
|
|
12
|
+
parseEventLogs,
|
|
13
|
+
encodeFunctionData,
|
|
14
|
+
} from 'viem';
|
|
15
|
+
import { TransactionOptions } from '../../types';
|
|
16
|
+
import * as AlphaTypes from '../../types/alpha';
|
|
17
|
+
import AlphaABI from '../../abi/Alpha.json';
|
|
18
|
+
|
|
19
|
+
export class AlphaViem {
|
|
20
|
+
private publicClient: PublicClient;
|
|
21
|
+
private walletClient?: WalletClient;
|
|
22
|
+
private contractAddress: Address;
|
|
23
|
+
private collateralTokenAddress?: Address;
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
contractAddress: Address,
|
|
27
|
+
publicClient: PublicClient,
|
|
28
|
+
walletClient?: WalletClient,
|
|
29
|
+
collateralTokenAddress?: Address,
|
|
30
|
+
) {
|
|
31
|
+
this.contractAddress = contractAddress;
|
|
32
|
+
this.publicClient = publicClient;
|
|
33
|
+
this.walletClient = walletClient;
|
|
34
|
+
this.collateralTokenAddress = collateralTokenAddress;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private get isNativeCollateral(): boolean {
|
|
38
|
+
return (
|
|
39
|
+
!this.collateralTokenAddress ||
|
|
40
|
+
this.collateralTokenAddress === '0x0000000000000000000000000000000000000000'
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============ Utility Functions ============
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Convert bytes32 (padded by ABI encoding) to bytes16
|
|
48
|
+
* Takes first 16 bytes from the 32 byte value
|
|
49
|
+
*/
|
|
50
|
+
private bytes32ToBytes16(bytes32: string): `0x${string}` {
|
|
51
|
+
// Remove 0x prefix if present
|
|
52
|
+
const hex = bytes32.startsWith('0x') ? bytes32.slice(2) : bytes32;
|
|
53
|
+
// Take first 32 characters (16 bytes)
|
|
54
|
+
const bytes16Hex = hex.slice(0, 32);
|
|
55
|
+
return `0x${bytes16Hex}` as `0x${string}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Simulate a contract transaction before writing to catch errors early
|
|
60
|
+
* This prevents sending failed transactions to the wallet
|
|
61
|
+
*/
|
|
62
|
+
private async simulateAndWriteContract<
|
|
63
|
+
T extends { functionName: string; args?: readonly unknown[] },
|
|
64
|
+
>(
|
|
65
|
+
writeParams: T & {
|
|
66
|
+
address: Address;
|
|
67
|
+
abi: typeof AlphaABI;
|
|
68
|
+
account: Address;
|
|
69
|
+
value?: bigint;
|
|
70
|
+
},
|
|
71
|
+
): Promise<Hash> {
|
|
72
|
+
if (!this.walletClient) {
|
|
73
|
+
throw new Error('Wallet client required for write operations');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!this.walletClient.account) {
|
|
77
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Simulate the transaction first to catch errors early
|
|
81
|
+
try {
|
|
82
|
+
await this.publicClient.simulateContract({
|
|
83
|
+
address: writeParams.address,
|
|
84
|
+
abi: writeParams.abi,
|
|
85
|
+
functionName: writeParams.functionName,
|
|
86
|
+
args: writeParams.args,
|
|
87
|
+
account: this.walletClient.account.address,
|
|
88
|
+
value: writeParams.value,
|
|
89
|
+
});
|
|
90
|
+
} catch (simulationError: any) {
|
|
91
|
+
console.error('[simulateContract] Raw viem error:', simulationError);
|
|
92
|
+
let errorMessage = 'Transaction simulation failed';
|
|
93
|
+
let errorName: string | undefined;
|
|
94
|
+
let errorArgs: any[] | undefined;
|
|
95
|
+
|
|
96
|
+
// Walk the viem error chain to find decoded custom error data
|
|
97
|
+
for (let cur = simulationError; cur; cur = cur.cause) {
|
|
98
|
+
if (cur.data?.errorName) {
|
|
99
|
+
errorName = cur.data.errorName;
|
|
100
|
+
errorArgs = cur.data.args ? [...cur.data.args] : undefined;
|
|
101
|
+
errorMessage = errorName!;
|
|
102
|
+
if (errorArgs && errorArgs.length > 0) {
|
|
103
|
+
errorMessage += `: ${errorArgs
|
|
104
|
+
.map((arg: any) => (typeof arg === 'bigint' ? arg.toString() : String(arg)))
|
|
105
|
+
.join(', ')}`;
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// If no decoded error found, fall back to message extraction
|
|
112
|
+
if (!errorName) {
|
|
113
|
+
if (simulationError.cause?.shortMessage) {
|
|
114
|
+
errorMessage = simulationError.cause.shortMessage;
|
|
115
|
+
} else if (simulationError.shortMessage) {
|
|
116
|
+
errorMessage = simulationError.shortMessage;
|
|
117
|
+
} else if (simulationError.cause?.message) {
|
|
118
|
+
errorMessage = simulationError.cause.message;
|
|
119
|
+
} else if (simulationError.message) {
|
|
120
|
+
errorMessage = simulationError.message;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const error: any = new Error(errorMessage);
|
|
125
|
+
error.code = 'SIMULATION_FAILED';
|
|
126
|
+
error.cause = simulationError;
|
|
127
|
+
error.simulationError = true;
|
|
128
|
+
error.contractErrorName = errorName;
|
|
129
|
+
error.contractErrorArgs = errorArgs;
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// If simulation succeeds, proceed with the actual write
|
|
134
|
+
// Note: writeContract needs the full account object, not just the address
|
|
135
|
+
return await this.walletClient.writeContract({
|
|
136
|
+
...writeParams,
|
|
137
|
+
account: this.walletClient.account,
|
|
138
|
+
} as any);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ============ Admin Functions ============
|
|
142
|
+
|
|
143
|
+
async initializeMarket(
|
|
144
|
+
market: Address,
|
|
145
|
+
maxLeverage: bigint,
|
|
146
|
+
maintenanceMargin: bigint,
|
|
147
|
+
liquidationFee: bigint,
|
|
148
|
+
options?: TransactionOptions,
|
|
149
|
+
): Promise<Hash> {
|
|
150
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
151
|
+
|
|
152
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
153
|
+
|
|
154
|
+
if (!this.walletClient.account) {
|
|
155
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const hash = await this.walletClient.writeContract({
|
|
159
|
+
address: this.contractAddress,
|
|
160
|
+
abi: AlphaABI,
|
|
161
|
+
functionName: 'initializeMarket',
|
|
162
|
+
args: [market, maxLeverage, maintenanceMargin, liquidationFee],
|
|
163
|
+
account: this.walletClient.account,
|
|
164
|
+
...txOptions,
|
|
165
|
+
} as any);
|
|
166
|
+
|
|
167
|
+
if (waitForConfirmation) {
|
|
168
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
169
|
+
return hash;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return hash;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async addMarket(market: Address, symbol: string, options?: TransactionOptions): Promise<Hash> {
|
|
176
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
177
|
+
|
|
178
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
179
|
+
|
|
180
|
+
if (!this.walletClient.account) {
|
|
181
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const hash = await this.walletClient.writeContract({
|
|
185
|
+
address: this.contractAddress,
|
|
186
|
+
abi: AlphaABI,
|
|
187
|
+
functionName: 'addMarket',
|
|
188
|
+
args: [market, symbol],
|
|
189
|
+
account: this.walletClient.account,
|
|
190
|
+
...txOptions,
|
|
191
|
+
} as any);
|
|
192
|
+
|
|
193
|
+
if (waitForConfirmation) {
|
|
194
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
195
|
+
return hash;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return hash;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async seedVault(amount: bigint, options?: TransactionOptions): Promise<Hash> {
|
|
202
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
203
|
+
|
|
204
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
205
|
+
|
|
206
|
+
if (!this.walletClient.account) {
|
|
207
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const hash = await this.walletClient.writeContract({
|
|
211
|
+
address: this.contractAddress,
|
|
212
|
+
abi: AlphaABI,
|
|
213
|
+
functionName: 'seedVault',
|
|
214
|
+
args: [amount],
|
|
215
|
+
account: this.walletClient.account,
|
|
216
|
+
...txOptions,
|
|
217
|
+
} as any);
|
|
218
|
+
|
|
219
|
+
if (waitForConfirmation) {
|
|
220
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
221
|
+
return hash;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return hash;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ============ Margin Management ============
|
|
228
|
+
|
|
229
|
+
async deposit(amount: bigint, options?: TransactionOptions): Promise<Hash> {
|
|
230
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
231
|
+
|
|
232
|
+
if (!this.walletClient?.account) {
|
|
233
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const hash = await this.simulateAndWriteContract({
|
|
237
|
+
address: this.contractAddress,
|
|
238
|
+
abi: AlphaABI,
|
|
239
|
+
functionName: 'deposit',
|
|
240
|
+
args: [amount],
|
|
241
|
+
account: this.walletClient.account.address,
|
|
242
|
+
...(this.isNativeCollateral ? { value: amount } : {}),
|
|
243
|
+
...txOptions,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if (waitForConfirmation) {
|
|
247
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
248
|
+
return hash;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return hash;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async withdraw(amount: bigint, options?: TransactionOptions): Promise<Hash> {
|
|
255
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
256
|
+
|
|
257
|
+
if (!this.walletClient?.account) {
|
|
258
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const hash = await this.simulateAndWriteContract({
|
|
262
|
+
address: this.contractAddress,
|
|
263
|
+
abi: AlphaABI,
|
|
264
|
+
functionName: 'withdraw',
|
|
265
|
+
args: [amount],
|
|
266
|
+
account: this.walletClient.account.address,
|
|
267
|
+
...txOptions,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (waitForConfirmation) {
|
|
271
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
272
|
+
return hash;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return hash;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ============ Position Management ============
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Opens a market position with specified margin, leverage, and slippage tolerance
|
|
282
|
+
* @param market - Market address
|
|
283
|
+
* @param isLong - True for long position, false for short
|
|
284
|
+
* @param margin - Collateral amount to risk (in wei)
|
|
285
|
+
* @param leverage - Leverage multiplier in 1e18 format (e.g., 3e18 for 3x)
|
|
286
|
+
* @param maxSlippage - Maximum allowed slippage in basis points
|
|
287
|
+
* @param options - Transaction options
|
|
288
|
+
* @returns Transaction hash
|
|
289
|
+
*/
|
|
290
|
+
async openMarketPosition(
|
|
291
|
+
market: Address,
|
|
292
|
+
isLong: boolean,
|
|
293
|
+
margin: bigint,
|
|
294
|
+
leverage: bigint,
|
|
295
|
+
maxSlippage: bigint,
|
|
296
|
+
options?: TransactionOptions,
|
|
297
|
+
): Promise<Hash> {
|
|
298
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
299
|
+
|
|
300
|
+
if (!this.walletClient?.account) {
|
|
301
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Contract expects a single struct parameter: MarketPositionParams
|
|
305
|
+
const params = {
|
|
306
|
+
market,
|
|
307
|
+
isLong,
|
|
308
|
+
margin,
|
|
309
|
+
leverage,
|
|
310
|
+
maxSlippage,
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const hash = await this.simulateAndWriteContract({
|
|
314
|
+
address: this.contractAddress,
|
|
315
|
+
abi: AlphaABI,
|
|
316
|
+
functionName: 'openMarketPosition',
|
|
317
|
+
args: [params],
|
|
318
|
+
account: this.walletClient.account.address,
|
|
319
|
+
...txOptions,
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
if (waitForConfirmation) {
|
|
323
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
324
|
+
return hash;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return hash;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
async openMarketPositionWithStruct(
|
|
331
|
+
params: AlphaTypes.MarketPositionParams,
|
|
332
|
+
options?: TransactionOptions,
|
|
333
|
+
): Promise<Hash> {
|
|
334
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
335
|
+
|
|
336
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
337
|
+
|
|
338
|
+
if (!this.walletClient.account) {
|
|
339
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const hash = await this.walletClient.writeContract({
|
|
343
|
+
address: this.contractAddress,
|
|
344
|
+
abi: AlphaABI,
|
|
345
|
+
functionName: 'openMarketPositionWithStruct',
|
|
346
|
+
args: [params],
|
|
347
|
+
account: this.walletClient.account,
|
|
348
|
+
...txOptions,
|
|
349
|
+
} as any);
|
|
350
|
+
|
|
351
|
+
if (waitForConfirmation) {
|
|
352
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
353
|
+
return hash;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return hash;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async closePosition(
|
|
360
|
+
positionId: AlphaTypes.Bytes16,
|
|
361
|
+
sizeToClose: bigint,
|
|
362
|
+
options?: TransactionOptions,
|
|
363
|
+
): Promise<Hash> {
|
|
364
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
365
|
+
|
|
366
|
+
if (!this.walletClient?.account) {
|
|
367
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const hash = await this.simulateAndWriteContract({
|
|
371
|
+
address: this.contractAddress,
|
|
372
|
+
abi: AlphaABI,
|
|
373
|
+
functionName: 'closePosition',
|
|
374
|
+
args: [positionId, sizeToClose],
|
|
375
|
+
account: this.walletClient.account.address,
|
|
376
|
+
...txOptions,
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
if (waitForConfirmation) {
|
|
380
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
381
|
+
return hash;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return hash;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Batch multiple contract calls into a single transaction using multicall.
|
|
389
|
+
* Handles ABI encoding internally.
|
|
390
|
+
*
|
|
391
|
+
* @param calls - Array of { functionName, args } to batch
|
|
392
|
+
* @param options - Optional transaction options
|
|
393
|
+
* @returns Transaction hash
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* // Close multiple positions in one tx
|
|
397
|
+
* await alpha.multicall([
|
|
398
|
+
* { functionName: 'closePosition', args: [posId1, 0n] },
|
|
399
|
+
* { functionName: 'closePosition', args: [posId2, 0n] },
|
|
400
|
+
* ]);
|
|
401
|
+
*/
|
|
402
|
+
async multicall(
|
|
403
|
+
calls: { functionName: string; args?: readonly unknown[] }[],
|
|
404
|
+
options?: TransactionOptions,
|
|
405
|
+
): Promise<Hash> {
|
|
406
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
407
|
+
|
|
408
|
+
if (!this.walletClient?.account) {
|
|
409
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (calls.length === 0) {
|
|
413
|
+
throw new Error('No calls provided');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const calldata = calls.map((call) =>
|
|
417
|
+
encodeFunctionData({
|
|
418
|
+
abi: AlphaABI,
|
|
419
|
+
functionName: call.functionName,
|
|
420
|
+
args: call.args ?? [],
|
|
421
|
+
}),
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
const hash = await this.simulateAndWriteContract({
|
|
425
|
+
address: this.contractAddress,
|
|
426
|
+
abi: AlphaABI,
|
|
427
|
+
functionName: 'multicall',
|
|
428
|
+
args: [calldata],
|
|
429
|
+
account: this.walletClient.account.address,
|
|
430
|
+
...txOptions,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
if (waitForConfirmation) {
|
|
434
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return hash;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Close multiple positions in a single transaction using multicall
|
|
442
|
+
* @param positionIds - Array of position IDs to close (bytes16 format)
|
|
443
|
+
* @param options - Optional transaction options
|
|
444
|
+
* @returns Transaction hash
|
|
445
|
+
*/
|
|
446
|
+
async closeAllPositions(
|
|
447
|
+
positionIds: AlphaTypes.Bytes16[],
|
|
448
|
+
options?: TransactionOptions,
|
|
449
|
+
): Promise<Hash> {
|
|
450
|
+
if (positionIds.length === 0) {
|
|
451
|
+
throw new Error('No position IDs provided');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return this.multicall(
|
|
455
|
+
positionIds.map((positionId) => ({
|
|
456
|
+
functionName: 'closePosition',
|
|
457
|
+
args: [positionId, 0n],
|
|
458
|
+
})),
|
|
459
|
+
options,
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// ============ Order Management ============
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Places a limit order with specified margin and leverage
|
|
467
|
+
* @param market - Market address
|
|
468
|
+
* @param isBuy - True for buy order, false for sell
|
|
469
|
+
* @param margin - Collateral amount to risk
|
|
470
|
+
* @param leverage - Leverage multiplier in 1e18 format (e.g., 3e18 for 3x)
|
|
471
|
+
* @param price - Limit price in wei
|
|
472
|
+
* @param reduceOnlyType - 0=Normal, 1=TakeProfit (natural side), 2=StopLoss (opposite side)
|
|
473
|
+
* @param options - Transaction options
|
|
474
|
+
* @returns Transaction hash
|
|
475
|
+
*/
|
|
476
|
+
async placeLimitOrder(
|
|
477
|
+
market: Address,
|
|
478
|
+
isBuy: boolean,
|
|
479
|
+
margin: bigint,
|
|
480
|
+
leverage: bigint,
|
|
481
|
+
price: bigint,
|
|
482
|
+
reduceOnlyType: number = 0,
|
|
483
|
+
options?: TransactionOptions,
|
|
484
|
+
): Promise<Hash> {
|
|
485
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
486
|
+
|
|
487
|
+
if (!this.walletClient?.account) {
|
|
488
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const params = {
|
|
492
|
+
market,
|
|
493
|
+
isBuy,
|
|
494
|
+
margin,
|
|
495
|
+
leverage,
|
|
496
|
+
price,
|
|
497
|
+
reduceOnlyType,
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
const hash = await this.simulateAndWriteContract({
|
|
501
|
+
address: this.contractAddress,
|
|
502
|
+
abi: AlphaABI,
|
|
503
|
+
functionName: 'placeLimitOrder',
|
|
504
|
+
args: [params],
|
|
505
|
+
account: this.walletClient.account.address,
|
|
506
|
+
...txOptions,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
if (waitForConfirmation) {
|
|
510
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
511
|
+
return hash;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return hash;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Places a limit order using a params struct
|
|
519
|
+
* @param params - LimitOrderParams struct containing all order parameters
|
|
520
|
+
* @param options - Transaction options
|
|
521
|
+
* @returns Transaction hash
|
|
522
|
+
*/
|
|
523
|
+
async placeLimitOrderWithStruct(
|
|
524
|
+
params: AlphaTypes.LimitOrderParams,
|
|
525
|
+
options?: TransactionOptions,
|
|
526
|
+
): Promise<Hash> {
|
|
527
|
+
return this.placeLimitOrder(
|
|
528
|
+
params.market,
|
|
529
|
+
params.isBuy,
|
|
530
|
+
params.margin,
|
|
531
|
+
params.leverage,
|
|
532
|
+
params.price,
|
|
533
|
+
params.reduceOnlyType,
|
|
534
|
+
options,
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async cancelOrder(market: Address, orderId: AlphaTypes.Bytes16, options?: TransactionOptions): Promise<Hash> {
|
|
539
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
540
|
+
|
|
541
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
542
|
+
|
|
543
|
+
if (!this.walletClient.account) {
|
|
544
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const hash = await this.walletClient.writeContract({
|
|
548
|
+
address: this.contractAddress,
|
|
549
|
+
abi: AlphaABI,
|
|
550
|
+
functionName: 'cancelOrder',
|
|
551
|
+
args: [market, orderId],
|
|
552
|
+
account: this.walletClient.account,
|
|
553
|
+
...txOptions,
|
|
554
|
+
} as any);
|
|
555
|
+
|
|
556
|
+
if (waitForConfirmation) {
|
|
557
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
558
|
+
return hash;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return hash;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async cancelAllOrders(market: Address, options?: TransactionOptions): Promise<Hash> {
|
|
565
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
566
|
+
|
|
567
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
568
|
+
|
|
569
|
+
if (!this.walletClient.account) {
|
|
570
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const hash = await this.walletClient.writeContract({
|
|
574
|
+
address: this.contractAddress,
|
|
575
|
+
abi: AlphaABI,
|
|
576
|
+
functionName: 'cancelAllOrders',
|
|
577
|
+
args: [market],
|
|
578
|
+
account: this.walletClient.account,
|
|
579
|
+
...txOptions,
|
|
580
|
+
} as any);
|
|
581
|
+
|
|
582
|
+
if (waitForConfirmation) {
|
|
583
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
584
|
+
return hash;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return hash;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// ============ Liquidation Functions ============
|
|
591
|
+
|
|
592
|
+
async liquidatePosition(
|
|
593
|
+
positionId: AlphaTypes.Bytes16,
|
|
594
|
+
options?: TransactionOptions,
|
|
595
|
+
): Promise<Hash> {
|
|
596
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
597
|
+
|
|
598
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
599
|
+
|
|
600
|
+
if (!this.walletClient.account) {
|
|
601
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const hash = await this.walletClient.writeContract({
|
|
605
|
+
address: this.contractAddress,
|
|
606
|
+
abi: AlphaABI,
|
|
607
|
+
functionName: 'liquidatePosition',
|
|
608
|
+
args: [positionId],
|
|
609
|
+
account: this.walletClient.account,
|
|
610
|
+
...txOptions,
|
|
611
|
+
} as any);
|
|
612
|
+
|
|
613
|
+
if (waitForConfirmation) {
|
|
614
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
615
|
+
return hash;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
return hash;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
async claimKeeperRewards(options?: TransactionOptions): Promise<Hash> {
|
|
622
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
623
|
+
|
|
624
|
+
const { waitForConfirmation, ...txOptions } = options || {};
|
|
625
|
+
|
|
626
|
+
if (!this.walletClient.account) {
|
|
627
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const hash = await this.walletClient.writeContract({
|
|
631
|
+
address: this.contractAddress,
|
|
632
|
+
abi: AlphaABI,
|
|
633
|
+
functionName: 'claimKeeperRewards',
|
|
634
|
+
args: [],
|
|
635
|
+
account: this.walletClient.account,
|
|
636
|
+
...txOptions,
|
|
637
|
+
} as any);
|
|
638
|
+
|
|
639
|
+
if (waitForConfirmation) {
|
|
640
|
+
await this.publicClient.waitForTransactionReceipt({ hash });
|
|
641
|
+
return hash;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
return hash;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// ============ View Functions - Positions ============
|
|
648
|
+
|
|
649
|
+
async getPositionId(trader: Address, market: Address): Promise<AlphaTypes.Bytes16> {
|
|
650
|
+
const result = await this.publicClient.readContract({
|
|
651
|
+
address: this.contractAddress,
|
|
652
|
+
abi: AlphaABI,
|
|
653
|
+
functionName: 'getPositionId',
|
|
654
|
+
args: [trader, market],
|
|
655
|
+
});
|
|
656
|
+
// Convert bytes32 (padded) to bytes16
|
|
657
|
+
return this.bytes32ToBytes16(result as string) as AlphaTypes.Bytes16;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
async hasPosition(trader: Address, market: Address): Promise<boolean> {
|
|
661
|
+
return (await this.publicClient.readContract({
|
|
662
|
+
address: this.contractAddress,
|
|
663
|
+
abi: AlphaABI,
|
|
664
|
+
functionName: 'hasPosition',
|
|
665
|
+
args: [trader, market],
|
|
666
|
+
})) as boolean;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
async getPosition(positionId: AlphaTypes.Bytes16): Promise<AlphaTypes.MonolithPosition> {
|
|
670
|
+
const result = (await this.publicClient.readContract({
|
|
671
|
+
address: this.contractAddress,
|
|
672
|
+
abi: AlphaABI,
|
|
673
|
+
functionName: 'getPosition',
|
|
674
|
+
args: [positionId],
|
|
675
|
+
})) as any[];
|
|
676
|
+
|
|
677
|
+
// Ensure values are BigInt for calculations
|
|
678
|
+
const notionalValue = BigInt(result[3]);
|
|
679
|
+
const margin = BigInt(result[4]);
|
|
680
|
+
|
|
681
|
+
return {
|
|
682
|
+
// Based on ABI: [0] owner, [1] market, [2] isLong, [3] notionalValue, [4] margin, [5] entryPrice, [6] fundingIndex, [7] lastUpdateTime
|
|
683
|
+
notionalValue: result[3],
|
|
684
|
+
margin: result[4],
|
|
685
|
+
entryPrice: result[5],
|
|
686
|
+
isLong: result[2],
|
|
687
|
+
lastUpdated: result[7], // Maps from contract's lastUpdateTime
|
|
688
|
+
fundingIndex: result[6],
|
|
689
|
+
leverage: margin > 0n ? (notionalValue * 1000000000000000000n) / margin : 0n, // Calculate leverage in 1e18 format (notionalValue/margin * 1e18)
|
|
690
|
+
liquidationPrice: 0n, // Not returned by ABI - would need separate calculation
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
async getPositionDetails(positionId: AlphaTypes.Bytes16): Promise<AlphaTypes.PositionDetails> {
|
|
695
|
+
return (await this.publicClient.readContract({
|
|
696
|
+
address: this.contractAddress,
|
|
697
|
+
abi: AlphaABI,
|
|
698
|
+
functionName: 'getPositionDetails',
|
|
699
|
+
args: [positionId],
|
|
700
|
+
})) as AlphaTypes.PositionDetails;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
async getUserPositions(trader: Address): Promise<AlphaTypes.Bytes16[]> {
|
|
704
|
+
const result = (await this.publicClient.readContract({
|
|
705
|
+
address: this.contractAddress,
|
|
706
|
+
abi: AlphaABI,
|
|
707
|
+
functionName: 'getUserPositions',
|
|
708
|
+
args: [trader],
|
|
709
|
+
})) as string[];
|
|
710
|
+
// Convert each bytes32 (padded) to bytes16
|
|
711
|
+
return result.map((id) => this.bytes32ToBytes16(id) as AlphaTypes.Bytes16);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
async getUserPositionsSummary(trader: Address): Promise<AlphaTypes.PositionSummary> {
|
|
715
|
+
const result = (await this.publicClient.readContract({
|
|
716
|
+
address: this.contractAddress,
|
|
717
|
+
abi: AlphaABI,
|
|
718
|
+
functionName: 'getUserPositionsSummary',
|
|
719
|
+
args: [trader],
|
|
720
|
+
})) as any[];
|
|
721
|
+
return {
|
|
722
|
+
totalPositions: result[0],
|
|
723
|
+
totalMargin: result[1],
|
|
724
|
+
totalUnrealizedPnl: result[2],
|
|
725
|
+
totalSize: result[3],
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
async calculateUnrealizedPnl(positionId: AlphaTypes.Bytes16): Promise<bigint> {
|
|
730
|
+
return (await this.publicClient.readContract({
|
|
731
|
+
address: this.contractAddress,
|
|
732
|
+
abi: AlphaABI,
|
|
733
|
+
functionName: 'calculateUnrealizedPnl',
|
|
734
|
+
args: [positionId],
|
|
735
|
+
})) as bigint;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async getMarginRatio(positionId: AlphaTypes.Bytes16): Promise<bigint> {
|
|
739
|
+
return (await this.publicClient.readContract({
|
|
740
|
+
address: this.contractAddress,
|
|
741
|
+
abi: AlphaABI,
|
|
742
|
+
functionName: 'getMarginRatio',
|
|
743
|
+
args: [positionId],
|
|
744
|
+
})) as bigint;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
async isLiquidatable(positionId: AlphaTypes.Bytes16): Promise<boolean> {
|
|
748
|
+
return (await this.publicClient.readContract({
|
|
749
|
+
address: this.contractAddress,
|
|
750
|
+
abi: AlphaABI,
|
|
751
|
+
functionName: 'isLiquidatable',
|
|
752
|
+
args: [positionId],
|
|
753
|
+
})) as boolean;
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
async getLiquidationStatus(
|
|
757
|
+
positionId: AlphaTypes.Bytes16,
|
|
758
|
+
): Promise<AlphaTypes.LiquidationStatus> {
|
|
759
|
+
const result = (await this.publicClient.readContract({
|
|
760
|
+
address: this.contractAddress,
|
|
761
|
+
abi: AlphaABI,
|
|
762
|
+
functionName: 'getLiquidationStatus',
|
|
763
|
+
args: [positionId],
|
|
764
|
+
})) as any[];
|
|
765
|
+
return {
|
|
766
|
+
isLiquidatable: result[0],
|
|
767
|
+
marginRatio: result[1],
|
|
768
|
+
requiredMargin: result[2],
|
|
769
|
+
liquidationPrice: result[3],
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Get account-level liquidation status (cross-margin)
|
|
775
|
+
* When account equity < total maintenance, ALL positions are liquidatable (Hyperliquid-style)
|
|
776
|
+
*/
|
|
777
|
+
async getAccountLiquidationStatus(
|
|
778
|
+
trader: Address,
|
|
779
|
+
): Promise<{
|
|
780
|
+
liquidatable: boolean;
|
|
781
|
+
worstPositionId: AlphaTypes.Bytes16;
|
|
782
|
+
accountEquity: bigint;
|
|
783
|
+
totalMaintenanceRequired: bigint;
|
|
784
|
+
}> {
|
|
785
|
+
const result = (await this.publicClient.readContract({
|
|
786
|
+
address: this.contractAddress,
|
|
787
|
+
abi: AlphaABI,
|
|
788
|
+
functionName: 'getAccountLiquidationStatus',
|
|
789
|
+
args: [trader],
|
|
790
|
+
})) as any[];
|
|
791
|
+
return {
|
|
792
|
+
liquidatable: result[0],
|
|
793
|
+
worstPositionId: result[1] as AlphaTypes.Bytes16,
|
|
794
|
+
accountEquity: result[2],
|
|
795
|
+
totalMaintenanceRequired: result[3],
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Get cross-margin liquidation price for a position
|
|
801
|
+
* Uses Hyperliquid formula: accounts for all positions' PnL in the account
|
|
802
|
+
*/
|
|
803
|
+
async getAccountLiquidationPrice(positionId: AlphaTypes.Bytes16): Promise<bigint> {
|
|
804
|
+
return (await this.publicClient.readContract({
|
|
805
|
+
address: this.contractAddress,
|
|
806
|
+
abi: AlphaABI,
|
|
807
|
+
functionName: 'getAccountLiquidationPrice',
|
|
808
|
+
args: [positionId],
|
|
809
|
+
})) as bigint;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// ============ View Functions - Margin ============
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* Get raw deposited margin balance (excludes unrealized PnL).
|
|
816
|
+
* For account equity (balance + PnL), use getAccountSummary().
|
|
817
|
+
*/
|
|
818
|
+
async getMarginBalance(trader: Address): Promise<bigint> {
|
|
819
|
+
return (await this.publicClient.readContract({
|
|
820
|
+
address: this.contractAddress,
|
|
821
|
+
abi: AlphaABI,
|
|
822
|
+
functionName: 'getMarginBalance',
|
|
823
|
+
args: [trader],
|
|
824
|
+
})) as bigint;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Get comprehensive account summary in a single RPC call.
|
|
829
|
+
* Returns deposited balance, account equity, unrealized PnL, locked margin, and available margin.
|
|
830
|
+
*/
|
|
831
|
+
async getAccountSummary(trader: Address): Promise<AlphaTypes.AccountSummary> {
|
|
832
|
+
const result = (await this.publicClient.readContract({
|
|
833
|
+
address: this.contractAddress,
|
|
834
|
+
abi: AlphaABI,
|
|
835
|
+
functionName: 'getAccountSummary',
|
|
836
|
+
args: [trader],
|
|
837
|
+
})) as any;
|
|
838
|
+
return {
|
|
839
|
+
depositedBalance: result.depositedBalance ?? result[0],
|
|
840
|
+
accountEquity: result.accountEquity ?? result[1],
|
|
841
|
+
unrealizedPnl: result.unrealizedPnl ?? result[2],
|
|
842
|
+
lockedMargin: result.lockedMargin ?? result[3],
|
|
843
|
+
availableMargin: result.availableMargin ?? result[4],
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
async getLockedMargin(trader: Address): Promise<bigint> {
|
|
848
|
+
return (await this.publicClient.readContract({
|
|
849
|
+
address: this.contractAddress,
|
|
850
|
+
abi: AlphaABI,
|
|
851
|
+
functionName: 'getLockedMargin',
|
|
852
|
+
args: [trader],
|
|
853
|
+
})) as bigint;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
async getAvailableMargin(trader: Address): Promise<bigint> {
|
|
857
|
+
return (await this.publicClient.readContract({
|
|
858
|
+
address: this.contractAddress,
|
|
859
|
+
abi: AlphaABI,
|
|
860
|
+
functionName: 'getAvailableMargin',
|
|
861
|
+
args: [trader],
|
|
862
|
+
})) as bigint;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
async getMaxUserDeposit(): Promise<bigint> {
|
|
866
|
+
return (await this.publicClient.readContract({
|
|
867
|
+
address: this.contractAddress,
|
|
868
|
+
abi: AlphaABI,
|
|
869
|
+
functionName: 'getMaxUserDeposit',
|
|
870
|
+
args: [],
|
|
871
|
+
})) as bigint;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
async getMaxGlobalDeposits(): Promise<bigint> {
|
|
875
|
+
return (await this.publicClient.readContract({
|
|
876
|
+
address: this.contractAddress,
|
|
877
|
+
abi: AlphaABI,
|
|
878
|
+
functionName: 'getMaxGlobalDeposits',
|
|
879
|
+
args: [],
|
|
880
|
+
})) as bigint;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
async getTotalDeposits(): Promise<bigint> {
|
|
884
|
+
return (await this.publicClient.readContract({
|
|
885
|
+
address: this.contractAddress,
|
|
886
|
+
abi: AlphaABI,
|
|
887
|
+
functionName: 'getTotalDeposits',
|
|
888
|
+
args: [],
|
|
889
|
+
})) as bigint;
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// ============ View Functions - Markets ============
|
|
893
|
+
|
|
894
|
+
async getMarketInfo(market: Address): Promise<AlphaTypes.MarketInfo> {
|
|
895
|
+
const result = (await this.publicClient.readContract({
|
|
896
|
+
address: this.contractAddress,
|
|
897
|
+
abi: AlphaABI,
|
|
898
|
+
functionName: 'getMarketInfo',
|
|
899
|
+
args: [market],
|
|
900
|
+
})) as any[];
|
|
901
|
+
|
|
902
|
+
if (!result || !Array.isArray(result) || result.length < 8) {
|
|
903
|
+
throw new Error(
|
|
904
|
+
`Invalid market info response for market ${market}. ` +
|
|
905
|
+
`Expected array with 8 elements, got: ${JSON.stringify(result)}`,
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
return {
|
|
910
|
+
longOpenInterest: result[0],
|
|
911
|
+
shortOpenInterest: result[1],
|
|
912
|
+
fundingRate: result[2],
|
|
913
|
+
lastFundingUpdate: result[3],
|
|
914
|
+
isActive: result[4],
|
|
915
|
+
maxLeverage: result[5],
|
|
916
|
+
maintenanceMargin: result[6],
|
|
917
|
+
liquidationFee: result[7],
|
|
918
|
+
};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
async getMarketOpenInterest(market: Address): Promise<{ longOI: bigint; shortOI: bigint }> {
|
|
922
|
+
const result = (await this.publicClient.readContract({
|
|
923
|
+
address: this.contractAddress,
|
|
924
|
+
abi: AlphaABI,
|
|
925
|
+
functionName: 'getMarketOpenInterest',
|
|
926
|
+
args: [market],
|
|
927
|
+
})) as any[];
|
|
928
|
+
return {
|
|
929
|
+
longOI: result[0],
|
|
930
|
+
shortOI: result[1],
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
async getFundingRate(market: Address): Promise<bigint> {
|
|
935
|
+
return (await this.publicClient.readContract({
|
|
936
|
+
address: this.contractAddress,
|
|
937
|
+
abi: AlphaABI,
|
|
938
|
+
functionName: 'getFundingRate',
|
|
939
|
+
args: [market],
|
|
940
|
+
})) as bigint;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
async getFundingState(market: Address): Promise<AlphaTypes.FundingState> {
|
|
944
|
+
const result = (await this.publicClient.readContract({
|
|
945
|
+
address: this.contractAddress,
|
|
946
|
+
abi: AlphaABI,
|
|
947
|
+
functionName: 'getFundingState',
|
|
948
|
+
args: [market],
|
|
949
|
+
})) as any[];
|
|
950
|
+
|
|
951
|
+
// Based on ABI, getFundingState returns:
|
|
952
|
+
// [0] currentRate (int256), [1] cumulativeIndex (int256), [2] lastUpdate (uint256)
|
|
953
|
+
const lastUpdateTime = result[2] !== undefined ? BigInt(result[2]) : 0n;
|
|
954
|
+
|
|
955
|
+
return {
|
|
956
|
+
fundingRate: result[0] || 0n,
|
|
957
|
+
fundingIndex: result[1] || 0n,
|
|
958
|
+
lastUpdateTime: result[2] || 0n,
|
|
959
|
+
nextFundingTime: lastUpdateTime + 8n * 3600n, // Add 8 hours to lastUpdate
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// ============ Funding Rate Updates (Keeper Function) ============
|
|
964
|
+
|
|
965
|
+
async updateFundingRates(
|
|
966
|
+
markets: Address[],
|
|
967
|
+
options?: TransactionOptions,
|
|
968
|
+
): Promise<{
|
|
969
|
+
hash: Hash;
|
|
970
|
+
wait: () => Promise<TransactionReceipt>;
|
|
971
|
+
}> {
|
|
972
|
+
if (!this.walletClient) throw new Error('Wallet client required for write operations');
|
|
973
|
+
|
|
974
|
+
const { waitForConfirmation: _waitForConfirmation, ...txOptions } = options || {};
|
|
975
|
+
|
|
976
|
+
if (!this.walletClient.account) {
|
|
977
|
+
throw new Error('Wallet client must have an account for transactions');
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
const hash = await this.walletClient.writeContract({
|
|
981
|
+
address: this.contractAddress,
|
|
982
|
+
abi: AlphaABI,
|
|
983
|
+
functionName: 'updateFundingRates',
|
|
984
|
+
args: [markets],
|
|
985
|
+
account: this.walletClient.account,
|
|
986
|
+
...txOptions,
|
|
987
|
+
} as any);
|
|
988
|
+
|
|
989
|
+
return {
|
|
990
|
+
hash,
|
|
991
|
+
wait: async () => {
|
|
992
|
+
const receipt = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
993
|
+
return receipt;
|
|
994
|
+
},
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// ============ View Functions - Orders ============
|
|
999
|
+
|
|
1000
|
+
async getBestBid(market: Address): Promise<AlphaTypes.Bytes16> {
|
|
1001
|
+
return (await this.publicClient.readContract({
|
|
1002
|
+
address: this.contractAddress,
|
|
1003
|
+
abi: AlphaABI,
|
|
1004
|
+
functionName: 'getBestBid',
|
|
1005
|
+
args: [market],
|
|
1006
|
+
})) as AlphaTypes.Bytes16;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
async getBestAsk(market: Address): Promise<AlphaTypes.Bytes16> {
|
|
1010
|
+
return (await this.publicClient.readContract({
|
|
1011
|
+
address: this.contractAddress,
|
|
1012
|
+
abi: AlphaABI,
|
|
1013
|
+
functionName: 'getBestAsk',
|
|
1014
|
+
args: [market],
|
|
1015
|
+
})) as AlphaTypes.Bytes16;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Get all open order IDs for a trader in a specific market
|
|
1020
|
+
* @param trader - Trader address
|
|
1021
|
+
* @param market - Market address
|
|
1022
|
+
* @returns Array of order IDs (bytes16)
|
|
1023
|
+
*/
|
|
1024
|
+
async getUserOrders(trader: Address, market: Address): Promise<AlphaTypes.Bytes16[]> {
|
|
1025
|
+
const result = (await this.publicClient.readContract({
|
|
1026
|
+
address: this.contractAddress,
|
|
1027
|
+
abi: AlphaABI,
|
|
1028
|
+
functionName: 'getUserOrders',
|
|
1029
|
+
args: [trader, market],
|
|
1030
|
+
})) as string[];
|
|
1031
|
+
// Convert each bytes32 (padded) to bytes16
|
|
1032
|
+
return result.map((id) => this.bytes32ToBytes16(id) as AlphaTypes.Bytes16);
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
/**
|
|
1036
|
+
* Get details of a specific order
|
|
1037
|
+
* @param market - The market address
|
|
1038
|
+
* @param orderId - The order ID (bytes16)
|
|
1039
|
+
* @returns Order details including trader, direction, size, price, reduceOnly
|
|
1040
|
+
*/
|
|
1041
|
+
async getOrderDetails(market: Address, orderId: AlphaTypes.Bytes16): Promise<AlphaTypes.OrderDetails> {
|
|
1042
|
+
const result = (await this.publicClient.readContract({
|
|
1043
|
+
address: this.contractAddress,
|
|
1044
|
+
abi: AlphaABI,
|
|
1045
|
+
functionName: 'getOrderDetails',
|
|
1046
|
+
args: [market, orderId],
|
|
1047
|
+
})) as any;
|
|
1048
|
+
|
|
1049
|
+
// Contract returns a struct with: trader, isLong, notionalValue, price, reduceOnly, next, prev
|
|
1050
|
+
return {
|
|
1051
|
+
trader: result.trader,
|
|
1052
|
+
isLong: result.isLong,
|
|
1053
|
+
notionalValue: result.notionalValue,
|
|
1054
|
+
price: result.price,
|
|
1055
|
+
reduceOnly: result.reduceOnly,
|
|
1056
|
+
next: result.next,
|
|
1057
|
+
prev: result.prev,
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
// ============ View Functions - Vault ============
|
|
1062
|
+
|
|
1063
|
+
async getVaultStats(): Promise<AlphaTypes.VaultStats> {
|
|
1064
|
+
const result = (await this.publicClient.readContract({
|
|
1065
|
+
address: this.contractAddress,
|
|
1066
|
+
abi: AlphaABI,
|
|
1067
|
+
functionName: 'getVaultStats',
|
|
1068
|
+
args: [],
|
|
1069
|
+
})) as any[];
|
|
1070
|
+
return {
|
|
1071
|
+
totalReserves: result[0],
|
|
1072
|
+
availableReserves: result[1],
|
|
1073
|
+
totalExposure: result[2],
|
|
1074
|
+
unrealizedPnl: result[3],
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
async getVaultBalance(): Promise<bigint> {
|
|
1079
|
+
return (await this.publicClient.readContract({
|
|
1080
|
+
address: this.contractAddress,
|
|
1081
|
+
abi: AlphaABI,
|
|
1082
|
+
functionName: 'getVaultBalance',
|
|
1083
|
+
args: [],
|
|
1084
|
+
})) as bigint;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
async getVaultState(): Promise<AlphaTypes.VaultState> {
|
|
1088
|
+
return (await this.publicClient.readContract({
|
|
1089
|
+
address: this.contractAddress,
|
|
1090
|
+
abi: AlphaABI,
|
|
1091
|
+
functionName: 'getVaultState',
|
|
1092
|
+
args: [],
|
|
1093
|
+
})) as AlphaTypes.VaultState;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
async getVaultExposure(market: Address): Promise<AlphaTypes.VaultExposure> {
|
|
1097
|
+
const result = (await this.publicClient.readContract({
|
|
1098
|
+
address: this.contractAddress,
|
|
1099
|
+
abi: AlphaABI,
|
|
1100
|
+
functionName: 'getVaultExposure',
|
|
1101
|
+
args: [market],
|
|
1102
|
+
})) as any[];
|
|
1103
|
+
return {
|
|
1104
|
+
longExposure: result[0],
|
|
1105
|
+
shortExposure: result[1],
|
|
1106
|
+
netExposure: result[2],
|
|
1107
|
+
utilizationRate: result[3],
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// ============ View Functions - Protocol Limits ============
|
|
1112
|
+
|
|
1113
|
+
async getMaxPositionSize(market: Address): Promise<bigint> {
|
|
1114
|
+
return (await this.publicClient.readContract({
|
|
1115
|
+
address: this.contractAddress,
|
|
1116
|
+
abi: AlphaABI,
|
|
1117
|
+
functionName: 'getMaxPositionSize',
|
|
1118
|
+
args: [market],
|
|
1119
|
+
})) as bigint;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// ============ View Functions - Execution Analysis ============
|
|
1123
|
+
|
|
1124
|
+
async calculateVaultSlippage(market: Address, size: bigint, isBuy: boolean): Promise<bigint> {
|
|
1125
|
+
return (await this.publicClient.readContract({
|
|
1126
|
+
address: this.contractAddress,
|
|
1127
|
+
abi: AlphaABI,
|
|
1128
|
+
functionName: 'calculateVaultSlippage',
|
|
1129
|
+
args: [market, size, isBuy],
|
|
1130
|
+
})) as bigint;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
async getOptimalExecutionPlan(
|
|
1134
|
+
market: Address,
|
|
1135
|
+
size: bigint,
|
|
1136
|
+
isLong: boolean,
|
|
1137
|
+
maxSlippage: bigint,
|
|
1138
|
+
): Promise<AlphaTypes.ExecutionPlan> {
|
|
1139
|
+
return (await this.publicClient.readContract({
|
|
1140
|
+
address: this.contractAddress,
|
|
1141
|
+
abi: AlphaABI,
|
|
1142
|
+
functionName: 'getOptimalExecutionPlan',
|
|
1143
|
+
args: [market, size, isLong, maxSlippage],
|
|
1144
|
+
})) as AlphaTypes.ExecutionPlan;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
async getExecutionStrategyAnalysis(
|
|
1148
|
+
market: Address,
|
|
1149
|
+
size: bigint,
|
|
1150
|
+
isLong: boolean,
|
|
1151
|
+
maxSlippage: bigint,
|
|
1152
|
+
): Promise<AlphaTypes.ExecutionStrategyAnalysis> {
|
|
1153
|
+
return (await this.publicClient.readContract({
|
|
1154
|
+
address: this.contractAddress,
|
|
1155
|
+
abi: AlphaABI,
|
|
1156
|
+
functionName: 'getExecutionStrategyAnalysisStruct',
|
|
1157
|
+
args: [market, size, isLong, maxSlippage],
|
|
1158
|
+
})) as AlphaTypes.ExecutionStrategyAnalysis;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// ============ View Functions - Market Depth ============
|
|
1162
|
+
|
|
1163
|
+
async getMarketDepth(
|
|
1164
|
+
market: Address,
|
|
1165
|
+
isBuy: boolean,
|
|
1166
|
+
levels: bigint,
|
|
1167
|
+
): Promise<{ depth: AlphaTypes.DepthLevel[]; totalLiquidity: bigint }> {
|
|
1168
|
+
const result = (await this.publicClient.readContract({
|
|
1169
|
+
address: this.contractAddress,
|
|
1170
|
+
abi: AlphaABI,
|
|
1171
|
+
functionName: 'getMarketDepth',
|
|
1172
|
+
args: [market, isBuy, levels],
|
|
1173
|
+
})) as any[];
|
|
1174
|
+
return {
|
|
1175
|
+
depth: result[0],
|
|
1176
|
+
totalLiquidity: result[1],
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
async getOrderbookDepth(
|
|
1181
|
+
market: Address,
|
|
1182
|
+
levels: number,
|
|
1183
|
+
): Promise<{ bidPrices: bigint[]; bidNotionals: bigint[]; askPrices: bigint[]; askNotionals: bigint[] }> {
|
|
1184
|
+
const result = (await this.publicClient.readContract({
|
|
1185
|
+
address: this.contractAddress,
|
|
1186
|
+
abi: AlphaABI,
|
|
1187
|
+
functionName: 'getOrderbookDepth',
|
|
1188
|
+
args: [market, levels],
|
|
1189
|
+
})) as [bigint[], bigint[], bigint[], bigint[]];
|
|
1190
|
+
return {
|
|
1191
|
+
bidPrices: result[0],
|
|
1192
|
+
bidNotionals: result[1],
|
|
1193
|
+
askPrices: result[2],
|
|
1194
|
+
askNotionals: result[3],
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
async getVWAP(market: Address, size: bigint, isBuy: boolean): Promise<AlphaTypes.VWAPResult> {
|
|
1199
|
+
return (await this.publicClient.readContract({
|
|
1200
|
+
address: this.contractAddress,
|
|
1201
|
+
abi: AlphaABI,
|
|
1202
|
+
functionName: 'getVWAP',
|
|
1203
|
+
args: [market, size, isBuy],
|
|
1204
|
+
})) as AlphaTypes.VWAPResult;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1207
|
+
async getBidAskSpread(market: Address): Promise<AlphaTypes.SpreadInfo> {
|
|
1208
|
+
return (await this.publicClient.readContract({
|
|
1209
|
+
address: this.contractAddress,
|
|
1210
|
+
abi: AlphaABI,
|
|
1211
|
+
functionName: 'getBidAskSpread',
|
|
1212
|
+
args: [market],
|
|
1213
|
+
})) as AlphaTypes.SpreadInfo;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
async estimateMarketImpact(
|
|
1217
|
+
market: Address,
|
|
1218
|
+
size: bigint,
|
|
1219
|
+
isBuy: boolean,
|
|
1220
|
+
): Promise<AlphaTypes.MarketImpactEstimate> {
|
|
1221
|
+
const result = (await this.publicClient.readContract({
|
|
1222
|
+
address: this.contractAddress,
|
|
1223
|
+
abi: AlphaABI,
|
|
1224
|
+
functionName: 'estimateMarketImpact',
|
|
1225
|
+
args: [market, size, isBuy],
|
|
1226
|
+
})) as any[];
|
|
1227
|
+
return {
|
|
1228
|
+
averagePrice: result[0],
|
|
1229
|
+
worstPrice: result[1],
|
|
1230
|
+
priceImpact: result[2],
|
|
1231
|
+
filledFromOrderbook: result[3],
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
async getMarketLiquidityAnalysis(market: Address): Promise<AlphaTypes.MarketLiquidityAnalysis> {
|
|
1236
|
+
return (await this.publicClient.readContract({
|
|
1237
|
+
address: this.contractAddress,
|
|
1238
|
+
abi: AlphaABI,
|
|
1239
|
+
functionName: 'getMarketLiquidityAnalysisStruct',
|
|
1240
|
+
args: [market],
|
|
1241
|
+
})) as AlphaTypes.MarketLiquidityAnalysis;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// ============ Event Parsing ============
|
|
1245
|
+
|
|
1246
|
+
parsePositionOpenedEvent(
|
|
1247
|
+
receipt: TransactionReceipt,
|
|
1248
|
+
): AlphaTypes.PositionOpenedEvent | undefined {
|
|
1249
|
+
const logs = parseEventLogs({
|
|
1250
|
+
abi: AlphaABI,
|
|
1251
|
+
logs: receipt.logs,
|
|
1252
|
+
eventName: 'PositionOpened',
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
if (logs.length > 0) {
|
|
1256
|
+
const event = logs[0] as any;
|
|
1257
|
+
// Convert bytes32 (padded) back to bytes16
|
|
1258
|
+
const positionId = this.bytes32ToBytes16(event.args.positionId);
|
|
1259
|
+
return {
|
|
1260
|
+
positionId: positionId,
|
|
1261
|
+
trader: event.args.trader,
|
|
1262
|
+
market: event.args.market,
|
|
1263
|
+
isLong: event.args.isLong,
|
|
1264
|
+
notionalValue: event.args.notionalValue,
|
|
1265
|
+
margin: event.args.margin,
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
return undefined;
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
parsePositionClosedEvent(
|
|
1272
|
+
receipt: TransactionReceipt,
|
|
1273
|
+
): AlphaTypes.PositionClosedEvent | undefined {
|
|
1274
|
+
const logs = parseEventLogs({
|
|
1275
|
+
abi: AlphaABI,
|
|
1276
|
+
logs: receipt.logs,
|
|
1277
|
+
eventName: 'PositionClosed',
|
|
1278
|
+
});
|
|
1279
|
+
|
|
1280
|
+
if (logs.length > 0) {
|
|
1281
|
+
const event = logs[0] as any;
|
|
1282
|
+
// Convert bytes32 (padded) back to bytes16
|
|
1283
|
+
const positionId = this.bytes32ToBytes16(event.args.positionId);
|
|
1284
|
+
return {
|
|
1285
|
+
positionId: positionId,
|
|
1286
|
+
trader: event.args.trader,
|
|
1287
|
+
pnl: event.args.pnl,
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
return undefined;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
parseOrderPlacedEvent(receipt: TransactionReceipt): AlphaTypes.OrderPlacedEvent | undefined {
|
|
1294
|
+
const logs = parseEventLogs({
|
|
1295
|
+
abi: AlphaABI,
|
|
1296
|
+
logs: receipt.logs,
|
|
1297
|
+
eventName: 'OrderPlaced',
|
|
1298
|
+
});
|
|
1299
|
+
|
|
1300
|
+
if (logs.length > 0) {
|
|
1301
|
+
const event = logs[0] as any;
|
|
1302
|
+
return {
|
|
1303
|
+
orderId: event.args.orderId,
|
|
1304
|
+
trader: event.args.trader,
|
|
1305
|
+
market: event.args.market,
|
|
1306
|
+
isBuy: event.args.isBuy,
|
|
1307
|
+
notionalValue: event.args.notionalValue,
|
|
1308
|
+
price: event.args.price,
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
return undefined;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
parseHybridOrderExecutedEvent(
|
|
1315
|
+
receipt: TransactionReceipt,
|
|
1316
|
+
): AlphaTypes.HybridOrderExecutedEvent | undefined {
|
|
1317
|
+
const logs = parseEventLogs({
|
|
1318
|
+
abi: AlphaABI,
|
|
1319
|
+
logs: receipt.logs,
|
|
1320
|
+
eventName: 'HybridOrderExecuted',
|
|
1321
|
+
});
|
|
1322
|
+
|
|
1323
|
+
if (logs.length > 0) {
|
|
1324
|
+
const event = logs[0] as any;
|
|
1325
|
+
return {
|
|
1326
|
+
trader: event.args.trader,
|
|
1327
|
+
market: event.args.market,
|
|
1328
|
+
orderbookFilled: event.args.orderbookFilled,
|
|
1329
|
+
vaultFilled: event.args.vaultFilled,
|
|
1330
|
+
avgPrice: event.args.avgPrice,
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
return undefined;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// ============ Helper Functions ============
|
|
1337
|
+
|
|
1338
|
+
async waitForTransaction(hash: Hash): Promise<TransactionReceipt> {
|
|
1339
|
+
return this.publicClient.waitForTransactionReceipt({ hash });
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
getContractAddress(): Address {
|
|
1343
|
+
return this.contractAddress;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// Generic readContract method for custom function calls
|
|
1347
|
+
async readContract({ functionName, args }: { functionName: string; args: any[] }): Promise<any> {
|
|
1348
|
+
return await this.publicClient.readContract({
|
|
1349
|
+
address: this.contractAddress,
|
|
1350
|
+
abi: AlphaABI,
|
|
1351
|
+
functionName,
|
|
1352
|
+
args,
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
async getConstants(): Promise<AlphaTypes.MonolithConfig> {
|
|
1357
|
+
const [
|
|
1358
|
+
precision,
|
|
1359
|
+
basisPoints,
|
|
1360
|
+
maxLeverage,
|
|
1361
|
+
defaultLeverage,
|
|
1362
|
+
minPositionSize,
|
|
1363
|
+
liquidationThreshold,
|
|
1364
|
+
partialLiquidationThreshold,
|
|
1365
|
+
liquidationFee,
|
|
1366
|
+
fundingInterval,
|
|
1367
|
+
maxFundingRate,
|
|
1368
|
+
maxExposurePerAsset,
|
|
1369
|
+
maxTotalUtilization,
|
|
1370
|
+
criticalUtilizationBps,
|
|
1371
|
+
maxLossRateBps,
|
|
1372
|
+
maxLiquidationsPerBlock,
|
|
1373
|
+
] = await Promise.all([
|
|
1374
|
+
this.publicClient.readContract({
|
|
1375
|
+
address: this.contractAddress,
|
|
1376
|
+
abi: AlphaABI,
|
|
1377
|
+
functionName: 'PRECISION',
|
|
1378
|
+
args: [],
|
|
1379
|
+
}) as Promise<bigint>,
|
|
1380
|
+
this.publicClient.readContract({
|
|
1381
|
+
address: this.contractAddress,
|
|
1382
|
+
abi: AlphaABI,
|
|
1383
|
+
functionName: 'BASIS_POINTS',
|
|
1384
|
+
args: [],
|
|
1385
|
+
}) as Promise<bigint>,
|
|
1386
|
+
this.publicClient.readContract({
|
|
1387
|
+
address: this.contractAddress,
|
|
1388
|
+
abi: AlphaABI,
|
|
1389
|
+
functionName: 'MAX_LEVERAGE',
|
|
1390
|
+
args: [],
|
|
1391
|
+
}) as Promise<bigint>,
|
|
1392
|
+
this.publicClient.readContract({
|
|
1393
|
+
address: this.contractAddress,
|
|
1394
|
+
abi: AlphaABI,
|
|
1395
|
+
functionName: 'DEFAULT_LEVERAGE',
|
|
1396
|
+
args: [],
|
|
1397
|
+
}) as Promise<bigint>,
|
|
1398
|
+
this.publicClient.readContract({
|
|
1399
|
+
address: this.contractAddress,
|
|
1400
|
+
abi: AlphaABI,
|
|
1401
|
+
functionName: 'MIN_POSITION_SIZE',
|
|
1402
|
+
args: [],
|
|
1403
|
+
}) as Promise<bigint>,
|
|
1404
|
+
this.publicClient.readContract({
|
|
1405
|
+
address: this.contractAddress,
|
|
1406
|
+
abi: AlphaABI,
|
|
1407
|
+
functionName: 'LIQUIDATION_THRESHOLD',
|
|
1408
|
+
args: [],
|
|
1409
|
+
}) as Promise<bigint>,
|
|
1410
|
+
this.publicClient.readContract({
|
|
1411
|
+
address: this.contractAddress,
|
|
1412
|
+
abi: AlphaABI,
|
|
1413
|
+
functionName: 'PARTIAL_LIQUIDATION_THRESHOLD',
|
|
1414
|
+
args: [],
|
|
1415
|
+
}) as Promise<bigint>,
|
|
1416
|
+
this.publicClient.readContract({
|
|
1417
|
+
address: this.contractAddress,
|
|
1418
|
+
abi: AlphaABI,
|
|
1419
|
+
functionName: 'LIQUIDATION_FEE',
|
|
1420
|
+
args: [],
|
|
1421
|
+
}) as Promise<bigint>,
|
|
1422
|
+
this.publicClient.readContract({
|
|
1423
|
+
address: this.contractAddress,
|
|
1424
|
+
abi: AlphaABI,
|
|
1425
|
+
functionName: 'FUNDING_INTERVAL',
|
|
1426
|
+
args: [],
|
|
1427
|
+
}) as Promise<bigint>,
|
|
1428
|
+
this.publicClient.readContract({
|
|
1429
|
+
address: this.contractAddress,
|
|
1430
|
+
abi: AlphaABI,
|
|
1431
|
+
functionName: 'MAX_FUNDING_RATE',
|
|
1432
|
+
args: [],
|
|
1433
|
+
}) as Promise<bigint>,
|
|
1434
|
+
this.publicClient.readContract({
|
|
1435
|
+
address: this.contractAddress,
|
|
1436
|
+
abi: AlphaABI,
|
|
1437
|
+
functionName: 'MAX_EXPOSURE_PER_ASSET',
|
|
1438
|
+
args: [],
|
|
1439
|
+
}) as Promise<bigint>,
|
|
1440
|
+
this.publicClient.readContract({
|
|
1441
|
+
address: this.contractAddress,
|
|
1442
|
+
abi: AlphaABI,
|
|
1443
|
+
functionName: 'MAX_TOTAL_UTILIZATION',
|
|
1444
|
+
args: [],
|
|
1445
|
+
}) as Promise<bigint>,
|
|
1446
|
+
this.publicClient.readContract({
|
|
1447
|
+
address: this.contractAddress,
|
|
1448
|
+
abi: AlphaABI,
|
|
1449
|
+
functionName: 'CRITICAL_UTILIZATION_BPS',
|
|
1450
|
+
args: [],
|
|
1451
|
+
}) as Promise<bigint>,
|
|
1452
|
+
this.publicClient.readContract({
|
|
1453
|
+
address: this.contractAddress,
|
|
1454
|
+
abi: AlphaABI,
|
|
1455
|
+
functionName: 'MAX_LOSS_RATE_BPS',
|
|
1456
|
+
args: [],
|
|
1457
|
+
}) as Promise<bigint>,
|
|
1458
|
+
this.publicClient.readContract({
|
|
1459
|
+
address: this.contractAddress,
|
|
1460
|
+
abi: AlphaABI,
|
|
1461
|
+
functionName: 'MAX_LIQUIDATIONS_PER_BLOCK',
|
|
1462
|
+
args: [],
|
|
1463
|
+
}) as Promise<bigint>,
|
|
1464
|
+
]);
|
|
1465
|
+
|
|
1466
|
+
return {
|
|
1467
|
+
precision,
|
|
1468
|
+
basisPoints,
|
|
1469
|
+
maxLeverage,
|
|
1470
|
+
defaultLeverage,
|
|
1471
|
+
minPositionSize,
|
|
1472
|
+
liquidationThreshold,
|
|
1473
|
+
partialLiquidationThreshold,
|
|
1474
|
+
liquidationFee,
|
|
1475
|
+
fundingInterval,
|
|
1476
|
+
maxFundingRate,
|
|
1477
|
+
maxExposurePerAsset,
|
|
1478
|
+
maxTotalUtilization,
|
|
1479
|
+
criticalUtilizationBps,
|
|
1480
|
+
maxLossRateBps,
|
|
1481
|
+
maxLiquidationsPerBlock,
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// Event watching methods
|
|
1486
|
+
|
|
1487
|
+
/**
|
|
1488
|
+
* Watch for MarginDeposited events
|
|
1489
|
+
*/
|
|
1490
|
+
watchMarginDepositedEvent(callback: (trader: Address, amount: bigint) => void) {
|
|
1491
|
+
return this.publicClient.watchContractEvent({
|
|
1492
|
+
address: this.contractAddress,
|
|
1493
|
+
abi: AlphaABI,
|
|
1494
|
+
eventName: 'MarginDeposited',
|
|
1495
|
+
onLogs: (logs) => {
|
|
1496
|
+
for (const log of logs) {
|
|
1497
|
+
const { trader, amount } = (log as any).args as { trader: Address; amount: bigint };
|
|
1498
|
+
callback(trader, amount);
|
|
1499
|
+
}
|
|
1500
|
+
},
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* Watch for MarginWithdrawn events
|
|
1506
|
+
*/
|
|
1507
|
+
watchMarginWithdrawnEvent(callback: (trader: Address, amount: bigint) => void) {
|
|
1508
|
+
return this.publicClient.watchContractEvent({
|
|
1509
|
+
address: this.contractAddress,
|
|
1510
|
+
abi: AlphaABI,
|
|
1511
|
+
eventName: 'MarginWithdrawn',
|
|
1512
|
+
onLogs: (logs) => {
|
|
1513
|
+
for (const log of logs) {
|
|
1514
|
+
const { trader, amount } = (log as any).args as { trader: Address; amount: bigint };
|
|
1515
|
+
callback(trader, amount);
|
|
1516
|
+
}
|
|
1517
|
+
},
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
/**
|
|
1522
|
+
* Watch for PositionOpened events
|
|
1523
|
+
*/
|
|
1524
|
+
watchPositionOpenedEvent(
|
|
1525
|
+
callback: (
|
|
1526
|
+
positionId: `0x${string}`,
|
|
1527
|
+
trader: Address,
|
|
1528
|
+
market: Address,
|
|
1529
|
+
isLong: boolean,
|
|
1530
|
+
notionalValue: bigint,
|
|
1531
|
+
entryPrice: bigint,
|
|
1532
|
+
margin: bigint,
|
|
1533
|
+
) => void,
|
|
1534
|
+
) {
|
|
1535
|
+
return this.publicClient.watchContractEvent({
|
|
1536
|
+
address: this.contractAddress,
|
|
1537
|
+
abi: AlphaABI,
|
|
1538
|
+
eventName: 'PositionOpened',
|
|
1539
|
+
onLogs: (logs) => {
|
|
1540
|
+
for (const log of logs) {
|
|
1541
|
+
const { positionId, trader, market, isLong, notionalValue, entryPrice, margin } = (
|
|
1542
|
+
log as any
|
|
1543
|
+
).args as any;
|
|
1544
|
+
callback(positionId, trader, market, isLong, notionalValue, entryPrice, margin);
|
|
1545
|
+
}
|
|
1546
|
+
},
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
/**
|
|
1551
|
+
* Get past MarginDeposited events
|
|
1552
|
+
*/
|
|
1553
|
+
async getMarginDepositedEvents(fromBlock?: bigint, toBlock?: bigint, trader?: Address) {
|
|
1554
|
+
return await this.publicClient.getContractEvents({
|
|
1555
|
+
address: this.contractAddress,
|
|
1556
|
+
abi: AlphaABI,
|
|
1557
|
+
eventName: 'MarginDeposited',
|
|
1558
|
+
fromBlock: fromBlock || 'earliest',
|
|
1559
|
+
toBlock: toBlock || 'latest',
|
|
1560
|
+
args: trader ? { trader } : undefined,
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
/**
|
|
1565
|
+
* Get past MarginWithdrawn events
|
|
1566
|
+
*/
|
|
1567
|
+
async getMarginWithdrawnEvents(fromBlock?: bigint, toBlock?: bigint, trader?: Address) {
|
|
1568
|
+
return await this.publicClient.getContractEvents({
|
|
1569
|
+
address: this.contractAddress,
|
|
1570
|
+
abi: AlphaABI,
|
|
1571
|
+
eventName: 'MarginWithdrawn',
|
|
1572
|
+
fromBlock: fromBlock || 'earliest',
|
|
1573
|
+
toBlock: toBlock || 'latest',
|
|
1574
|
+
args: trader ? { trader } : undefined,
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
/**
|
|
1579
|
+
* Get past PositionOpened events
|
|
1580
|
+
*/
|
|
1581
|
+
async getPositionOpenedEvents(fromBlock?: bigint, toBlock?: bigint, trader?: Address) {
|
|
1582
|
+
return await this.publicClient.getContractEvents({
|
|
1583
|
+
address: this.contractAddress,
|
|
1584
|
+
abi: AlphaABI,
|
|
1585
|
+
eventName: 'PositionOpened',
|
|
1586
|
+
fromBlock: fromBlock || 'earliest',
|
|
1587
|
+
toBlock: toBlock || 'latest',
|
|
1588
|
+
args: trader ? { trader } : undefined,
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/**
|
|
1593
|
+
* Estimate gas for a transaction
|
|
1594
|
+
*/
|
|
1595
|
+
async estimateGas(
|
|
1596
|
+
functionName: string,
|
|
1597
|
+
args: any[],
|
|
1598
|
+
options?: TransactionOptions,
|
|
1599
|
+
): Promise<bigint> {
|
|
1600
|
+
if (!this.walletClient) {
|
|
1601
|
+
throw new Error('Wallet client is required for gas estimation');
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
const { gasPrice: _gasPrice, waitForConfirmation: _waitForConfirmation, ...estimateOptions } = options || {};
|
|
1605
|
+
|
|
1606
|
+
return await this.publicClient.estimateContractGas({
|
|
1607
|
+
address: this.contractAddress,
|
|
1608
|
+
abi: AlphaABI,
|
|
1609
|
+
functionName,
|
|
1610
|
+
args,
|
|
1611
|
+
account: this.walletClient.account,
|
|
1612
|
+
...estimateOptions,
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
}
|