@lifi/perps-sdk 18.1.0 → 20.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/dist/cjs/client/PerpsClient.d.ts +14 -8
- package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
- package/dist/cjs/client/PerpsClient.js +145 -53
- package/dist/cjs/client/PerpsClient.js.map +1 -1
- package/dist/cjs/client/createPerpsClient.d.ts +1 -6
- package/dist/cjs/client/createPerpsClient.d.ts.map +1 -1
- package/dist/cjs/client/createPerpsClient.js +1 -14
- package/dist/cjs/client/createPerpsClient.js.map +1 -1
- package/dist/cjs/index.d.ts +6 -4
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +11 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/services/getOhlcv.d.ts.map +1 -1
- package/dist/cjs/services/getOhlcv.js.map +1 -1
- package/dist/cjs/types/api.d.ts +7 -6
- package/dist/cjs/types/api.d.ts.map +1 -1
- package/dist/cjs/types/config.d.ts +0 -9
- package/dist/cjs/types/config.d.ts.map +1 -1
- package/dist/cjs/types/provider.d.ts +5 -6
- package/dist/cjs/types/provider.d.ts.map +1 -1
- package/dist/cjs/types/withdrawal.d.ts +4 -2
- package/dist/cjs/types/withdrawal.d.ts.map +1 -1
- package/dist/cjs/utils/calculations.d.ts.map +1 -1
- package/dist/cjs/utils/calculations.js +107 -38
- package/dist/cjs/utils/calculations.js.map +1 -1
- package/dist/cjs/utils/decimal.d.ts +4 -0
- package/dist/cjs/utils/decimal.d.ts.map +1 -0
- package/dist/cjs/utils/decimal.js +14 -0
- package/dist/cjs/utils/decimal.js.map +1 -0
- package/dist/cjs/utils/math.d.ts +4 -0
- package/dist/cjs/utils/math.d.ts.map +1 -0
- package/dist/cjs/utils/math.js +8 -0
- package/dist/cjs/utils/math.js.map +1 -0
- package/dist/cjs/utils/parse.d.ts +1 -0
- package/dist/cjs/utils/parse.d.ts.map +1 -1
- package/dist/cjs/utils/parse.js +15 -0
- package/dist/cjs/utils/parse.js.map +1 -1
- package/dist/cjs/utils/positionMath.d.ts +5 -0
- package/dist/cjs/utils/positionMath.d.ts.map +1 -1
- package/dist/cjs/utils/positionMath.js +53 -6
- package/dist/cjs/utils/positionMath.js.map +1 -1
- package/dist/cjs/utils/setupActions.d.ts +4 -2
- package/dist/cjs/utils/setupActions.d.ts.map +1 -1
- package/dist/cjs/utils/setupActions.js +9 -1
- package/dist/cjs/utils/setupActions.js.map +1 -1
- package/dist/cjs/utils/switchChain.js +1 -1
- package/dist/cjs/utils/switchChain.js.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/cjs/websocket/PerpsWsClient.d.ts +0 -1
- package/dist/cjs/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/cjs/websocket/PerpsWsClient.js +0 -7
- package/dist/cjs/websocket/PerpsWsClient.js.map +1 -1
- package/dist/esm/client/PerpsClient.d.ts +70 -61
- package/dist/esm/client/PerpsClient.d.ts.map +1 -1
- package/dist/esm/client/PerpsClient.js +215 -107
- package/dist/esm/client/PerpsClient.js.map +1 -1
- package/dist/esm/client/createPerpsClient.d.ts +1 -12
- package/dist/esm/client/createPerpsClient.d.ts.map +1 -1
- package/dist/esm/client/createPerpsClient.js +1 -19
- package/dist/esm/client/createPerpsClient.js.map +1 -1
- package/dist/esm/client/requireProvider.d.ts +1 -1
- package/dist/esm/client/requireProvider.js +1 -1
- package/dist/esm/index.d.ts +6 -4
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +5 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/services/getOhlcv.d.ts +1 -0
- package/dist/esm/services/getOhlcv.d.ts.map +1 -1
- package/dist/esm/services/getOhlcv.js.map +1 -1
- package/dist/esm/types/api.d.ts +39 -36
- package/dist/esm/types/api.d.ts.map +1 -1
- package/dist/esm/types/config.d.ts +0 -27
- package/dist/esm/types/config.d.ts.map +1 -1
- package/dist/esm/types/provider.d.ts +28 -33
- package/dist/esm/types/provider.d.ts.map +1 -1
- package/dist/esm/types/withdrawal.d.ts +24 -15
- package/dist/esm/types/withdrawal.d.ts.map +1 -1
- package/dist/esm/utils/calculations.d.ts +3 -2
- package/dist/esm/utils/calculations.d.ts.map +1 -1
- package/dist/esm/utils/calculations.js +109 -39
- package/dist/esm/utils/calculations.js.map +1 -1
- package/dist/esm/utils/decimal.d.ts +9 -0
- package/dist/esm/utils/decimal.d.ts.map +1 -0
- package/dist/esm/utils/decimal.js +12 -0
- package/dist/esm/utils/decimal.js.map +1 -0
- package/dist/esm/utils/math.d.ts +6 -0
- package/dist/esm/utils/math.d.ts.map +1 -0
- package/dist/esm/utils/math.js +5 -0
- package/dist/esm/utils/math.js.map +1 -0
- package/dist/esm/utils/parse.d.ts +7 -0
- package/dist/esm/utils/parse.d.ts.map +1 -1
- package/dist/esm/utils/parse.js +20 -0
- package/dist/esm/utils/parse.js.map +1 -1
- package/dist/esm/utils/positionMath.d.ts +13 -0
- package/dist/esm/utils/positionMath.d.ts.map +1 -1
- package/dist/esm/utils/positionMath.js +60 -6
- package/dist/esm/utils/positionMath.js.map +1 -1
- package/dist/esm/utils/setupActions.d.ts +17 -8
- package/dist/esm/utils/setupActions.d.ts.map +1 -1
- package/dist/esm/utils/setupActions.js +20 -7
- package/dist/esm/utils/setupActions.js.map +1 -1
- package/dist/esm/utils/switchChain.js +1 -1
- package/dist/esm/utils/switchChain.js.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/websocket/PerpsWsClient.d.ts +0 -7
- package/dist/esm/websocket/PerpsWsClient.d.ts.map +1 -1
- package/dist/esm/websocket/PerpsWsClient.js +0 -7
- package/dist/esm/websocket/PerpsWsClient.js.map +1 -1
- package/dist/types/client/PerpsClient.d.ts +70 -61
- package/dist/types/client/PerpsClient.d.ts.map +1 -1
- package/dist/types/client/createPerpsClient.d.ts +1 -12
- package/dist/types/client/createPerpsClient.d.ts.map +1 -1
- package/dist/types/client/requireProvider.d.ts +1 -1
- package/dist/types/index.d.ts +6 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/services/getOhlcv.d.ts +1 -0
- package/dist/types/services/getOhlcv.d.ts.map +1 -1
- package/dist/types/types/api.d.ts +39 -36
- package/dist/types/types/api.d.ts.map +1 -1
- package/dist/types/types/config.d.ts +0 -27
- package/dist/types/types/config.d.ts.map +1 -1
- package/dist/types/types/provider.d.ts +28 -33
- package/dist/types/types/provider.d.ts.map +1 -1
- package/dist/types/types/withdrawal.d.ts +24 -15
- package/dist/types/types/withdrawal.d.ts.map +1 -1
- package/dist/types/utils/calculations.d.ts +3 -2
- package/dist/types/utils/calculations.d.ts.map +1 -1
- package/dist/types/utils/decimal.d.ts +9 -0
- package/dist/types/utils/decimal.d.ts.map +1 -0
- package/dist/types/utils/math.d.ts +6 -0
- package/dist/types/utils/math.d.ts.map +1 -0
- package/dist/types/utils/parse.d.ts +7 -0
- package/dist/types/utils/parse.d.ts.map +1 -1
- package/dist/types/utils/positionMath.d.ts +13 -0
- package/dist/types/utils/positionMath.d.ts.map +1 -1
- package/dist/types/utils/setupActions.d.ts +17 -8
- package/dist/types/utils/setupActions.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types/websocket/PerpsWsClient.d.ts +0 -7
- package/dist/types/websocket/PerpsWsClient.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/client/PerpsClient.ts +296 -137
- package/src/client/createPerpsClient.ts +4 -32
- package/src/client/requireProvider.ts +1 -1
- package/src/index.ts +8 -5
- package/src/services/getOhlcv.ts +1 -0
- package/src/types/api.ts +40 -40
- package/src/types/config.ts +0 -30
- package/src/types/provider.ts +29 -36
- package/src/types/withdrawal.ts +24 -15
- package/src/utils/calculations.ts +114 -43
- package/src/utils/decimal.ts +13 -0
- package/src/utils/math.ts +7 -0
- package/src/utils/parse.ts +25 -0
- package/src/utils/positionMath.ts +66 -6
- package/src/utils/setupActions.ts +27 -13
- package/src/utils/switchChain.ts +1 -1
- package/src/version.ts +1 -1
- package/src/websocket/PerpsWsClient.ts +0 -19
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActionType, META_PROVIDER, PerpsErrorCode, PerpsSigner, SigningMethod, } from '@lifi/perps-types';
|
|
1
|
+
import { ActionRelay, ActionType, META_PROVIDER, PerpsErrorCode, PerpsSigner, SigningMethod, } from '@lifi/perps-types';
|
|
2
2
|
import Big from 'big.js';
|
|
3
3
|
import { PerpsError } from '../errors/PerpsError.js';
|
|
4
4
|
import { getAssetRegistry, toAssetDisplay } from '../registry/assetRegistry.js';
|
|
@@ -9,10 +9,95 @@ import { getAccount as fetchAccount } from '../services/getAccount.js';
|
|
|
9
9
|
import { getOrder as fetchOrder, } from '../services/getOrder.js';
|
|
10
10
|
import { getOrders as fetchOrders, } from '../services/getOrders.js';
|
|
11
11
|
import { getProviders } from '../services/getProviders.js';
|
|
12
|
+
import { isUserFacingSetupStep } from '../utils/setupActions.js';
|
|
12
13
|
import { signTypedDataWithSigner } from '../utils/signTypedData.js';
|
|
13
14
|
import { eip712DomainChainId, switchSigningChain, userEip712TargetChainId, } from '../utils/switchChain.js';
|
|
14
15
|
import { createPerpsClient } from './createPerpsClient.js';
|
|
15
16
|
import { requireProvider as resolveProvider } from './requireProvider.js';
|
|
17
|
+
/** Absent `sequence` sorts last, so an unordered step gates nothing. */
|
|
18
|
+
function sequenceOf(descriptor) {
|
|
19
|
+
return descriptor.sequence ?? Number.MAX_SAFE_INTEGER;
|
|
20
|
+
}
|
|
21
|
+
const SIGNERS = new Set(Object.values(PerpsSigner));
|
|
22
|
+
const RELAYS = new Set(Object.values(ActionRelay));
|
|
23
|
+
/**
|
|
24
|
+
* Reject provider metadata whose steps the SDK cannot classify. A step with no
|
|
25
|
+
* known `signer`, `relay`, `options` or `revoke` would otherwise read as
|
|
26
|
+
* satisfied and report a not-onboarded account as ready.
|
|
27
|
+
*/
|
|
28
|
+
function assertSetupContract(metadata) {
|
|
29
|
+
const reject = (step, problem) => {
|
|
30
|
+
throw new PerpsError(PerpsErrorCode.SDKError, `Provider '${metadata.key}' step '${step}' ${problem}; this SDK needs a ` +
|
|
31
|
+
`backend that emits signer, relay, options and revoke.`);
|
|
32
|
+
};
|
|
33
|
+
for (const descriptor of [...metadata.setup, ...metadata.actions]) {
|
|
34
|
+
if (!SIGNERS.has(descriptor.signer)) {
|
|
35
|
+
reject(descriptor.type, 'declares no known signer');
|
|
36
|
+
}
|
|
37
|
+
if (!RELAYS.has(descriptor.relay)) {
|
|
38
|
+
reject(descriptor.type, 'declares no known relay');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const actionTypes = new Set(metadata.actions.map((d) => d.type));
|
|
42
|
+
for (const descriptor of metadata.setup) {
|
|
43
|
+
if (descriptor.options === undefined) {
|
|
44
|
+
reject(descriptor.type, 'declares no options');
|
|
45
|
+
}
|
|
46
|
+
if (descriptor.options !== null && descriptor.options.length === 0) {
|
|
47
|
+
reject(descriptor.type, 'declares an empty options list');
|
|
48
|
+
}
|
|
49
|
+
if (descriptor.revoke === undefined) {
|
|
50
|
+
reject(descriptor.type, 'declares no revoke');
|
|
51
|
+
}
|
|
52
|
+
if (descriptor.revoke !== null && !actionTypes.has(descriptor.revoke)) {
|
|
53
|
+
reject(descriptor.type, `revokes with '${descriptor.revoke}', which Provider.actions does not declare`);
|
|
54
|
+
}
|
|
55
|
+
const defaults = (descriptor.options ?? []).filter((option) => option.default === true);
|
|
56
|
+
if (defaults.length > 1) {
|
|
57
|
+
reject(descriptor.type, 'marks more than one option default');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/** Only a USER-signed non-choice step is staged for the user to sign. */
|
|
62
|
+
function isStageable(descriptor) {
|
|
63
|
+
return descriptor.options === null && descriptor.signer === PerpsSigner.USER;
|
|
64
|
+
}
|
|
65
|
+
function satisfiedTypes(settings) {
|
|
66
|
+
return new Set(settings.filter((setting) => setting.satisfied).map((s) => s.type));
|
|
67
|
+
}
|
|
68
|
+
/** The choice option whose every bound param equals the projected value. */
|
|
69
|
+
function selectedOption(descriptor, settings) {
|
|
70
|
+
if (descriptor.options === null) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const values = settings.find((setting) => setting.type === descriptor.type)?.values ?? [];
|
|
74
|
+
return (descriptor.options.find((option) => {
|
|
75
|
+
const bound = Object.entries(option.params);
|
|
76
|
+
return (bound.length > 0 &&
|
|
77
|
+
bound.every(([name, value]) => values.some((v) => v.name === name && v.value === value)));
|
|
78
|
+
}) ?? null);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The projected value of the param a choice's options bind, as the venue
|
|
82
|
+
* names it, whether or not an option binds that value.
|
|
83
|
+
*/
|
|
84
|
+
function currentChoiceValue(descriptor, settings) {
|
|
85
|
+
if (descriptor.options === null) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const names = new Set(descriptor.options.flatMap((option) => Object.keys(option.params)));
|
|
89
|
+
const value = settings
|
|
90
|
+
.find((setting) => setting.type === descriptor.type)
|
|
91
|
+
?.values.find((v) => names.has(v.name))?.value ?? null;
|
|
92
|
+
return value === null ? null : String(value);
|
|
93
|
+
}
|
|
94
|
+
/** Throw the first per-action failure a 200 OK `/executeAction` carried. */
|
|
95
|
+
function assertAllSucceeded(results) {
|
|
96
|
+
const failure = results.find((r) => !r.success);
|
|
97
|
+
if (failure) {
|
|
98
|
+
throw new PerpsError(failure.errorCode ?? PerpsErrorCode.ExchangeRejected, failure.error);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
16
101
|
function isNonNegativeDecimal(value) {
|
|
17
102
|
try {
|
|
18
103
|
return new Big(value).gte(0);
|
|
@@ -26,11 +111,7 @@ function isNonNegativeDecimal(value) {
|
|
|
26
111
|
* action isn't declared — defensive: better to fail loudly than to mis-sign.
|
|
27
112
|
*/
|
|
28
113
|
function findActionDescriptor(metadata, action) {
|
|
29
|
-
const descriptor = [
|
|
30
|
-
...metadata.setup,
|
|
31
|
-
...metadata.options,
|
|
32
|
-
...metadata.actions,
|
|
33
|
-
].find((d) => d.type === action);
|
|
114
|
+
const descriptor = [...metadata.setup, ...metadata.actions].find((d) => d.type === action);
|
|
34
115
|
if (!descriptor) {
|
|
35
116
|
throw new PerpsError(PerpsErrorCode.SDKError, `Provider '${metadata.key}' does not declare action '${action}'.`);
|
|
36
117
|
}
|
|
@@ -82,7 +163,7 @@ export class PerpsClient {
|
|
|
82
163
|
}
|
|
83
164
|
/**
|
|
84
165
|
* Set or update the end-user's wallet. Used whenever an action's descriptor
|
|
85
|
-
* names
|
|
166
|
+
* names `PerpsSigner.USER` as its `signer`. Pass undefined to clear.
|
|
86
167
|
*
|
|
87
168
|
* @public
|
|
88
169
|
*/
|
|
@@ -113,20 +194,20 @@ export class PerpsClient {
|
|
|
113
194
|
return this.sdkClient;
|
|
114
195
|
}
|
|
115
196
|
async getProviderMetadata(provider) {
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.providerMetadataCache.
|
|
197
|
+
let metadata = this.providerMetadataCache.get(provider);
|
|
198
|
+
if (!metadata) {
|
|
199
|
+
const { providers } = await getProviders(this.sdkClient);
|
|
200
|
+
for (const d of providers) {
|
|
201
|
+
this.providerMetadataCache.set(d.key, d);
|
|
202
|
+
}
|
|
203
|
+
metadata = this.providerMetadataCache.get(provider);
|
|
123
204
|
}
|
|
124
|
-
const metadata = this.providerMetadataCache.get(provider);
|
|
125
205
|
if (!metadata) {
|
|
126
206
|
const error = new PerpsError(PerpsErrorCode.SDKError, `Unsupported provider: ${provider}`);
|
|
127
207
|
error.tool = '@lifi/perps-sdk';
|
|
128
208
|
throw error;
|
|
129
209
|
}
|
|
210
|
+
assertSetupContract(metadata);
|
|
130
211
|
return metadata;
|
|
131
212
|
}
|
|
132
213
|
/**
|
|
@@ -149,7 +230,7 @@ export class PerpsClient {
|
|
|
149
230
|
* Ask the provider plugin for the signer-bearing wire fields of `action` —
|
|
150
231
|
* the on-wire `signerAddress` and any signer-derived params (e.g.
|
|
151
232
|
* Hyperliquid's `agentAddress` for `APPROVE_AGENT`). Forwards the descriptor's
|
|
152
|
-
* `
|
|
233
|
+
* `signer` so the plugin can branch on signer role. Returns empty when the
|
|
153
234
|
* plugin signs as the user or with a non-EVM credential. Core constructs no
|
|
154
235
|
* `signerAddress` itself; signer identity is plugin-owned.
|
|
155
236
|
*/
|
|
@@ -158,15 +239,15 @@ export class PerpsClient {
|
|
|
158
239
|
if (typeof plugin.resolveActionRequest !== 'function') {
|
|
159
240
|
return {};
|
|
160
241
|
}
|
|
161
|
-
return plugin.resolveActionRequest(descriptor.type, address, descriptor.
|
|
242
|
+
return plugin.resolveActionRequest(descriptor.type, address, descriptor.signer);
|
|
162
243
|
}
|
|
163
244
|
/**
|
|
164
245
|
* Delegate signing of `actions` to the provider plugin, one call per
|
|
165
246
|
* consecutive run of steps sharing a descriptor. Each call carries that
|
|
166
|
-
* descriptor's `signingMethod` and `
|
|
247
|
+
* descriptor's `signingMethod` and `signer`, so a mixed batch signs every
|
|
167
248
|
* step with the signer its own descriptor declares; the signed steps come
|
|
168
249
|
* back in the batch's original order. The plugin owns every signing arm and
|
|
169
|
-
* branches on `
|
|
250
|
+
* branches on `signer` internally, reading the end-user's wallet from the
|
|
170
251
|
* {@link SignActionsContext} when an arm signs as the user.
|
|
171
252
|
*
|
|
172
253
|
* @throws {PerpsError} When a step's action is not declared by the provider,
|
|
@@ -250,9 +331,9 @@ export class PerpsClient {
|
|
|
250
331
|
}
|
|
251
332
|
/**
|
|
252
333
|
* Assemble the per-call context the provider plugin needs in order to sign:
|
|
253
|
-
* the end-user's wallet and the descriptor's declared `
|
|
254
|
-
* forwards `
|
|
255
|
-
* branch on
|
|
334
|
+
* the end-user's wallet and the descriptor's declared `signer`. Core
|
|
335
|
+
* forwards `signer` as data so the plugin can pick WHO signs; it does not
|
|
336
|
+
* branch on it. Provider-owned session credentials (the Hyperliquid agent
|
|
256
337
|
* keypair, Lighter's API key) are resolved inside the provider's
|
|
257
338
|
* `signActions`, not threaded through here.
|
|
258
339
|
*
|
|
@@ -266,7 +347,7 @@ export class PerpsClient {
|
|
|
266
347
|
* `sdkClient.userWallet`.
|
|
267
348
|
*/
|
|
268
349
|
buildSignActionsContext(descriptor, userWallet, onProgress) {
|
|
269
|
-
const ctx = {
|
|
350
|
+
const ctx = { signer: descriptor.signer };
|
|
270
351
|
if (onProgress !== undefined) {
|
|
271
352
|
ctx.onProgress = onProgress;
|
|
272
353
|
}
|
|
@@ -321,7 +402,7 @@ export class PerpsClient {
|
|
|
321
402
|
/**
|
|
322
403
|
* Fetch the user's account state from the backend and attach the
|
|
323
404
|
* SDK-projected `settings` array — one `AccountConfigSetting` per
|
|
324
|
-
* descriptor on `Provider.setup
|
|
405
|
+
* descriptor on `Provider.setup`. Callers read
|
|
325
406
|
* `result.settings` directly without re-deriving values from the typed
|
|
326
407
|
* `AccountConfig`.
|
|
327
408
|
*
|
|
@@ -335,21 +416,22 @@ export class PerpsClient {
|
|
|
335
416
|
fetchAccount(this.sdkClient, params),
|
|
336
417
|
this.getProviderMetadata(params.provider),
|
|
337
418
|
]);
|
|
338
|
-
const settings = plugin.projectConfig(response.config, metadata.setup
|
|
419
|
+
const settings = plugin.projectConfig(response.config, metadata.setup);
|
|
339
420
|
return { ...response, settings };
|
|
340
421
|
}
|
|
341
422
|
/**
|
|
342
|
-
* The user's current venue-side settings for a market — the margin
|
|
343
|
-
* and leverage the next order on it will use. Resolves
|
|
344
|
-
*
|
|
345
|
-
* has no such read at all).
|
|
423
|
+
* The user's current venue-side settings for a perps market — the margin
|
|
424
|
+
* mode and leverage the next order on it will use. Resolves the venue value
|
|
425
|
+
* or throws.
|
|
346
426
|
*
|
|
347
|
-
* @throws {PerpsError} When the provider plugin is not registered
|
|
427
|
+
* @throws {PerpsError} When the provider plugin is not registered,
|
|
428
|
+
* `ValidationError` for a spot market, or any other `PerpsError` when the
|
|
429
|
+
* venue read fails or carries no usable value.
|
|
348
430
|
* @public
|
|
349
431
|
*/
|
|
350
432
|
async getMarketSettings(params) {
|
|
351
433
|
const plugin = this.requireProvider(params.provider);
|
|
352
|
-
return plugin.getMarketSettings
|
|
434
|
+
return plugin.getMarketSettings({
|
|
353
435
|
address: params.address,
|
|
354
436
|
market: params.market,
|
|
355
437
|
});
|
|
@@ -461,7 +543,7 @@ export class PerpsClient {
|
|
|
461
543
|
* @returns `undefined` when the registered plugin declares no withdrawable
|
|
462
544
|
* read.
|
|
463
545
|
* @throws {PerpsError} When the provider plugin is not registered, when
|
|
464
|
-
* either the plugin read or the asset sync fails,
|
|
546
|
+
* either the plugin read or the asset sync fails, when a row's
|
|
465
547
|
* `withdrawalFee` is not a non-negative decimal.
|
|
466
548
|
* @public
|
|
467
549
|
*/
|
|
@@ -505,6 +587,9 @@ export class PerpsClient {
|
|
|
505
587
|
route: row.route,
|
|
506
588
|
available: row.available,
|
|
507
589
|
withdrawalFee: row.withdrawalFee,
|
|
590
|
+
...(row.isFeeDeducted === undefined
|
|
591
|
+
? {}
|
|
592
|
+
: { isFeeDeducted: row.isFeeDeducted }),
|
|
508
593
|
},
|
|
509
594
|
];
|
|
510
595
|
});
|
|
@@ -525,15 +610,17 @@ export class PerpsClient {
|
|
|
525
610
|
return plugin.getPortfolioHistory({ address: params.address, range: params.range }, options);
|
|
526
611
|
}
|
|
527
612
|
/**
|
|
528
|
-
* Return the
|
|
529
|
-
*
|
|
530
|
-
* `checklist` carries the renderable onboarding list: every
|
|
531
|
-
*
|
|
532
|
-
* omitted.
|
|
613
|
+
* Return the setup steps this account must still sign as a flat,
|
|
614
|
+
* self-describing list. Trading is gated on `isReady === true`.
|
|
615
|
+
* `checklist` carries the renderable onboarding list: every choice step and
|
|
616
|
+
* every `USER`-signed step with its satisfied state, with not-required
|
|
617
|
+
* conditional steps omitted.
|
|
533
618
|
*
|
|
534
|
-
* `
|
|
535
|
-
*
|
|
536
|
-
*
|
|
619
|
+
* A non-choice `SDK`-signed step is NEVER returned here — the SDK drains it
|
|
620
|
+
* with the provider's own credentials. A choice is listed but never staged
|
|
621
|
+
* or executed, whatever its `signer`: the user picks an option through
|
|
622
|
+
* {@link executeProviderOption}, and `isReady` stays `false` until every
|
|
623
|
+
* choice on `checklist` is satisfied.
|
|
537
624
|
*
|
|
538
625
|
* @public
|
|
539
626
|
*/
|
|
@@ -554,61 +641,58 @@ export class PerpsClient {
|
|
|
554
641
|
checklist: [],
|
|
555
642
|
};
|
|
556
643
|
}
|
|
557
|
-
const
|
|
644
|
+
const settings = await this.resolveSetupSettings(provider, address);
|
|
645
|
+
const satisfiedSetup = satisfiedTypes(settings);
|
|
558
646
|
const pendingSetup = metadata.setup.filter((descriptor) => !satisfiedSetup.has(descriptor.type));
|
|
559
647
|
const plugin = this.sdkClient.getProvider(provider);
|
|
560
|
-
const
|
|
561
|
-
const isInternal = (descriptor) => internalTypes.has(descriptor.type) &&
|
|
562
|
-
!descriptor.signers.includes(PerpsSigner.USER);
|
|
563
|
-
const visibleSetup = pendingSetup.filter((descriptor) => !isInternal(descriptor));
|
|
648
|
+
const stageable = pendingSetup.filter(isStageable);
|
|
564
649
|
// The backend filters already-satisfied setup actions and returns typed
|
|
565
650
|
// data for those still outstanding; each plugin contributes its own
|
|
566
651
|
// signer-bearing request fields.
|
|
567
|
-
const actions = await this.buildProviderSetupActions(provider, address,
|
|
652
|
+
const actions = await this.buildProviderSetupActions(provider, address, stageable);
|
|
568
653
|
// A staged step is one the build produced actions for — a pending
|
|
569
654
|
// descriptor the backend staged nothing for is either satisfied
|
|
570
655
|
// (backend-gated) or not applicable to this account.
|
|
571
656
|
const stagedTypes = new Set(actions.map((step) => step.action));
|
|
572
657
|
const conditionalTypes = new Set(plugin?.conditionalSetupActions ?? []);
|
|
573
|
-
const sequenceOf = (descriptor) => descriptor.sequence ?? Number.MAX_SAFE_INTEGER;
|
|
574
658
|
const checklist = metadata.setup
|
|
575
|
-
.filter(
|
|
576
|
-
.filter((descriptor) =>
|
|
659
|
+
.filter(isUserFacingSetupStep)
|
|
660
|
+
.filter((descriptor) => descriptor.options !== null ||
|
|
661
|
+
!conditionalTypes.has(descriptor.type) ||
|
|
577
662
|
stagedTypes.has(descriptor.type))
|
|
578
663
|
.sort((a, b) => sequenceOf(a) - sequenceOf(b))
|
|
579
664
|
.map((descriptor) => ({
|
|
580
665
|
descriptor,
|
|
581
|
-
satisfied:
|
|
666
|
+
satisfied: descriptor.options !== null
|
|
667
|
+
? satisfiedSetup.has(descriptor.type)
|
|
668
|
+
: !stagedTypes.has(descriptor.type),
|
|
669
|
+
selected: selectedOption(descriptor, settings),
|
|
670
|
+
currentValue: currentChoiceValue(descriptor, settings),
|
|
582
671
|
}));
|
|
583
|
-
await this.
|
|
672
|
+
await this.drainSetup(provider, address, pendingSetup.filter((descriptor) => descriptor.options === null && descriptor.signer === PerpsSigner.SDK), stageable.filter((descriptor) => stagedTypes.has(descriptor.type)));
|
|
584
673
|
return {
|
|
585
674
|
accountExists: true,
|
|
586
675
|
setup: actions,
|
|
587
|
-
isReady: actions.length === 0,
|
|
676
|
+
isReady: actions.length === 0 && checklist.every((item) => item.satisfied),
|
|
588
677
|
checklist,
|
|
589
678
|
};
|
|
590
679
|
}
|
|
591
680
|
/**
|
|
592
|
-
*
|
|
593
|
-
*
|
|
594
|
-
*
|
|
681
|
+
* Project the provider's own typed account config onto its setup steps. This
|
|
682
|
+
* catches client-held auth state (e.g. Ondo SIWE/JWT) that the backend
|
|
683
|
+
* cannot observe, and carries the current value of each choice.
|
|
595
684
|
*/
|
|
596
|
-
async
|
|
685
|
+
async resolveSetupSettings(provider, address) {
|
|
597
686
|
const metadata = await this.getProviderMetadata(provider);
|
|
598
687
|
const plugin = this.sdkClient.getProvider(provider);
|
|
599
688
|
if (!plugin || typeof plugin.getAccount !== 'function') {
|
|
600
|
-
return
|
|
689
|
+
return [];
|
|
601
690
|
}
|
|
602
691
|
const account = await plugin.getAccount({ address });
|
|
603
|
-
|
|
604
|
-
const setupTypes = new Set(metadata.setup.map((descriptor) => descriptor.type));
|
|
605
|
-
return new Set(settings
|
|
606
|
-
.filter((setting) => setting.satisfied && setupTypes.has(setting.type))
|
|
607
|
-
.map((setting) => setting.type));
|
|
692
|
+
return plugin.projectConfig(account.config, metadata.setup);
|
|
608
693
|
}
|
|
609
694
|
/**
|
|
610
|
-
* Drain the pending
|
|
611
|
-
* `PerpsProviderPlugin.internalSetupActions`. Each step is built, signed,
|
|
695
|
+
* Drain the pending `SDK`-signed non-choice steps: each is built, signed,
|
|
612
696
|
* and executed in place with the provider's own credentials. A step is
|
|
613
697
|
* deferred while any staged user-facing step with a lower `sequence` is
|
|
614
698
|
* outstanding — it cannot succeed before its prerequisite (e.g. SET_REFERRAL
|
|
@@ -617,11 +701,10 @@ export class PerpsClient {
|
|
|
617
701
|
* blocks setup — the step stays unsatisfied and is retried on a later
|
|
618
702
|
* `checkSetup`.
|
|
619
703
|
*/
|
|
620
|
-
async
|
|
621
|
-
const
|
|
622
|
-
for (const descriptor of internalPending) {
|
|
704
|
+
async drainSetup(provider, address, drainable, stagedVisible) {
|
|
705
|
+
for (const descriptor of drainable) {
|
|
623
706
|
// A tie (equal sequences, or both absent) declares no order, so the
|
|
624
|
-
//
|
|
707
|
+
// drained step defers to the next checkSetup rather than racing.
|
|
625
708
|
const blocked = stagedVisible.some((staged) => sequenceOf(staged) <= sequenceOf(descriptor));
|
|
626
709
|
if (blocked) {
|
|
627
710
|
continue;
|
|
@@ -643,7 +726,7 @@ export class PerpsClient {
|
|
|
643
726
|
}
|
|
644
727
|
}
|
|
645
728
|
catch (error) {
|
|
646
|
-
console.debug(`[perps-sdk]
|
|
729
|
+
console.debug(`[perps-sdk] setup step '${descriptor.type}' for '${provider}' did not drain; will retry on the next checkSetup.`, error);
|
|
647
730
|
}
|
|
648
731
|
}
|
|
649
732
|
}
|
|
@@ -680,16 +763,18 @@ export class PerpsClient {
|
|
|
680
763
|
}
|
|
681
764
|
/**
|
|
682
765
|
* Build the unsigned setup `ActionStep`s still outstanding for an account,
|
|
683
|
-
* ordered by descriptor `sequence`.
|
|
684
|
-
*
|
|
685
|
-
*
|
|
766
|
+
* ordered by descriptor `sequence`. Only a `USER`-signed non-choice step is
|
|
767
|
+
* staged: an `SDK`-signed non-choice step drains inside {@link checkSetup}
|
|
768
|
+
* and a choice executes the option the user picks. The backend filters
|
|
769
|
+
* already-satisfied setup; each plugin contributes its own signer-bearing
|
|
770
|
+
* request fields and any local-state params (e.g. Lighter's known pubkey).
|
|
686
771
|
*
|
|
687
772
|
* @public
|
|
688
773
|
*/
|
|
689
774
|
async buildProviderSetup(params) {
|
|
690
775
|
const { provider, address } = params;
|
|
691
776
|
const metadata = await this.getProviderMetadata(provider);
|
|
692
|
-
const actions = await this.buildProviderSetupActions(provider, address, metadata.setup);
|
|
777
|
+
const actions = await this.buildProviderSetupActions(provider, address, metadata.setup.filter(isStageable));
|
|
693
778
|
return { actions };
|
|
694
779
|
}
|
|
695
780
|
/**
|
|
@@ -718,35 +803,33 @@ export class PerpsClient {
|
|
|
718
803
|
});
|
|
719
804
|
const results = this.resolveExplorerLinks(provider, response.results);
|
|
720
805
|
await this.notifyExecuteResults(provider, address, results);
|
|
721
|
-
|
|
722
|
-
if (failure) {
|
|
723
|
-
throw new PerpsError(failure.errorCode ?? PerpsErrorCode.ExchangeRejected, failure.error);
|
|
724
|
-
}
|
|
806
|
+
assertAllSucceeded(results);
|
|
725
807
|
return { results: { results } };
|
|
726
808
|
}
|
|
727
809
|
/**
|
|
728
810
|
* Sign and submit one pre-staged setup `ActionStep` end-to-end.
|
|
729
811
|
*
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
*
|
|
734
|
-
* through submit. If the cached step has gone stale (Lighter's `/nextNonce`
|
|
735
|
-
* advanced underneath us), `executeProviderSetup` will surface a nonce
|
|
736
|
-
* conflict that the caller invalidates on, refetches `checkSetup`, and
|
|
737
|
-
* retries with a fresh step.
|
|
812
|
+
* `step` and `checklist` must come from the same {@link checkSetup} result.
|
|
813
|
+
* This preserves the nonce allocated while staging and validates sequence
|
|
814
|
+
* prerequisites against that same snapshot, without another `createAction`
|
|
815
|
+
* round-trip.
|
|
738
816
|
*
|
|
739
817
|
* @throws {PerpsError} When the step's action is not in the provider's
|
|
740
|
-
* `setup` descriptors
|
|
818
|
+
* `setup` descriptors, or when a lower-`sequence` step on the same
|
|
819
|
+
* checklist, a choice included, is still unsatisfied.
|
|
741
820
|
* @public
|
|
742
821
|
*/
|
|
743
822
|
async executeProviderSetupAction(params) {
|
|
744
|
-
const { provider, address, step } = params;
|
|
823
|
+
const { provider, address, step, checklist } = params;
|
|
745
824
|
const metadata = await this.getProviderMetadata(provider);
|
|
746
825
|
const descriptor = metadata.setup.find((d) => d.type === step.action);
|
|
747
826
|
if (!descriptor) {
|
|
748
827
|
throw new PerpsError(PerpsErrorCode.SDKError, `Action '${step.action}' is not in '${provider}'.setup`);
|
|
749
828
|
}
|
|
829
|
+
const blockingStep = checklist.find((item) => !item.satisfied && sequenceOf(item.descriptor) < sequenceOf(descriptor));
|
|
830
|
+
if (blockingStep) {
|
|
831
|
+
throw new PerpsError(PerpsErrorCode.SDKError, `Setup step '${step.action}' for '${provider}' is blocked: '${blockingStep.descriptor.type}' runs first and is not satisfied.`);
|
|
832
|
+
}
|
|
750
833
|
const signed = await this.signProviderSetupAction(provider, address, step);
|
|
751
834
|
// A client-executed setup action (Lighter SET_REFERRAL) produces no
|
|
752
835
|
// backend-bound step — it already ran during signing, so there is nothing
|
|
@@ -762,22 +845,13 @@ export class PerpsClient {
|
|
|
762
845
|
});
|
|
763
846
|
}
|
|
764
847
|
/**
|
|
765
|
-
*
|
|
766
|
-
*
|
|
767
|
-
*
|
|
848
|
+
* Execute one option of a choice setup step (Lighter `ACCOUNT_TYPE`,
|
|
849
|
+
* Hyperliquid `ACCOUNT_MODE`) end-to-end with its bound `{ type, params }`,
|
|
850
|
+
* throwing on a venue rejection.
|
|
768
851
|
*
|
|
769
|
-
*
|
|
770
|
-
* trades, but unlike a trade an option change is a single mandatory action:
|
|
852
|
+
* The option runs through the same {@link execute} pipeline as a trade, but
|
|
771
853
|
* a per-action `success: false` (returned as a 200 OK) means the user's
|
|
772
|
-
* selection was rejected and must surface, not be silently dropped.
|
|
773
|
-
* wrapper inspects the result and throws a {@link PerpsError} carrying the
|
|
774
|
-
* venue `error`, giving options the same throw contract that setup has via
|
|
775
|
-
* {@link executeProviderSetupAction}. The only structural difference is that
|
|
776
|
-
* an option carries `params` (the selected value) rather than a pre-staged
|
|
777
|
-
* step.
|
|
778
|
-
*
|
|
779
|
-
* `execute` itself is unchanged — it still returns results without throwing,
|
|
780
|
-
* which the trade hooks rely on for partial-fill handling.
|
|
854
|
+
* selection was rejected and must surface, not be silently dropped.
|
|
781
855
|
*
|
|
782
856
|
* @throws {PerpsError} Carrying the venue error when any returned result has
|
|
783
857
|
* `success: false`, under that result's `errorCode` when the backend
|
|
@@ -787,11 +861,40 @@ export class PerpsClient {
|
|
|
787
861
|
* @public
|
|
788
862
|
*/
|
|
789
863
|
async executeProviderOption(params) {
|
|
790
|
-
const {
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
864
|
+
const { provider, address, option } = params;
|
|
865
|
+
const { results } = await this.execute({
|
|
866
|
+
provider,
|
|
867
|
+
address,
|
|
868
|
+
action: option.type,
|
|
869
|
+
params: option.params,
|
|
870
|
+
});
|
|
871
|
+
assertAllSucceeded(results);
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* Undo a satisfied setup step by executing its `revoke` action through
|
|
875
|
+
* create, sign and execute, signed with that action's `Provider.actions`
|
|
876
|
+
* descriptor. The step stages again on the next {@link checkSetup}.
|
|
877
|
+
*
|
|
878
|
+
* @throws {PerpsError} `SDKError` when `step.revoke` is `null` or when
|
|
879
|
+
* `createAction` stages nothing to revoke; otherwise the errors
|
|
880
|
+
* {@link executeProviderOption} throws.
|
|
881
|
+
* @public
|
|
882
|
+
*/
|
|
883
|
+
async executeProviderRevoke(params) {
|
|
884
|
+
const { provider, address, step } = params;
|
|
885
|
+
if (step.revoke === null) {
|
|
886
|
+
throw new PerpsError(PerpsErrorCode.SDKError, `Setup step '${step.type}' for '${provider}' declares no revoke.`);
|
|
887
|
+
}
|
|
888
|
+
const { staged, results } = await this.runAction({
|
|
889
|
+
provider,
|
|
890
|
+
address,
|
|
891
|
+
action: step.revoke,
|
|
892
|
+
params: {},
|
|
893
|
+
});
|
|
894
|
+
if (staged.length === 0) {
|
|
895
|
+
throw new PerpsError(PerpsErrorCode.SDKError, `Nothing to revoke for setup step '${step.type}' on '${provider}'.`);
|
|
794
896
|
}
|
|
897
|
+
assertAllSucceeded(results);
|
|
795
898
|
}
|
|
796
899
|
/**
|
|
797
900
|
* Place a market or limit order. Convenience wrapper over {@link execute}
|
|
@@ -936,6 +1039,11 @@ export class PerpsClient {
|
|
|
936
1039
|
* @public
|
|
937
1040
|
*/
|
|
938
1041
|
async execute(params) {
|
|
1042
|
+
const { results } = await this.runAction(params);
|
|
1043
|
+
return { results };
|
|
1044
|
+
}
|
|
1045
|
+
/** {@link execute}, also returning the steps `createAction` staged. */
|
|
1046
|
+
async runAction(params) {
|
|
939
1047
|
const { provider, address, action, onProgress } = params;
|
|
940
1048
|
const metadata = await this.getProviderMetadata(provider);
|
|
941
1049
|
const descriptor = findActionDescriptor(metadata, action);
|
|
@@ -955,7 +1063,7 @@ export class PerpsClient {
|
|
|
955
1063
|
// token-authenticated venue mutations), leaving no backend-bound step. With
|
|
956
1064
|
// nothing to submit, skip the `/executeAction` hop.
|
|
957
1065
|
if (signedActions.length === 0) {
|
|
958
|
-
return { results: [] };
|
|
1066
|
+
return { staged: actions, results: [] };
|
|
959
1067
|
}
|
|
960
1068
|
const response = await executeAction(this.sdkClient, {
|
|
961
1069
|
provider,
|
|
@@ -968,7 +1076,7 @@ export class PerpsClient {
|
|
|
968
1076
|
});
|
|
969
1077
|
const results = this.resolveExplorerLinks(provider, response.results);
|
|
970
1078
|
await this.notifyExecuteResults(provider, address, results);
|
|
971
|
-
return { results };
|
|
1079
|
+
return { staged: actions, results };
|
|
972
1080
|
}
|
|
973
1081
|
/**
|
|
974
1082
|
* Accept the current terms and, when a code is supplied and the backend
|