@lifi/perps-sdk 25.0.1 → 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 +57 -3
- 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 +107 -87
- 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 +70 -50
- package/dist/esm/client/PerpsClient.d.ts.map +1 -1
- package/dist/esm/client/PerpsClient.js +176 -144
- 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 +70 -50
- 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 +224 -186
- 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
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
ActionParamsMap,
|
|
6
6
|
ActionResult,
|
|
7
7
|
ActionStep,
|
|
8
|
+
Asset,
|
|
8
9
|
AvailableToTrade,
|
|
9
10
|
CreateActionResponse,
|
|
10
11
|
DecimalString,
|
|
@@ -29,11 +30,13 @@ import {
|
|
|
29
30
|
PerpsErrorCode,
|
|
30
31
|
PerpsSigner,
|
|
31
32
|
SigningMethod,
|
|
33
|
+
UserActionStatus,
|
|
32
34
|
} from '@lifi/perps-types'
|
|
33
35
|
import type { Address } from 'viem'
|
|
34
36
|
import { isDecimalStringGreaterThan } from '../decimal/compare.js'
|
|
35
37
|
import { unknownToDecimalString } from '../decimal/parse.js'
|
|
36
38
|
import { PerpsError } from '../errors/PerpsError.js'
|
|
39
|
+
import { rethrowWithKnownResults } from '../errors/PerpsExecutionError.js'
|
|
37
40
|
import { getAssetRegistry, toAssetDisplay } from '../registry/assetRegistry.js'
|
|
38
41
|
import { getMarketRegistry } from '../registry/marketRegistry.js'
|
|
39
42
|
import { createAction } from '../services/createAction.js'
|
|
@@ -54,8 +57,6 @@ import type {
|
|
|
54
57
|
CancelTwapOrderParams,
|
|
55
58
|
CreateReferralCodeActionParams,
|
|
56
59
|
ExecuteMetaActionParams,
|
|
57
|
-
ExecuteProviderSetupParams,
|
|
58
|
-
ExecuteProviderSetupResult,
|
|
59
60
|
GetAccountResult,
|
|
60
61
|
GetDepositFlowParams,
|
|
61
62
|
GetPortfolioHistoryParams,
|
|
@@ -86,6 +87,7 @@ import type {
|
|
|
86
87
|
PerpsSDKClient,
|
|
87
88
|
SignActionProgress,
|
|
88
89
|
SignActionsContext,
|
|
90
|
+
SignActionsResult,
|
|
89
91
|
} from '../types/provider.js'
|
|
90
92
|
import type {
|
|
91
93
|
WithdrawableBalance,
|
|
@@ -212,15 +214,11 @@ function currentChoiceValue(
|
|
|
212
214
|
return value === null ? null : String(value)
|
|
213
215
|
}
|
|
214
216
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
failure.errorCode ?? PerpsErrorCode.ExchangeRejected,
|
|
221
|
-
failure.error
|
|
222
|
-
)
|
|
223
|
-
}
|
|
217
|
+
function isAcknowledged(result: ActionResult): boolean {
|
|
218
|
+
return (
|
|
219
|
+
result.status === UserActionStatus.Submitted ||
|
|
220
|
+
result.status === UserActionStatus.Accepted
|
|
221
|
+
)
|
|
224
222
|
}
|
|
225
223
|
|
|
226
224
|
function isNonNegativeDecimal(value: string): boolean {
|
|
@@ -234,6 +232,25 @@ function isNonNegativeDecimal(value: string): boolean {
|
|
|
234
232
|
}
|
|
235
233
|
}
|
|
236
234
|
|
|
235
|
+
/** @throws {PerpsError} When `asset.minWithdrawalAmount` is not a decimal. */
|
|
236
|
+
function minWithdrawalAmountOf(asset: Asset): DecimalString | undefined {
|
|
237
|
+
if (asset.minWithdrawalAmount === undefined) {
|
|
238
|
+
return undefined
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
return unknownToDecimalString(
|
|
242
|
+
asset.minWithdrawalAmount,
|
|
243
|
+
'minWithdrawalAmount',
|
|
244
|
+
'perps-sdk'
|
|
245
|
+
)
|
|
246
|
+
} catch {
|
|
247
|
+
throw new PerpsError(
|
|
248
|
+
PerpsErrorCode.SDKError,
|
|
249
|
+
`Asset '${asset.id}' field \`minWithdrawalAmount\` is not a valid decimal.`
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
237
254
|
/**
|
|
238
255
|
* Look up an action's descriptor in the provider's metadata. Throws if the
|
|
239
256
|
* action isn't declared — defensive: better to fail loudly than to mis-sign.
|
|
@@ -431,10 +448,13 @@ export class PerpsClient {
|
|
|
431
448
|
* step with the signer its own descriptor declares; the signed steps come
|
|
432
449
|
* back in the batch's original order. The plugin owns every signing arm and
|
|
433
450
|
* branches on `signer` internally, reading the end-user's wallet from the
|
|
434
|
-
* {@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.
|
|
435
453
|
*
|
|
436
454
|
* @throws {PerpsError} When a step's action is not declared by the provider,
|
|
437
455
|
* or the plugin implements no `signActions`.
|
|
456
|
+
* @throws {PerpsExecutionError} When a group throws after an earlier group
|
|
457
|
+
* executed steps.
|
|
438
458
|
*/
|
|
439
459
|
private async delegateSignActions(
|
|
440
460
|
provider: string,
|
|
@@ -442,36 +462,90 @@ export class PerpsClient {
|
|
|
442
462
|
metadata: Provider,
|
|
443
463
|
actions: ActionStep[],
|
|
444
464
|
onProgress?: (progress: SignActionProgress) => void
|
|
445
|
-
): Promise<
|
|
465
|
+
): Promise<SignActionsResult> {
|
|
446
466
|
const plugin = this.requireProvider(provider)
|
|
447
|
-
|
|
467
|
+
const signActions = plugin.signActions?.bind(plugin)
|
|
468
|
+
if (signActions === undefined) {
|
|
448
469
|
throw new PerpsError(
|
|
449
470
|
PerpsErrorCode.SDKError,
|
|
450
471
|
`Provider '${provider}' does not implement signActions.`
|
|
451
472
|
)
|
|
452
473
|
}
|
|
453
474
|
const signed: SignedActionStep[] = []
|
|
475
|
+
const executed: ActionResult[] = []
|
|
454
476
|
for (const group of groupStepsByDescriptor(metadata, actions)) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
477
|
+
try {
|
|
478
|
+
const userWallet = await this.resolveSigningWallet(
|
|
479
|
+
group.descriptor,
|
|
480
|
+
group.steps
|
|
481
|
+
)
|
|
482
|
+
const result = await signActions(
|
|
461
483
|
group.descriptor.signingMethod,
|
|
462
484
|
group.steps,
|
|
463
485
|
address,
|
|
464
486
|
this.buildSignActionsContext(group.descriptor, userWallet, onProgress)
|
|
465
|
-
)
|
|
466
|
-
|
|
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
|
+
}
|
|
467
496
|
}
|
|
468
|
-
return signed
|
|
497
|
+
return { signed, executed }
|
|
469
498
|
}
|
|
470
499
|
|
|
471
500
|
/**
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
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.
|
|
475
549
|
*/
|
|
476
550
|
private async notifyExecuteResults(
|
|
477
551
|
provider: string,
|
|
@@ -485,9 +559,10 @@ export class PerpsClient {
|
|
|
485
559
|
}
|
|
486
560
|
|
|
487
561
|
/**
|
|
488
|
-
* Attach a provider-built explorer URL to every result
|
|
489
|
-
*
|
|
490
|
-
*
|
|
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.
|
|
491
566
|
*/
|
|
492
567
|
private resolveExplorerLinks(
|
|
493
568
|
provider: string,
|
|
@@ -499,7 +574,7 @@ export class PerpsClient {
|
|
|
499
574
|
return results
|
|
500
575
|
}
|
|
501
576
|
return results.map((result) => {
|
|
502
|
-
if (
|
|
577
|
+
if (result.txHash === undefined) {
|
|
503
578
|
return result
|
|
504
579
|
}
|
|
505
580
|
const explorerLink = resolveLink(result.txHash)
|
|
@@ -590,30 +665,47 @@ export class PerpsClient {
|
|
|
590
665
|
}
|
|
591
666
|
|
|
592
667
|
/**
|
|
593
|
-
* Sign
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
* Returns `undefined` when the plugin executed the action entirely
|
|
599
|
-
* client-side (e.g. Lighter's token-authenticated venue mutations), leaving
|
|
600
|
-
* 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.
|
|
601
672
|
*
|
|
602
|
-
* @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.
|
|
603
677
|
*/
|
|
604
|
-
private async
|
|
678
|
+
private async runProviderSetupStep(
|
|
605
679
|
provider: string,
|
|
606
680
|
address: Address,
|
|
607
681
|
step: ActionStep
|
|
608
|
-
): Promise<
|
|
682
|
+
): Promise<ActionResult[]> {
|
|
609
683
|
const metadata = await this.getProviderMetadata(provider)
|
|
610
|
-
const
|
|
684
|
+
const descriptor = findActionDescriptor(metadata, step.action)
|
|
685
|
+
const signResult = await this.delegateSignActions(
|
|
611
686
|
provider,
|
|
612
687
|
address,
|
|
613
688
|
metadata,
|
|
614
689
|
[step]
|
|
615
690
|
)
|
|
616
|
-
|
|
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
|
+
)
|
|
617
709
|
}
|
|
618
710
|
|
|
619
711
|
/**
|
|
@@ -886,24 +978,12 @@ export class PerpsClient {
|
|
|
886
978
|
if (asset === undefined) {
|
|
887
979
|
return []
|
|
888
980
|
}
|
|
889
|
-
const minimum = asset
|
|
890
|
-
if (
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
'minWithdrawalAmount',
|
|
896
|
-
'perps-sdk'
|
|
897
|
-
)
|
|
898
|
-
} catch {
|
|
899
|
-
throw new PerpsError(
|
|
900
|
-
PerpsErrorCode.SDKError,
|
|
901
|
-
`Asset '${asset.id}' field \`minWithdrawalAmount\` is not a valid decimal.`
|
|
902
|
-
)
|
|
903
|
-
}
|
|
904
|
-
if (isDecimalStringGreaterThan(floor, row.available)) {
|
|
905
|
-
return []
|
|
906
|
-
}
|
|
981
|
+
const minimum = minWithdrawalAmountOf(asset)
|
|
982
|
+
if (
|
|
983
|
+
minimum !== undefined &&
|
|
984
|
+
isDecimalStringGreaterThan(minimum, row.available)
|
|
985
|
+
) {
|
|
986
|
+
return []
|
|
907
987
|
}
|
|
908
988
|
if (row.withdrawalFee === undefined) {
|
|
909
989
|
return [
|
|
@@ -939,12 +1019,16 @@ export class PerpsClient {
|
|
|
939
1019
|
/**
|
|
940
1020
|
* The withdrawal types `params.address` can choose per holding at
|
|
941
1021
|
* `params.provider`, each with its cap and the `withdrawalOptions` to pass
|
|
942
|
-
* unchanged in `WithdrawalParams`.
|
|
1022
|
+
* unchanged in `WithdrawalParams`. Every option whose `max` is below the
|
|
1023
|
+
* source asset's core `/assets` `minWithdrawalAmount` is removed, since no
|
|
1024
|
+
* amount can satisfy both limits; a source asset the provider's registry
|
|
1025
|
+
* does not carry keeps its options.
|
|
943
1026
|
*
|
|
944
1027
|
* @returns `undefined` when the registered plugin declares no withdrawal
|
|
945
1028
|
* types read.
|
|
946
|
-
* @throws {PerpsError} When the provider plugin is not registered,
|
|
947
|
-
* the plugin read fails
|
|
1029
|
+
* @throws {PerpsError} When the provider plugin is not registered, when
|
|
1030
|
+
* either the plugin read or the asset sync fails, or when a source asset's
|
|
1031
|
+
* `minWithdrawalAmount` is not a decimal.
|
|
948
1032
|
* @public
|
|
949
1033
|
*/
|
|
950
1034
|
async getWithdrawalTypes(
|
|
@@ -952,7 +1036,30 @@ export class PerpsClient {
|
|
|
952
1036
|
options?: SDKRequestOptions
|
|
953
1037
|
): Promise<WithdrawalSourceTypes[] | undefined> {
|
|
954
1038
|
const plugin = this.requireProvider(params.provider)
|
|
955
|
-
|
|
1039
|
+
const sources = await plugin.getWithdrawalTypes?.(
|
|
1040
|
+
{ address: params.address },
|
|
1041
|
+
options
|
|
1042
|
+
)
|
|
1043
|
+
if (sources === undefined) {
|
|
1044
|
+
return undefined
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
const registry = getAssetRegistry(this.sdkClient, params.provider)
|
|
1048
|
+
await registry.sync()
|
|
1049
|
+
return sources.map((row) => {
|
|
1050
|
+
const asset = registry.get(row.source.asset.id)
|
|
1051
|
+
const minimum =
|
|
1052
|
+
asset === undefined ? undefined : minWithdrawalAmountOf(asset)
|
|
1053
|
+
if (minimum === undefined) {
|
|
1054
|
+
return row
|
|
1055
|
+
}
|
|
1056
|
+
return {
|
|
1057
|
+
...row,
|
|
1058
|
+
options: row.options.filter(
|
|
1059
|
+
(option) => !isDecimalStringGreaterThan(minimum, option.max)
|
|
1060
|
+
),
|
|
1061
|
+
}
|
|
1062
|
+
})
|
|
956
1063
|
}
|
|
957
1064
|
|
|
958
1065
|
/**
|
|
@@ -1107,9 +1214,9 @@ export class PerpsClient {
|
|
|
1107
1214
|
* deferred while any staged user-facing step with a lower `sequence` is
|
|
1108
1215
|
* outstanding — it cannot succeed before its prerequisite (e.g. SET_REFERRAL
|
|
1109
1216
|
* authenticates with the credential REGISTER_API_KEY installs) and each
|
|
1110
|
-
* doomed attempt is venue traffic. A drain failure
|
|
1111
|
-
* blocks setup — the step stays unsatisfied and is
|
|
1112
|
-
* `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`.
|
|
1113
1220
|
*/
|
|
1114
1221
|
private async drainSetup(
|
|
1115
1222
|
provider: string,
|
|
@@ -1131,18 +1238,18 @@ export class PerpsClient {
|
|
|
1131
1238
|
descriptor,
|
|
1132
1239
|
])
|
|
1133
1240
|
for (const step of steps) {
|
|
1134
|
-
const
|
|
1241
|
+
const results = await this.runProviderSetupStep(
|
|
1135
1242
|
provider,
|
|
1136
1243
|
address,
|
|
1137
1244
|
step
|
|
1138
1245
|
)
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
|
1146
1253
|
}
|
|
1147
1254
|
}
|
|
1148
1255
|
} catch (error) {
|
|
@@ -1221,49 +1328,6 @@ export class PerpsClient {
|
|
|
1221
1328
|
return { actions }
|
|
1222
1329
|
}
|
|
1223
1330
|
|
|
1224
|
-
/**
|
|
1225
|
-
* Submit the signed setup steps returned by `checkSetup` (and signed by the
|
|
1226
|
-
* caller / `signProviderSetupAction`). Routes the batch on the first step's
|
|
1227
|
-
* action and lets the plugin contribute that action's `signerAddress`.
|
|
1228
|
-
* Throws on any per-step venue rejection.
|
|
1229
|
-
*/
|
|
1230
|
-
private async executeProviderSetup(
|
|
1231
|
-
params: ExecuteProviderSetupParams
|
|
1232
|
-
): Promise<ExecuteProviderSetupResult> {
|
|
1233
|
-
const { provider, address, setup, signedActions } = params
|
|
1234
|
-
|
|
1235
|
-
if (signedActions.length === 0) {
|
|
1236
|
-
return { results: { results: [] } }
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
const action = setup[0]?.action ?? signedActions[0].action
|
|
1240
|
-
const metadata = await this.getProviderMetadata(provider)
|
|
1241
|
-
const descriptor = findActionDescriptor(metadata, action)
|
|
1242
|
-
const { signerAddress } = await this.resolveActionRequest(
|
|
1243
|
-
provider,
|
|
1244
|
-
descriptor,
|
|
1245
|
-
address
|
|
1246
|
-
)
|
|
1247
|
-
|
|
1248
|
-
const response = await executeAction(this.sdkClient, {
|
|
1249
|
-
provider,
|
|
1250
|
-
address,
|
|
1251
|
-
// The submitting account: the plugin-resolved signer (Hyperliquid's
|
|
1252
|
-
// agent) when present, else the end-user's address.
|
|
1253
|
-
signerAddress: signerAddress ?? address,
|
|
1254
|
-
action,
|
|
1255
|
-
actions: signedActions,
|
|
1256
|
-
})
|
|
1257
|
-
|
|
1258
|
-
const results = this.resolveExplorerLinks(provider, response.results)
|
|
1259
|
-
|
|
1260
|
-
await this.notifyExecuteResults(provider, address, results)
|
|
1261
|
-
|
|
1262
|
-
assertAllSucceeded(results)
|
|
1263
|
-
|
|
1264
|
-
return { results: { results } }
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
1331
|
/**
|
|
1268
1332
|
* Sign and submit one pre-staged setup `ActionStep` end-to-end.
|
|
1269
1333
|
*
|
|
@@ -1272,9 +1336,15 @@ export class PerpsClient {
|
|
|
1272
1336
|
* prerequisites against that same snapshot, without another `createAction`
|
|
1273
1337
|
* round-trip.
|
|
1274
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
|
+
*
|
|
1275
1342
|
* @throws {PerpsError} When the step's action is not in the provider's
|
|
1276
|
-
* `setup` descriptors,
|
|
1277
|
-
* 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.
|
|
1278
1348
|
* @public
|
|
1279
1349
|
*/
|
|
1280
1350
|
async executeProviderSetupAction(params: {
|
|
@@ -1282,7 +1352,7 @@ export class PerpsClient {
|
|
|
1282
1352
|
address: Address
|
|
1283
1353
|
step: ActionStep
|
|
1284
1354
|
checklist: SetupChecklistItem[]
|
|
1285
|
-
}): Promise<
|
|
1355
|
+
}): Promise<ExecuteActionResponse> {
|
|
1286
1356
|
const { provider, address, step, checklist } = params
|
|
1287
1357
|
|
|
1288
1358
|
const metadata = await this.getProviderMetadata(provider)
|
|
@@ -1305,52 +1375,34 @@ export class PerpsClient {
|
|
|
1305
1375
|
)
|
|
1306
1376
|
}
|
|
1307
1377
|
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
// A client-executed setup action (Lighter SET_REFERRAL) produces no
|
|
1311
|
-
// backend-bound step — it already ran during signing, so there is nothing
|
|
1312
|
-
// to submit.
|
|
1313
|
-
if (signed === undefined) {
|
|
1314
|
-
return
|
|
1378
|
+
return {
|
|
1379
|
+
results: await this.runProviderSetupStep(provider, address, step),
|
|
1315
1380
|
}
|
|
1316
|
-
|
|
1317
|
-
await this.executeProviderSetup({
|
|
1318
|
-
provider,
|
|
1319
|
-
address,
|
|
1320
|
-
setup: [step],
|
|
1321
|
-
signedActions: [signed],
|
|
1322
|
-
})
|
|
1323
1381
|
}
|
|
1324
1382
|
|
|
1325
1383
|
/**
|
|
1326
1384
|
* Execute one option of a choice setup step (Lighter `ACCOUNT_TYPE`,
|
|
1327
1385
|
* Hyperliquid `ACCOUNT_MODE`) end-to-end with its bound `{ type, params }`,
|
|
1328
|
-
*
|
|
1386
|
+
* through the same {@link execute} pipeline as a trade.
|
|
1329
1387
|
*
|
|
1330
|
-
*
|
|
1331
|
-
*
|
|
1332
|
-
* 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.
|
|
1333
1390
|
*
|
|
1334
|
-
* @throws {PerpsError}
|
|
1335
|
-
* `success: false`, under that result's `errorCode` when the backend
|
|
1336
|
-
* classified the failure and `PerpsErrorCode.ExchangeRejected` otherwise;
|
|
1337
|
-
* also the errors `execute` itself can throw (unregistered provider, no
|
|
1338
|
-
* signer, signing failure).
|
|
1391
|
+
* @throws {PerpsError} The errors {@link execute} throws.
|
|
1339
1392
|
* @public
|
|
1340
1393
|
*/
|
|
1341
1394
|
async executeProviderOption(params: {
|
|
1342
1395
|
provider: string
|
|
1343
1396
|
address: Address
|
|
1344
1397
|
option: SetupOption
|
|
1345
|
-
}): Promise<
|
|
1398
|
+
}): Promise<ExecuteActionResponse> {
|
|
1346
1399
|
const { provider, address, option } = params
|
|
1347
|
-
|
|
1400
|
+
return this.execute({
|
|
1348
1401
|
provider,
|
|
1349
1402
|
address,
|
|
1350
1403
|
action: option.type,
|
|
1351
1404
|
params: option.params,
|
|
1352
1405
|
})
|
|
1353
|
-
assertAllSucceeded(results)
|
|
1354
1406
|
}
|
|
1355
1407
|
|
|
1356
1408
|
/**
|
|
@@ -1358,16 +1410,18 @@ export class PerpsClient {
|
|
|
1358
1410
|
* create, sign and execute, signed with that action's `Provider.actions`
|
|
1359
1411
|
* descriptor. The step stages again on the next {@link checkSetup}.
|
|
1360
1412
|
*
|
|
1413
|
+
* Resolves to the execution report.
|
|
1414
|
+
*
|
|
1361
1415
|
* @throws {PerpsError} `SDKError` when `step.revoke` is `null` or when
|
|
1362
1416
|
* `createAction` stages nothing to revoke; otherwise the errors
|
|
1363
|
-
* {@link
|
|
1417
|
+
* {@link execute} throws.
|
|
1364
1418
|
* @public
|
|
1365
1419
|
*/
|
|
1366
1420
|
async executeProviderRevoke(params: {
|
|
1367
1421
|
provider: string
|
|
1368
1422
|
address: Address
|
|
1369
1423
|
step: SetupAction
|
|
1370
|
-
}): Promise<
|
|
1424
|
+
}): Promise<ExecuteActionResponse> {
|
|
1371
1425
|
const { provider, address, step } = params
|
|
1372
1426
|
if (step.revoke === null) {
|
|
1373
1427
|
throw new PerpsError(
|
|
@@ -1387,7 +1441,7 @@ export class PerpsClient {
|
|
|
1387
1441
|
`Nothing to revoke for setup step '${step.type}' on '${provider}'.`
|
|
1388
1442
|
)
|
|
1389
1443
|
}
|
|
1390
|
-
|
|
1444
|
+
return { results }
|
|
1391
1445
|
}
|
|
1392
1446
|
|
|
1393
1447
|
/**
|
|
@@ -1555,8 +1609,15 @@ export class PerpsClient {
|
|
|
1555
1609
|
* descriptor's scheme and signer internally), and submit. Core stays
|
|
1556
1610
|
* signer-agnostic — the plugin owns WHO signs and HOW.
|
|
1557
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
|
+
*
|
|
1558
1616
|
* @throws {PerpsError} When the action is not declared by the provider,
|
|
1559
|
-
* 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.
|
|
1560
1621
|
* @public
|
|
1561
1622
|
*/
|
|
1562
1623
|
async execute<T extends ActionType>(params: {
|
|
@@ -1598,7 +1659,7 @@ export class PerpsClient {
|
|
|
1598
1659
|
} as ActionParamsMap[T],
|
|
1599
1660
|
})
|
|
1600
1661
|
|
|
1601
|
-
const
|
|
1662
|
+
const signResult = await this.delegateSignActions(
|
|
1602
1663
|
provider,
|
|
1603
1664
|
address,
|
|
1604
1665
|
metadata,
|
|
@@ -1606,27 +1667,13 @@ export class PerpsClient {
|
|
|
1606
1667
|
onProgress
|
|
1607
1668
|
)
|
|
1608
1669
|
|
|
1609
|
-
|
|
1610
|
-
// token-authenticated venue mutations), leaving no backend-bound step. With
|
|
1611
|
-
// nothing to submit, skip the `/executeAction` hop.
|
|
1612
|
-
if (signedActions.length === 0) {
|
|
1613
|
-
return { staged: actions, results: [] }
|
|
1614
|
-
}
|
|
1615
|
-
|
|
1616
|
-
const response = await executeAction(this.sdkClient, {
|
|
1670
|
+
const results = await this.submitSignedActions(
|
|
1617
1671
|
provider,
|
|
1618
1672
|
address,
|
|
1619
|
-
// The submitting account: the plugin-resolved signer (Hyperliquid's
|
|
1620
|
-
// agent) when present, else the end-user's address.
|
|
1621
|
-
signerAddress: signerAddress ?? address,
|
|
1622
1673
|
action,
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
const results = this.resolveExplorerLinks(provider, response.results)
|
|
1627
|
-
|
|
1628
|
-
await this.notifyExecuteResults(provider, address, results)
|
|
1629
|
-
|
|
1674
|
+
signerAddress,
|
|
1675
|
+
signResult
|
|
1676
|
+
)
|
|
1630
1677
|
return { staged: actions, results }
|
|
1631
1678
|
}
|
|
1632
1679
|
|
|
@@ -1682,12 +1729,13 @@ export class PerpsClient {
|
|
|
1682
1729
|
* typed data with the configured user wallet.
|
|
1683
1730
|
*
|
|
1684
1731
|
* The backend returns at most one step. Resolves to `{ results: [] }` when it
|
|
1685
|
-
* 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`.
|
|
1686
1734
|
*
|
|
1687
1735
|
* @throws {PerpsError} When no user wallet is configured, the backend returns
|
|
1688
|
-
* more than one step or a non-EIP-712 step, or the
|
|
1689
|
-
*
|
|
1690
|
-
*
|
|
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.
|
|
1691
1739
|
* @public
|
|
1692
1740
|
*/
|
|
1693
1741
|
async executeMetaAction<T extends MetaActionType>(
|
|
@@ -1722,21 +1770,11 @@ export class PerpsClient {
|
|
|
1722
1770
|
const wallet = await this.resolveMetaSigningWallet(actions)
|
|
1723
1771
|
const signature = await signTypedDataWithSigner(wallet, step.typedData)
|
|
1724
1772
|
|
|
1725
|
-
|
|
1773
|
+
return executeAction(this.sdkClient, {
|
|
1726
1774
|
provider: META_PROVIDER,
|
|
1727
1775
|
address,
|
|
1728
1776
|
action,
|
|
1729
1777
|
actions: [{ ...step, signature }],
|
|
1730
1778
|
})
|
|
1731
|
-
|
|
1732
|
-
const failure = response.results.find((r) => !r.success)
|
|
1733
|
-
if (failure) {
|
|
1734
|
-
throw new PerpsError(
|
|
1735
|
-
failure.errorCode ?? PerpsErrorCode.ExchangeRejected,
|
|
1736
|
-
failure.error
|
|
1737
|
-
)
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
return response
|
|
1741
1779
|
}
|
|
1742
1780
|
}
|