@metaflux-dex/client 0.0.3 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +196 -16
- package/dist/client.d.ts +173 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +264 -11
- package/dist/client.js.map +1 -1
- package/dist/faucet.js +1 -1
- package/dist/faucet.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -9
- package/dist/index.js.map +1 -1
- package/dist/native/actions.d.ts +46 -0
- package/dist/native/actions.d.ts.map +1 -0
- package/dist/native/actions.js +593 -0
- package/dist/native/actions.js.map +1 -0
- package/dist/native/digest.d.ts +28 -0
- package/dist/native/digest.d.ts.map +1 -0
- package/dist/{native.js → native/digest.js} +71 -86
- package/dist/native/digest.js.map +1 -0
- package/dist/native/index.d.ts +3 -0
- package/dist/native/index.d.ts.map +1 -0
- package/dist/native/index.js +5 -0
- package/dist/native/index.js.map +1 -0
- package/dist/rest/http.d.ts.map +1 -0
- package/dist/rest/http.js.map +1 -0
- package/dist/{info.d.ts → rest/info.d.ts} +1 -1
- package/dist/rest/info.d.ts.map +1 -0
- package/dist/{info.js → rest/info.js} +8 -5
- package/dist/rest/info.js.map +1 -0
- package/dist/types/account.d.ts +52 -0
- package/dist/types/account.d.ts.map +1 -0
- package/dist/types/account.js +8 -0
- package/dist/types/account.js.map +1 -0
- package/dist/types/encrypted.d.ts +8 -0
- package/dist/types/encrypted.d.ts.map +1 -0
- package/dist/types/encrypted.js +7 -0
- package/dist/types/encrypted.js.map +1 -0
- package/dist/types/governance.d.ts +11 -0
- package/dist/types/governance.d.ts.map +1 -0
- package/dist/types/governance.js +7 -0
- package/dist/types/governance.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/info/core.d.ts +97 -0
- package/dist/types/info/core.d.ts.map +1 -0
- package/dist/types/info/core.js +15 -0
- package/dist/types/info/core.js.map +1 -0
- package/dist/{info-types.d.ts → types/info/hl-parity.d.ts} +8 -177
- package/dist/types/info/hl-parity.d.ts.map +1 -0
- package/dist/types/info/hl-parity.js +8 -0
- package/dist/types/info/hl-parity.js.map +1 -0
- package/dist/types/info/index.d.ts +4 -0
- package/dist/types/info/index.d.ts.map +1 -0
- package/dist/types/info/index.js +6 -0
- package/dist/types/info/index.js.map +1 -0
- package/dist/types/info/reads.d.ts +81 -0
- package/dist/types/info/reads.d.ts.map +1 -0
- package/dist/types/info/reads.js +7 -0
- package/dist/types/info/reads.js.map +1 -0
- package/dist/types/meta-bridge.d.ts +8 -0
- package/dist/types/meta-bridge.d.ts.map +1 -0
- package/dist/types/meta-bridge.js +7 -0
- package/dist/types/meta-bridge.js.map +1 -0
- package/dist/types/spot.d.ts +41 -0
- package/dist/types/spot.d.ts.map +1 -0
- package/dist/types/spot.js +7 -0
- package/dist/types/spot.js.map +1 -0
- package/dist/types/staking.d.ts +12 -0
- package/dist/types/staking.d.ts.map +1 -0
- package/dist/types/staking.js +6 -0
- package/dist/types/staking.js.map +1 -0
- package/dist/{types.d.ts → types/trading.d.ts} +33 -1
- package/dist/types/trading.d.ts.map +1 -0
- package/dist/{types.js → types/trading.js} +3 -3
- package/dist/types/trading.js.map +1 -0
- package/dist/types/twap.d.ts +13 -0
- package/dist/types/twap.d.ts.map +1 -0
- package/dist/types/twap.js +7 -0
- package/dist/types/twap.js.map +1 -0
- package/dist/types/vault.d.ts +24 -0
- package/dist/types/vault.d.ts.map +1 -0
- package/dist/types/vault.js +6 -0
- package/dist/types/vault.js.map +1 -0
- package/dist/{wasm.d.ts → wallet/wasm.d.ts} +1 -1
- package/dist/wallet/wasm.d.ts.map +1 -0
- package/dist/{wasm.js → wallet/wasm.js} +9 -8
- package/dist/wallet/wasm.js.map +1 -0
- package/dist/{ws.d.ts → ws/ws.d.ts} +14 -1
- package/dist/ws/ws.d.ts.map +1 -0
- package/dist/{ws.js → ws/ws.js} +27 -15
- package/dist/ws/ws.js.map +1 -0
- package/package.json +3 -1
- package/src/client.ts +610 -12
- package/src/faucet.ts +1 -1
- package/src/index.ts +117 -15
- package/src/native/actions.ts +820 -0
- package/src/{native.ts → native/digest.ts} +78 -95
- package/src/native/index.ts +5 -0
- package/src/{http.ts → rest/http.ts} +1 -1
- package/src/{info.ts → rest/info.ts} +9 -6
- package/src/types/account.ts +111 -0
- package/src/types/encrypted.ts +21 -0
- package/src/types/governance.ts +27 -0
- package/src/types/index.ts +79 -0
- package/src/types/info/core.ts +214 -0
- package/src/types/info/hl-parity.ts +428 -0
- package/src/types/info/index.ts +78 -0
- package/src/types/info/reads.ts +165 -0
- package/src/types/meta-bridge.ts +22 -0
- package/src/types/spot.ts +114 -0
- package/src/types/staking.ts +27 -0
- package/src/{types.ts → types/trading.ts} +92 -8
- package/src/types/twap.ts +29 -0
- package/src/types/vault.ts +55 -0
- package/src/{wasm.ts → wallet/wasm.ts} +10 -9
- package/src/{ws.ts → ws/ws.ts} +75 -22
- package/dist/http.d.ts.map +0 -1
- package/dist/http.js.map +0 -1
- package/dist/info-types.d.ts.map +0 -1
- package/dist/info-types.js +0 -16
- package/dist/info-types.js.map +0 -1
- package/dist/info.d.ts.map +0 -1
- package/dist/info.js.map +0 -1
- package/dist/native.d.ts +0 -12
- package/dist/native.d.ts.map +0 -1
- package/dist/native.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/wasm.d.ts.map +0 -1
- package/dist/wasm.js.map +0 -1
- package/dist/ws.d.ts.map +0 -1
- package/dist/ws.js.map +0 -1
- package/src/info-types.ts +0 -783
- /package/dist/{http.d.ts → rest/http.d.ts} +0 -0
- /package/dist/{http.js → rest/http.js} +0 -0
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
// MTF-native signed-action builders — hand-built canonical snake_case JSON.
|
|
2
|
+
//
|
|
3
|
+
// Each `build*Action(payload): string` produces the EXACT bytes that are BOTH
|
|
4
|
+
// signed (`signNativeAction`) and POSTed verbatim (`nativeRequestBody`). The
|
|
5
|
+
// same string is signed and sent, so the server verifies the signature over
|
|
6
|
+
// identical bytes (it parses `action` as `serde_json::value::RawValue`); never
|
|
7
|
+
// re-stringify a parsed object.
|
|
8
|
+
//
|
|
9
|
+
// The 5 original builders (submit_order / cancel_order / set_position_mode /
|
|
10
|
+
// spot_order / spot_cancel) are byte-pinned by `__tests__/native.test.ts`.
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
jsonStr,
|
|
14
|
+
validateAddress,
|
|
15
|
+
validateCloid,
|
|
16
|
+
validateDecimalString,
|
|
17
|
+
validateMarket,
|
|
18
|
+
validateU8,
|
|
19
|
+
validateU16,
|
|
20
|
+
validateU32,
|
|
21
|
+
validateU64,
|
|
22
|
+
} from './digest.js';
|
|
23
|
+
import type {
|
|
24
|
+
AgentSetAbstraction,
|
|
25
|
+
ApproveAgent,
|
|
26
|
+
ApproveBuilderFee,
|
|
27
|
+
BatchCancel,
|
|
28
|
+
BatchModify,
|
|
29
|
+
BatchOrder,
|
|
30
|
+
CancelAllOrders,
|
|
31
|
+
CancelByCloid,
|
|
32
|
+
ClaimRewards,
|
|
33
|
+
ConvertToMultiSigUser,
|
|
34
|
+
CreateVault,
|
|
35
|
+
LinkStakingUser,
|
|
36
|
+
MbWithdraw,
|
|
37
|
+
Modify,
|
|
38
|
+
NativeBuilder,
|
|
39
|
+
NativeCancel,
|
|
40
|
+
NativeEarnDeposit,
|
|
41
|
+
NativeEarnWithdraw,
|
|
42
|
+
NativeOrder,
|
|
43
|
+
NativeSetPositionMode,
|
|
44
|
+
NativeSpotCancel,
|
|
45
|
+
NativeSpotMarginClose,
|
|
46
|
+
NativeSpotMarginDeposit,
|
|
47
|
+
NativeSpotMarginOpen,
|
|
48
|
+
NativeSpotMarginWithdraw,
|
|
49
|
+
NativeSpotOrder,
|
|
50
|
+
PriorityBid,
|
|
51
|
+
RegisterMetaliquidityOperator,
|
|
52
|
+
ScheduleCancel,
|
|
53
|
+
SetDisplayName,
|
|
54
|
+
SetMetaliquidityWhitelist,
|
|
55
|
+
SetReferrer,
|
|
56
|
+
SubmitEncryptedOrder,
|
|
57
|
+
TokenDelegate,
|
|
58
|
+
TopUpIsolatedOnlyMargin,
|
|
59
|
+
TwapCancel,
|
|
60
|
+
TwapOrder,
|
|
61
|
+
UpdateIsolatedMargin,
|
|
62
|
+
UpdateLeverage,
|
|
63
|
+
UserDexAbstraction,
|
|
64
|
+
UserPortfolioMargin,
|
|
65
|
+
UserSetAbstraction,
|
|
66
|
+
VaultModify,
|
|
67
|
+
VaultTransfer,
|
|
68
|
+
VaultWithdraw,
|
|
69
|
+
} from '../types/index.js';
|
|
70
|
+
|
|
71
|
+
/// Build the canonical native `submit_order` action JSON string.
|
|
72
|
+
///
|
|
73
|
+
/// Field order mirrors the server `NativeOrder` exactly. Optional `cloid` /
|
|
74
|
+
/// `builder` are omitted entirely when absent (matching the server's
|
|
75
|
+
/// `#[serde(default)]` + KAT vector, where neither appears). The returned
|
|
76
|
+
/// string is BOTH what gets signed and what gets sent — do not re-serialize.
|
|
77
|
+
export function buildNativeOrderAction(order: NativeOrder): string {
|
|
78
|
+
validateAddress(order.owner, 'owner');
|
|
79
|
+
validateMarket(order.market);
|
|
80
|
+
validateU64(order.size, 'size');
|
|
81
|
+
validateU64(order.limit_px, 'limit_px');
|
|
82
|
+
|
|
83
|
+
const parts: string[] = [
|
|
84
|
+
`${jsonStr('owner')}:${jsonStr(order.owner)}`,
|
|
85
|
+
`${jsonStr('market')}:${order.market}`,
|
|
86
|
+
`${jsonStr('side')}:${jsonStr(order.side)}`,
|
|
87
|
+
`${jsonStr('kind')}:${jsonStr(order.kind)}`,
|
|
88
|
+
`${jsonStr('size')}:${order.size}`,
|
|
89
|
+
`${jsonStr('limit_px')}:${order.limit_px}`,
|
|
90
|
+
`${jsonStr('tif')}:${jsonStr(order.tif)}`,
|
|
91
|
+
`${jsonStr('stp_mode')}:${jsonStr(order.stp_mode)}`,
|
|
92
|
+
`${jsonStr('reduce_only')}:${order.reduce_only ? 'true' : 'false'}`,
|
|
93
|
+
];
|
|
94
|
+
if (order.cloid !== undefined) {
|
|
95
|
+
validateCloid(order.cloid);
|
|
96
|
+
parts.push(`${jsonStr('cloid')}:${jsonStr(order.cloid)}`);
|
|
97
|
+
}
|
|
98
|
+
if (order.builder !== undefined) {
|
|
99
|
+
parts.push(`${jsonStr('builder')}:${buildBuilder(order.builder)}`);
|
|
100
|
+
}
|
|
101
|
+
// HEDGE MODE: `position_side` is OMITTED on a one-way account so the signed
|
|
102
|
+
// bytes stay byte-identical to a pre-hedge SDK; it rides last (after
|
|
103
|
+
// cloid/builder), matching the server `NativeOrder` field declaration order.
|
|
104
|
+
if (order.position_side !== undefined) {
|
|
105
|
+
parts.push(`${jsonStr('position_side')}:${jsonStr(order.position_side)}`);
|
|
106
|
+
}
|
|
107
|
+
const orderJson = `{${parts.join(',')}}`;
|
|
108
|
+
return `{${jsonStr('type')}:${jsonStr('submit_order')},${jsonStr('order')}:${orderJson}}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Serialize a builder carve in the server-expected `{fee, user}` order.
|
|
112
|
+
function buildBuilder(b: NativeBuilder): string {
|
|
113
|
+
if (!Number.isInteger(b.fee) || b.fee < 0 || b.fee > 0xffff) {
|
|
114
|
+
throw new RangeError('builder.fee must be a u16 (0..=65535)');
|
|
115
|
+
}
|
|
116
|
+
validateAddress(b.user, 'builder.user');
|
|
117
|
+
return `{${jsonStr('fee')}:${b.fee},${jsonStr('user')}:${jsonStr(b.user)}}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/// Build the canonical native `cancel_order` action JSON string.
|
|
121
|
+
///
|
|
122
|
+
/// Field order mirrors the server `NativeCancel`: `owner`, `market`, then `oid`
|
|
123
|
+
/// / `cloid` when present. The server's `CancelParams` bridge cancels by `oid`,
|
|
124
|
+
/// so an `oid` is REQUIRED for the cancel to lower successfully (a `cloid`-only
|
|
125
|
+
/// cancel is accepted on the wire but rejected at lowering with
|
|
126
|
+
/// `CancelMissingOid`); we still emit either form so the bytes stay
|
|
127
|
+
/// caller-controlled. The returned string is BOTH signed and sent.
|
|
128
|
+
export function buildNativeCancelAction(cancel: NativeCancel): string {
|
|
129
|
+
validateAddress(cancel.owner, 'owner');
|
|
130
|
+
validateMarket(cancel.market);
|
|
131
|
+
if (cancel.oid === undefined && cancel.cloid === undefined) {
|
|
132
|
+
throw new RangeError('cancel requires an oid (server cancels by oid)');
|
|
133
|
+
}
|
|
134
|
+
const parts: string[] = [
|
|
135
|
+
`${jsonStr('owner')}:${jsonStr(cancel.owner)}`,
|
|
136
|
+
`${jsonStr('market')}:${cancel.market}`,
|
|
137
|
+
];
|
|
138
|
+
if (cancel.oid !== undefined) {
|
|
139
|
+
validateU64(cancel.oid, 'oid');
|
|
140
|
+
parts.push(`${jsonStr('oid')}:${cancel.oid}`);
|
|
141
|
+
}
|
|
142
|
+
if (cancel.cloid !== undefined) {
|
|
143
|
+
validateCloid(cancel.cloid);
|
|
144
|
+
parts.push(`${jsonStr('cloid')}:${jsonStr(cancel.cloid)}`);
|
|
145
|
+
}
|
|
146
|
+
const cancelJson = `{${parts.join(',')}}`;
|
|
147
|
+
return `{${jsonStr('type')}:${jsonStr('cancel_order')},${jsonStr('cancel')}:${cancelJson}}`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/// Build the canonical native `set_position_mode` action JSON string.
|
|
151
|
+
///
|
|
152
|
+
/// `{"type":"set_position_mode","params":{"hedge":<bool>}}` — toggles the
|
|
153
|
+
/// account between one-way (`false`) and hedge / two-way (`true`). Sender-
|
|
154
|
+
/// authorized: the recovered signer IS the account (no `owner`), so this is
|
|
155
|
+
/// signed exactly like the order builders and POSTed verbatim. The node only
|
|
156
|
+
/// permits the switch while flat on every market.
|
|
157
|
+
export function buildNativeSetPositionModeAction(
|
|
158
|
+
mode: NativeSetPositionMode,
|
|
159
|
+
): string {
|
|
160
|
+
if (typeof mode.hedge !== 'boolean') {
|
|
161
|
+
throw new RangeError('set_position_mode: hedge must be a boolean');
|
|
162
|
+
}
|
|
163
|
+
const paramsJson = `{${jsonStr('hedge')}:${mode.hedge ? 'true' : 'false'}}`;
|
|
164
|
+
return `{${jsonStr('type')}:${jsonStr('set_position_mode')},${jsonStr('params')}:${paramsJson}}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/// Build the canonical native `spot_order` action JSON string (spot CLOB).
|
|
168
|
+
///
|
|
169
|
+
/// Field order mirrors the server `NativeSpotOrder` exactly. `tif` defaults to
|
|
170
|
+
/// `"ioc"` because v0 accepts ONLY IOC limit orders (`tif:"ioc"` + `limit_px > 0`);
|
|
171
|
+
/// the node rejects Gtc / Alo and a zero (market) price. Sender-authorized (no
|
|
172
|
+
/// `owner`); the returned string is BOTH signed and sent.
|
|
173
|
+
export function buildNativeSpotOrderAction(order: NativeSpotOrder): string {
|
|
174
|
+
validateMarket(order.pair);
|
|
175
|
+
validateU64(order.size, 'size');
|
|
176
|
+
validateU64(order.limit_px, 'limit_px');
|
|
177
|
+
// v0 constraint: IOC limit only, strictly-positive price. The node enforces
|
|
178
|
+
// both; we fail loud here so a misconfigured order never reaches the wire.
|
|
179
|
+
const tif = order.tif ?? 'ioc';
|
|
180
|
+
if (tif !== 'ioc') {
|
|
181
|
+
throw new RangeError('spot_order v0 requires tif="ioc" (Gtc/Alo rejected)');
|
|
182
|
+
}
|
|
183
|
+
if (order.limit_px <= 0) {
|
|
184
|
+
throw new RangeError('spot_order v0 requires limit_px > 0 (market px rejected)');
|
|
185
|
+
}
|
|
186
|
+
const parts: string[] = [
|
|
187
|
+
`${jsonStr('pair')}:${order.pair}`,
|
|
188
|
+
`${jsonStr('side')}:${jsonStr(order.side)}`,
|
|
189
|
+
`${jsonStr('size')}:${order.size}`,
|
|
190
|
+
`${jsonStr('limit_px')}:${order.limit_px}`,
|
|
191
|
+
`${jsonStr('tif')}:${jsonStr(tif)}`,
|
|
192
|
+
`${jsonStr('stp_mode')}:${jsonStr(order.stp_mode)}`,
|
|
193
|
+
];
|
|
194
|
+
if (order.cloid !== undefined) {
|
|
195
|
+
validateCloid(order.cloid);
|
|
196
|
+
parts.push(`${jsonStr('cloid')}:${jsonStr(order.cloid)}`);
|
|
197
|
+
}
|
|
198
|
+
const orderJson = `{${parts.join(',')}}`;
|
|
199
|
+
return `{${jsonStr('type')}:${jsonStr('spot_order')},${jsonStr('order')}:${orderJson}}`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Build the canonical native `spot_cancel` action JSON string.
|
|
203
|
+
///
|
|
204
|
+
/// `{"type":"spot_cancel","cancel":{"pair":<u32>,"oid":<u64>}}`. The node
|
|
205
|
+
/// cancels a resting spot order by `oid`, so `oid` is REQUIRED. Field order
|
|
206
|
+
/// mirrors the server `NativeSpotCancel`; the returned string is signed and sent.
|
|
207
|
+
export function buildNativeSpotCancelAction(cancel: NativeSpotCancel): string {
|
|
208
|
+
validateMarket(cancel.pair);
|
|
209
|
+
validateU64(cancel.oid, 'oid');
|
|
210
|
+
const cancelJson = `{${jsonStr('pair')}:${cancel.pair},${jsonStr('oid')}:${cancel.oid}}`;
|
|
211
|
+
return `{${jsonStr('type')}:${jsonStr('spot_cancel')},${jsonStr('cancel')}:${cancelJson}}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ============================================================================
|
|
215
|
+
// Real node /exchange write actions. Each hand-builds canonical snake_case JSON
|
|
216
|
+
// (the same string is signed and POSTed). All are sender-authorized (no `owner`
|
|
217
|
+
// field) except the inner orders/cancels of batch_order / batch_cancel, which
|
|
218
|
+
// carry an `owner` the client checks against the recovered signer.
|
|
219
|
+
// ============================================================================
|
|
220
|
+
|
|
221
|
+
/// Build the inner `{...}` body of one perp order (the value under `order` in
|
|
222
|
+
/// submit_order, and each element of a batch_order). Mirrors `NativeOrder`.
|
|
223
|
+
function buildOrderBody(order: NativeOrder): string {
|
|
224
|
+
validateAddress(order.owner, 'owner');
|
|
225
|
+
validateMarket(order.market);
|
|
226
|
+
validateU64(order.size, 'size');
|
|
227
|
+
validateU64(order.limit_px, 'limit_px');
|
|
228
|
+
const parts: string[] = [
|
|
229
|
+
`${jsonStr('owner')}:${jsonStr(order.owner)}`,
|
|
230
|
+
`${jsonStr('market')}:${order.market}`,
|
|
231
|
+
`${jsonStr('side')}:${jsonStr(order.side)}`,
|
|
232
|
+
`${jsonStr('kind')}:${jsonStr(order.kind)}`,
|
|
233
|
+
`${jsonStr('size')}:${order.size}`,
|
|
234
|
+
`${jsonStr('limit_px')}:${order.limit_px}`,
|
|
235
|
+
`${jsonStr('tif')}:${jsonStr(order.tif)}`,
|
|
236
|
+
`${jsonStr('stp_mode')}:${jsonStr(order.stp_mode)}`,
|
|
237
|
+
`${jsonStr('reduce_only')}:${order.reduce_only ? 'true' : 'false'}`,
|
|
238
|
+
];
|
|
239
|
+
if (order.cloid !== undefined) {
|
|
240
|
+
validateCloid(order.cloid);
|
|
241
|
+
parts.push(`${jsonStr('cloid')}:${jsonStr(order.cloid)}`);
|
|
242
|
+
}
|
|
243
|
+
if (order.builder !== undefined) {
|
|
244
|
+
parts.push(`${jsonStr('builder')}:${buildBuilder(order.builder)}`);
|
|
245
|
+
}
|
|
246
|
+
if (order.position_side !== undefined) {
|
|
247
|
+
parts.push(`${jsonStr('position_side')}:${jsonStr(order.position_side)}`);
|
|
248
|
+
}
|
|
249
|
+
return `{${parts.join(',')}}`;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/// Build the inner `{...}` body of one cancel (each element of a batch_cancel).
|
|
253
|
+
/// `oid` required (a cloid-only cancel is rejected at lowering).
|
|
254
|
+
function buildCancelBody(cancel: NativeCancel): string {
|
|
255
|
+
validateAddress(cancel.owner, 'owner');
|
|
256
|
+
validateMarket(cancel.market);
|
|
257
|
+
if (cancel.oid === undefined && cancel.cloid === undefined) {
|
|
258
|
+
throw new RangeError('cancel requires an oid (server cancels by oid)');
|
|
259
|
+
}
|
|
260
|
+
const parts: string[] = [
|
|
261
|
+
`${jsonStr('owner')}:${jsonStr(cancel.owner)}`,
|
|
262
|
+
`${jsonStr('market')}:${cancel.market}`,
|
|
263
|
+
];
|
|
264
|
+
if (cancel.oid !== undefined) {
|
|
265
|
+
validateU64(cancel.oid, 'oid');
|
|
266
|
+
parts.push(`${jsonStr('oid')}:${cancel.oid}`);
|
|
267
|
+
}
|
|
268
|
+
if (cancel.cloid !== undefined) {
|
|
269
|
+
validateCloid(cancel.cloid);
|
|
270
|
+
parts.push(`${jsonStr('cloid')}:${jsonStr(cancel.cloid)}`);
|
|
271
|
+
}
|
|
272
|
+
return `{${parts.join(',')}}`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/// Build the inner `{...}` body of one modify (modify / batch_modify).
|
|
276
|
+
function buildModifyBody(m: Modify): string {
|
|
277
|
+
validateMarket(m.market);
|
|
278
|
+
validateU64(m.oid, 'oid');
|
|
279
|
+
const parts: string[] = [
|
|
280
|
+
`${jsonStr('market')}:${m.market}`,
|
|
281
|
+
`${jsonStr('oid')}:${m.oid}`,
|
|
282
|
+
];
|
|
283
|
+
if (m.new_px !== undefined) {
|
|
284
|
+
validateU64(m.new_px, 'new_px');
|
|
285
|
+
parts.push(`${jsonStr('new_px')}:${m.new_px}`);
|
|
286
|
+
}
|
|
287
|
+
if (m.new_size !== undefined) {
|
|
288
|
+
validateU64(m.new_size, 'new_size');
|
|
289
|
+
parts.push(`${jsonStr('new_size')}:${m.new_size}`);
|
|
290
|
+
}
|
|
291
|
+
return `{${parts.join(',')}}`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/// Wrap an already-built params body into `{"type":<type>,"params":<body>}`.
|
|
295
|
+
function wrapParams(type: string, paramsJson: string): string {
|
|
296
|
+
return `{${jsonStr('type')}:${jsonStr(type)},${jsonStr('params')}:${paramsJson}}`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ---- order management ----
|
|
300
|
+
|
|
301
|
+
/// `cancel_by_cloid` — cancel a resting order by its client order id.
|
|
302
|
+
export function buildNativeCancelByCloidAction(params: CancelByCloid): string {
|
|
303
|
+
validateMarket(params.asset);
|
|
304
|
+
validateCloid(params.cloid);
|
|
305
|
+
return wrapParams(
|
|
306
|
+
'cancel_by_cloid',
|
|
307
|
+
`{${jsonStr('asset')}:${params.asset},${jsonStr('cloid')}:${jsonStr(params.cloid)}}`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/// `modify` — amend a resting order's price and/or size in place.
|
|
312
|
+
export function buildNativeModifyAction(params: Modify): string {
|
|
313
|
+
return wrapParams('modify', buildModifyBody(params));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/// `batch_modify` — N modifies under one signature.
|
|
317
|
+
export function buildNativeBatchModifyAction(params: BatchModify): string {
|
|
318
|
+
const arr = params.modifications.map(buildModifyBody).join(',');
|
|
319
|
+
return wrapParams('batch_modify', `{${jsonStr('modifications')}:[${arr}]}`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/// `batch_order` — N orders under one signature. Each order's `owner` must equal
|
|
323
|
+
/// the signing wallet (enforced client-side by `Client.batchOrder`).
|
|
324
|
+
export function buildNativeBatchOrderAction(params: BatchOrder): string {
|
|
325
|
+
const grouping = params.grouping ?? 'na';
|
|
326
|
+
if (grouping !== 'na' && grouping !== 'normalTpsl' && grouping !== 'positionTpsl') {
|
|
327
|
+
throw new RangeError('grouping must be na | normalTpsl | positionTpsl');
|
|
328
|
+
}
|
|
329
|
+
const arr = params.orders.map(buildOrderBody).join(',');
|
|
330
|
+
return wrapParams(
|
|
331
|
+
'batch_order',
|
|
332
|
+
`{${jsonStr('orders')}:[${arr}],${jsonStr('grouping')}:${jsonStr(grouping)}}`,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/// `batch_cancel` — N cancels under one signature.
|
|
337
|
+
export function buildNativeBatchCancelAction(params: BatchCancel): string {
|
|
338
|
+
const arr = params.cancels.map(buildCancelBody).join(',');
|
|
339
|
+
return wrapParams('batch_cancel', `{${jsonStr('cancels')}:[${arr}]}`);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/// `schedule_cancel` — cancel-all of the sender's open orders at a future block.
|
|
343
|
+
export function buildNativeScheduleCancelAction(params: ScheduleCancel): string {
|
|
344
|
+
validateU64(params.cancel_at_block, 'cancel_at_block');
|
|
345
|
+
return wrapParams(
|
|
346
|
+
'schedule_cancel',
|
|
347
|
+
`{${jsonStr('cancel_at_block')}:${params.cancel_at_block}}`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/// `cancel_all_orders` — cancel all of the sender's open orders (optional asset).
|
|
352
|
+
export function buildNativeCancelAllOrdersAction(
|
|
353
|
+
params: CancelAllOrders = {},
|
|
354
|
+
): string {
|
|
355
|
+
const parts: string[] = [];
|
|
356
|
+
if (params.asset !== undefined) {
|
|
357
|
+
validateMarket(params.asset);
|
|
358
|
+
parts.push(`${jsonStr('asset')}:${params.asset}`);
|
|
359
|
+
}
|
|
360
|
+
return wrapParams('cancel_all_orders', `{${parts.join(',')}}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// ---- TWAP ----
|
|
364
|
+
|
|
365
|
+
/// `twap_order` — submit a sliced (TWAP) order.
|
|
366
|
+
export function buildNativeTwapOrderAction(params: TwapOrder): string {
|
|
367
|
+
validateMarket(params.market);
|
|
368
|
+
validateU64(params.total_size, 'total_size');
|
|
369
|
+
validateU32(params.slice_count, 'slice_count');
|
|
370
|
+
validateU64(params.delay_ms, 'delay_ms');
|
|
371
|
+
return wrapParams(
|
|
372
|
+
'twap_order',
|
|
373
|
+
`{${jsonStr('market')}:${params.market},${jsonStr('side')}:${jsonStr(params.side)},${jsonStr('total_size')}:${params.total_size},${jsonStr('slice_count')}:${params.slice_count},${jsonStr('delay_ms')}:${params.delay_ms},${jsonStr('reduce_only')}:${params.reduce_only ? 'true' : 'false'}}`,
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/// `twap_cancel` — cancel a running TWAP parent by id.
|
|
378
|
+
export function buildNativeTwapCancelAction(params: TwapCancel): string {
|
|
379
|
+
validateU64(params.twap_id, 'twap_id');
|
|
380
|
+
return wrapParams('twap_cancel', `{${jsonStr('twap_id')}:${params.twap_id}}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// ---- leverage & margin ----
|
|
384
|
+
|
|
385
|
+
/// `update_leverage` — set per-asset leverage (and optionally flip to isolated).
|
|
386
|
+
export function buildNativeUpdateLeverageAction(params: UpdateLeverage): string {
|
|
387
|
+
validateMarket(params.asset);
|
|
388
|
+
validateU32(params.leverage, 'leverage');
|
|
389
|
+
return wrapParams(
|
|
390
|
+
'update_leverage',
|
|
391
|
+
`{${jsonStr('asset')}:${params.asset},${jsonStr('leverage')}:${params.leverage},${jsonStr('is_isolated')}:${params.is_isolated ? 'true' : 'false'}}`,
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/// `update_isolated_margin` — add (`+`) or remove (`-`) isolated margin.
|
|
396
|
+
export function buildNativeUpdateIsolatedMarginAction(
|
|
397
|
+
params: UpdateIsolatedMargin,
|
|
398
|
+
): string {
|
|
399
|
+
validateMarket(params.asset);
|
|
400
|
+
validateDecimalString(params.delta, 'delta', { allowNegative: true });
|
|
401
|
+
return wrapParams(
|
|
402
|
+
'update_isolated_margin',
|
|
403
|
+
`{${jsonStr('asset')}:${params.asset},${jsonStr('delta')}:${jsonStr(params.delta)}}`,
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/// `top_up_isolated_only_margin` — top up a strict-isolated-only position.
|
|
408
|
+
export function buildNativeTopUpIsolatedOnlyMarginAction(
|
|
409
|
+
params: TopUpIsolatedOnlyMargin,
|
|
410
|
+
): string {
|
|
411
|
+
validateMarket(params.asset);
|
|
412
|
+
validateDecimalString(params.amount, 'amount');
|
|
413
|
+
return wrapParams(
|
|
414
|
+
'top_up_isolated_only_margin',
|
|
415
|
+
`{${jsonStr('asset')}:${params.asset},${jsonStr('amount')}:${jsonStr(params.amount)}}`,
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/// `user_portfolio_margin` — enroll into or out of portfolio margin.
|
|
420
|
+
export function buildNativeUserPortfolioMarginAction(
|
|
421
|
+
params: UserPortfolioMargin,
|
|
422
|
+
): string {
|
|
423
|
+
return wrapParams(
|
|
424
|
+
'user_portfolio_margin',
|
|
425
|
+
`{${jsonStr('enroll')}:${params.enroll ? 'true' : 'false'}}`,
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ---- account & agent settings ----
|
|
430
|
+
|
|
431
|
+
/// `set_display_name` — set the account display name (handle).
|
|
432
|
+
export function buildNativeSetDisplayNameAction(params: SetDisplayName): string {
|
|
433
|
+
if (typeof params.display_name !== 'string' || params.display_name.length === 0) {
|
|
434
|
+
throw new RangeError('display_name must be a non-empty string');
|
|
435
|
+
}
|
|
436
|
+
return wrapParams(
|
|
437
|
+
'set_display_name',
|
|
438
|
+
`{${jsonStr('display_name')}:${jsonStr(params.display_name)}}`,
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/// `set_referrer` — set the account referrer (one-time, immutable once set).
|
|
443
|
+
export function buildNativeSetReferrerAction(params: SetReferrer): string {
|
|
444
|
+
validateAddress(params.referrer, 'referrer');
|
|
445
|
+
return wrapParams(
|
|
446
|
+
'set_referrer',
|
|
447
|
+
`{${jsonStr('referrer')}:${jsonStr(params.referrer)}}`,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/// `approve_agent` — approve an agent wallet to sign on this account's behalf.
|
|
452
|
+
export function buildNativeApproveAgentAction(params: ApproveAgent): string {
|
|
453
|
+
validateAddress(params.agent, 'agent');
|
|
454
|
+
const parts: string[] = [`${jsonStr('agent')}:${jsonStr(params.agent)}`];
|
|
455
|
+
if (params.name !== undefined) {
|
|
456
|
+
parts.push(`${jsonStr('name')}:${jsonStr(params.name)}`);
|
|
457
|
+
}
|
|
458
|
+
if (params.expires_at_ms !== undefined) {
|
|
459
|
+
validateU64(params.expires_at_ms, 'expires_at_ms');
|
|
460
|
+
parts.push(`${jsonStr('expires_at_ms')}:${params.expires_at_ms}`);
|
|
461
|
+
}
|
|
462
|
+
return wrapParams('approve_agent', `{${parts.join(',')}}`);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/// `approve_builder_fee` — approve a builder up to `max_bps` (`0` revokes).
|
|
466
|
+
export function buildNativeApproveBuilderFeeAction(
|
|
467
|
+
params: ApproveBuilderFee,
|
|
468
|
+
): string {
|
|
469
|
+
validateAddress(params.builder, 'builder');
|
|
470
|
+
validateU16(params.max_bps, 'max_bps');
|
|
471
|
+
return wrapParams(
|
|
472
|
+
'approve_builder_fee',
|
|
473
|
+
`{${jsonStr('builder')}:${jsonStr(params.builder)},${jsonStr('max_bps')}:${params.max_bps}}`,
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/// `convert_to_multi_sig_user` — convert the account to an M-of-N multisig.
|
|
478
|
+
export function buildNativeConvertToMultiSigUserAction(
|
|
479
|
+
params: ConvertToMultiSigUser,
|
|
480
|
+
): string {
|
|
481
|
+
const arr = params.signers
|
|
482
|
+
.map((s, i) => {
|
|
483
|
+
validateAddress(s, `signers[${i}]`);
|
|
484
|
+
return jsonStr(s);
|
|
485
|
+
})
|
|
486
|
+
.join(',');
|
|
487
|
+
validateU32(params.threshold, 'threshold');
|
|
488
|
+
return wrapParams(
|
|
489
|
+
'convert_to_multi_sig_user',
|
|
490
|
+
`{${jsonStr('signers')}:[${arr}],${jsonStr('threshold')}:${params.threshold}}`,
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/// `user_dex_abstraction` — toggle the account's DEX-abstraction opt-in flag.
|
|
495
|
+
export function buildNativeUserDexAbstractionAction(
|
|
496
|
+
params: UserDexAbstraction,
|
|
497
|
+
): string {
|
|
498
|
+
return wrapParams(
|
|
499
|
+
'user_dex_abstraction',
|
|
500
|
+
`{${jsonStr('enabled')}:${params.enabled ? 'true' : 'false'}}`,
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/// `user_set_abstraction` — set a self-scoped abstraction config value.
|
|
505
|
+
export function buildNativeUserSetAbstractionAction(
|
|
506
|
+
params: UserSetAbstraction,
|
|
507
|
+
): string {
|
|
508
|
+
validateU8(params.kind, 'kind');
|
|
509
|
+
validateDecimalString(params.value, 'value', {
|
|
510
|
+
allowZero: true,
|
|
511
|
+
allowNegative: true,
|
|
512
|
+
});
|
|
513
|
+
return wrapParams(
|
|
514
|
+
'user_set_abstraction',
|
|
515
|
+
`{${jsonStr('kind')}:${params.kind},${jsonStr('value')}:${jsonStr(params.value)}}`,
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/// `agent_set_abstraction` — an approved agent sets a config value for `user`.
|
|
520
|
+
export function buildNativeAgentSetAbstractionAction(
|
|
521
|
+
params: AgentSetAbstraction,
|
|
522
|
+
): string {
|
|
523
|
+
validateAddress(params.user, 'user');
|
|
524
|
+
validateU8(params.kind, 'kind');
|
|
525
|
+
validateDecimalString(params.value, 'value', {
|
|
526
|
+
allowZero: true,
|
|
527
|
+
allowNegative: true,
|
|
528
|
+
});
|
|
529
|
+
return wrapParams(
|
|
530
|
+
'agent_set_abstraction',
|
|
531
|
+
`{${jsonStr('user')}:${jsonStr(params.user)},${jsonStr('kind')}:${params.kind},${jsonStr('value')}:${jsonStr(params.value)}}`,
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/// `priority_bid` — pay a priority fee (bps) for block-front placement.
|
|
536
|
+
export function buildNativePriorityBidAction(params: PriorityBid): string {
|
|
537
|
+
validateMarket(params.asset);
|
|
538
|
+
validateU16(params.bid_bps, 'bid_bps');
|
|
539
|
+
return wrapParams(
|
|
540
|
+
'priority_bid',
|
|
541
|
+
`{${jsonStr('asset')}:${params.asset},${jsonStr('bid_bps')}:${params.bid_bps}}`,
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// ---- staking ----
|
|
546
|
+
|
|
547
|
+
/// `token_delegate` — delegate stake to a validator, or queue an undelegation.
|
|
548
|
+
export function buildNativeTokenDelegateAction(params: TokenDelegate): string {
|
|
549
|
+
validateAddress(params.validator, 'validator');
|
|
550
|
+
validateDecimalString(params.amount, 'amount');
|
|
551
|
+
return wrapParams(
|
|
552
|
+
'token_delegate',
|
|
553
|
+
`{${jsonStr('validator')}:${jsonStr(params.validator)},${jsonStr('amount')}:${jsonStr(params.amount)},${jsonStr('is_undelegate')}:${params.is_undelegate ? 'true' : 'false'}}`,
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/// `claim_rewards` — claim accrued staking rewards (optional validator filter).
|
|
558
|
+
export function buildNativeClaimRewardsAction(
|
|
559
|
+
params: ClaimRewards = {},
|
|
560
|
+
): string {
|
|
561
|
+
const parts: string[] = [];
|
|
562
|
+
if (params.validator !== undefined) {
|
|
563
|
+
validateAddress(params.validator, 'validator');
|
|
564
|
+
parts.push(`${jsonStr('validator')}:${jsonStr(params.validator)}`);
|
|
565
|
+
}
|
|
566
|
+
return wrapParams('claim_rewards', `{${parts.join(',')}}`);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/// `link_staking_user` — alias another account as this account's staking target.
|
|
570
|
+
export function buildNativeLinkStakingUserAction(
|
|
571
|
+
params: LinkStakingUser,
|
|
572
|
+
): string {
|
|
573
|
+
validateAddress(params.target, 'target');
|
|
574
|
+
return wrapParams(
|
|
575
|
+
'link_staking_user',
|
|
576
|
+
`{${jsonStr('target')}:${jsonStr(params.target)}}`,
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// ---- encrypted orders ----
|
|
581
|
+
|
|
582
|
+
/// `submit_encrypted_order` — submit a threshold-encrypted order ciphertext.
|
|
583
|
+
export function buildNativeSubmitEncryptedOrderAction(
|
|
584
|
+
params: SubmitEncryptedOrder,
|
|
585
|
+
): string {
|
|
586
|
+
if (!(params.ciphertext instanceof Uint8Array)) {
|
|
587
|
+
throw new RangeError('ciphertext must be a Uint8Array');
|
|
588
|
+
}
|
|
589
|
+
if (!(params.commitment instanceof Uint8Array) || params.commitment.length !== 32) {
|
|
590
|
+
throw new RangeError('commitment must be a 32-byte Uint8Array');
|
|
591
|
+
}
|
|
592
|
+
validateU8(params.threshold, 'threshold');
|
|
593
|
+
validateU64(params.target_block, 'target_block');
|
|
594
|
+
validateU64(params.reveal_deadline_ms, 'reveal_deadline_ms');
|
|
595
|
+
const ct = `[${Array.from(params.ciphertext).join(',')}]`;
|
|
596
|
+
const cm = `[${Array.from(params.commitment).join(',')}]`;
|
|
597
|
+
return wrapParams(
|
|
598
|
+
'submit_encrypted_order',
|
|
599
|
+
`{${jsonStr('ciphertext')}:${ct},${jsonStr('commitment')}:${cm},${jsonStr('threshold')}:${params.threshold},${jsonStr('target_block')}:${params.target_block},${jsonStr('reveal_deadline_ms')}:${params.reveal_deadline_ms}}`,
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// ---- vaults ----
|
|
604
|
+
|
|
605
|
+
/// `create_vault` — create a new vault. The signing wallet becomes the leader.
|
|
606
|
+
export function buildNativeCreateVaultAction(params: CreateVault): string {
|
|
607
|
+
if (typeof params.name !== 'string' || params.name.length === 0) {
|
|
608
|
+
throw new RangeError('name must be a non-empty string');
|
|
609
|
+
}
|
|
610
|
+
validateU64(params.lock_period_secs, 'lock_period_secs');
|
|
611
|
+
const kind = params.kind ?? 'User';
|
|
612
|
+
if (kind !== 'User' && kind !== 'Metaliquidity') {
|
|
613
|
+
throw new RangeError('kind must be User | Metaliquidity');
|
|
614
|
+
}
|
|
615
|
+
const parts: string[] = [
|
|
616
|
+
`${jsonStr('name')}:${jsonStr(params.name)}`,
|
|
617
|
+
`${jsonStr('lock_period_secs')}:${params.lock_period_secs}`,
|
|
618
|
+
];
|
|
619
|
+
if (params.parent !== undefined) {
|
|
620
|
+
validateU64(params.parent, 'parent');
|
|
621
|
+
parts.push(`${jsonStr('parent')}:${params.parent}`);
|
|
622
|
+
}
|
|
623
|
+
parts.push(`${jsonStr('kind')}:${jsonStr(kind)}`);
|
|
624
|
+
return wrapParams('create_vault', `{${parts.join(',')}}`);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/// `vault_transfer` — leader moves capital into / out of a vault.
|
|
628
|
+
export function buildNativeVaultTransferAction(params: VaultTransfer): string {
|
|
629
|
+
validateU64(params.vault_id, 'vault_id');
|
|
630
|
+
validateDecimalString(params.amount, 'amount');
|
|
631
|
+
return wrapParams(
|
|
632
|
+
'vault_transfer',
|
|
633
|
+
`{${jsonStr('vault_id')}:${params.vault_id},${jsonStr('deposit')}:${params.deposit ? 'true' : 'false'},${jsonStr('amount')}:${jsonStr(params.amount)}}`,
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
/// `vault_modify` — leader updates vault configuration (omitted = unchanged).
|
|
638
|
+
export function buildNativeVaultModifyAction(params: VaultModify): string {
|
|
639
|
+
validateU64(params.vault_id, 'vault_id');
|
|
640
|
+
const parts: string[] = [`${jsonStr('vault_id')}:${params.vault_id}`];
|
|
641
|
+
if (params.new_name !== undefined) {
|
|
642
|
+
parts.push(`${jsonStr('new_name')}:${jsonStr(params.new_name)}`);
|
|
643
|
+
}
|
|
644
|
+
if (params.new_lock_period_secs !== undefined) {
|
|
645
|
+
validateU64(params.new_lock_period_secs, 'new_lock_period_secs');
|
|
646
|
+
parts.push(`${jsonStr('new_lock_period_secs')}:${params.new_lock_period_secs}`);
|
|
647
|
+
}
|
|
648
|
+
if (params.new_management_fee_bps !== undefined) {
|
|
649
|
+
validateU16(params.new_management_fee_bps, 'new_management_fee_bps');
|
|
650
|
+
parts.push(`${jsonStr('new_management_fee_bps')}:${params.new_management_fee_bps}`);
|
|
651
|
+
}
|
|
652
|
+
if (params.new_paused !== undefined) {
|
|
653
|
+
parts.push(`${jsonStr('new_paused')}:${params.new_paused ? 'true' : 'false'}`);
|
|
654
|
+
}
|
|
655
|
+
return wrapParams('vault_modify', `{${parts.join(',')}}`);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/// `vault_withdraw` — follower redeems shares (decimal string) from a vault.
|
|
659
|
+
export function buildNativeVaultWithdrawAction(params: VaultWithdraw): string {
|
|
660
|
+
validateU64(params.vault_id, 'vault_id');
|
|
661
|
+
validateDecimalString(params.shares, 'shares');
|
|
662
|
+
return wrapParams(
|
|
663
|
+
'vault_withdraw',
|
|
664
|
+
`{${jsonStr('vault_id')}:${params.vault_id},${jsonStr('shares')}:${jsonStr(params.shares)}}`,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// ---- MetaBridge ----
|
|
669
|
+
|
|
670
|
+
/// `mb_withdraw` — withdraw cross-collateral to a destination chain. `dst_addr`
|
|
671
|
+
/// is `0x` + 40 hex (EVM, Base/Arbitrum) or 64 hex (32-byte, Solana).
|
|
672
|
+
export function buildNativeMbWithdrawAction(params: MbWithdraw): string {
|
|
673
|
+
if (params.chain !== 'Base' && params.chain !== 'Arbitrum' && params.chain !== 'Solana') {
|
|
674
|
+
throw new RangeError('chain must be Base | Arbitrum | Solana');
|
|
675
|
+
}
|
|
676
|
+
validateU32(params.asset, 'asset');
|
|
677
|
+
validateU64(params.amount, 'amount');
|
|
678
|
+
const dstHex = params.dst_addr.startsWith('0x')
|
|
679
|
+
? params.dst_addr.slice(2)
|
|
680
|
+
: params.dst_addr;
|
|
681
|
+
if (!/^[0-9a-fA-F]+$/.test(dstHex) || (dstHex.length !== 40 && dstHex.length !== 64)) {
|
|
682
|
+
throw new RangeError('dst_addr must be 0x + 40 (EVM) or 64 (Solana) hex chars');
|
|
683
|
+
}
|
|
684
|
+
return wrapParams(
|
|
685
|
+
'mb_withdraw',
|
|
686
|
+
`{${jsonStr('chain')}:${jsonStr(params.chain)},${jsonStr('asset')}:${params.asset},${jsonStr('amount')}:${params.amount},${jsonStr('dst_addr')}:${jsonStr(params.dst_addr)}}`,
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// ---- governance / operator ----
|
|
691
|
+
|
|
692
|
+
/// `set_metaliquidity_whitelist` — set an MLP whitelist membership (validator).
|
|
693
|
+
export function buildNativeSetMetaliquidityWhitelistAction(
|
|
694
|
+
params: SetMetaliquidityWhitelist,
|
|
695
|
+
): string {
|
|
696
|
+
validateAddress(params.address, 'address');
|
|
697
|
+
return wrapParams(
|
|
698
|
+
'set_metaliquidity_whitelist',
|
|
699
|
+
`{${jsonStr('address')}:${jsonStr(params.address)},${jsonStr('allowed')}:${params.allowed ? 'true' : 'false'}}`,
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/// `register_metaliquidity_operator` — register / revoke a vault strategy operator.
|
|
704
|
+
export function buildNativeRegisterMetaliquidityOperatorAction(
|
|
705
|
+
params: RegisterMetaliquidityOperator,
|
|
706
|
+
): string {
|
|
707
|
+
validateU64(params.vault_id, 'vault_id');
|
|
708
|
+
validateAddress(params.operator, 'operator');
|
|
709
|
+
const parts: string[] = [
|
|
710
|
+
`${jsonStr('vault_id')}:${params.vault_id}`,
|
|
711
|
+
`${jsonStr('operator')}:${jsonStr(params.operator)}`,
|
|
712
|
+
`${jsonStr('allowed')}:${params.allowed ? 'true' : 'false'}`,
|
|
713
|
+
];
|
|
714
|
+
if (params.expires_at_ms !== undefined) {
|
|
715
|
+
validateU64(params.expires_at_ms, 'expires_at_ms');
|
|
716
|
+
parts.push(`${jsonStr('expires_at_ms')}:${params.expires_at_ms}`);
|
|
717
|
+
}
|
|
718
|
+
return wrapParams('register_metaliquidity_operator', `{${parts.join(',')}}`);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// ============================================================================
|
|
722
|
+
// Spot margin (leveraged spot) + Earn (lending pool) — devnet preview.
|
|
723
|
+
// All SENDER-AUTHORIZED (no owner field; the recovered signer is the actor).
|
|
724
|
+
// Decimal magnitudes (amount / borrow / shares) are emitted as JSON STRINGS;
|
|
725
|
+
// size / limit_px are bare integers on the raw-lot / 1e8 planes. Field order =
|
|
726
|
+
// the server struct declaration order.
|
|
727
|
+
// ============================================================================
|
|
728
|
+
|
|
729
|
+
/// Build the canonical native `spot_margin_deposit` action JSON string.
|
|
730
|
+
///
|
|
731
|
+
/// `{"type":"spot_margin_deposit","params":{"pair":<u32>,"amount":"<decimal>"}}`.
|
|
732
|
+
/// Posts quote collateral into the `(account, pair)` margin account (margin must
|
|
733
|
+
/// be enabled for the pair). SENDER-AUTHORIZED.
|
|
734
|
+
export function buildNativeSpotMarginDepositAction(
|
|
735
|
+
params: NativeSpotMarginDeposit,
|
|
736
|
+
): string {
|
|
737
|
+
validateMarket(params.pair);
|
|
738
|
+
validateDecimalString(params.amount, 'amount');
|
|
739
|
+
const paramsJson = `{${jsonStr('pair')}:${params.pair},${jsonStr('amount')}:${jsonStr(params.amount)}}`;
|
|
740
|
+
return `{${jsonStr('type')}:${jsonStr('spot_margin_deposit')},${jsonStr('params')}:${paramsJson}}`;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/// Build the canonical native `spot_margin_withdraw` action JSON string.
|
|
744
|
+
///
|
|
745
|
+
/// `{"type":"spot_margin_withdraw","params":{"pair":<u32>,"amount":"<decimal>"}}`.
|
|
746
|
+
/// Withdraws free collateral (initial-margin-gated while a position is open).
|
|
747
|
+
/// SENDER-AUTHORIZED.
|
|
748
|
+
export function buildNativeSpotMarginWithdrawAction(
|
|
749
|
+
params: NativeSpotMarginWithdraw,
|
|
750
|
+
): string {
|
|
751
|
+
validateMarket(params.pair);
|
|
752
|
+
validateDecimalString(params.amount, 'amount');
|
|
753
|
+
const paramsJson = `{${jsonStr('pair')}:${params.pair},${jsonStr('amount')}:${jsonStr(params.amount)}}`;
|
|
754
|
+
return `{${jsonStr('type')}:${jsonStr('spot_margin_withdraw')},${jsonStr('params')}:${paramsJson}}`;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
/// Build the canonical native `spot_margin_open` action JSON string.
|
|
758
|
+
///
|
|
759
|
+
/// `{"type":"spot_margin_open","params":{"pair":<u32>,"size":<u64>,"limit_px":<u64>,"borrow":"<decimal>"}}`.
|
|
760
|
+
/// Borrows quote from the pair's Earn pool and IOC-buys `size` base on leverage;
|
|
761
|
+
/// gated by the initial-margin requirement on the worst-case cost. SENDER-AUTHORIZED.
|
|
762
|
+
export function buildNativeSpotMarginOpenAction(
|
|
763
|
+
params: NativeSpotMarginOpen,
|
|
764
|
+
): string {
|
|
765
|
+
validateMarket(params.pair);
|
|
766
|
+
validateU64(params.size, 'size');
|
|
767
|
+
validateU64(params.limit_px, 'limit_px');
|
|
768
|
+
if (params.size <= 0) {
|
|
769
|
+
throw new RangeError('spot_margin_open requires size > 0');
|
|
770
|
+
}
|
|
771
|
+
if (params.limit_px <= 0) {
|
|
772
|
+
throw new RangeError('spot_margin_open requires limit_px > 0');
|
|
773
|
+
}
|
|
774
|
+
validateDecimalString(params.borrow, 'borrow');
|
|
775
|
+
const paramsJson = `{${jsonStr('pair')}:${params.pair},${jsonStr('size')}:${params.size},${jsonStr('limit_px')}:${params.limit_px},${jsonStr('borrow')}:${jsonStr(params.borrow)}}`;
|
|
776
|
+
return `{${jsonStr('type')}:${jsonStr('spot_margin_open')},${jsonStr('params')}:${paramsJson}}`;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/// Build the canonical native `spot_margin_close` action JSON string.
|
|
780
|
+
///
|
|
781
|
+
/// `{"type":"spot_margin_close","params":{"pair":<u32>,"limit_px":<u64>}}`.
|
|
782
|
+
/// IOC-sells the held base, repays principal + interest, returns the remainder
|
|
783
|
+
/// (a partial fill keeps the account open). SENDER-AUTHORIZED.
|
|
784
|
+
export function buildNativeSpotMarginCloseAction(
|
|
785
|
+
params: NativeSpotMarginClose,
|
|
786
|
+
): string {
|
|
787
|
+
validateMarket(params.pair);
|
|
788
|
+
validateU64(params.limit_px, 'limit_px');
|
|
789
|
+
if (params.limit_px <= 0) {
|
|
790
|
+
throw new RangeError('spot_margin_close requires limit_px > 0');
|
|
791
|
+
}
|
|
792
|
+
const paramsJson = `{${jsonStr('pair')}:${params.pair},${jsonStr('limit_px')}:${params.limit_px}}`;
|
|
793
|
+
return `{${jsonStr('type')}:${jsonStr('spot_margin_close')},${jsonStr('params')}:${paramsJson}}`;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/// Build the canonical native `earn_deposit` action JSON string.
|
|
797
|
+
///
|
|
798
|
+
/// `{"type":"earn_deposit","params":{"asset":<u32>,"amount":"<decimal>"}}`.
|
|
799
|
+
/// Supplies quote into a lending pool for shares (1:1 on a fresh pool, else
|
|
800
|
+
/// priced off NAV; the pool auto-creates on first deposit). SENDER-AUTHORIZED.
|
|
801
|
+
export function buildNativeEarnDepositAction(params: NativeEarnDeposit): string {
|
|
802
|
+
validateU32(params.asset, 'asset');
|
|
803
|
+
validateDecimalString(params.amount, 'amount');
|
|
804
|
+
const paramsJson = `{${jsonStr('asset')}:${params.asset},${jsonStr('amount')}:${jsonStr(params.amount)}}`;
|
|
805
|
+
return `{${jsonStr('type')}:${jsonStr('earn_deposit')},${jsonStr('params')}:${paramsJson}}`;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/// Build the canonical native `earn_withdraw` action JSON string.
|
|
809
|
+
///
|
|
810
|
+
/// `{"type":"earn_withdraw","params":{"asset":<u32>,"shares":"<decimal>"}}`.
|
|
811
|
+
/// Redeems pool shares back to quote, clamped to the pool's idle liquidity.
|
|
812
|
+
/// SENDER-AUTHORIZED.
|
|
813
|
+
export function buildNativeEarnWithdrawAction(
|
|
814
|
+
params: NativeEarnWithdraw,
|
|
815
|
+
): string {
|
|
816
|
+
validateU32(params.asset, 'asset');
|
|
817
|
+
validateDecimalString(params.shares, 'shares');
|
|
818
|
+
const paramsJson = `{${jsonStr('asset')}:${params.asset},${jsonStr('shares')}:${jsonStr(params.shares)}}`;
|
|
819
|
+
return `{${jsonStr('type')}:${jsonStr('earn_withdraw')},${jsonStr('params')}:${paramsJson}}`;
|
|
820
|
+
}
|