@lifi/perps-sdk-provider-hyperliquid 18.1.1 → 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 +11 -0
- package/dist/cjs/HyperliquidProvider.d.ts.map +1 -1
- package/dist/cjs/HyperliquidProvider.js +22 -5
- package/dist/cjs/HyperliquidProvider.js.map +1 -1
- package/dist/cjs/accountConfig.d.ts +2 -2
- package/dist/cjs/accountConfig.d.ts.map +1 -1
- package/dist/cjs/accountConfig.js +22 -5
- package/dist/cjs/accountConfig.js.map +1 -1
- package/dist/cjs/services/getAccount.d.ts.map +1 -1
- package/dist/cjs/services/getAccount.js +12 -8
- package/dist/cjs/services/getAccount.js.map +1 -1
- package/dist/cjs/services/getActivity.d.ts.map +1 -1
- package/dist/cjs/services/getActivity.js +3 -1
- package/dist/cjs/services/getActivity.js.map +1 -1
- package/dist/cjs/services/getMarketSettings.d.ts +2 -2
- package/dist/cjs/services/getMarketSettings.d.ts.map +1 -1
- package/dist/cjs/services/getMarketSettings.js +8 -3
- package/dist/cjs/services/getMarketSettings.js.map +1 -1
- package/dist/cjs/signers/signActions.d.ts.map +1 -1
- package/dist/cjs/signers/signActions.js +1 -1
- package/dist/cjs/signers/signActions.js.map +1 -1
- package/dist/cjs/types/ledger.d.ts +17 -1
- package/dist/cjs/types/ledger.d.ts.map +1 -1
- package/dist/cjs/types/ledger.js +10 -1
- package/dist/cjs/types/ledger.js.map +1 -1
- package/dist/cjs/utils/mapActivity.d.ts.map +1 -1
- package/dist/cjs/utils/mapActivity.js +41 -0
- package/dist/cjs/utils/mapActivity.js.map +1 -1
- package/dist/cjs/utils/withdrawableBalances.d.ts.map +1 -1
- package/dist/cjs/utils/withdrawableBalances.js +3 -1
- package/dist/cjs/utils/withdrawableBalances.js.map +1 -1
- package/dist/esm/HyperliquidProvider.d.ts.map +1 -1
- package/dist/esm/HyperliquidProvider.js +29 -8
- package/dist/esm/HyperliquidProvider.js.map +1 -1
- package/dist/esm/accountConfig.d.ts +5 -5
- package/dist/esm/accountConfig.d.ts.map +1 -1
- package/dist/esm/accountConfig.js +32 -11
- package/dist/esm/accountConfig.js.map +1 -1
- package/dist/esm/services/getAccount.d.ts.map +1 -1
- package/dist/esm/services/getAccount.js +10 -9
- package/dist/esm/services/getAccount.js.map +1 -1
- package/dist/esm/services/getActivity.d.ts.map +1 -1
- package/dist/esm/services/getActivity.js +4 -2
- package/dist/esm/services/getActivity.js.map +1 -1
- package/dist/esm/services/getMarketSettings.d.ts +5 -3
- package/dist/esm/services/getMarketSettings.d.ts.map +1 -1
- package/dist/esm/services/getMarketSettings.js +13 -7
- package/dist/esm/services/getMarketSettings.js.map +1 -1
- package/dist/esm/signers/signActions.d.ts.map +1 -1
- package/dist/esm/signers/signActions.js +1 -1
- package/dist/esm/signers/signActions.js.map +1 -1
- package/dist/esm/types/ledger.d.ts +42 -1
- package/dist/esm/types/ledger.d.ts.map +1 -1
- package/dist/esm/types/ledger.js +17 -0
- package/dist/esm/types/ledger.js.map +1 -1
- package/dist/esm/utils/mapActivity.d.ts +1 -0
- package/dist/esm/utils/mapActivity.d.ts.map +1 -1
- package/dist/esm/utils/mapActivity.js +49 -1
- package/dist/esm/utils/mapActivity.js.map +1 -1
- package/dist/esm/utils/withdrawableBalances.d.ts +2 -1
- package/dist/esm/utils/withdrawableBalances.d.ts.map +1 -1
- package/dist/esm/utils/withdrawableBalances.js +5 -2
- package/dist/esm/utils/withdrawableBalances.js.map +1 -1
- package/dist/types/HyperliquidProvider.d.ts.map +1 -1
- package/dist/types/accountConfig.d.ts +5 -5
- package/dist/types/accountConfig.d.ts.map +1 -1
- package/dist/types/services/getAccount.d.ts.map +1 -1
- package/dist/types/services/getActivity.d.ts.map +1 -1
- package/dist/types/services/getMarketSettings.d.ts +5 -3
- package/dist/types/services/getMarketSettings.d.ts.map +1 -1
- package/dist/types/signers/signActions.d.ts.map +1 -1
- package/dist/types/types/ledger.d.ts +42 -1
- package/dist/types/types/ledger.d.ts.map +1 -1
- package/dist/types/utils/mapActivity.d.ts +1 -0
- package/dist/types/utils/mapActivity.d.ts.map +1 -1
- package/dist/types/utils/withdrawableBalances.d.ts +2 -1
- package/dist/types/utils/withdrawableBalances.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/HyperliquidProvider.ts +45 -12
- package/src/accountConfig.ts +38 -14
- package/src/services/getAccount.ts +22 -12
- package/src/services/getActivity.ts +5 -1
- package/src/services/getMarketSettings.ts +20 -8
- package/src/signers/signActions.ts +4 -3
- package/src/types/ledger.ts +61 -0
- package/src/utils/mapActivity.ts +56 -0
- package/src/utils/withdrawableBalances.ts +8 -3
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type AccountConfigSetting,
|
|
31
31
|
type AccountResponse,
|
|
32
32
|
type AccountSummary,
|
|
33
|
+
type ActionResult,
|
|
33
34
|
type ActionStep,
|
|
34
35
|
ActionType,
|
|
35
36
|
type ActivitiesResponse,
|
|
@@ -45,8 +46,8 @@ import {
|
|
|
45
46
|
type PortfolioHistoryResponse,
|
|
46
47
|
type Position,
|
|
47
48
|
type PositionsResponse,
|
|
48
|
-
type ProviderAction,
|
|
49
49
|
type Quote,
|
|
50
|
+
type SetupAction,
|
|
50
51
|
type SignedActionStep,
|
|
51
52
|
type SigningMethod,
|
|
52
53
|
} from '@lifi/perps-types'
|
|
@@ -229,8 +230,6 @@ export function hyperliquidProvider(
|
|
|
229
230
|
return {
|
|
230
231
|
type: PROVIDER_KEY,
|
|
231
232
|
|
|
232
|
-
internalSetupActions: [ActionType.SET_REFERRAL],
|
|
233
|
-
|
|
234
233
|
// REVOKE_AGENT stages a step only when every named agent slot is taken,
|
|
235
234
|
// so `checkSetup` omits it from `ProviderSetup.checklist` instead of
|
|
236
235
|
// rendering it as a satisfied step.
|
|
@@ -254,7 +253,7 @@ export function hyperliquidProvider(
|
|
|
254
253
|
resolveActionRequest: async (
|
|
255
254
|
action: ActionType,
|
|
256
255
|
address: Address,
|
|
257
|
-
|
|
256
|
+
signer: PerpsSigner
|
|
258
257
|
): Promise<ActionSignerContribution> => {
|
|
259
258
|
// APPROVE_AGENT is user-signed: the user authorises the agent, so the
|
|
260
259
|
// agent address rides as a param (not signerAddress). The agent is
|
|
@@ -264,10 +263,30 @@ export function hyperliquidProvider(
|
|
|
264
263
|
const agentAddress = await resolveApproveAgentAddress(address)
|
|
265
264
|
return { params: { agentAddress } }
|
|
266
265
|
}
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
|
|
270
|
-
|
|
266
|
+
// REVOKE_SESSION_AGENT targets the stored agent by address and never
|
|
267
|
+
// provisions one: with no stored agent there is nothing to revoke.
|
|
268
|
+
if (action === ActionType.REVOKE_SESSION_AGENT) {
|
|
269
|
+
try {
|
|
270
|
+
const agent = await agentStore.get(address)
|
|
271
|
+
return { params: { agentAddress: agent.address } }
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (
|
|
274
|
+
error instanceof PerpsError &&
|
|
275
|
+
error.message === PerpsErrorMessage.AgentNotFound
|
|
276
|
+
) {
|
|
277
|
+
throw new PerpsError(
|
|
278
|
+
PerpsErrorCode.SDKError,
|
|
279
|
+
`Nothing to revoke for '${action}' on '${PROVIDER_KEY}': no session agent is stored for ${address}.`
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
throw error
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
// SDK-signed actions (trades) carry the agent as the on-wire
|
|
286
|
+
// signerAddress. User-signed actions (builder-fee, account mode,
|
|
287
|
+
// withdrawal) contribute nothing — core submits under the user's own
|
|
288
|
+
// address.
|
|
289
|
+
if (signer === PerpsSigner.SDK) {
|
|
271
290
|
const agent = await agentStore.get(address)
|
|
272
291
|
return { signerAddress: agent.address }
|
|
273
292
|
}
|
|
@@ -282,6 +301,21 @@ export function hyperliquidProvider(
|
|
|
282
301
|
): Promise<SignedActionStep[]> =>
|
|
283
302
|
hyperliquidSignActions(agentStore, method, steps, address, ctx),
|
|
284
303
|
|
|
304
|
+
// A confirmed session-agent revoke leaves the stored key unusable.
|
|
305
|
+
onExecuteResults: async (
|
|
306
|
+
address: Address,
|
|
307
|
+
results: ActionResult[]
|
|
308
|
+
): Promise<void> => {
|
|
309
|
+
if (
|
|
310
|
+
results.some(
|
|
311
|
+
(result) =>
|
|
312
|
+
result.success && result.action === ActionType.REVOKE_SESSION_AGENT
|
|
313
|
+
)
|
|
314
|
+
) {
|
|
315
|
+
await agentStore.remove(address)
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
|
|
285
319
|
getAccount: (
|
|
286
320
|
params: ProviderGetAccountParams,
|
|
287
321
|
opts?: SDKRequestOptions
|
|
@@ -329,7 +363,7 @@ export function hyperliquidProvider(
|
|
|
329
363
|
getMarketSettings: (
|
|
330
364
|
params: ProviderGetMarketSettingsParams,
|
|
331
365
|
opts?: SDKRequestOptions
|
|
332
|
-
): Promise<MarketSettings
|
|
366
|
+
): Promise<MarketSettings> =>
|
|
333
367
|
getMarketSettings(
|
|
334
368
|
contextRef.require(),
|
|
335
369
|
{ address: params.address, market: params.market },
|
|
@@ -456,8 +490,7 @@ export function hyperliquidProvider(
|
|
|
456
490
|
|
|
457
491
|
projectConfig: (
|
|
458
492
|
config: AccountConfig,
|
|
459
|
-
setup:
|
|
460
|
-
options: ProviderAction[]
|
|
493
|
+
setup: SetupAction[]
|
|
461
494
|
): AccountConfigSetting[] => {
|
|
462
495
|
if (config.provider !== PROVIDER_KEY) {
|
|
463
496
|
throw new PerpsError(
|
|
@@ -466,7 +499,7 @@ export function hyperliquidProvider(
|
|
|
466
499
|
`'${config.provider}'.`
|
|
467
500
|
)
|
|
468
501
|
}
|
|
469
|
-
return projectHyperliquidConfigSettings(config, setup
|
|
502
|
+
return projectHyperliquidConfigSettings(config, setup)
|
|
470
503
|
},
|
|
471
504
|
}
|
|
472
505
|
}
|
package/src/accountConfig.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import { PerpsError } from '@lifi/perps-sdk'
|
|
1
|
+
import { isSetupOptionFor, PerpsError } from '@lifi/perps-sdk'
|
|
2
2
|
import type {
|
|
3
3
|
AccountConfigSetting,
|
|
4
4
|
HyperliquidAccountConfig,
|
|
5
|
-
|
|
5
|
+
SetupAction,
|
|
6
6
|
} from '@lifi/perps-types'
|
|
7
7
|
import { ActionType, PerpsErrorCode } from '@lifi/perps-types'
|
|
8
|
+
import { HlAbstractionMode } from './types/account.js'
|
|
9
|
+
|
|
10
|
+
// `null`, `'default'` and `'disabled'` are the same off state; an option may
|
|
11
|
+
// bind either spelling of it.
|
|
12
|
+
const ABSTRACTION_OFF_VALUES: ReadonlySet<string> = new Set([
|
|
13
|
+
HlAbstractionMode.DEFAULT,
|
|
14
|
+
HlAbstractionMode.DISABLED,
|
|
15
|
+
])
|
|
8
16
|
|
|
9
17
|
function assertNever(value: never): never {
|
|
10
18
|
throw new Error(
|
|
@@ -24,14 +32,14 @@ function assertNever(value: never): never {
|
|
|
24
32
|
* | REVOKE_AGENT | [] (no parameters)
|
|
25
33
|
* | APPROVE_BUILDER_FEE | [] (no parameters)
|
|
26
34
|
* | SET_REFERRAL | [] (no parameters)
|
|
27
|
-
* | ACCOUNT_MODE | [{ name: 'mode', value: config.abstractionMode }]
|
|
35
|
+
* | ACCOUNT_MODE | [{ name: 'mode', value: config.abstractionMode }] (off → bound off spelling)
|
|
28
36
|
*
|
|
29
37
|
* The switch is exhaustive over `ActionType` so enum additions force a
|
|
30
38
|
* compile error in the `default` arm. ActionTypes that are not valid on
|
|
31
|
-
* `Provider.setup`
|
|
39
|
+
* `Provider.setup` for Hyperliquid throw at runtime.
|
|
32
40
|
*/
|
|
33
41
|
function projectHyperliquidDescriptor(
|
|
34
|
-
descriptor:
|
|
42
|
+
descriptor: SetupAction,
|
|
35
43
|
config: HyperliquidAccountConfig
|
|
36
44
|
): AccountConfigSetting {
|
|
37
45
|
switch (descriptor.type) {
|
|
@@ -41,11 +49,25 @@ function projectHyperliquidDescriptor(
|
|
|
41
49
|
case ActionType.SET_REFERRAL:
|
|
42
50
|
return { type: descriptor.type, values: [] }
|
|
43
51
|
|
|
44
|
-
case ActionType.ACCOUNT_MODE:
|
|
52
|
+
case ActionType.ACCOUNT_MODE: {
|
|
53
|
+
const mode = config.abstractionMode
|
|
54
|
+
const bound = (descriptor.options ?? []).flatMap((option) =>
|
|
55
|
+
isSetupOptionFor(option, ActionType.ACCOUNT_MODE)
|
|
56
|
+
? [option.params.mode]
|
|
57
|
+
: []
|
|
58
|
+
)
|
|
59
|
+
// The off state projects as whichever off spelling an option binds, so
|
|
60
|
+
// the value always matches the option it is satisfied by.
|
|
61
|
+
const matched =
|
|
62
|
+
mode === null || ABSTRACTION_OFF_VALUES.has(mode)
|
|
63
|
+
? bound.find((value) => ABSTRACTION_OFF_VALUES.has(value))
|
|
64
|
+
: bound.find((value) => value === mode)
|
|
45
65
|
return {
|
|
46
66
|
type: descriptor.type,
|
|
47
|
-
values: [{ name: 'mode', value:
|
|
67
|
+
values: [{ name: 'mode', value: matched ?? mode }],
|
|
68
|
+
satisfied: matched !== undefined,
|
|
48
69
|
}
|
|
70
|
+
}
|
|
49
71
|
|
|
50
72
|
case ActionType.APPROVE_INTEGRATOR:
|
|
51
73
|
case ActionType.ACCOUNT_TYPE:
|
|
@@ -73,11 +95,14 @@ function projectHyperliquidDescriptor(
|
|
|
73
95
|
case ActionType.META_ONBOARD:
|
|
74
96
|
case ActionType.META_CREATE_REFERRAL_CODE:
|
|
75
97
|
case ActionType.SYNC_FEE_ATTRIBUTION:
|
|
98
|
+
case ActionType.REVOKE_BUILDER_FEE:
|
|
99
|
+
case ActionType.REVOKE_SESSION_AGENT:
|
|
100
|
+
case ActionType.REVOKE_INTEGRATOR:
|
|
76
101
|
throw new PerpsError(
|
|
77
102
|
PerpsErrorCode.SDKError,
|
|
78
103
|
`Hyperliquid account-config mapper has no projection for ` +
|
|
79
104
|
`descriptor type '${descriptor.type}' — this ActionType is not ` +
|
|
80
|
-
`valid on Provider.setup
|
|
105
|
+
`valid on Provider.setup for Hyperliquid.`
|
|
81
106
|
)
|
|
82
107
|
|
|
83
108
|
default:
|
|
@@ -86,18 +111,17 @@ function projectHyperliquidDescriptor(
|
|
|
86
111
|
}
|
|
87
112
|
|
|
88
113
|
/**
|
|
89
|
-
* Project the
|
|
90
|
-
*
|
|
91
|
-
*
|
|
114
|
+
* Project the Hyperliquid setup descriptors against the typed
|
|
115
|
+
* `HyperliquidAccountConfig`. Produces exactly one `AccountConfigSetting` per
|
|
116
|
+
* descriptor, in `Provider.setup` order.
|
|
92
117
|
*
|
|
93
118
|
* @public
|
|
94
119
|
*/
|
|
95
120
|
export function projectHyperliquidConfigSettings(
|
|
96
121
|
config: HyperliquidAccountConfig,
|
|
97
|
-
setup:
|
|
98
|
-
options: ProviderAction[]
|
|
122
|
+
setup: SetupAction[]
|
|
99
123
|
): AccountConfigSetting[] {
|
|
100
|
-
return
|
|
124
|
+
return setup.map((descriptor) =>
|
|
101
125
|
projectHyperliquidDescriptor(descriptor, config)
|
|
102
126
|
)
|
|
103
127
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getMarketRegistry,
|
|
3
3
|
getMarketsContext,
|
|
4
|
+
maxOf,
|
|
5
|
+
minOf,
|
|
4
6
|
type ProviderGetAccountParams,
|
|
5
7
|
type SDKRequestOptions,
|
|
6
8
|
stringToFloat,
|
|
@@ -14,7 +16,7 @@ import type {
|
|
|
14
16
|
HyperliquidDexAccountState,
|
|
15
17
|
Position,
|
|
16
18
|
} from '@lifi/perps-types'
|
|
17
|
-
import
|
|
19
|
+
import Big from 'big.js'
|
|
18
20
|
import { PROVIDER_KEY } from '../constants.js'
|
|
19
21
|
import type { HyperliquidContext } from '../context.js'
|
|
20
22
|
import type {
|
|
@@ -66,13 +68,6 @@ interface BalancePartition {
|
|
|
66
68
|
collateralBalances: Balance[]
|
|
67
69
|
}
|
|
68
70
|
|
|
69
|
-
const transferableWithin = (venueFigure: Big, units: Big): string => {
|
|
70
|
-
if (venueFigure.lt(0)) {
|
|
71
|
-
return '0'
|
|
72
|
-
}
|
|
73
|
-
return (venueFigure.gt(units) ? units : venueFigure).toFixed()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
71
|
const buildBalances = (
|
|
77
72
|
abstraction: HlAbstractionMode | null,
|
|
78
73
|
spotState: HlSpotClearinghouseState,
|
|
@@ -84,7 +79,19 @@ const buildBalances = (
|
|
|
84
79
|
const { balances, collateralBalances } = partitionSpotBalances(
|
|
85
80
|
spotState.balances
|
|
86
81
|
.filter((b) => !assetIsOutcome(b.coin))
|
|
87
|
-
.map((b) =>
|
|
82
|
+
.map((b) => {
|
|
83
|
+
const total = toWireBig(b.total, 'spotClearinghouseState.total')
|
|
84
|
+
return {
|
|
85
|
+
...spotBalance(spotAssetFromToken(b), b.total, priceById),
|
|
86
|
+
transferable: minOf(
|
|
87
|
+
maxOf(
|
|
88
|
+
total.minus(toWireBig(b.hold, 'spotClearinghouseState.hold')),
|
|
89
|
+
new Big(0)
|
|
90
|
+
),
|
|
91
|
+
total
|
|
92
|
+
).toFixed(),
|
|
93
|
+
}
|
|
94
|
+
}),
|
|
88
95
|
quoteAssetIds
|
|
89
96
|
)
|
|
90
97
|
|
|
@@ -107,10 +114,13 @@ const buildBalances = (
|
|
|
107
114
|
units: value.toFixed(),
|
|
108
115
|
valueUsd: value.toFixed(),
|
|
109
116
|
price: '1',
|
|
110
|
-
transferable:
|
|
111
|
-
|
|
117
|
+
transferable: minOf(
|
|
118
|
+
maxOf(
|
|
119
|
+
toWireBig(state.withdrawable, 'clearinghouseState.withdrawable'),
|
|
120
|
+
new Big(0)
|
|
121
|
+
),
|
|
112
122
|
value
|
|
113
|
-
),
|
|
123
|
+
).toFixed(),
|
|
114
124
|
})
|
|
115
125
|
}
|
|
116
126
|
}
|
|
@@ -24,7 +24,9 @@ import type {
|
|
|
24
24
|
HlUserNonFundingLedgerUpdates,
|
|
25
25
|
} from '../types/index.js'
|
|
26
26
|
import {
|
|
27
|
+
isBorrowLendDelta,
|
|
27
28
|
isCollateralTransferDelta,
|
|
29
|
+
isCStakingTransferDelta,
|
|
28
30
|
isDepositDelta,
|
|
29
31
|
isLiquidationDelta,
|
|
30
32
|
isSendAssetDelta,
|
|
@@ -130,7 +132,9 @@ const fetchActivityData = async (
|
|
|
130
132
|
(isSpotTransferDelta(entry.delta) ||
|
|
131
133
|
isSendAssetDelta(entry.delta) ||
|
|
132
134
|
isCollateralTransferDelta(entry.delta) ||
|
|
133
|
-
isVaultTransferDelta(entry.delta)
|
|
135
|
+
isVaultTransferDelta(entry.delta) ||
|
|
136
|
+
isCStakingTransferDelta(entry.delta) ||
|
|
137
|
+
isBorrowLendDelta(entry.delta))))
|
|
134
138
|
) {
|
|
135
139
|
return []
|
|
136
140
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { PerpsError, type SDKRequestOptions } from '@lifi/perps-sdk'
|
|
2
2
|
import {
|
|
3
3
|
MarginMode,
|
|
4
4
|
type MarketRef,
|
|
5
5
|
type MarketSettings,
|
|
6
|
+
PerpsErrorCode,
|
|
6
7
|
} from '@lifi/perps-types'
|
|
7
8
|
import type { Address } from 'viem'
|
|
8
|
-
import { SPOT_MARKET_ID } from '../constants.js'
|
|
9
|
+
import { PROVIDER_KEY, SPOT_MARKET_ID } from '../constants.js'
|
|
9
10
|
import type { HyperliquidContext } from '../context.js'
|
|
10
11
|
import { fetchActiveAssetData } from './activeAssetData.js'
|
|
11
12
|
|
|
@@ -22,17 +23,23 @@ export interface GetMarketSettingsParams {
|
|
|
22
23
|
/**
|
|
23
24
|
* The user's venue-stored margin mode and leverage for one perps market,
|
|
24
25
|
* read from `activeAssetData` — present whether or not a position is open.
|
|
25
|
-
* @throws {PerpsError}
|
|
26
|
+
* @throws {PerpsError} `ValidationError` for a spot market; `SDKError` when
|
|
27
|
+
* the response carries no positive leverage; any other `PerpsError` on
|
|
28
|
+
* Hyperliquid REST error, network, or parsing failures.
|
|
26
29
|
* @public
|
|
27
30
|
*/
|
|
28
31
|
export const getMarketSettings = async (
|
|
29
32
|
context: HyperliquidContext,
|
|
30
33
|
params: GetMarketSettingsParams,
|
|
31
34
|
options?: SDKRequestOptions
|
|
32
|
-
): Promise<MarketSettings
|
|
33
|
-
// Spot markets carry no leverage state on the venue.
|
|
35
|
+
): Promise<MarketSettings> => {
|
|
34
36
|
if (params.market.categoryId === SPOT_MARKET_ID) {
|
|
35
|
-
|
|
37
|
+
const error = new PerpsError(
|
|
38
|
+
PerpsErrorCode.ValidationError,
|
|
39
|
+
`Hyperliquid market '${params.market.marketId}' is a spot market and carries no leverage setting`
|
|
40
|
+
)
|
|
41
|
+
error.tool = PROVIDER_KEY
|
|
42
|
+
throw error
|
|
36
43
|
}
|
|
37
44
|
const data = await fetchActiveAssetData(
|
|
38
45
|
context,
|
|
@@ -41,8 +48,13 @@ export const getMarketSettings = async (
|
|
|
41
48
|
options
|
|
42
49
|
)
|
|
43
50
|
const leverage = data.leverage
|
|
44
|
-
if (!leverage) {
|
|
45
|
-
|
|
51
|
+
if (!leverage || !(leverage.value > 0)) {
|
|
52
|
+
const error = new PerpsError(
|
|
53
|
+
PerpsErrorCode.SDKError,
|
|
54
|
+
`Hyperliquid returned no leverage for market '${params.market.marketId}'`
|
|
55
|
+
)
|
|
56
|
+
error.tool = PROVIDER_KEY
|
|
57
|
+
throw error
|
|
46
58
|
}
|
|
47
59
|
return {
|
|
48
60
|
marginMode:
|
|
@@ -39,9 +39,10 @@ export async function hyperliquidSignActions(
|
|
|
39
39
|
)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
const sign =
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
const sign =
|
|
43
|
+
ctx?.signer === PerpsSigner.USER
|
|
44
|
+
? await userWalletSigner(ctx)
|
|
45
|
+
: await agentSigner(agentStore, address)
|
|
45
46
|
|
|
46
47
|
return Promise.all(
|
|
47
48
|
(steps as Eip712ActionStep[]).map(
|
package/src/types/ledger.ts
CHANGED
|
@@ -130,6 +130,38 @@ export type HlLiquidationDelta = {
|
|
|
130
130
|
liquidatedPositions?: HlLiquidatedPosition[]
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
/**
|
|
134
|
+
* Hyperliquid `cStakingTransfer` ledger delta. Moves `token` between the
|
|
135
|
+
* account's spot balance and its staking balance; `isDeposit` is `true` for a
|
|
136
|
+
* move into staking. `amount` is unsigned. A completed unstake row carries the
|
|
137
|
+
* zero hash.
|
|
138
|
+
* @public
|
|
139
|
+
*/
|
|
140
|
+
export type HlCStakingTransferDelta = {
|
|
141
|
+
type: 'cStakingTransfer'
|
|
142
|
+
token: string
|
|
143
|
+
amount: string
|
|
144
|
+
isDeposit: boolean
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Borrow-lend market action named by a `borrowLend` ledger delta. @public */
|
|
148
|
+
export type HlBorrowLendOperation = 'supply' | 'withdraw' | 'borrow' | 'repay'
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Hyperliquid `borrowLend` ledger delta. Moves `token` between the account's
|
|
152
|
+
* spot balance and the borrow-lend market; `operation` gives the direction and
|
|
153
|
+
* `amount` is unsigned. `interestAmount` is the accrued interest that
|
|
154
|
+
* `amount` includes on a `withdraw` or `repay`, and `"0.0"` otherwise.
|
|
155
|
+
* @public
|
|
156
|
+
*/
|
|
157
|
+
export type HlBorrowLendDelta = {
|
|
158
|
+
type: 'borrowLend'
|
|
159
|
+
token: string
|
|
160
|
+
operation: HlBorrowLendOperation
|
|
161
|
+
amount: string
|
|
162
|
+
interestAmount: string
|
|
163
|
+
}
|
|
164
|
+
|
|
133
165
|
/**
|
|
134
166
|
* Union of known Hyperliquid non-funding ledger deltas plus an open fallback
|
|
135
167
|
* for endpoint variants the provider does not map.
|
|
@@ -143,6 +175,8 @@ export type HlLedgerDelta =
|
|
|
143
175
|
| HlDepositDelta
|
|
144
176
|
| HlWithdrawDelta
|
|
145
177
|
| HlLiquidationDelta
|
|
178
|
+
| HlCStakingTransferDelta
|
|
179
|
+
| HlBorrowLendDelta
|
|
146
180
|
| {
|
|
147
181
|
type: string
|
|
148
182
|
usdc?: string
|
|
@@ -202,6 +236,33 @@ export const isLiquidationDelta = (
|
|
|
202
236
|
delta: HlLedgerDelta
|
|
203
237
|
): delta is HlLiquidationDelta => delta.type === 'liquidation'
|
|
204
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Type guard for `HlCStakingTransferDelta`. `isDeposit` is part of the guard:
|
|
241
|
+
* a row without a boolean `isDeposit` names no direction for the movement.
|
|
242
|
+
* @public
|
|
243
|
+
*/
|
|
244
|
+
export const isCStakingTransferDelta = (
|
|
245
|
+
delta: HlLedgerDelta
|
|
246
|
+
): delta is HlCStakingTransferDelta =>
|
|
247
|
+
delta.type === 'cStakingTransfer' &&
|
|
248
|
+
'isDeposit' in delta &&
|
|
249
|
+
typeof delta.isDeposit === 'boolean'
|
|
250
|
+
|
|
251
|
+
const HL_BORROW_LEND_OPERATIONS: ReadonlySet<unknown> =
|
|
252
|
+
new Set<HlBorrowLendOperation>(['supply', 'withdraw', 'borrow', 'repay'])
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Type guard for `HlBorrowLendDelta`. The operation is part of the guard: an
|
|
256
|
+
* unknown operation names no direction for the movement.
|
|
257
|
+
* @public
|
|
258
|
+
*/
|
|
259
|
+
export const isBorrowLendDelta = (
|
|
260
|
+
delta: HlLedgerDelta
|
|
261
|
+
): delta is HlBorrowLendDelta =>
|
|
262
|
+
delta.type === 'borrowLend' &&
|
|
263
|
+
'operation' in delta &&
|
|
264
|
+
HL_BORROW_LEND_OPERATIONS.has(delta.operation)
|
|
265
|
+
|
|
205
266
|
/**
|
|
206
267
|
* One timestamped non-funding ledger update. `time` is milliseconds since
|
|
207
268
|
* epoch and `hash` is the upstream transaction identifier.
|
package/src/utils/mapActivity.ts
CHANGED
|
@@ -17,13 +17,17 @@ import type {
|
|
|
17
17
|
} from '@lifi/perps-types'
|
|
18
18
|
import { ActivityType, PerpsErrorCode } from '@lifi/perps-types'
|
|
19
19
|
import Big from 'big.js'
|
|
20
|
+
import { zeroHash } from 'viem'
|
|
20
21
|
import type {
|
|
22
|
+
HlBorrowLendOperation,
|
|
21
23
|
HlFundingUpdate,
|
|
22
24
|
HlLedgerUpdate,
|
|
23
25
|
HlUserFill,
|
|
24
26
|
} from '../types/index.js'
|
|
25
27
|
import {
|
|
28
|
+
isBorrowLendDelta,
|
|
26
29
|
isCollateralTransferDelta,
|
|
30
|
+
isCStakingTransferDelta,
|
|
27
31
|
isDepositDelta,
|
|
28
32
|
isLiquidationDelta,
|
|
29
33
|
isSendAssetDelta,
|
|
@@ -47,6 +51,17 @@ const HL_NATIVE_TOKEN_SYMBOL = 'HYPE'
|
|
|
47
51
|
// Hyperliquid reserves spot token index 0 for USDC.
|
|
48
52
|
const HL_COLLATERAL_ASSET_ID = '0'
|
|
49
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Direction relative to the account's spot balance, the same frame as an
|
|
56
|
+
* outbound `vaultDeposit`.
|
|
57
|
+
*/
|
|
58
|
+
const BORROW_LEND_DIRECTION: Record<HlBorrowLendOperation, 'IN' | 'OUT'> = {
|
|
59
|
+
supply: 'OUT',
|
|
60
|
+
withdraw: 'IN',
|
|
61
|
+
borrow: 'IN',
|
|
62
|
+
repay: 'OUT',
|
|
63
|
+
}
|
|
64
|
+
|
|
50
65
|
/**
|
|
51
66
|
* A ledger row moves a spot asset — a perp contract cannot be sent, received
|
|
52
67
|
* or withdrawn — so the delta's token symbol resolves inside the spot asset
|
|
@@ -69,6 +84,7 @@ const resolveLedgerAsset = (symbol: string, registry: AssetRegistry): Asset => {
|
|
|
69
84
|
|
|
70
85
|
/**
|
|
71
86
|
* Map supported ledger entries; exclude same-account moves and unsupported types.
|
|
87
|
+
* Staking and borrow-lend moves map with the queried account as counterparty.
|
|
72
88
|
* Missing assets throw; unresolved liquidation markets omit only those positions.
|
|
73
89
|
* @public
|
|
74
90
|
*/
|
|
@@ -143,6 +159,46 @@ export const mapLedgerEntry = (
|
|
|
143
159
|
} satisfies TransferActivity
|
|
144
160
|
}
|
|
145
161
|
|
|
162
|
+
if (isCStakingTransferDelta(delta) || isBorrowLendDelta(delta)) {
|
|
163
|
+
// A completed unstake is a system event with the zero hash and no venue
|
|
164
|
+
// transaction, so the id is synthesized and no explorer link applies.
|
|
165
|
+
const hasTransaction = entry.hash !== zeroHash
|
|
166
|
+
const direction = isCStakingTransferDelta(delta)
|
|
167
|
+
? delta.isDeposit
|
|
168
|
+
? 'OUT'
|
|
169
|
+
: 'IN'
|
|
170
|
+
: BORROW_LEND_DIRECTION[delta.operation]
|
|
171
|
+
const action = isCStakingTransferDelta(delta) ? direction : delta.operation
|
|
172
|
+
return {
|
|
173
|
+
...base,
|
|
174
|
+
...(hasTransaction
|
|
175
|
+
? {}
|
|
176
|
+
: {
|
|
177
|
+
id: `${delta.type}:${delta.token}:${action}:${delta.amount}:${base.timestamp}`,
|
|
178
|
+
}),
|
|
179
|
+
type: ActivityType.TRANSFER,
|
|
180
|
+
direction,
|
|
181
|
+
counterpartyAddress: queriedAddress.toLowerCase(),
|
|
182
|
+
asset: resolveLedgerAsset(delta.token, assetRegistry),
|
|
183
|
+
amount: delta.amount,
|
|
184
|
+
meta: isCStakingTransferDelta(delta)
|
|
185
|
+
? { transferType: delta.type }
|
|
186
|
+
: {
|
|
187
|
+
transferType: delta.type,
|
|
188
|
+
operation: delta.operation,
|
|
189
|
+
interestAmount: delta.interestAmount,
|
|
190
|
+
},
|
|
191
|
+
...(hasTransaction
|
|
192
|
+
? {
|
|
193
|
+
explorerLink: explorerTxUrl(
|
|
194
|
+
ExplorerChainId.HYPERLIQUID,
|
|
195
|
+
entry.hash
|
|
196
|
+
),
|
|
197
|
+
}
|
|
198
|
+
: {}),
|
|
199
|
+
} satisfies TransferActivity
|
|
200
|
+
}
|
|
201
|
+
|
|
146
202
|
if (isVaultTransferDelta(delta)) {
|
|
147
203
|
const queried = queriedAddress.toLowerCase()
|
|
148
204
|
const vault = delta.vault.toLowerCase()
|
|
@@ -17,7 +17,8 @@ import { toWireBig } from './decimal.js'
|
|
|
17
17
|
*
|
|
18
18
|
* @param quoteAssetId - `Asset.id` the perps-route row is keyed by.
|
|
19
19
|
* @param withdrawalFee - Flat venue fee in quote-asset units, set on every
|
|
20
|
-
* quote-asset row.
|
|
20
|
+
* quote-asset row. Hyperliquid deducts it from the requested amount. Absent
|
|
21
|
+
* leaves every row without a fee.
|
|
21
22
|
* @public
|
|
22
23
|
*/
|
|
23
24
|
export const hyperliquidWithdrawableBalances = (
|
|
@@ -32,8 +33,12 @@ export const hyperliquidWithdrawableBalances = (
|
|
|
32
33
|
withdrawalFee === undefined
|
|
33
34
|
? undefined
|
|
34
35
|
: toWireBig(withdrawalFee, 'providers.withdrawalFeeUsd').toFixed()
|
|
35
|
-
const feeFor = (
|
|
36
|
-
|
|
36
|
+
const feeFor = (
|
|
37
|
+
assetId: string
|
|
38
|
+
): Pick<ProviderWithdrawableBalance, 'withdrawalFee' | 'isFeeDeducted'> =>
|
|
39
|
+
fee === undefined || assetId !== quoteAssetId
|
|
40
|
+
? {}
|
|
41
|
+
: { withdrawalFee: fee, isFeeDeducted: true }
|
|
37
42
|
|
|
38
43
|
if (isUnifiedAbstraction(abstraction)) {
|
|
39
44
|
for (const balance of spotState.balances) {
|