@lifi/perps-sdk-provider-lighter 4.0.0 → 5.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 +16 -3
- package/dist/cjs/LighterProvider.d.ts +3 -0
- package/dist/cjs/LighterProvider.d.ts.map +1 -1
- package/dist/cjs/LighterProvider.js +35 -29
- package/dist/cjs/LighterProvider.js.map +1 -1
- package/dist/cjs/constants.d.ts +19 -1
- package/dist/cjs/constants.d.ts.map +1 -1
- package/dist/cjs/constants.js +21 -1
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +9 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/signers/LighterReadOnlyTokenManager.d.ts +3 -1
- package/dist/cjs/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
- package/dist/cjs/signers/LighterReadOnlyTokenManager.js +4 -3
- package/dist/cjs/signers/LighterReadOnlyTokenManager.js.map +1 -1
- package/dist/cjs/signers/LighterSigner.d.ts +4 -0
- package/dist/cjs/signers/LighterSigner.d.ts.map +1 -1
- package/dist/cjs/signers/LighterSigner.js +26 -3
- package/dist/cjs/signers/LighterSigner.js.map +1 -1
- package/dist/cjs/signers/signActions.d.ts.map +1 -1
- package/dist/cjs/signers/signActions.js +30 -0
- package/dist/cjs/signers/signActions.js.map +1 -1
- package/dist/cjs/signers/wasmLoader.d.ts +3 -1
- package/dist/cjs/signers/wasmLoader.d.ts.map +1 -1
- package/dist/cjs/signers/wasmLoader.js.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/cjs/websocket/LighterWsProvider.js +5 -5
- package/dist/cjs/websocket/LighterWsProvider.js.map +1 -1
- package/dist/esm/LighterProvider.d.ts +15 -0
- package/dist/esm/LighterProvider.d.ts.map +1 -1
- package/dist/esm/LighterProvider.js +37 -31
- package/dist/esm/LighterProvider.js.map +1 -1
- package/dist/esm/constants.d.ts +81 -1
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +50 -0
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/signers/LighterReadOnlyTokenManager.d.ts +9 -1
- package/dist/esm/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
- package/dist/esm/signers/LighterReadOnlyTokenManager.js +5 -4
- package/dist/esm/signers/LighterReadOnlyTokenManager.js.map +1 -1
- package/dist/esm/signers/LighterSigner.d.ts +21 -6
- package/dist/esm/signers/LighterSigner.d.ts.map +1 -1
- package/dist/esm/signers/LighterSigner.js +42 -10
- package/dist/esm/signers/LighterSigner.js.map +1 -1
- package/dist/esm/signers/signActions.d.ts.map +1 -1
- package/dist/esm/signers/signActions.js +42 -0
- package/dist/esm/signers/signActions.js.map +1 -1
- package/dist/esm/signers/wasmLoader.d.ts +6 -2
- package/dist/esm/signers/wasmLoader.d.ts.map +1 -1
- package/dist/esm/signers/wasmLoader.js.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.d.ts.map +1 -1
- package/dist/esm/websocket/LighterWsProvider.js +5 -5
- package/dist/esm/websocket/LighterWsProvider.js.map +1 -1
- package/dist/types/LighterProvider.d.ts +15 -0
- package/dist/types/LighterProvider.d.ts.map +1 -1
- package/dist/types/constants.d.ts +81 -1
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/signers/LighterReadOnlyTokenManager.d.ts +9 -1
- package/dist/types/signers/LighterReadOnlyTokenManager.d.ts.map +1 -1
- package/dist/types/signers/LighterSigner.d.ts +21 -6
- package/dist/types/signers/LighterSigner.d.ts.map +1 -1
- package/dist/types/signers/signActions.d.ts.map +1 -1
- package/dist/types/signers/wasmLoader.d.ts +6 -2
- package/dist/types/signers/wasmLoader.d.ts.map +1 -1
- package/dist/types/websocket/LighterWsProvider.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/LighterProvider.ts +53 -40
- package/src/constants.ts +104 -1
- package/src/index.ts +12 -0
- package/src/signers/LighterReadOnlyTokenManager.ts +13 -5
- package/src/signers/LighterSigner.ts +67 -12
- package/src/signers/signActions.ts +57 -0
- package/src/signers/wasmLoader.ts +6 -2
- package/src/websocket/LighterWsProvider.ts +5 -5
package/src/constants.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FeeTier } from '@lifi/perps-types'
|
|
1
|
+
import type { FeeTier, LighterProviderKey } from '@lifi/perps-types'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Lighter provider key as it appears on `Provider.key` from the backend.
|
|
@@ -24,6 +24,109 @@ export const LIGHTER_SPOT_CATEGORY_ID = 'spot'
|
|
|
24
24
|
export const DEFAULT_LIGHTER_REST_URL = 'https://mainnet.zklighter.elliot.ai'
|
|
25
25
|
export const DEFAULT_LIGHTER_WS_URL = 'wss://mainnet.zklighter.elliot.ai/stream'
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* zkLighter L2 signing chain id fed to {@link LighterSigner} on mainnet.
|
|
29
|
+
*
|
|
30
|
+
* @public
|
|
31
|
+
*/
|
|
32
|
+
export const DEFAULT_LIGHTER_SIGNER_CHAIN_ID = 304
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Base URL the mainnet Lighter zkLighter explorer resolves a tx hash against
|
|
36
|
+
* (`${base}${txHash}`).
|
|
37
|
+
*
|
|
38
|
+
* @public
|
|
39
|
+
*/
|
|
40
|
+
export const DEFAULT_LIGHTER_EXPLORER_TX_BASE_URL =
|
|
41
|
+
'https://app.lighter.xyz/explorer/logs/'
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The venue-specific facts that distinguish one Lighter deployment from
|
|
45
|
+
* another. One provider + WS pair is built per instance from these; a client
|
|
46
|
+
* may register several so long as their `providerKey`s differ.
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export interface LighterInstanceConfig {
|
|
51
|
+
/** Wire-visible `Provider.key` and the plugin `type`; unique per instance. */
|
|
52
|
+
providerKey: LighterProviderKey
|
|
53
|
+
restUrl: string
|
|
54
|
+
wsUrl: string
|
|
55
|
+
/**
|
|
56
|
+
* zkLighter L2 signing chain id to construct this instance's
|
|
57
|
+
* {@link LighterSigner} with.
|
|
58
|
+
*/
|
|
59
|
+
signerChainId: number
|
|
60
|
+
/**
|
|
61
|
+
* Explorer tx base URL for transfer-activity links (`${base}${txHash}`).
|
|
62
|
+
* When omitted, transfer links are not emitted for this instance.
|
|
63
|
+
*/
|
|
64
|
+
explorerTxBaseUrl?: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* The built-in mainnet Lighter instance — the defaults `lighterProvider()` and
|
|
69
|
+
* `lighterWsProvider()` apply when constructed with no instance overrides.
|
|
70
|
+
*
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
export const LIGHTER_MAINNET_INSTANCE: LighterInstanceConfig = {
|
|
74
|
+
providerKey: LIGHTER_PROVIDER_KEY,
|
|
75
|
+
restUrl: DEFAULT_LIGHTER_REST_URL,
|
|
76
|
+
wsUrl: DEFAULT_LIGHTER_WS_URL,
|
|
77
|
+
signerChainId: DEFAULT_LIGHTER_SIGNER_CHAIN_ID,
|
|
78
|
+
explorerTxBaseUrl: DEFAULT_LIGHTER_EXPLORER_TX_BASE_URL,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Provider key for the Lighter instance running on Robinhood chain.
|
|
83
|
+
*
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
export const LIGHTER_RH_PROVIDER_KEY = 'lighter-rh'
|
|
87
|
+
|
|
88
|
+
/** @public */
|
|
89
|
+
export const LIGHTER_RH_REST_URL = 'https://api.rh.lighter.xyz'
|
|
90
|
+
/** @public */
|
|
91
|
+
export const LIGHTER_RH_WS_URL = 'wss://api.rh.lighter.xyz/stream'
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Deployment-supplied facts the RH instance cannot source from public
|
|
95
|
+
* documentation and the caller must confirm.
|
|
96
|
+
*
|
|
97
|
+
* @public
|
|
98
|
+
*/
|
|
99
|
+
export interface LighterRhInstanceOverrides {
|
|
100
|
+
/**
|
|
101
|
+
* zkLighter L2 signing chain id for the RH instance. Unverified from public
|
|
102
|
+
* sources — must be the value confirmed against lighter-go / RH support, not
|
|
103
|
+
* mainnet's 304 nor the RH L1 chain id 4663.
|
|
104
|
+
*/
|
|
105
|
+
signerChainId: number
|
|
106
|
+
/**
|
|
107
|
+
* RH zkLighter explorer tx base URL. Omit until confirmed; transfer links are
|
|
108
|
+
* then left unset rather than pointed at the mainnet explorer.
|
|
109
|
+
*/
|
|
110
|
+
explorerTxBaseUrl?: string
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Build the `lighter-rh` {@link LighterInstanceConfig}. The signing chain id is
|
|
115
|
+
* a required argument because it is not verifiable from public sources — the
|
|
116
|
+
* caller must pass the value confirmed against lighter-go / RH support.
|
|
117
|
+
*
|
|
118
|
+
* @public
|
|
119
|
+
*/
|
|
120
|
+
export const lighterRhInstance = (
|
|
121
|
+
overrides: LighterRhInstanceOverrides
|
|
122
|
+
): LighterInstanceConfig => ({
|
|
123
|
+
providerKey: LIGHTER_RH_PROVIDER_KEY,
|
|
124
|
+
restUrl: LIGHTER_RH_REST_URL,
|
|
125
|
+
wsUrl: LIGHTER_RH_WS_URL,
|
|
126
|
+
signerChainId: overrides.signerChainId,
|
|
127
|
+
explorerTxBaseUrl: overrides.explorerTxBaseUrl,
|
|
128
|
+
})
|
|
129
|
+
|
|
27
130
|
/** @internal */
|
|
28
131
|
export const DEFAULT_TRADES_LIMIT = 50
|
|
29
132
|
|
package/src/index.ts
CHANGED
|
@@ -15,11 +15,23 @@
|
|
|
15
15
|
|
|
16
16
|
export { projectLighterConfigSettings } from './accountConfig.js'
|
|
17
17
|
export { getAccountSummary } from './accountSummary.js'
|
|
18
|
+
export type {
|
|
19
|
+
LighterInstanceConfig,
|
|
20
|
+
LighterRhInstanceOverrides,
|
|
21
|
+
} from './constants.js'
|
|
18
22
|
export {
|
|
19
23
|
DEFAULT_API_KEY_INDEX,
|
|
24
|
+
DEFAULT_LIGHTER_EXPLORER_TX_BASE_URL,
|
|
20
25
|
DEFAULT_LIGHTER_REST_URL,
|
|
26
|
+
DEFAULT_LIGHTER_SIGNER_CHAIN_ID,
|
|
27
|
+
DEFAULT_LIGHTER_WS_URL,
|
|
28
|
+
LIGHTER_MAINNET_INSTANCE,
|
|
21
29
|
LIGHTER_PROVIDER_KEY,
|
|
30
|
+
LIGHTER_RH_PROVIDER_KEY,
|
|
31
|
+
LIGHTER_RH_REST_URL,
|
|
32
|
+
LIGHTER_RH_WS_URL,
|
|
22
33
|
LIGHTER_SPOT_CATEGORY_ID,
|
|
34
|
+
lighterRhInstance,
|
|
23
35
|
} from './constants.js'
|
|
24
36
|
export type {
|
|
25
37
|
LighterPerpsProvider,
|
|
@@ -7,12 +7,11 @@ import {
|
|
|
7
7
|
import type {
|
|
8
8
|
ApproveReadOnlyTokenParams,
|
|
9
9
|
LighterAccountConfig,
|
|
10
|
+
LighterProviderKey,
|
|
10
11
|
} from '@lifi/perps-types'
|
|
11
12
|
import { PerpsErrorCode } from '@lifi/perps-types'
|
|
12
13
|
import type { Address } from 'viem'
|
|
13
|
-
import { DEFAULT_LIGHTER_REST_URL } from '../constants.js'
|
|
14
|
-
|
|
15
|
-
const STORAGE_PREFIX = 'lifi:perps:lighter:rotoken'
|
|
14
|
+
import { DEFAULT_LIGHTER_REST_URL, LIGHTER_PROVIDER_KEY } from '../constants.js'
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Default token name persisted alongside Lighter's `tokens/create` row.
|
|
@@ -73,6 +72,13 @@ export type LighterTokenFetcher = (params: {
|
|
|
73
72
|
/** @public */
|
|
74
73
|
export interface LighterReadOnlyTokenManagerOptions {
|
|
75
74
|
storage?: StorageAdapter
|
|
75
|
+
/**
|
|
76
|
+
* Provider instance key. Namespaces persisted tokens so two Lighter
|
|
77
|
+
* instances sharing a storage backend never serve each other's token for a
|
|
78
|
+
* coincident `(address, accountIndex)` pair, and stamps the `provider` of
|
|
79
|
+
* the {@link ApproveReadOnlyTokenResult} config. Defaults to `'lighter'`.
|
|
80
|
+
*/
|
|
81
|
+
providerKey?: LighterProviderKey
|
|
76
82
|
/** Lighter API host. Defaults to {@link DEFAULT_LIGHTER_REST_URL}. */
|
|
77
83
|
lighterApiUrl?: string
|
|
78
84
|
/** Override the multipart POST. Defaults to a `fetch`-based implementation. */
|
|
@@ -145,6 +151,7 @@ const isLighterReadOnlyToken = (
|
|
|
145
151
|
*/
|
|
146
152
|
export class LighterReadOnlyTokenManager {
|
|
147
153
|
private readonly storage: StorageAdapter
|
|
154
|
+
private readonly providerKey: LighterProviderKey
|
|
148
155
|
private readonly lighterApiUrl: string
|
|
149
156
|
private readonly fetcher: LighterTokenFetcher
|
|
150
157
|
private readonly now: () => number
|
|
@@ -152,13 +159,14 @@ export class LighterReadOnlyTokenManager {
|
|
|
152
159
|
|
|
153
160
|
constructor(options: LighterReadOnlyTokenManagerOptions = {}) {
|
|
154
161
|
this.storage = options.storage ?? localStorageAdapter
|
|
162
|
+
this.providerKey = options.providerKey ?? LIGHTER_PROVIDER_KEY
|
|
155
163
|
this.lighterApiUrl = options.lighterApiUrl ?? DEFAULT_LIGHTER_REST_URL
|
|
156
164
|
this.fetcher = options.fetcher ?? defaultLighterTokenFetcher
|
|
157
165
|
this.now = options.now ?? (() => Date.now())
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
private storageKey(address: Address, accountIndex: number): string {
|
|
161
|
-
return
|
|
169
|
+
return `lifi:perps:${this.providerKey}:rotoken:${address.toLowerCase()}:${accountIndex}`
|
|
162
170
|
}
|
|
163
171
|
|
|
164
172
|
private isExpired(token: LighterReadOnlyToken): boolean {
|
|
@@ -291,7 +299,7 @@ export class LighterReadOnlyTokenManager {
|
|
|
291
299
|
return {
|
|
292
300
|
token,
|
|
293
301
|
config: {
|
|
294
|
-
provider:
|
|
302
|
+
provider: this.providerKey,
|
|
295
303
|
accountIndex: token.accountIndex,
|
|
296
304
|
readOnlyTokenApproved: true,
|
|
297
305
|
readOnlyTokenExpiry: token.expiry,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { ActionType } from '@lifi/perps-types'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_LIGHTER_REST_URL,
|
|
4
|
+
DEFAULT_LIGHTER_SIGNER_CHAIN_ID,
|
|
5
|
+
} from '../constants.js'
|
|
3
6
|
import {
|
|
4
7
|
LT_ASSET_ID_USDC,
|
|
5
8
|
LT_ROUTE_PERP,
|
|
@@ -49,14 +52,18 @@ export interface ChangePubKeyResult extends LighterSignedBlob {
|
|
|
49
52
|
messageToSign: string
|
|
50
53
|
}
|
|
51
54
|
|
|
55
|
+
/** @public */
|
|
56
|
+
export interface ApproveIntegratorResult extends LighterSignedBlob {
|
|
57
|
+
/** EIP-191 `L2ApproveIntegrator` L1 body the user's wallet must countersign to authorize the integrator approval. */
|
|
58
|
+
messageToSign: string
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
/** @public */
|
|
53
62
|
export interface ApiKeyPair {
|
|
54
63
|
publicKey: string
|
|
55
64
|
privateKey: string
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
const DEFAULT_CHAIN_ID = 304
|
|
59
|
-
|
|
60
67
|
// Signing "unset" sentinels mirror lighter-go `types/txtypes/constants.go`.
|
|
61
68
|
// Passing them yields an empty `L2TxAttributes` — no integrator fees, default
|
|
62
69
|
// self-trade rules.
|
|
@@ -97,7 +104,7 @@ export class LighterSigner {
|
|
|
97
104
|
|
|
98
105
|
constructor(config: LighterSignerConfig = {}) {
|
|
99
106
|
this.apiUrl = config.apiUrl ?? DEFAULT_LIGHTER_REST_URL
|
|
100
|
-
this.chainId = config.chainId ??
|
|
107
|
+
this.chainId = config.chainId ?? DEFAULT_LIGHTER_SIGNER_CHAIN_ID
|
|
101
108
|
this.loaderOptions = {
|
|
102
109
|
wasmBinaryUrl: config.wasmBinaryUrl,
|
|
103
110
|
wasmExecJsUrl: config.wasmExecJsUrl,
|
|
@@ -133,8 +140,9 @@ export class LighterSigner {
|
|
|
133
140
|
* `WasmBlobActionStep`. Returns the signed `{ txType, txInfo, txHash }`
|
|
134
141
|
* triple the backend forwards to Lighter's `sendTx` endpoint.
|
|
135
142
|
*
|
|
136
|
-
* For REGISTER_API_KEY use `signChangePubKey`
|
|
137
|
-
*
|
|
143
|
+
* For REGISTER_API_KEY use `signChangePubKey` and for APPROVE_INTEGRATOR
|
|
144
|
+
* use `signApproveIntegrator` instead — both return an additional
|
|
145
|
+
* `messageToSign` the L1 wallet must countersign.
|
|
138
146
|
*/
|
|
139
147
|
async sign(
|
|
140
148
|
action: ActionType,
|
|
@@ -147,6 +155,12 @@ export class LighterSigner {
|
|
|
147
155
|
'must be coordinated by the caller.'
|
|
148
156
|
)
|
|
149
157
|
}
|
|
158
|
+
if (action === ActionType.APPROVE_INTEGRATOR) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
'Use signApproveIntegrator() for APPROVE_INTEGRATOR — sign() does ' +
|
|
161
|
+
'not collect the required L1 user wallet signature.'
|
|
162
|
+
)
|
|
163
|
+
}
|
|
150
164
|
const wasm = await this.ensureLoaded()
|
|
151
165
|
await this.ensureClient(context)
|
|
152
166
|
const result = this.dispatch(wasm, action, wasmSignParams, context)
|
|
@@ -211,10 +225,51 @@ export class LighterSigner {
|
|
|
211
225
|
}
|
|
212
226
|
|
|
213
227
|
/**
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
228
|
+
* Sign an APPROVE_INTEGRATOR action with the stored API key and return the
|
|
229
|
+
* signed blob alongside the EIP-191 `L2ApproveIntegrator` L1 message the
|
|
230
|
+
* user's Ethereum wallet must countersign. Unlike {@link sign}, this exposes
|
|
231
|
+
* `messageToSign`: Lighter requires `L1Sig` (injected via
|
|
232
|
+
* {@link embedL1Signature} before submission) when the integrator account
|
|
233
|
+
* belongs to a different L1 address with non-zero fee caps.
|
|
234
|
+
*/
|
|
235
|
+
async signApproveIntegrator(
|
|
236
|
+
wasmSignParams: Record<string, unknown>,
|
|
237
|
+
context: LighterSignerContext
|
|
238
|
+
): Promise<ApproveIntegratorResult> {
|
|
239
|
+
const wasm = await this.ensureLoaded()
|
|
240
|
+
await this.ensureClient(context)
|
|
241
|
+
const result = this.dispatch(
|
|
242
|
+
wasm,
|
|
243
|
+
ActionType.APPROVE_INTEGRATOR,
|
|
244
|
+
wasmSignParams,
|
|
245
|
+
context
|
|
246
|
+
)
|
|
247
|
+
if (result.error) {
|
|
248
|
+
throw new Error(`Lighter SignApproveIntegrator failed: ${result.error}`)
|
|
249
|
+
}
|
|
250
|
+
if (
|
|
251
|
+
result.txType === undefined ||
|
|
252
|
+
result.txInfo === undefined ||
|
|
253
|
+
result.txHash === undefined ||
|
|
254
|
+
!result.messageToSign
|
|
255
|
+
) {
|
|
256
|
+
throw new Error(
|
|
257
|
+
'Lighter SignApproveIntegrator returned an incomplete result'
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
txType: result.txType,
|
|
262
|
+
txInfo: result.txInfo,
|
|
263
|
+
txHash: result.txHash,
|
|
264
|
+
messageToSign: result.messageToSign,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Inject the L1 signature produced by the user's Ethereum wallet into a
|
|
270
|
+
* signed txInfo JSON. `L1Sig` is the only field that depends on the L1
|
|
271
|
+
* signature; txHash does NOT include it (so we do not recompute it). Shared
|
|
272
|
+
* by the REGISTER_API_KEY (ChangePubKey) and APPROVE_INTEGRATOR flows.
|
|
218
273
|
*/
|
|
219
274
|
embedL1Signature(txInfo: string, l1Signature: string): string {
|
|
220
275
|
let parsed: Record<string, unknown>
|
|
@@ -222,7 +277,7 @@ export class LighterSigner {
|
|
|
222
277
|
parsed = JSON.parse(txInfo) as Record<string, unknown>
|
|
223
278
|
} catch (err) {
|
|
224
279
|
throw new Error(
|
|
225
|
-
`Failed to parse
|
|
280
|
+
`Failed to parse signed txInfo as JSON: ${(err as Error).message}`
|
|
226
281
|
)
|
|
227
282
|
}
|
|
228
283
|
parsed.L1Sig = l1Signature
|
|
@@ -291,7 +346,7 @@ export class LighterSigner {
|
|
|
291
346
|
action: ActionType,
|
|
292
347
|
p: Record<string, unknown>,
|
|
293
348
|
ctx: LighterSignerContext
|
|
294
|
-
): SignResult {
|
|
349
|
+
): SignResult & { messageToSign?: string } {
|
|
295
350
|
const nonce = numberField(p, 'nonce')
|
|
296
351
|
switch (action) {
|
|
297
352
|
case ActionType.PLACE_ORDER:
|
|
@@ -81,6 +81,8 @@ export async function signWasmBlobActions(
|
|
|
81
81
|
for (const step of steps) {
|
|
82
82
|
if (step.action === ActionType.REGISTER_API_KEY) {
|
|
83
83
|
signed.push(await signRegisterApiKey(deps, address, step, ctx))
|
|
84
|
+
} else if (step.action === ActionType.APPROVE_INTEGRATOR) {
|
|
85
|
+
signed.push(await signApproveIntegrator(deps, address, step, ctx))
|
|
84
86
|
} else if (
|
|
85
87
|
TOKEN_AUTH_MUTATION_KINDS.has(step.wasmSignParams.kind as string)
|
|
86
88
|
) {
|
|
@@ -196,6 +198,61 @@ async function signRegisterApiKey(
|
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
|
|
201
|
+
/**
|
|
202
|
+
* APPROVE_INTEGRATOR flow:
|
|
203
|
+
* 1. Load the user's stored API key (no keypair generation).
|
|
204
|
+
* 2. Wasm-sign the `L2ApproveIntegrator` blob with that key, obtaining the
|
|
205
|
+
* EIP-191 L1 message the wallet must countersign.
|
|
206
|
+
* 3. Have the user's L1 Ethereum wallet sign that message.
|
|
207
|
+
* 4. Inject the L1 signature into the txInfo JSON as `L1Sig`.
|
|
208
|
+
*
|
|
209
|
+
* Requires the end-user's wallet in `ctx.userWallet`: Lighter rejects the tx
|
|
210
|
+
* without the user's `L1Sig` when the integrator account belongs to a
|
|
211
|
+
* different L1 address with non-zero fee caps.
|
|
212
|
+
*/
|
|
213
|
+
async function signApproveIntegrator(
|
|
214
|
+
deps: LighterSignActionsDeps,
|
|
215
|
+
address: Address,
|
|
216
|
+
step: WasmBlobActionStep,
|
|
217
|
+
ctx: SignActionsContext | undefined
|
|
218
|
+
): Promise<WasmBlobSignedActionStep> {
|
|
219
|
+
const walletSigner = ctx?.userWallet
|
|
220
|
+
if (!walletSigner) {
|
|
221
|
+
throw new PerpsError(
|
|
222
|
+
PerpsErrorCode.SDKError,
|
|
223
|
+
'APPROVE_INTEGRATOR requires the end-user wallet — pass `userWallet` to ' +
|
|
224
|
+
'createPerpsClient or call setUserWallet(walletClient).'
|
|
225
|
+
)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const apiKey = await requireApiKey(deps, address)
|
|
229
|
+
const signed = await deps.signer.signApproveIntegrator(step.wasmSignParams, {
|
|
230
|
+
apiKeyPrivateKey: apiKey.apiKeyPrivateKey,
|
|
231
|
+
apiKeyIndex: apiKey.apiKeyIndex,
|
|
232
|
+
accountIndex: apiKey.accountIndex,
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
const l1Signature = await walletSigner.signMessage({
|
|
236
|
+
account: walletSigner.account,
|
|
237
|
+
message: signed.messageToSign,
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
const txInfoWithL1Sig = deps.signer.embedL1Signature(
|
|
241
|
+
signed.txInfo,
|
|
242
|
+
l1Signature
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
action: step.action,
|
|
247
|
+
wasmSignParams: step.wasmSignParams,
|
|
248
|
+
signedTx: {
|
|
249
|
+
txType: signed.txType,
|
|
250
|
+
txInfo: txInfoWithL1Sig,
|
|
251
|
+
txHash: signed.txHash,
|
|
252
|
+
},
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
199
256
|
/**
|
|
200
257
|
* Execute a token-authenticated Lighter mutation (`ACCOUNT_TYPE` →
|
|
201
258
|
* `/changeAccountTier`, `SET_REFERRAL` → `/referral/use`) client-side.
|
|
@@ -163,9 +163,13 @@ export interface LighterWasmExports {
|
|
|
163
163
|
* 10 args: integratorAccountIndex, maxPerpsTakerFee, maxPerpsMakerFee,
|
|
164
164
|
* maxSpotTakerFee, maxSpotMakerFee, approvalExpiry, skipNonce, then the
|
|
165
165
|
* trailing three. Fees are uint32 ppm of `FeeTick` (1_000_000); a non-nil
|
|
166
|
-
* fee requires a non-nil integrator index.
|
|
166
|
+
* fee requires a non-nil integrator index. `messageToSign` is the EIP-191
|
|
167
|
+
* `L2ApproveIntegrator` L1 body the user's wallet must countersign — the
|
|
168
|
+
* venue rejects the tx without the resulting `L1Sig`.
|
|
167
169
|
*/
|
|
168
|
-
SignApproveIntegrator: (
|
|
170
|
+
SignApproveIntegrator: (
|
|
171
|
+
...args: unknown[]
|
|
172
|
+
) => SignResult & { messageToSign?: string }
|
|
169
173
|
/**
|
|
170
174
|
* 5 args: accountTradingMode (0 = Classic/Simple, 1 = Unified), skipNonce,
|
|
171
175
|
* then the trailing three. `accountTradingMode` is validated to {0, 1} by
|
|
@@ -185,7 +185,7 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
185
185
|
|
|
186
186
|
constructor(
|
|
187
187
|
wsUrl: string = DEFAULT_LIGHTER_WS_URL,
|
|
188
|
-
providerKey =
|
|
188
|
+
providerKey: string = LIGHTER_PROVIDER_KEY,
|
|
189
189
|
options: LighterWsProviderOptions = {},
|
|
190
190
|
client?: PerpsSDKClient
|
|
191
191
|
) {
|
|
@@ -199,7 +199,7 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
199
199
|
policy: resolveRetryPolicy(
|
|
200
200
|
LIGHTER_RETRY_DEFAULTS,
|
|
201
201
|
client?.config.retry,
|
|
202
|
-
|
|
202
|
+
providerKey
|
|
203
203
|
),
|
|
204
204
|
fetchImpl: client?.config.fetch,
|
|
205
205
|
}
|
|
@@ -468,19 +468,19 @@ export class LighterWsProvider extends WsProviderBase<SubState> {
|
|
|
468
468
|
try {
|
|
469
469
|
msg = JSON.parse(raw) as LtWsMessage
|
|
470
470
|
} catch {
|
|
471
|
-
wsLog.parseFailure(
|
|
471
|
+
wsLog.parseFailure(this.providerKey, raw)
|
|
472
472
|
return
|
|
473
473
|
}
|
|
474
474
|
|
|
475
475
|
if (!isValidLighterFrame(msg)) {
|
|
476
|
-
wsLog.parseFailure(
|
|
476
|
+
wsLog.parseFailure(this.providerKey, raw)
|
|
477
477
|
return
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
try {
|
|
481
481
|
this.dispatch(msg)
|
|
482
482
|
} catch (error) {
|
|
483
|
-
wsLog.handlerFailure(
|
|
483
|
+
wsLog.handlerFailure(this.providerKey, error)
|
|
484
484
|
}
|
|
485
485
|
}
|
|
486
486
|
|