@lifi/perps-sdk 25.0.2 → 26.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 +54 -1
- package/dist/cjs/client/PerpsClient.d.ts +5 -5
- package/dist/cjs/client/PerpsClient.d.ts.map +1 -1
- package/dist/cjs/client/PerpsClient.js +75 -74
- package/dist/cjs/client/PerpsClient.js.map +1 -1
- package/dist/cjs/errors/PerpsExecutionError.d.ts +9 -0
- package/dist/cjs/errors/PerpsExecutionError.d.ts.map +1 -0
- package/dist/cjs/errors/PerpsExecutionError.js +29 -0
- package/dist/cjs/errors/PerpsExecutionError.js.map +1 -0
- package/dist/cjs/index.d.ts +5 -4
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +14 -12
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/math/fills.d.ts +0 -1
- package/dist/cjs/math/fills.d.ts.map +1 -1
- package/dist/cjs/math/fills.js +0 -18
- package/dist/cjs/math/fills.js.map +1 -1
- package/dist/cjs/math/order.d.ts +5 -5
- package/dist/cjs/math/order.d.ts.map +1 -1
- package/dist/cjs/math/order.js +10 -10
- package/dist/cjs/math/order.js.map +1 -1
- package/dist/cjs/math/position.d.ts +2 -2
- package/dist/cjs/math/position.d.ts.map +1 -1
- package/dist/cjs/math/position.js +5 -5
- package/dist/cjs/math/position.js.map +1 -1
- package/dist/cjs/types/api.d.ts +1 -10
- package/dist/cjs/types/api.d.ts.map +1 -1
- package/dist/cjs/types/provider.d.ts +5 -1
- package/dist/cjs/types/provider.d.ts.map +1 -1
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client/PerpsClient.d.ts +63 -47
- package/dist/esm/client/PerpsClient.d.ts.map +1 -1
- package/dist/esm/client/PerpsClient.js +136 -128
- package/dist/esm/client/PerpsClient.js.map +1 -1
- package/dist/esm/errors/PerpsExecutionError.d.ts +24 -0
- package/dist/esm/errors/PerpsExecutionError.d.ts.map +1 -0
- package/dist/esm/errors/PerpsExecutionError.js +39 -0
- package/dist/esm/errors/PerpsExecutionError.js.map +1 -0
- package/dist/esm/index.d.ts +5 -4
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/math/fills.d.ts +0 -9
- package/dist/esm/math/fills.d.ts.map +1 -1
- package/dist/esm/math/fills.js +0 -25
- package/dist/esm/math/fills.js.map +1 -1
- package/dist/esm/math/order.d.ts +6 -6
- package/dist/esm/math/order.d.ts.map +1 -1
- package/dist/esm/math/order.js +6 -6
- package/dist/esm/math/order.js.map +1 -1
- package/dist/esm/math/position.d.ts +2 -2
- package/dist/esm/math/position.d.ts.map +1 -1
- package/dist/esm/math/position.js +3 -3
- package/dist/esm/math/position.js.map +1 -1
- package/dist/esm/types/api.d.ts +1 -18
- package/dist/esm/types/api.d.ts.map +1 -1
- package/dist/esm/types/provider.d.ts +21 -4
- package/dist/esm/types/provider.d.ts.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/types/client/PerpsClient.d.ts +63 -47
- package/dist/types/client/PerpsClient.d.ts.map +1 -1
- package/dist/types/errors/PerpsExecutionError.d.ts +24 -0
- package/dist/types/errors/PerpsExecutionError.d.ts.map +1 -0
- package/dist/types/index.d.ts +5 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/math/fills.d.ts +0 -9
- package/dist/types/math/fills.d.ts.map +1 -1
- package/dist/types/math/order.d.ts +6 -6
- package/dist/types/math/order.d.ts.map +1 -1
- package/dist/types/math/position.d.ts +2 -2
- package/dist/types/math/position.d.ts.map +1 -1
- package/dist/types/types/api.d.ts +1 -18
- package/dist/types/types/api.d.ts.map +1 -1
- package/dist/types/types/provider.d.ts +21 -4
- package/dist/types/types/provider.d.ts.map +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +2 -2
- package/src/client/PerpsClient.ts +167 -164
- package/src/errors/PerpsExecutionError.ts +54 -0
- package/src/index.ts +12 -8
- package/src/math/fills.ts +0 -29
- package/src/math/order.ts +11 -11
- package/src/math/position.ts +3 -3
- package/src/types/api.ts +0 -21
- package/src/types/provider.ts +22 -4
- package/src/version.ts +1 -1
|
@@ -30,11 +30,13 @@ import {
|
|
|
30
30
|
PerpsErrorCode,
|
|
31
31
|
PerpsSigner,
|
|
32
32
|
SigningMethod,
|
|
33
|
+
UserActionStatus,
|
|
33
34
|
} from '@lifi/perps-types'
|
|
34
35
|
import type { Address } from 'viem'
|
|
35
36
|
import { isDecimalStringGreaterThan } from '../decimal/compare.js'
|
|
36
37
|
import { unknownToDecimalString } from '../decimal/parse.js'
|
|
37
38
|
import { PerpsError } from '../errors/PerpsError.js'
|
|
39
|
+
import { rethrowWithKnownResults } from '../errors/PerpsExecutionError.js'
|
|
38
40
|
import { getAssetRegistry, toAssetDisplay } from '../registry/assetRegistry.js'
|
|
39
41
|
import { getMarketRegistry } from '../registry/marketRegistry.js'
|
|
40
42
|
import { createAction } from '../services/createAction.js'
|
|
@@ -55,8 +57,6 @@ import type {
|
|
|
55
57
|
CancelTwapOrderParams,
|
|
56
58
|
CreateReferralCodeActionParams,
|
|
57
59
|
ExecuteMetaActionParams,
|
|
58
|
-
ExecuteProviderSetupParams,
|
|
59
|
-
ExecuteProviderSetupResult,
|
|
60
60
|
GetAccountResult,
|
|
61
61
|
GetDepositFlowParams,
|
|
62
62
|
GetPortfolioHistoryParams,
|
|
@@ -87,6 +87,7 @@ import type {
|
|
|
87
87
|
PerpsSDKClient,
|
|
88
88
|
SignActionProgress,
|
|
89
89
|
SignActionsContext,
|
|
90
|
+
SignActionsResult,
|
|
90
91
|
} from '../types/provider.js'
|
|
91
92
|
import type {
|
|
92
93
|
WithdrawableBalance,
|
|
@@ -213,15 +214,11 @@ function currentChoiceValue(
|
|
|
213
214
|
return value === null ? null : String(value)
|
|
214
215
|
}
|
|
215
216
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
failure.errorCode ?? PerpsErrorCode.ExchangeRejected,
|
|
222
|
-
failure.error
|
|
223
|
-
)
|
|
224
|
-
}
|
|
217
|
+
function isAcknowledged(result: ActionResult): boolean {
|
|
218
|
+
return (
|
|
219
|
+
result.status === UserActionStatus.Submitted ||
|
|
220
|
+
result.status === UserActionStatus.Accepted
|
|
221
|
+
)
|
|
225
222
|
}
|
|
226
223
|
|
|
227
224
|
function isNonNegativeDecimal(value: string): boolean {
|
|
@@ -451,10 +448,13 @@ export class PerpsClient {
|
|
|
451
448
|
* step with the signer its own descriptor declares; the signed steps come
|
|
452
449
|
* back in the batch's original order. The plugin owns every signing arm and
|
|
453
450
|
* branches on `signer` internally, reading the end-user's wallet from the
|
|
454
|
-
* {@link SignActionsContext} when an arm signs as the user.
|
|
451
|
+
* {@link SignActionsContext} when an arm signs as the user. Signing stops
|
|
452
|
+
* after the first group that executed a step without an acknowledgement.
|
|
455
453
|
*
|
|
456
454
|
* @throws {PerpsError} When a step's action is not declared by the provider,
|
|
457
455
|
* or the plugin implements no `signActions`.
|
|
456
|
+
* @throws {PerpsExecutionError} When a group throws after an earlier group
|
|
457
|
+
* executed steps.
|
|
458
458
|
*/
|
|
459
459
|
private async delegateSignActions(
|
|
460
460
|
provider: string,
|
|
@@ -462,36 +462,90 @@ export class PerpsClient {
|
|
|
462
462
|
metadata: Provider,
|
|
463
463
|
actions: ActionStep[],
|
|
464
464
|
onProgress?: (progress: SignActionProgress) => void
|
|
465
|
-
): Promise<
|
|
465
|
+
): Promise<SignActionsResult> {
|
|
466
466
|
const plugin = this.requireProvider(provider)
|
|
467
|
-
|
|
467
|
+
const signActions = plugin.signActions?.bind(plugin)
|
|
468
|
+
if (signActions === undefined) {
|
|
468
469
|
throw new PerpsError(
|
|
469
470
|
PerpsErrorCode.SDKError,
|
|
470
471
|
`Provider '${provider}' does not implement signActions.`
|
|
471
472
|
)
|
|
472
473
|
}
|
|
473
474
|
const signed: SignedActionStep[] = []
|
|
475
|
+
const executed: ActionResult[] = []
|
|
474
476
|
for (const group of groupStepsByDescriptor(metadata, actions)) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
477
|
+
try {
|
|
478
|
+
const userWallet = await this.resolveSigningWallet(
|
|
479
|
+
group.descriptor,
|
|
480
|
+
group.steps
|
|
481
|
+
)
|
|
482
|
+
const result = await signActions(
|
|
481
483
|
group.descriptor.signingMethod,
|
|
482
484
|
group.steps,
|
|
483
485
|
address,
|
|
484
486
|
this.buildSignActionsContext(group.descriptor, userWallet, onProgress)
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
+
)
|
|
488
|
+
signed.push(...result.signed)
|
|
489
|
+
executed.push(...result.executed)
|
|
490
|
+
} catch (error) {
|
|
491
|
+
rethrowWithKnownResults(executed, error)
|
|
492
|
+
}
|
|
493
|
+
if (!executed.every(isAcknowledged)) {
|
|
494
|
+
break
|
|
495
|
+
}
|
|
487
496
|
}
|
|
488
|
-
return signed
|
|
497
|
+
return { signed, executed }
|
|
489
498
|
}
|
|
490
499
|
|
|
491
500
|
/**
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
501
|
+
* Submit `signed` to `/executeAction` when every step the plugin executed
|
|
502
|
+
* while signing was acknowledged, then resolve explorer links and notify the
|
|
503
|
+
* plugin. Resolves to the full report: `executed`, then the `/executeAction`
|
|
504
|
+
* results. An empty report means nothing executed.
|
|
505
|
+
*
|
|
506
|
+
* @throws {PerpsExecutionError} When the submission or the plugin callback
|
|
507
|
+
* throws after a result is known.
|
|
508
|
+
*/
|
|
509
|
+
private async submitSignedActions(
|
|
510
|
+
provider: string,
|
|
511
|
+
address: Address,
|
|
512
|
+
action: ActionType,
|
|
513
|
+
signerAddress: Address | undefined,
|
|
514
|
+
{ signed, executed }: SignActionsResult
|
|
515
|
+
): Promise<ActionResult[]> {
|
|
516
|
+
let results = executed
|
|
517
|
+
if (signed.length > 0 && executed.every(isAcknowledged)) {
|
|
518
|
+
try {
|
|
519
|
+
const response = await executeAction(this.sdkClient, {
|
|
520
|
+
provider,
|
|
521
|
+
address,
|
|
522
|
+
// The submitting account: the plugin-resolved signer (Hyperliquid's
|
|
523
|
+
// agent) when present, else the end-user's address.
|
|
524
|
+
signerAddress: signerAddress ?? address,
|
|
525
|
+
action,
|
|
526
|
+
actions: signed,
|
|
527
|
+
})
|
|
528
|
+
results = [...executed, ...response.results]
|
|
529
|
+
} catch (error) {
|
|
530
|
+
rethrowWithKnownResults(executed, error)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (results.length === 0) {
|
|
534
|
+
return results
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
try {
|
|
538
|
+
results = this.resolveExplorerLinks(provider, results)
|
|
539
|
+
await this.notifyExecuteResults(provider, address, results)
|
|
540
|
+
} catch (error) {
|
|
541
|
+
rethrowWithKnownResults(results, error)
|
|
542
|
+
}
|
|
543
|
+
return results
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Let the plugin observe an execution report before the core returns it —
|
|
548
|
+
* e.g. to evict a locally stored credential the venue refused.
|
|
495
549
|
*/
|
|
496
550
|
private async notifyExecuteResults(
|
|
497
551
|
provider: string,
|
|
@@ -505,9 +559,10 @@ export class PerpsClient {
|
|
|
505
559
|
}
|
|
506
560
|
|
|
507
561
|
/**
|
|
508
|
-
* Attach a provider-built explorer URL to every result
|
|
509
|
-
*
|
|
510
|
-
*
|
|
562
|
+
* Attach a provider-built explorer URL to every result that carries a venue
|
|
563
|
+
* `txHash`, whatever its status: an unacknowledged hash is what the user
|
|
564
|
+
* inspects. The provider owns its explorer target, so core only asks — a
|
|
565
|
+
* plugin without the hook leaves results untouched.
|
|
511
566
|
*/
|
|
512
567
|
private resolveExplorerLinks(
|
|
513
568
|
provider: string,
|
|
@@ -519,7 +574,7 @@ export class PerpsClient {
|
|
|
519
574
|
return results
|
|
520
575
|
}
|
|
521
576
|
return results.map((result) => {
|
|
522
|
-
if (
|
|
577
|
+
if (result.txHash === undefined) {
|
|
523
578
|
return result
|
|
524
579
|
}
|
|
525
580
|
const explorerLink = resolveLink(result.txHash)
|
|
@@ -610,30 +665,47 @@ export class PerpsClient {
|
|
|
610
665
|
}
|
|
611
666
|
|
|
612
667
|
/**
|
|
613
|
-
* Sign
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
* Returns `undefined` when the plugin executed the action entirely
|
|
619
|
-
* client-side (e.g. Lighter's token-authenticated venue mutations), leaving
|
|
620
|
-
* no backend-bound step to submit.
|
|
668
|
+
* Sign one provider setup step through the provider plugin and submit it.
|
|
669
|
+
* A step the plugin executed client-side (e.g. Lighter's token-authenticated
|
|
670
|
+
* venue mutations) reports the plugin's own result instead of an
|
|
671
|
+
* `/executeAction` result.
|
|
621
672
|
*
|
|
622
|
-
* @throws {PerpsError} When the step's action is not declared by the provider
|
|
673
|
+
* @throws {PerpsError} When the step's action is not declared by the provider,
|
|
674
|
+
* or signing or submission fails before any result is known.
|
|
675
|
+
* @throws {PerpsExecutionError} When a later stage fails after a result is
|
|
676
|
+
* known.
|
|
623
677
|
*/
|
|
624
|
-
private async
|
|
678
|
+
private async runProviderSetupStep(
|
|
625
679
|
provider: string,
|
|
626
680
|
address: Address,
|
|
627
681
|
step: ActionStep
|
|
628
|
-
): Promise<
|
|
682
|
+
): Promise<ActionResult[]> {
|
|
629
683
|
const metadata = await this.getProviderMetadata(provider)
|
|
630
|
-
const
|
|
684
|
+
const descriptor = findActionDescriptor(metadata, step.action)
|
|
685
|
+
const signResult = await this.delegateSignActions(
|
|
631
686
|
provider,
|
|
632
687
|
address,
|
|
633
688
|
metadata,
|
|
634
689
|
[step]
|
|
635
690
|
)
|
|
636
|
-
|
|
691
|
+
let signerAddress: Address | undefined
|
|
692
|
+
try {
|
|
693
|
+
const request = await this.resolveActionRequest(
|
|
694
|
+
provider,
|
|
695
|
+
descriptor,
|
|
696
|
+
address
|
|
697
|
+
)
|
|
698
|
+
signerAddress = request.signerAddress
|
|
699
|
+
} catch (error) {
|
|
700
|
+
rethrowWithKnownResults(signResult.executed, error)
|
|
701
|
+
}
|
|
702
|
+
return this.submitSignedActions(
|
|
703
|
+
provider,
|
|
704
|
+
address,
|
|
705
|
+
step.action,
|
|
706
|
+
signerAddress,
|
|
707
|
+
signResult
|
|
708
|
+
)
|
|
637
709
|
}
|
|
638
710
|
|
|
639
711
|
/**
|
|
@@ -1142,9 +1214,9 @@ export class PerpsClient {
|
|
|
1142
1214
|
* deferred while any staged user-facing step with a lower `sequence` is
|
|
1143
1215
|
* outstanding — it cannot succeed before its prerequisite (e.g. SET_REFERRAL
|
|
1144
1216
|
* authenticates with the credential REGISTER_API_KEY installs) and each
|
|
1145
|
-
* doomed attempt is venue traffic. A drain failure
|
|
1146
|
-
* blocks setup — the step stays unsatisfied and is
|
|
1147
|
-
* `checkSetup`.
|
|
1217
|
+
* doomed attempt is venue traffic. A drain failure, thrown or reported, is
|
|
1218
|
+
* swallowed so it never blocks setup — the step stays unsatisfied and is
|
|
1219
|
+
* retried on a later `checkSetup`.
|
|
1148
1220
|
*/
|
|
1149
1221
|
private async drainSetup(
|
|
1150
1222
|
provider: string,
|
|
@@ -1166,18 +1238,18 @@ export class PerpsClient {
|
|
|
1166
1238
|
descriptor,
|
|
1167
1239
|
])
|
|
1168
1240
|
for (const step of steps) {
|
|
1169
|
-
const
|
|
1241
|
+
const results = await this.runProviderSetupStep(
|
|
1170
1242
|
provider,
|
|
1171
1243
|
address,
|
|
1172
1244
|
step
|
|
1173
1245
|
)
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1246
|
+
const failure = results.find((result) => !isAcknowledged(result))
|
|
1247
|
+
if (failure !== undefined) {
|
|
1248
|
+
console.debug(
|
|
1249
|
+
`[perps-sdk] setup step '${descriptor.type}' for '${provider}' reported '${failure.status}'; will retry on the next checkSetup.`,
|
|
1250
|
+
failure
|
|
1251
|
+
)
|
|
1252
|
+
break
|
|
1181
1253
|
}
|
|
1182
1254
|
}
|
|
1183
1255
|
} catch (error) {
|
|
@@ -1256,49 +1328,6 @@ export class PerpsClient {
|
|
|
1256
1328
|
return { actions }
|
|
1257
1329
|
}
|
|
1258
1330
|
|
|
1259
|
-
/**
|
|
1260
|
-
* Submit the signed setup steps returned by `checkSetup` (and signed by the
|
|
1261
|
-
* caller / `signProviderSetupAction`). Routes the batch on the first step's
|
|
1262
|
-
* action and lets the plugin contribute that action's `signerAddress`.
|
|
1263
|
-
* Throws on any per-step venue rejection.
|
|
1264
|
-
*/
|
|
1265
|
-
private async executeProviderSetup(
|
|
1266
|
-
params: ExecuteProviderSetupParams
|
|
1267
|
-
): Promise<ExecuteProviderSetupResult> {
|
|
1268
|
-
const { provider, address, setup, signedActions } = params
|
|
1269
|
-
|
|
1270
|
-
if (signedActions.length === 0) {
|
|
1271
|
-
return { results: { results: [] } }
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
const action = setup[0]?.action ?? signedActions[0].action
|
|
1275
|
-
const metadata = await this.getProviderMetadata(provider)
|
|
1276
|
-
const descriptor = findActionDescriptor(metadata, action)
|
|
1277
|
-
const { signerAddress } = await this.resolveActionRequest(
|
|
1278
|
-
provider,
|
|
1279
|
-
descriptor,
|
|
1280
|
-
address
|
|
1281
|
-
)
|
|
1282
|
-
|
|
1283
|
-
const response = await executeAction(this.sdkClient, {
|
|
1284
|
-
provider,
|
|
1285
|
-
address,
|
|
1286
|
-
// The submitting account: the plugin-resolved signer (Hyperliquid's
|
|
1287
|
-
// agent) when present, else the end-user's address.
|
|
1288
|
-
signerAddress: signerAddress ?? address,
|
|
1289
|
-
action,
|
|
1290
|
-
actions: signedActions,
|
|
1291
|
-
})
|
|
1292
|
-
|
|
1293
|
-
const results = this.resolveExplorerLinks(provider, response.results)
|
|
1294
|
-
|
|
1295
|
-
await this.notifyExecuteResults(provider, address, results)
|
|
1296
|
-
|
|
1297
|
-
assertAllSucceeded(results)
|
|
1298
|
-
|
|
1299
|
-
return { results: { results } }
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
1331
|
/**
|
|
1303
1332
|
* Sign and submit one pre-staged setup `ActionStep` end-to-end.
|
|
1304
1333
|
*
|
|
@@ -1307,9 +1336,15 @@ export class PerpsClient {
|
|
|
1307
1336
|
* prerequisites against that same snapshot, without another `createAction`
|
|
1308
1337
|
* round-trip.
|
|
1309
1338
|
*
|
|
1339
|
+
* Resolves to the step's execution report. A refused or indeterminate step
|
|
1340
|
+
* is a result in the report, not an exception.
|
|
1341
|
+
*
|
|
1310
1342
|
* @throws {PerpsError} When the step's action is not in the provider's
|
|
1311
|
-
* `setup` descriptors,
|
|
1312
|
-
* checklist, a choice included, is still unsatisfied
|
|
1343
|
+
* `setup` descriptors, when a lower-`sequence` step on the same
|
|
1344
|
+
* checklist, a choice included, is still unsatisfied, or when signing or
|
|
1345
|
+
* submission fails before any result is known.
|
|
1346
|
+
* @throws {PerpsExecutionError} When a later stage fails after a result is
|
|
1347
|
+
* known; its `response` holds the known results.
|
|
1313
1348
|
* @public
|
|
1314
1349
|
*/
|
|
1315
1350
|
async executeProviderSetupAction(params: {
|
|
@@ -1317,7 +1352,7 @@ export class PerpsClient {
|
|
|
1317
1352
|
address: Address
|
|
1318
1353
|
step: ActionStep
|
|
1319
1354
|
checklist: SetupChecklistItem[]
|
|
1320
|
-
}): Promise<
|
|
1355
|
+
}): Promise<ExecuteActionResponse> {
|
|
1321
1356
|
const { provider, address, step, checklist } = params
|
|
1322
1357
|
|
|
1323
1358
|
const metadata = await this.getProviderMetadata(provider)
|
|
@@ -1340,52 +1375,34 @@ export class PerpsClient {
|
|
|
1340
1375
|
)
|
|
1341
1376
|
}
|
|
1342
1377
|
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
// A client-executed setup action (Lighter SET_REFERRAL) produces no
|
|
1346
|
-
// backend-bound step — it already ran during signing, so there is nothing
|
|
1347
|
-
// to submit.
|
|
1348
|
-
if (signed === undefined) {
|
|
1349
|
-
return
|
|
1378
|
+
return {
|
|
1379
|
+
results: await this.runProviderSetupStep(provider, address, step),
|
|
1350
1380
|
}
|
|
1351
|
-
|
|
1352
|
-
await this.executeProviderSetup({
|
|
1353
|
-
provider,
|
|
1354
|
-
address,
|
|
1355
|
-
setup: [step],
|
|
1356
|
-
signedActions: [signed],
|
|
1357
|
-
})
|
|
1358
1381
|
}
|
|
1359
1382
|
|
|
1360
1383
|
/**
|
|
1361
1384
|
* Execute one option of a choice setup step (Lighter `ACCOUNT_TYPE`,
|
|
1362
1385
|
* Hyperliquid `ACCOUNT_MODE`) end-to-end with its bound `{ type, params }`,
|
|
1363
|
-
*
|
|
1386
|
+
* through the same {@link execute} pipeline as a trade.
|
|
1364
1387
|
*
|
|
1365
|
-
*
|
|
1366
|
-
*
|
|
1367
|
-
* selection was rejected and must surface, not be silently dropped.
|
|
1388
|
+
* Resolves to the execution report. Read each result's `status`: a refused
|
|
1389
|
+
* selection is a `Failed` result, not an exception.
|
|
1368
1390
|
*
|
|
1369
|
-
* @throws {PerpsError}
|
|
1370
|
-
* `success: false`, under that result's `errorCode` when the backend
|
|
1371
|
-
* classified the failure and `PerpsErrorCode.ExchangeRejected` otherwise;
|
|
1372
|
-
* also the errors `execute` itself can throw (unregistered provider, no
|
|
1373
|
-
* signer, signing failure).
|
|
1391
|
+
* @throws {PerpsError} The errors {@link execute} throws.
|
|
1374
1392
|
* @public
|
|
1375
1393
|
*/
|
|
1376
1394
|
async executeProviderOption(params: {
|
|
1377
1395
|
provider: string
|
|
1378
1396
|
address: Address
|
|
1379
1397
|
option: SetupOption
|
|
1380
|
-
}): Promise<
|
|
1398
|
+
}): Promise<ExecuteActionResponse> {
|
|
1381
1399
|
const { provider, address, option } = params
|
|
1382
|
-
|
|
1400
|
+
return this.execute({
|
|
1383
1401
|
provider,
|
|
1384
1402
|
address,
|
|
1385
1403
|
action: option.type,
|
|
1386
1404
|
params: option.params,
|
|
1387
1405
|
})
|
|
1388
|
-
assertAllSucceeded(results)
|
|
1389
1406
|
}
|
|
1390
1407
|
|
|
1391
1408
|
/**
|
|
@@ -1393,16 +1410,18 @@ export class PerpsClient {
|
|
|
1393
1410
|
* create, sign and execute, signed with that action's `Provider.actions`
|
|
1394
1411
|
* descriptor. The step stages again on the next {@link checkSetup}.
|
|
1395
1412
|
*
|
|
1413
|
+
* Resolves to the execution report.
|
|
1414
|
+
*
|
|
1396
1415
|
* @throws {PerpsError} `SDKError` when `step.revoke` is `null` or when
|
|
1397
1416
|
* `createAction` stages nothing to revoke; otherwise the errors
|
|
1398
|
-
* {@link
|
|
1417
|
+
* {@link execute} throws.
|
|
1399
1418
|
* @public
|
|
1400
1419
|
*/
|
|
1401
1420
|
async executeProviderRevoke(params: {
|
|
1402
1421
|
provider: string
|
|
1403
1422
|
address: Address
|
|
1404
1423
|
step: SetupAction
|
|
1405
|
-
}): Promise<
|
|
1424
|
+
}): Promise<ExecuteActionResponse> {
|
|
1406
1425
|
const { provider, address, step } = params
|
|
1407
1426
|
if (step.revoke === null) {
|
|
1408
1427
|
throw new PerpsError(
|
|
@@ -1422,7 +1441,7 @@ export class PerpsClient {
|
|
|
1422
1441
|
`Nothing to revoke for setup step '${step.type}' on '${provider}'.`
|
|
1423
1442
|
)
|
|
1424
1443
|
}
|
|
1425
|
-
|
|
1444
|
+
return { results }
|
|
1426
1445
|
}
|
|
1427
1446
|
|
|
1428
1447
|
/**
|
|
@@ -1590,8 +1609,15 @@ export class PerpsClient {
|
|
|
1590
1609
|
* descriptor's scheme and signer internally), and submit. Core stays
|
|
1591
1610
|
* signer-agnostic — the plugin owns WHO signs and HOW.
|
|
1592
1611
|
*
|
|
1612
|
+
* Resolves to the execution report: one result per executed step, in
|
|
1613
|
+
* execution order, each with its own `status`. An empty report means
|
|
1614
|
+
* nothing executed.
|
|
1615
|
+
*
|
|
1593
1616
|
* @throws {PerpsError} When the action is not declared by the provider,
|
|
1594
|
-
* the plugin cannot sign it, or submission fails
|
|
1617
|
+
* the plugin cannot sign it, or submission fails before any result is
|
|
1618
|
+
* known.
|
|
1619
|
+
* @throws {PerpsExecutionError} When a later stage fails after a result is
|
|
1620
|
+
* known; its `response` holds the known results.
|
|
1595
1621
|
* @public
|
|
1596
1622
|
*/
|
|
1597
1623
|
async execute<T extends ActionType>(params: {
|
|
@@ -1633,7 +1659,7 @@ export class PerpsClient {
|
|
|
1633
1659
|
} as ActionParamsMap[T],
|
|
1634
1660
|
})
|
|
1635
1661
|
|
|
1636
|
-
const
|
|
1662
|
+
const signResult = await this.delegateSignActions(
|
|
1637
1663
|
provider,
|
|
1638
1664
|
address,
|
|
1639
1665
|
metadata,
|
|
@@ -1641,27 +1667,13 @@ export class PerpsClient {
|
|
|
1641
1667
|
onProgress
|
|
1642
1668
|
)
|
|
1643
1669
|
|
|
1644
|
-
|
|
1645
|
-
// token-authenticated venue mutations), leaving no backend-bound step. With
|
|
1646
|
-
// nothing to submit, skip the `/executeAction` hop.
|
|
1647
|
-
if (signedActions.length === 0) {
|
|
1648
|
-
return { staged: actions, results: [] }
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
const response = await executeAction(this.sdkClient, {
|
|
1670
|
+
const results = await this.submitSignedActions(
|
|
1652
1671
|
provider,
|
|
1653
1672
|
address,
|
|
1654
|
-
// The submitting account: the plugin-resolved signer (Hyperliquid's
|
|
1655
|
-
// agent) when present, else the end-user's address.
|
|
1656
|
-
signerAddress: signerAddress ?? address,
|
|
1657
1673
|
action,
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
const results = this.resolveExplorerLinks(provider, response.results)
|
|
1662
|
-
|
|
1663
|
-
await this.notifyExecuteResults(provider, address, results)
|
|
1664
|
-
|
|
1674
|
+
signerAddress,
|
|
1675
|
+
signResult
|
|
1676
|
+
)
|
|
1665
1677
|
return { staged: actions, results }
|
|
1666
1678
|
}
|
|
1667
1679
|
|
|
@@ -1717,12 +1729,13 @@ export class PerpsClient {
|
|
|
1717
1729
|
* typed data with the configured user wallet.
|
|
1718
1730
|
*
|
|
1719
1731
|
* The backend returns at most one step. Resolves to `{ results: [] }` when it
|
|
1720
|
-
* returns none, meaning no consent is outstanding for this address.
|
|
1732
|
+
* returns none, meaning no consent is outstanding for this address. Else it
|
|
1733
|
+
* resolves to the backend's execution report; read each result's `status`.
|
|
1721
1734
|
*
|
|
1722
1735
|
* @throws {PerpsError} When no user wallet is configured, the backend returns
|
|
1723
|
-
* more than one step or a non-EIP-712 step, or the
|
|
1724
|
-
*
|
|
1725
|
-
*
|
|
1736
|
+
* more than one step or a non-EIP-712 step, or the submission fails. A
|
|
1737
|
+
* signature refusal from the wallet client propagates as the wallet's own
|
|
1738
|
+
* error.
|
|
1726
1739
|
* @public
|
|
1727
1740
|
*/
|
|
1728
1741
|
async executeMetaAction<T extends MetaActionType>(
|
|
@@ -1757,21 +1770,11 @@ export class PerpsClient {
|
|
|
1757
1770
|
const wallet = await this.resolveMetaSigningWallet(actions)
|
|
1758
1771
|
const signature = await signTypedDataWithSigner(wallet, step.typedData)
|
|
1759
1772
|
|
|
1760
|
-
|
|
1773
|
+
return executeAction(this.sdkClient, {
|
|
1761
1774
|
provider: META_PROVIDER,
|
|
1762
1775
|
address,
|
|
1763
1776
|
action,
|
|
1764
1777
|
actions: [{ ...step, signature }],
|
|
1765
1778
|
})
|
|
1766
|
-
|
|
1767
|
-
const failure = response.results.find((r) => !r.success)
|
|
1768
|
-
if (failure) {
|
|
1769
|
-
throw new PerpsError(
|
|
1770
|
-
failure.errorCode ?? PerpsErrorCode.ExchangeRejected,
|
|
1771
|
-
failure.error
|
|
1772
|
-
)
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
return response
|
|
1776
1779
|
}
|
|
1777
1780
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ActionResult,
|
|
3
|
+
type ExecuteActionResponse,
|
|
4
|
+
PerpsErrorCode,
|
|
5
|
+
} from '@lifi/perps-types'
|
|
6
|
+
import { PerpsError } from './PerpsError.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when an execution stage fails after part of the execution
|
|
10
|
+
* report is known. `response` holds every result known at the failure, and
|
|
11
|
+
* `cause` holds the original error. Its `code` is the cause's code when the
|
|
12
|
+
* cause is a {@link PerpsError}, else `SDKError`.
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export class PerpsExecutionError extends PerpsError {
|
|
17
|
+
override name = 'PerpsExecutionError'
|
|
18
|
+
readonly response: ExecuteActionResponse
|
|
19
|
+
|
|
20
|
+
constructor(response: ExecuteActionResponse, cause: unknown) {
|
|
21
|
+
super(
|
|
22
|
+
cause instanceof PerpsError ? cause.code : PerpsErrorCode.SDKError,
|
|
23
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
24
|
+
)
|
|
25
|
+
this.response = response
|
|
26
|
+
this.cause = cause
|
|
27
|
+
if (cause instanceof PerpsError && cause.tool !== undefined) {
|
|
28
|
+
this.tool = cause.tool
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Rethrow `error` unchanged when `known` is empty. Otherwise throw a
|
|
35
|
+
* {@link PerpsExecutionError} whose report holds `known`, then any results
|
|
36
|
+
* `error` already carries.
|
|
37
|
+
*
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export function rethrowWithKnownResults(
|
|
41
|
+
known: ActionResult[],
|
|
42
|
+
error: unknown
|
|
43
|
+
): never {
|
|
44
|
+
if (known.length === 0) {
|
|
45
|
+
throw error
|
|
46
|
+
}
|
|
47
|
+
if (error instanceof PerpsExecutionError) {
|
|
48
|
+
throw new PerpsExecutionError(
|
|
49
|
+
{ results: [...known, ...error.response.results] },
|
|
50
|
+
error.cause
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
throw new PerpsExecutionError({ results: known }, error)
|
|
54
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -71,35 +71,38 @@ export {
|
|
|
71
71
|
export { PerpsErrorMessage } from './errors/constants.js'
|
|
72
72
|
export { PerpsError } from './errors/PerpsError.js'
|
|
73
73
|
export {
|
|
74
|
-
|
|
74
|
+
PerpsExecutionError,
|
|
75
|
+
rethrowWithKnownResults,
|
|
76
|
+
} from './errors/PerpsExecutionError.js'
|
|
77
|
+
export {
|
|
75
78
|
classifyFillFromPosition,
|
|
76
79
|
FillClassification,
|
|
77
80
|
} from './math/fills.js'
|
|
78
81
|
export type { ExpectedPnl } from './math/order.js'
|
|
79
82
|
export {
|
|
80
|
-
applySlippageToPrice,
|
|
81
83
|
buildQuote,
|
|
82
84
|
calculateExpectedPnl,
|
|
85
|
+
calculateOrderbookWalk,
|
|
83
86
|
calculateRealizedPnlPercent,
|
|
84
87
|
calculateSize,
|
|
88
|
+
calculateSlippagePrice,
|
|
85
89
|
calculateTriggerPercent,
|
|
86
90
|
calculateTriggerPrice,
|
|
87
91
|
estimateFees,
|
|
88
92
|
estimateRealizedPnl,
|
|
89
|
-
findMatchingPosition,
|
|
90
93
|
resolveCloseSize,
|
|
91
|
-
|
|
94
|
+
resolveMatchingPosition,
|
|
92
95
|
safeBuildQuote,
|
|
93
96
|
safeCalculateExpectedPnl,
|
|
97
|
+
safeCalculateOrderbookWalk,
|
|
94
98
|
safeCalculateRealizedPnlPercent,
|
|
95
99
|
safeCalculateSize,
|
|
100
|
+
safeCalculateSlippagePrice,
|
|
96
101
|
safeCalculateTriggerPercent,
|
|
97
102
|
safeCalculateTriggerPrice,
|
|
98
103
|
safeEstimateFees,
|
|
99
104
|
safeEstimateRealizedPnl,
|
|
100
105
|
safeResolveCloseSize,
|
|
101
|
-
safeWalkOrderbook,
|
|
102
|
-
walkOrderbook,
|
|
103
106
|
} from './math/order.js'
|
|
104
107
|
export {
|
|
105
108
|
calculateEffectiveLeverage,
|
|
@@ -114,8 +117,8 @@ export {
|
|
|
114
117
|
estimateLiquidationPriceAtMarketRate,
|
|
115
118
|
estimateNewLeverage,
|
|
116
119
|
estimateUnrealizedPnl,
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
isPositionMarginAdjustable,
|
|
121
|
+
isPositionMarginRemovable,
|
|
119
122
|
safeCalculateEffectiveLeverage,
|
|
120
123
|
safeCalculateLiquidationDistance,
|
|
121
124
|
safeCalculateNotionalValue,
|
|
@@ -283,6 +286,7 @@ export type {
|
|
|
283
286
|
QuoteListener,
|
|
284
287
|
SignActionProgress,
|
|
285
288
|
SignActionsContext,
|
|
289
|
+
SignActionsResult,
|
|
286
290
|
} from './types/provider.js'
|
|
287
291
|
export type {
|
|
288
292
|
ProviderWithdrawableBalance,
|