@metamask-previews/perps-controller 22.1.2-preview-2c49dc768 → 22.1.2-preview-4a5ac306d
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/CHANGELOG.md +16 -11
- package/dist/PerpsController-method-action-types.d.ts +5 -7
- package/dist/PerpsController-method-action-types.d.ts.map +1 -1
- package/dist/PerpsController-method-action-types.js.map +1 -1
- package/dist/PerpsController.d.ts +5 -7
- package/dist/PerpsController.d.ts.map +1 -1
- package/dist/PerpsController.js +5 -7
- package/dist/PerpsController.js.map +1 -1
- package/dist/constants/perpsConfig.d.ts +22 -0
- package/dist/constants/perpsConfig.d.ts.map +1 -1
- package/dist/constants/perpsConfig.js +18 -0
- package/dist/constants/perpsConfig.js.map +1 -1
- package/dist/errors.d.ts +2 -13
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +2 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/perpsErrorCodes.d.ts +0 -2
- package/dist/perpsErrorCodes.d.ts.map +1 -1
- package/dist/perpsErrorCodes.js +0 -3
- package/dist/perpsErrorCodes.js.map +1 -1
- package/dist/providers/AggregatedPerpsProvider.d.ts.map +1 -1
- package/dist/providers/AggregatedPerpsProvider.js +5 -6
- package/dist/providers/AggregatedPerpsProvider.js.map +1 -1
- package/dist/providers/HyperLiquidProvider.d.ts.map +1 -1
- package/dist/providers/HyperLiquidProvider.js +133 -107
- package/dist/providers/HyperLiquidProvider.js.map +1 -1
- package/dist/services/TradingService.d.ts.map +1 -1
- package/dist/services/TradingService.js +4 -7
- package/dist/services/TradingService.js.map +1 -1
- package/dist/types/index.d.ts +1 -36
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/utils/accountUtils.d.ts +61 -0
- package/dist/utils/accountUtils.d.ts.map +1 -1
- package/dist/utils/accountUtils.js +50 -0
- package/dist/utils/accountUtils.js.map +1 -1
- package/dist/utils/errorContext.d.ts +0 -33
- package/dist/utils/errorContext.d.ts.map +1 -1
- package/dist/utils/errorContext.js +0 -41
- package/dist/utils/errorContext.js.map +1 -1
- package/dist/utils/errorUtils.d.ts +0 -18
- package/dist/utils/errorUtils.d.ts.map +1 -1
- package/dist/utils/errorUtils.js +1 -127
- package/dist/utils/errorUtils.js.map +1 -1
- package/dist/utils/hyperLiquidValidation.d.ts +2 -9
- package/dist/utils/hyperLiquidValidation.d.ts.map +1 -1
- package/dist/utils/hyperLiquidValidation.js +17 -18
- package/dist/utils/hyperLiquidValidation.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/significantFigures.d.ts.map +1 -1
- package/dist/utils/significantFigures.js +4 -6
- package/dist/utils/significantFigures.js.map +1 -1
- package/dist/utils/stringParseUtils.d.ts +8 -0
- package/dist/utils/stringParseUtils.d.ts.map +1 -1
- package/dist/utils/stringParseUtils.js +10 -0
- package/dist/utils/stringParseUtils.js.map +1 -1
- package/dist/utils/tpslCalculations.d.ts +187 -0
- package/dist/utils/tpslCalculations.d.ts.map +1 -0
- package/dist/utils/tpslCalculations.js +405 -0
- package/dist/utils/tpslCalculations.js.map +1 -0
- package/package.json +2 -2
|
@@ -68,6 +68,67 @@ export declare function addSpotBalanceToAccountState(accountState: AccountState,
|
|
|
68
68
|
* @returns The summed summary, or undefined.
|
|
69
69
|
*/
|
|
70
70
|
export declare function sumCrossMarginSummaries(states: AccountState[]): CrossMarginSummary | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* A {@link CrossMarginSummary} whose figures may already be numbers.
|
|
73
|
+
*/
|
|
74
|
+
export type CrossMarginSummaryInput = Record<keyof CrossMarginSummary, number | string>;
|
|
75
|
+
/**
|
|
76
|
+
* Account figures derived from the venue's cross margin. Each is undefined
|
|
77
|
+
* while its inputs are unknown.
|
|
78
|
+
*/
|
|
79
|
+
export type PerpsAccountSummary = {
|
|
80
|
+
/** Maintenance margin the cross positions require, in USD. */
|
|
81
|
+
maintenanceMargin: number | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Cross maintenance margin over cross collateral (0.5 = 50%). Cross
|
|
84
|
+
* positions are liquidated when it reaches 1.
|
|
85
|
+
*/
|
|
86
|
+
accountRatio: number | undefined;
|
|
87
|
+
/** Cross notional as a multiple of total account equity. */
|
|
88
|
+
accountLeverage: number | undefined;
|
|
89
|
+
/** Cross margin used over cross collateral (0.25 = 25%). */
|
|
90
|
+
crossMarginUsage: number | undefined;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Inputs to {@link calculateAccountSummary}.
|
|
94
|
+
*/
|
|
95
|
+
export type CalculateAccountSummaryParams = {
|
|
96
|
+
/**
|
|
97
|
+
* Total account equity in USD, isolated margin included
|
|
98
|
+
* (`AccountState.totalBalance`).
|
|
99
|
+
*/
|
|
100
|
+
totalEquity: number | string;
|
|
101
|
+
/** The venue's cross margin figures; undefined until read. */
|
|
102
|
+
crossMarginSummary: CrossMarginSummaryInput | undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Spot USDC the venue counts as cross collateral (0 when it counts none);
|
|
105
|
+
* undefined until read.
|
|
106
|
+
*/
|
|
107
|
+
spotCollateral: number | string | undefined;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* The collateral the cross positions draw on: the cross account value plus
|
|
111
|
+
* the spot USDC the venue counts as collateral.
|
|
112
|
+
*
|
|
113
|
+
* @param crossMarginSummary - The venue's cross margin figures.
|
|
114
|
+
* @param spotCollateral - Spot USDC counted as cross collateral.
|
|
115
|
+
* @returns The cross collateral in USD, or undefined until both are known.
|
|
116
|
+
*/
|
|
117
|
+
export declare function getCrossCollateral(crossMarginSummary: CrossMarginSummaryInput | undefined, spotCollateral: number | string | undefined): number | undefined;
|
|
118
|
+
/**
|
|
119
|
+
* Derive the account summary from the venue's cross margin figures, matching
|
|
120
|
+
* Hyperliquid: account leverage divides cross notional by total account
|
|
121
|
+
* equity, isolated margin included; account ratio and cross margin usage
|
|
122
|
+
* divide the cross maintenance margin and margin used by cross collateral.
|
|
123
|
+
*
|
|
124
|
+
* @param params - The account inputs.
|
|
125
|
+
* @param params.totalEquity - Total account equity in USD.
|
|
126
|
+
* @param params.crossMarginSummary - The venue's cross margin figures.
|
|
127
|
+
* @param params.spotCollateral - Spot USDC counted as cross collateral.
|
|
128
|
+
* @returns The summary; a ratio is undefined while its inputs are unknown or
|
|
129
|
+
* its denominator is not positive.
|
|
130
|
+
*/
|
|
131
|
+
export declare function calculateAccountSummary({ totalEquity, crossMarginSummary, spotCollateral, }: CalculateAccountSummaryParams): PerpsAccountSummary;
|
|
71
132
|
/**
|
|
72
133
|
* Aggregate multiple per-DEX AccountState objects into one by summing numeric
|
|
73
134
|
* fields exactly. Collateral subtotals retain their full precision so spot
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accountUtils.d.ts","sourceRoot":"","sources":["../../src/utils/accountUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAKtE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AACpF,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAQ3B,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,CAAC,eAAe,GAAG,oBAAoB,CAAC,EAAE,GACnD,eAAe,GAAG,oBAAoB,GAAG,IAAI,CAM/C;AAED,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,CAAC,eAAe,GAAG,oBAAoB,CAAC,EAAE,GACnD;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAGjC;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,CAAC,eAAe,GAAG,oBAAoB,CAAC,EAAE,GACnD;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAEjC;AAED,KAAK,2BAA2B,GAAG;IACjC,IAAI,CACF,UAAU,EACN,uCAAuC,GACvC,2DAA2D,GAC9D,OAAO,CAAC;CACZ,CAAC;AAeF,wBAAgB,yCAAyC,CACvD,SAAS,EAAE,2BAA2B,GACrC,eAAe,GAAG,oBAAoB,GAAG,SAAS,CA0BpD;AAED,wBAAgB,kCAAkC,CAChD,SAAS,EAAE,2BAA2B,GACrC;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAIjC;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,aAAa,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC;CACjC,CAAC;AAEF,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,mBAAmB,EAAE,GAC9B,MAAM,CA4CR;AAuBD,wBAAgB,cAAc,CAC5B,SAAS,CAAC,EAAE,8BAA8B,GAAG,IAAI,GAChD,MAAM,CAER;AAED,wBAAgB,WAAW,CACzB,SAAS,CAAC,EAAE,8BAA8B,GAAG,IAAI,GAChD,MAAM,CAER;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;;;;;;;;;;;;;OAeG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC1C,YAAY,EAAE,YAAY,EAC1B,SAAS,CAAC,EAAE,8BAA8B,GAAG,IAAI,EACjD,OAAO,CAAC,EAAE,qBAAqB,GAC9B,YAAY,CA8Dd;AAyBD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,YAAY,EAAE,GACrB,kBAAkB,GAAG,SAAS,CAiBhC;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAiE3E"}
|
|
1
|
+
{"version":3,"file":"accountUtils.d.ts","sourceRoot":"","sources":["../../src/utils/accountUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -196,6 +196,56 @@ export function sumCrossMarginSummaries(states) {
|
|
|
196
196
|
notional: sum('notional'),
|
|
197
197
|
};
|
|
198
198
|
}
|
|
199
|
+
function toFiniteNumber(value) {
|
|
200
|
+
if (value === undefined) {
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
const parsed = typeof value === 'number' ? value : Number.parseFloat(value);
|
|
204
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
205
|
+
}
|
|
206
|
+
function share(part, whole) {
|
|
207
|
+
return part === undefined || whole === undefined || whole <= 0
|
|
208
|
+
? undefined
|
|
209
|
+
: part / whole;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The collateral the cross positions draw on: the cross account value plus
|
|
213
|
+
* the spot USDC the venue counts as collateral.
|
|
214
|
+
*
|
|
215
|
+
* @param crossMarginSummary - The venue's cross margin figures.
|
|
216
|
+
* @param spotCollateral - Spot USDC counted as cross collateral.
|
|
217
|
+
* @returns The cross collateral in USD, or undefined until both are known.
|
|
218
|
+
*/
|
|
219
|
+
export function getCrossCollateral(crossMarginSummary, spotCollateral) {
|
|
220
|
+
const accountValue = toFiniteNumber(crossMarginSummary?.accountValue);
|
|
221
|
+
const spot = toFiniteNumber(spotCollateral);
|
|
222
|
+
return accountValue === undefined || spot === undefined
|
|
223
|
+
? undefined
|
|
224
|
+
: accountValue + spot;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Derive the account summary from the venue's cross margin figures, matching
|
|
228
|
+
* Hyperliquid: account leverage divides cross notional by total account
|
|
229
|
+
* equity, isolated margin included; account ratio and cross margin usage
|
|
230
|
+
* divide the cross maintenance margin and margin used by cross collateral.
|
|
231
|
+
*
|
|
232
|
+
* @param params - The account inputs.
|
|
233
|
+
* @param params.totalEquity - Total account equity in USD.
|
|
234
|
+
* @param params.crossMarginSummary - The venue's cross margin figures.
|
|
235
|
+
* @param params.spotCollateral - Spot USDC counted as cross collateral.
|
|
236
|
+
* @returns The summary; a ratio is undefined while its inputs are unknown or
|
|
237
|
+
* its denominator is not positive.
|
|
238
|
+
*/
|
|
239
|
+
export function calculateAccountSummary({ totalEquity, crossMarginSummary, spotCollateral, }) {
|
|
240
|
+
const collateral = getCrossCollateral(crossMarginSummary, spotCollateral);
|
|
241
|
+
const maintenanceMargin = toFiniteNumber(crossMarginSummary?.maintenanceMargin);
|
|
242
|
+
return {
|
|
243
|
+
maintenanceMargin,
|
|
244
|
+
accountRatio: share(maintenanceMargin, collateral),
|
|
245
|
+
accountLeverage: share(toFiniteNumber(crossMarginSummary?.notional), toFiniteNumber(totalEquity)),
|
|
246
|
+
crossMarginUsage: share(toFiniteNumber(crossMarginSummary?.marginUsed), collateral),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
199
249
|
/**
|
|
200
250
|
* Aggregate multiple per-DEX AccountState objects into one by summing numeric
|
|
201
251
|
* fields exactly. Collateral subtotals retain their full precision so spot
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accountUtils.js","sourceRoot":"","sources":["../../src/utils/accountUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Account utilities for Perps components\n * Handles account selection and EVM account filtering\n */\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { BigNumber } from 'bignumber.js';\n\nimport { USDC_DECIMALS } from '../constants/hyperLiquidConfig.js';\nimport { PERPS_CONSTANTS } from '../constants/perpsConfig.js';\nimport type { SpotClearinghouseStateResponse } from '../types/hyperliquid-types.js';\nimport type {\n AccountState,\n CrossMarginSummary,\n PerpsInternalAccount,\n} from '../types/index.js';\n\nconst EVM_ACCOUNT_TYPES = new Set(['eip155:eoa', 'eip155:erc4337']);\n\nfunction isEvmAccountType(type: string): boolean {\n return EVM_ACCOUNT_TYPES.has(type);\n}\n\nexport function findEvmAccount(\n accounts: (InternalAccount | PerpsInternalAccount)[],\n): InternalAccount | PerpsInternalAccount | null {\n const evmAccount = accounts.find(\n (account) =>\n account && isEvmAccountType(account.type as InternalAccount['type']),\n );\n return evmAccount ?? null;\n}\n\nexport function getEvmAccountFromAccountGroup(\n accounts: (InternalAccount | PerpsInternalAccount)[],\n): { address: string } | undefined {\n const evmAccount = findEvmAccount(accounts);\n return evmAccount ? { address: evmAccount.address } : undefined;\n}\n\nexport function getSelectedEvmAccount(\n accounts: (InternalAccount | PerpsInternalAccount)[],\n): { address: string } | undefined {\n return getEvmAccountFromAccountGroup(accounts);\n}\n\ntype SelectedEvmAccountMessenger = {\n call(\n actionType:\n | 'AccountsController:getSelectedAccount'\n | 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ): unknown;\n};\n\nfunction isAccountLike(\n value: unknown,\n): value is InternalAccount | PerpsInternalAccount {\n const account = value as { address?: unknown; type?: unknown } | null;\n\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof account?.address === 'string' &&\n typeof account.type === 'string'\n );\n}\n\nexport function getSelectedEvmAccountDetailsFromMessenger(\n messenger: SelectedEvmAccountMessenger,\n): InternalAccount | PerpsInternalAccount | undefined {\n try {\n const selectedAccount = messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n if (isAccountLike(selectedAccount)) {\n const evmAccount = findEvmAccount([selectedAccount]);\n if (evmAccount) {\n return evmAccount;\n }\n }\n } catch {\n // Fall back to the selected account group if the direct lookup is unavailable.\n }\n\n try {\n const selectedAccountGroup = messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n return Array.isArray(selectedAccountGroup)\n ? (findEvmAccount(selectedAccountGroup.filter(isAccountLike)) ??\n undefined)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function getSelectedEvmAccountFromMessenger(\n messenger: SelectedEvmAccountMessenger,\n): { address: string } | undefined {\n const evmAccount = getSelectedEvmAccountDetailsFromMessenger(messenger);\n\n return evmAccount ? { address: evmAccount.address } : undefined;\n}\n\nexport type ReturnOnEquityInput = {\n unrealizedPnl: string | number;\n returnOnEquity: string | number;\n};\n\nexport function calculateWeightedReturnOnEquity(\n accounts: ReturnOnEquityInput[],\n): string {\n if (accounts.length === 0) {\n return '0';\n }\n\n let totalWeightedROE = 0;\n let totalMarginUsed = 0;\n\n for (const account of accounts) {\n const unrealizedPnl =\n typeof account.unrealizedPnl === 'string'\n ? Number.parseFloat(account.unrealizedPnl)\n : account.unrealizedPnl;\n const returnOnEquity =\n typeof account.returnOnEquity === 'string'\n ? Number.parseFloat(account.returnOnEquity)\n : account.returnOnEquity;\n\n if (Number.isNaN(unrealizedPnl) || Number.isNaN(returnOnEquity)) {\n continue;\n }\n\n if (returnOnEquity === 0) {\n continue;\n }\n\n const marginUsed = (unrealizedPnl / returnOnEquity) * 100;\n\n if (Number.isNaN(marginUsed) || marginUsed <= 0) {\n continue;\n }\n\n const roeDecimal = returnOnEquity / 100;\n\n totalWeightedROE += roeDecimal * marginUsed;\n totalMarginUsed += marginUsed;\n }\n\n if (totalMarginUsed <= 0) {\n return '0';\n }\n\n const weightedROE = (totalWeightedROE / totalMarginUsed) * 100;\n return weightedROE.toString();\n}\n\n// The release-branch balance bridge is USDC-only. Non-USDC spot assets must\n// not inflate the balances shown or validated by withdraw/payment flows.\nconst SPOT_COLLATERAL_COINS = new Set<string>(['USDC']);\n\nfunction getSpotCollateralAmount(\n spotState: SpotClearinghouseStateResponse | null | undefined,\n field: 'hold' | 'total',\n): BigNumber {\n if (!spotState?.balances || !Array.isArray(spotState.balances)) {\n return new BigNumber(0);\n }\n\n return spotState.balances.reduce((sum, balance) => {\n if (!balance.coin || !SPOT_COLLATERAL_COINS.has(balance.coin)) {\n return sum;\n }\n const value = new BigNumber(balance[field] ?? '0');\n return value.isFinite() ? sum.plus(value) : sum;\n }, new BigNumber(0));\n}\n\nexport function getSpotBalance(\n spotState?: SpotClearinghouseStateResponse | null,\n): number {\n return getSpotCollateralAmount(spotState, 'total').toNumber();\n}\n\nexport function getSpotHold(\n spotState?: SpotClearinghouseStateResponse | null,\n): number {\n return getSpotCollateralAmount(spotState, 'hold').toNumber();\n}\n\n/**\n * Options controlling how `addSpotBalanceToAccountState` folds spot balance\n * into the three-field AccountState contract.\n */\nexport type AddSpotBalanceOptions = {\n /**\n * When `true`, free spot USDC contributes to both `spendableBalance` and\n * `withdrawableBalance` in addition to `totalBalance` — appropriate for\n * venues where spot is automatically used as perps collateral (e.g.\n * HyperLiquid Unified/Portfolio mode, where `withdraw3` draws from the\n * unified ledger).\n *\n * When `false`, free spot contributes to `totalBalance` only; spendable\n * and withdrawable stay perps-only — appropriate for venues where spot\n * is a separate ledger the backend cannot auto-draw from (e.g. HL\n * Standard mode). The caller is responsible for translating\n * provider-specific state into this flag.\n *\n * Defaults to `true` for backward compatibility with call sites that\n * haven't yet been wired with provider-specific context.\n */\n foldIntoCollateral?: boolean;\n};\n\n/**\n * Add spot USDC to the AccountState contract. Caller decides whether the\n * spot balance counts as perps collateral via `options.foldIntoCollateral`\n * — the util stays provider-agnostic.\n *\n * @param accountState - Base AccountState produced by a provider adapter.\n * @param spotState - Raw spot clearinghouse response (HL-shaped); null or missing means no spot balance and the state is returned unchanged.\n * @param options - See {@link AddSpotBalanceOptions}.\n * @returns AccountState with spot folded into `totalBalance` always, and into spendable/withdrawable when `foldIntoCollateral` is true.\n */\nexport function addSpotBalanceToAccountState(\n accountState: AccountState,\n spotState?: SpotClearinghouseStateResponse | null,\n options?: AddSpotBalanceOptions,\n): AccountState {\n // Fail-closed default: align with `hyperLiquidModeFoldsSpot(null) → false`.\n // A caller that omits `options` should NOT silently fold spot — that would\n // over-report withdrawable funds for Standard / default users.\n const foldIntoCollateral = options?.foldIntoCollateral ?? false;\n\n const spotBalance = getSpotCollateralAmount(spotState, 'total');\n const spotHold = getSpotCollateralAmount(spotState, 'hold');\n const freeSpot = BigNumber.max(0, spotBalance.minus(spotHold));\n\n const currentTotal = new BigNumber(accountState.totalBalance);\n const currentSpendable = new BigNumber(accountState.spendableBalance);\n const currentWithdrawable = new BigNumber(accountState.withdrawableBalance);\n\n // Preserve sentinel totals (e.g. PERPS_CONSTANTS.FallbackDataDisplay '--')\n // rather than coercing them to NaN.\n if (!currentTotal.isFinite()) {\n return accountState;\n }\n\n // Folding is gated strictly on the resolved abstraction mode (see callers'\n // `foldIntoCollateral` argument). Standard / DEX-abstraction users keep\n // perps and spot independent, so spot must NOT surface as a perps-\n // withdrawable balance for them — withdraw3 only draws from the perps\n // ledger in those modes. Unified / portfolio-margin users get the fold;\n // live callers fail-CLOSED via `hyperLiquidModeFoldsSpot` when mode is\n // unresolved.\n const nextSpendable = resolveFoldedBalance(\n currentSpendable,\n accountState.spendableBalance,\n freeSpot,\n foldIntoCollateral,\n );\n const nextWithdrawable = resolveFoldedBalance(\n currentWithdrawable,\n accountState.withdrawableBalance,\n freeSpot,\n foldIntoCollateral,\n );\n\n // Total always reflects combined wealth: subtract spotHold to avoid\n // double-counting on Unified/PM accounts where marginSummary.accountValue\n // already includes the margin that HL surfaces via spot.hold. Standard\n // mode has spotHold = 0 by construction, so the subtraction is a no-op.\n const nextTotal = canonicalizeUsdc(\n currentTotal.plus(spotBalance).minus(spotHold),\n );\n\n if (\n nextTotal === accountState.totalBalance &&\n nextSpendable === accountState.spendableBalance &&\n nextWithdrawable === accountState.withdrawableBalance\n ) {\n return accountState;\n }\n\n return {\n ...accountState,\n totalBalance: nextTotal,\n spendableBalance: nextSpendable,\n withdrawableBalance: nextWithdrawable,\n };\n}\n\nfunction resolveFoldedBalance(\n currentNumeric: BigNumber,\n currentRaw: string,\n freeSpot: BigNumber,\n foldIntoCollateral: boolean,\n): string {\n if (!currentNumeric.isFinite()) {\n // Non-finite currentNumeric means the adapter passed a sentinel like\n // `PERPS_CONSTANTS.FallbackDataDisplay` (\"--\") during loading. Preserve\n // the sentinel rather than synthesising a numeric fold; the caller's\n // UI treats \"--\" as \"loading\" and would otherwise show a misleading\n // spot-only figure while the per-DEX perps fetch is still in flight.\n return currentRaw;\n }\n\n const spotAmount = foldIntoCollateral ? freeSpot : new BigNumber(0);\n return canonicalizeUsdc(currentNumeric.plus(spotAmount));\n}\n\nfunction canonicalizeUsdc(value: BigNumber): string {\n return value.decimalPlaces(USDC_DECIMALS, BigNumber.ROUND_DOWN).toFixed();\n}\n\n/**\n * Sum per-DEX cross-margin summaries. Undefined unless every state carries\n * one, so a partial sum never reads as the whole account.\n *\n * @param states - The per-DEX account states.\n * @returns The summed summary, or undefined.\n */\nexport function sumCrossMarginSummaries(\n states: AccountState[],\n): CrossMarginSummary | undefined {\n const summaries = states\n .map((state) => state.crossMarginSummary)\n .filter((summary): summary is CrossMarginSummary => summary !== undefined);\n if (summaries.length === 0 || summaries.length !== states.length) {\n return undefined;\n }\n const sum = (field: keyof CrossMarginSummary): string =>\n summaries\n .reduce((total, summary) => total.plus(summary[field]), new BigNumber(0))\n .toFixed();\n return {\n accountValue: sum('accountValue'),\n maintenanceMargin: sum('maintenanceMargin'),\n marginUsed: sum('marginUsed'),\n notional: sum('notional'),\n };\n}\n\n/**\n * Aggregate multiple per-DEX AccountState objects into one by summing numeric\n * fields exactly. Collateral subtotals retain their full precision so spot\n * collateral can be added before the final USDC canonicalization.\n * ROE is recalculated as (totalUnrealizedPnl / totalMarginUsed) * 100.\n *\n * @param states - The array of per-DEX account states to aggregate.\n * @returns The combined account state with summed balances and recalculated ROE.\n */\nexport function aggregateAccountStates(states: AccountState[]): AccountState {\n const fallback: AccountState = {\n spendableBalance: PERPS_CONSTANTS.FallbackDataDisplay,\n withdrawableBalance: PERPS_CONSTANTS.FallbackDataDisplay,\n totalBalance: PERPS_CONSTANTS.FallbackDataDisplay,\n marginUsed: PERPS_CONSTANTS.FallbackDataDisplay,\n unrealizedPnl: PERPS_CONSTANTS.FallbackDataDisplay,\n returnOnEquity: PERPS_CONSTANTS.FallbackDataDisplay,\n };\n\n if (states.length === 0) {\n return fallback;\n }\n\n const aggregated = states.reduce<AccountState>((acc, state, index) => {\n if (index === 0) {\n return { ...state };\n }\n\n return {\n spendableBalance: addDecimalValues(\n acc.spendableBalance,\n state.spendableBalance,\n ),\n withdrawableBalance: addDecimalValues(\n acc.withdrawableBalance,\n state.withdrawableBalance,\n ),\n totalBalance: addDecimalValues(acc.totalBalance, state.totalBalance),\n marginUsed: (\n parseFloat(acc.marginUsed) + parseFloat(state.marginUsed)\n ).toString(),\n unrealizedPnl: (\n parseFloat(acc.unrealizedPnl) + parseFloat(state.unrealizedPnl)\n ).toString(),\n returnOnEquity: '0',\n };\n }, fallback);\n\n aggregated.spendableBalance = normalizeDecimalString(\n aggregated.spendableBalance,\n );\n aggregated.withdrawableBalance = normalizeDecimalString(\n aggregated.withdrawableBalance,\n );\n aggregated.totalBalance = normalizeDecimalString(aggregated.totalBalance);\n\n // Recalculate ROE across all DEXs\n const totalMarginUsed = parseFloat(aggregated.marginUsed);\n const totalUnrealizedPnl = parseFloat(aggregated.unrealizedPnl);\n if (totalMarginUsed > 0) {\n aggregated.returnOnEquity = (\n (totalUnrealizedPnl / totalMarginUsed) *\n 100\n ).toString();\n } else {\n aggregated.returnOnEquity = '0';\n }\n\n const crossMarginSummary = sumCrossMarginSummaries(states);\n if (crossMarginSummary) {\n aggregated.crossMarginSummary = crossMarginSummary;\n }\n\n return aggregated;\n}\n\nfunction addDecimalValues(left: string, right: string): string {\n return new BigNumber(left).plus(right).toFixed();\n}\n\nfunction normalizeDecimalString(value: string): string {\n const amount = new BigNumber(value);\n return amount.isFinite() ? amount.toFixed() : value;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"accountUtils.js","sourceRoot":"","sources":["../../src/utils/accountUtils.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAQ9D,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAEpE,SAAS,gBAAgB,CAAC,IAAY;IACpC,OAAO,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,QAAoD;IAEpD,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAC9B,CAAC,OAAO,EAAE,EAAE,CACV,OAAO,IAAI,gBAAgB,CAAC,OAAO,CAAC,IAA+B,CAAC,CACvE,CAAC;IACF,OAAO,UAAU,IAAI,IAAI,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,6BAA6B,CAC3C,QAAoD;IAEpD,MAAM,UAAU,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC5C,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,QAAoD;IAEpD,OAAO,6BAA6B,CAAC,QAAQ,CAAC,CAAC;AACjD,CAAC;AAUD,SAAS,aAAa,CACpB,KAAc;IAEd,MAAM,OAAO,GAAG,KAAqD,CAAC;IAEtE,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,OAAO,EAAE,OAAO,KAAK,QAAQ;QACpC,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,yCAAyC,CACvD,SAAsC;IAEtC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,SAAS,CAAC,IAAI,CACpC,uCAAuC,CACxC,CAAC;QACF,IAAI,aAAa,CAAC,eAAe,CAAC,EAAE,CAAC;YACnC,MAAM,UAAU,GAAG,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC;YACrD,IAAI,UAAU,EAAE,CAAC;gBACf,OAAO,UAAU,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+EAA+E;IACjF,CAAC;IAED,IAAI,CAAC;QACH,MAAM,oBAAoB,GAAG,SAAS,CAAC,IAAI,CACzC,2DAA2D,CAC5D,CAAC;QACF,OAAO,KAAK,CAAC,OAAO,CAAC,oBAAoB,CAAC;YACxC,CAAC,CAAC,CAAC,cAAc,CAAC,oBAAoB,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;gBACzD,SAAS,CAAC;YACd,CAAC,CAAC,SAAS,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kCAAkC,CAChD,SAAsC;IAEtC,MAAM,UAAU,GAAG,yCAAyC,CAAC,SAAS,CAAC,CAAC;IAExE,OAAO,UAAU,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAClE,CAAC;AAOD,MAAM,UAAU,+BAA+B,CAC7C,QAA+B;IAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,eAAe,GAAG,CAAC,CAAC;IAExB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,aAAa,GACjB,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ;YACvC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC;YAC1C,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;QAC5B,MAAM,cAAc,GAClB,OAAO,OAAO,CAAC,cAAc,KAAK,QAAQ;YACxC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,cAAc,CAAC;YAC3C,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC;QAE7B,IAAI,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,CAAC;YAChE,SAAS;QACX,CAAC;QAED,IAAI,cAAc,KAAK,CAAC,EAAE,CAAC;YACzB,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAG,CAAC,aAAa,GAAG,cAAc,CAAC,GAAG,GAAG,CAAC;QAE1D,IAAI,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,UAAU,IAAI,CAAC,EAAE,CAAC;YAChD,SAAS;QACX,CAAC;QAED,MAAM,UAAU,GAAG,cAAc,GAAG,GAAG,CAAC;QAExC,gBAAgB,IAAI,UAAU,GAAG,UAAU,CAAC;QAC5C,eAAe,IAAI,UAAU,CAAC;IAChC,CAAC;IAED,IAAI,eAAe,IAAI,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,CAAC;IACb,CAAC;IAED,MAAM,WAAW,GAAG,CAAC,gBAAgB,GAAG,eAAe,CAAC,GAAG,GAAG,CAAC;IAC/D,OAAO,WAAW,CAAC,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,4EAA4E;AAC5E,yEAAyE;AACzE,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAExD,SAAS,uBAAuB,CAC9B,SAA4D,EAC5D,KAAuB;IAEvB,IAAI,CAAC,SAAS,EAAE,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/D,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,OAAO,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE;QAChD,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9D,OAAO,GAAG,CAAC;QACb,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC;QACnD,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAClD,CAAC,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,SAAiD;IAEjD,OAAO,uBAAuB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,SAAiD;IAEjD,OAAO,uBAAuB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC/D,CAAC;AA0BD;;;;;;;;;GASG;AACH,MAAM,UAAU,4BAA4B,CAC1C,YAA0B,EAC1B,SAAiD,EACjD,OAA+B;IAE/B,4EAA4E;IAC5E,2EAA2E;IAC3E,+DAA+D;IAC/D,MAAM,kBAAkB,GAAG,OAAO,EAAE,kBAAkB,IAAI,KAAK,CAAC;IAEhE,MAAM,WAAW,GAAG,uBAAuB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,uBAAuB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE/D,MAAM,YAAY,GAAG,IAAI,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IAC9D,MAAM,gBAAgB,GAAG,IAAI,SAAS,CAAC,YAAY,CAAC,gBAAgB,CAAC,CAAC;IACtE,MAAM,mBAAmB,GAAG,IAAI,SAAS,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAE5E,2EAA2E;IAC3E,oCAAoC;IACpC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC7B,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,2EAA2E;IAC3E,wEAAwE;IACxE,mEAAmE;IACnE,sEAAsE;IACtE,wEAAwE;IACxE,uEAAuE;IACvE,cAAc;IACd,MAAM,aAAa,GAAG,oBAAoB,CACxC,gBAAgB,EAChB,YAAY,CAAC,gBAAgB,EAC7B,QAAQ,EACR,kBAAkB,CACnB,CAAC;IACF,MAAM,gBAAgB,GAAG,oBAAoB,CAC3C,mBAAmB,EACnB,YAAY,CAAC,mBAAmB,EAChC,QAAQ,EACR,kBAAkB,CACnB,CAAC;IAEF,oEAAoE;IACpE,0EAA0E;IAC1E,uEAAuE;IACvE,wEAAwE;IACxE,MAAM,SAAS,GAAG,gBAAgB,CAChC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAC/C,CAAC;IAEF,IACE,SAAS,KAAK,YAAY,CAAC,YAAY;QACvC,aAAa,KAAK,YAAY,CAAC,gBAAgB;QAC/C,gBAAgB,KAAK,YAAY,CAAC,mBAAmB,EACrD,CAAC;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,OAAO;QACL,GAAG,YAAY;QACf,YAAY,EAAE,SAAS;QACvB,gBAAgB,EAAE,aAAa;QAC/B,mBAAmB,EAAE,gBAAgB;KACtC,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAC3B,cAAyB,EACzB,UAAkB,EAClB,QAAmB,EACnB,kBAA2B;IAE3B,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,CAAC;QAC/B,qEAAqE;QACrE,wEAAwE;QACxE,qEAAqE;QACrE,oEAAoE;QACpE,qEAAqE;QACrE,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,UAAU,GAAG,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;IACpE,OAAO,gBAAgB,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAgB;IACxC,OAAO,KAAK,CAAC,aAAa,CAAC,aAAa,EAAE,SAAS,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC;AAC5E,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAsB;IAEtB,MAAM,SAAS,GAAG,MAAM;SACrB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC;SACxC,MAAM,CAAC,CAAC,OAAO,EAAiC,EAAE,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;IAC7E,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC;QACjE,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,GAAG,GAAG,CAAC,KAA+B,EAAU,EAAE,CACtD,SAAS;SACN,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;SACxE,OAAO,EAAE,CAAC;IACf,OAAO;QACL,YAAY,EAAE,GAAG,CAAC,cAAc,CAAC;QACjC,iBAAiB,EAAE,GAAG,CAAC,mBAAmB,CAAC;QAC3C,UAAU,EAAE,GAAG,CAAC,YAAY,CAAC;QAC7B,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC;KAC1B,CAAC;AACJ,CAAC;AA8CD,SAAS,cAAc,CACrB,KAAkC;IAElC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC5E,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC;AAED,SAAS,KAAK,CACZ,IAAwB,EACxB,KAAyB;IAEzB,OAAO,IAAI,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,IAAI,CAAC;QAC5D,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,kBAAuD,EACvD,cAA2C;IAE3C,MAAM,YAAY,GAAG,cAAc,CAAC,kBAAkB,EAAE,YAAY,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;IAC5C,OAAO,YAAY,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS;QACrD,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,uBAAuB,CAAC,EACtC,WAAW,EACX,kBAAkB,EAClB,cAAc,GACgB;IAC9B,MAAM,UAAU,GAAG,kBAAkB,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;IAC1E,MAAM,iBAAiB,GAAG,cAAc,CACtC,kBAAkB,EAAE,iBAAiB,CACtC,CAAC;IACF,OAAO;QACL,iBAAiB;QACjB,YAAY,EAAE,KAAK,CAAC,iBAAiB,EAAE,UAAU,CAAC;QAClD,eAAe,EAAE,KAAK,CACpB,cAAc,CAAC,kBAAkB,EAAE,QAAQ,CAAC,EAC5C,cAAc,CAAC,WAAW,CAAC,CAC5B;QACD,gBAAgB,EAAE,KAAK,CACrB,cAAc,CAAC,kBAAkB,EAAE,UAAU,CAAC,EAC9C,UAAU,CACX;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAsB;IAC3D,MAAM,QAAQ,GAAiB;QAC7B,gBAAgB,EAAE,eAAe,CAAC,mBAAmB;QACrD,mBAAmB,EAAE,eAAe,CAAC,mBAAmB;QACxD,YAAY,EAAE,eAAe,CAAC,mBAAmB;QACjD,UAAU,EAAE,eAAe,CAAC,mBAAmB;QAC/C,aAAa,EAAE,eAAe,CAAC,mBAAmB;QAClD,cAAc,EAAE,eAAe,CAAC,mBAAmB;KACpD,CAAC;IAEF,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAe,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACnE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;QACtB,CAAC;QAED,OAAO;YACL,gBAAgB,EAAE,gBAAgB,CAChC,GAAG,CAAC,gBAAgB,EACpB,KAAK,CAAC,gBAAgB,CACvB;YACD,mBAAmB,EAAE,gBAAgB,CACnC,GAAG,CAAC,mBAAmB,EACvB,KAAK,CAAC,mBAAmB,CAC1B;YACD,YAAY,EAAE,gBAAgB,CAAC,GAAG,CAAC,YAAY,EAAE,KAAK,CAAC,YAAY,CAAC;YACpE,UAAU,EAAE,CACV,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAC1D,CAAC,QAAQ,EAAE;YACZ,aAAa,EAAE,CACb,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC,CAChE,CAAC,QAAQ,EAAE;YACZ,cAAc,EAAE,GAAG;SACpB,CAAC;IACJ,CAAC,EAAE,QAAQ,CAAC,CAAC;IAEb,UAAU,CAAC,gBAAgB,GAAG,sBAAsB,CAClD,UAAU,CAAC,gBAAgB,CAC5B,CAAC;IACF,UAAU,CAAC,mBAAmB,GAAG,sBAAsB,CACrD,UAAU,CAAC,mBAAmB,CAC/B,CAAC;IACF,UAAU,CAAC,YAAY,GAAG,sBAAsB,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IAE1E,kCAAkC;IAClC,MAAM,eAAe,GAAG,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAC1D,MAAM,kBAAkB,GAAG,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IAChE,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;QACxB,UAAU,CAAC,cAAc,GAAG,CAC1B,CAAC,kBAAkB,GAAG,eAAe,CAAC;YACtC,GAAG,CACJ,CAAC,QAAQ,EAAE,CAAC;IACf,CAAC;SAAM,CAAC;QACN,UAAU,CAAC,cAAc,GAAG,GAAG,CAAC;IAClC,CAAC;IAED,MAAM,kBAAkB,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAC3D,IAAI,kBAAkB,EAAE,CAAC;QACvB,UAAU,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;IACrD,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,KAAa;IACnD,OAAO,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;AACnD,CAAC;AAED,SAAS,sBAAsB,CAAC,KAAa;IAC3C,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC;IACpC,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AACtD,CAAC","sourcesContent":["/**\n * Account utilities for Perps components\n * Handles account selection and EVM account filtering\n */\nimport type { InternalAccount } from '@metamask/keyring-internal-api';\nimport { BigNumber } from 'bignumber.js';\n\nimport { USDC_DECIMALS } from '../constants/hyperLiquidConfig.js';\nimport { PERPS_CONSTANTS } from '../constants/perpsConfig.js';\nimport type { SpotClearinghouseStateResponse } from '../types/hyperliquid-types.js';\nimport type {\n AccountState,\n CrossMarginSummary,\n PerpsInternalAccount,\n} from '../types/index.js';\n\nconst EVM_ACCOUNT_TYPES = new Set(['eip155:eoa', 'eip155:erc4337']);\n\nfunction isEvmAccountType(type: string): boolean {\n return EVM_ACCOUNT_TYPES.has(type);\n}\n\nexport function findEvmAccount(\n accounts: (InternalAccount | PerpsInternalAccount)[],\n): InternalAccount | PerpsInternalAccount | null {\n const evmAccount = accounts.find(\n (account) =>\n account && isEvmAccountType(account.type as InternalAccount['type']),\n );\n return evmAccount ?? null;\n}\n\nexport function getEvmAccountFromAccountGroup(\n accounts: (InternalAccount | PerpsInternalAccount)[],\n): { address: string } | undefined {\n const evmAccount = findEvmAccount(accounts);\n return evmAccount ? { address: evmAccount.address } : undefined;\n}\n\nexport function getSelectedEvmAccount(\n accounts: (InternalAccount | PerpsInternalAccount)[],\n): { address: string } | undefined {\n return getEvmAccountFromAccountGroup(accounts);\n}\n\ntype SelectedEvmAccountMessenger = {\n call(\n actionType:\n | 'AccountsController:getSelectedAccount'\n | 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n ): unknown;\n};\n\nfunction isAccountLike(\n value: unknown,\n): value is InternalAccount | PerpsInternalAccount {\n const account = value as { address?: unknown; type?: unknown } | null;\n\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof account?.address === 'string' &&\n typeof account.type === 'string'\n );\n}\n\nexport function getSelectedEvmAccountDetailsFromMessenger(\n messenger: SelectedEvmAccountMessenger,\n): InternalAccount | PerpsInternalAccount | undefined {\n try {\n const selectedAccount = messenger.call(\n 'AccountsController:getSelectedAccount',\n );\n if (isAccountLike(selectedAccount)) {\n const evmAccount = findEvmAccount([selectedAccount]);\n if (evmAccount) {\n return evmAccount;\n }\n }\n } catch {\n // Fall back to the selected account group if the direct lookup is unavailable.\n }\n\n try {\n const selectedAccountGroup = messenger.call(\n 'AccountTreeController:getAccountsFromSelectedAccountGroup',\n );\n return Array.isArray(selectedAccountGroup)\n ? (findEvmAccount(selectedAccountGroup.filter(isAccountLike)) ??\n undefined)\n : undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function getSelectedEvmAccountFromMessenger(\n messenger: SelectedEvmAccountMessenger,\n): { address: string } | undefined {\n const evmAccount = getSelectedEvmAccountDetailsFromMessenger(messenger);\n\n return evmAccount ? { address: evmAccount.address } : undefined;\n}\n\nexport type ReturnOnEquityInput = {\n unrealizedPnl: string | number;\n returnOnEquity: string | number;\n};\n\nexport function calculateWeightedReturnOnEquity(\n accounts: ReturnOnEquityInput[],\n): string {\n if (accounts.length === 0) {\n return '0';\n }\n\n let totalWeightedROE = 0;\n let totalMarginUsed = 0;\n\n for (const account of accounts) {\n const unrealizedPnl =\n typeof account.unrealizedPnl === 'string'\n ? Number.parseFloat(account.unrealizedPnl)\n : account.unrealizedPnl;\n const returnOnEquity =\n typeof account.returnOnEquity === 'string'\n ? Number.parseFloat(account.returnOnEquity)\n : account.returnOnEquity;\n\n if (Number.isNaN(unrealizedPnl) || Number.isNaN(returnOnEquity)) {\n continue;\n }\n\n if (returnOnEquity === 0) {\n continue;\n }\n\n const marginUsed = (unrealizedPnl / returnOnEquity) * 100;\n\n if (Number.isNaN(marginUsed) || marginUsed <= 0) {\n continue;\n }\n\n const roeDecimal = returnOnEquity / 100;\n\n totalWeightedROE += roeDecimal * marginUsed;\n totalMarginUsed += marginUsed;\n }\n\n if (totalMarginUsed <= 0) {\n return '0';\n }\n\n const weightedROE = (totalWeightedROE / totalMarginUsed) * 100;\n return weightedROE.toString();\n}\n\n// The release-branch balance bridge is USDC-only. Non-USDC spot assets must\n// not inflate the balances shown or validated by withdraw/payment flows.\nconst SPOT_COLLATERAL_COINS = new Set<string>(['USDC']);\n\nfunction getSpotCollateralAmount(\n spotState: SpotClearinghouseStateResponse | null | undefined,\n field: 'hold' | 'total',\n): BigNumber {\n if (!spotState?.balances || !Array.isArray(spotState.balances)) {\n return new BigNumber(0);\n }\n\n return spotState.balances.reduce((sum, balance) => {\n if (!balance.coin || !SPOT_COLLATERAL_COINS.has(balance.coin)) {\n return sum;\n }\n const value = new BigNumber(balance[field] ?? '0');\n return value.isFinite() ? sum.plus(value) : sum;\n }, new BigNumber(0));\n}\n\nexport function getSpotBalance(\n spotState?: SpotClearinghouseStateResponse | null,\n): number {\n return getSpotCollateralAmount(spotState, 'total').toNumber();\n}\n\nexport function getSpotHold(\n spotState?: SpotClearinghouseStateResponse | null,\n): number {\n return getSpotCollateralAmount(spotState, 'hold').toNumber();\n}\n\n/**\n * Options controlling how `addSpotBalanceToAccountState` folds spot balance\n * into the three-field AccountState contract.\n */\nexport type AddSpotBalanceOptions = {\n /**\n * When `true`, free spot USDC contributes to both `spendableBalance` and\n * `withdrawableBalance` in addition to `totalBalance` — appropriate for\n * venues where spot is automatically used as perps collateral (e.g.\n * HyperLiquid Unified/Portfolio mode, where `withdraw3` draws from the\n * unified ledger).\n *\n * When `false`, free spot contributes to `totalBalance` only; spendable\n * and withdrawable stay perps-only — appropriate for venues where spot\n * is a separate ledger the backend cannot auto-draw from (e.g. HL\n * Standard mode). The caller is responsible for translating\n * provider-specific state into this flag.\n *\n * Defaults to `true` for backward compatibility with call sites that\n * haven't yet been wired with provider-specific context.\n */\n foldIntoCollateral?: boolean;\n};\n\n/**\n * Add spot USDC to the AccountState contract. Caller decides whether the\n * spot balance counts as perps collateral via `options.foldIntoCollateral`\n * — the util stays provider-agnostic.\n *\n * @param accountState - Base AccountState produced by a provider adapter.\n * @param spotState - Raw spot clearinghouse response (HL-shaped); null or missing means no spot balance and the state is returned unchanged.\n * @param options - See {@link AddSpotBalanceOptions}.\n * @returns AccountState with spot folded into `totalBalance` always, and into spendable/withdrawable when `foldIntoCollateral` is true.\n */\nexport function addSpotBalanceToAccountState(\n accountState: AccountState,\n spotState?: SpotClearinghouseStateResponse | null,\n options?: AddSpotBalanceOptions,\n): AccountState {\n // Fail-closed default: align with `hyperLiquidModeFoldsSpot(null) → false`.\n // A caller that omits `options` should NOT silently fold spot — that would\n // over-report withdrawable funds for Standard / default users.\n const foldIntoCollateral = options?.foldIntoCollateral ?? false;\n\n const spotBalance = getSpotCollateralAmount(spotState, 'total');\n const spotHold = getSpotCollateralAmount(spotState, 'hold');\n const freeSpot = BigNumber.max(0, spotBalance.minus(spotHold));\n\n const currentTotal = new BigNumber(accountState.totalBalance);\n const currentSpendable = new BigNumber(accountState.spendableBalance);\n const currentWithdrawable = new BigNumber(accountState.withdrawableBalance);\n\n // Preserve sentinel totals (e.g. PERPS_CONSTANTS.FallbackDataDisplay '--')\n // rather than coercing them to NaN.\n if (!currentTotal.isFinite()) {\n return accountState;\n }\n\n // Folding is gated strictly on the resolved abstraction mode (see callers'\n // `foldIntoCollateral` argument). Standard / DEX-abstraction users keep\n // perps and spot independent, so spot must NOT surface as a perps-\n // withdrawable balance for them — withdraw3 only draws from the perps\n // ledger in those modes. Unified / portfolio-margin users get the fold;\n // live callers fail-CLOSED via `hyperLiquidModeFoldsSpot` when mode is\n // unresolved.\n const nextSpendable = resolveFoldedBalance(\n currentSpendable,\n accountState.spendableBalance,\n freeSpot,\n foldIntoCollateral,\n );\n const nextWithdrawable = resolveFoldedBalance(\n currentWithdrawable,\n accountState.withdrawableBalance,\n freeSpot,\n foldIntoCollateral,\n );\n\n // Total always reflects combined wealth: subtract spotHold to avoid\n // double-counting on Unified/PM accounts where marginSummary.accountValue\n // already includes the margin that HL surfaces via spot.hold. Standard\n // mode has spotHold = 0 by construction, so the subtraction is a no-op.\n const nextTotal = canonicalizeUsdc(\n currentTotal.plus(spotBalance).minus(spotHold),\n );\n\n if (\n nextTotal === accountState.totalBalance &&\n nextSpendable === accountState.spendableBalance &&\n nextWithdrawable === accountState.withdrawableBalance\n ) {\n return accountState;\n }\n\n return {\n ...accountState,\n totalBalance: nextTotal,\n spendableBalance: nextSpendable,\n withdrawableBalance: nextWithdrawable,\n };\n}\n\nfunction resolveFoldedBalance(\n currentNumeric: BigNumber,\n currentRaw: string,\n freeSpot: BigNumber,\n foldIntoCollateral: boolean,\n): string {\n if (!currentNumeric.isFinite()) {\n // Non-finite currentNumeric means the adapter passed a sentinel like\n // `PERPS_CONSTANTS.FallbackDataDisplay` (\"--\") during loading. Preserve\n // the sentinel rather than synthesising a numeric fold; the caller's\n // UI treats \"--\" as \"loading\" and would otherwise show a misleading\n // spot-only figure while the per-DEX perps fetch is still in flight.\n return currentRaw;\n }\n\n const spotAmount = foldIntoCollateral ? freeSpot : new BigNumber(0);\n return canonicalizeUsdc(currentNumeric.plus(spotAmount));\n}\n\nfunction canonicalizeUsdc(value: BigNumber): string {\n return value.decimalPlaces(USDC_DECIMALS, BigNumber.ROUND_DOWN).toFixed();\n}\n\n/**\n * Sum per-DEX cross-margin summaries. Undefined unless every state carries\n * one, so a partial sum never reads as the whole account.\n *\n * @param states - The per-DEX account states.\n * @returns The summed summary, or undefined.\n */\nexport function sumCrossMarginSummaries(\n states: AccountState[],\n): CrossMarginSummary | undefined {\n const summaries = states\n .map((state) => state.crossMarginSummary)\n .filter((summary): summary is CrossMarginSummary => summary !== undefined);\n if (summaries.length === 0 || summaries.length !== states.length) {\n return undefined;\n }\n const sum = (field: keyof CrossMarginSummary): string =>\n summaries\n .reduce((total, summary) => total.plus(summary[field]), new BigNumber(0))\n .toFixed();\n return {\n accountValue: sum('accountValue'),\n maintenanceMargin: sum('maintenanceMargin'),\n marginUsed: sum('marginUsed'),\n notional: sum('notional'),\n };\n}\n\n/**\n * A {@link CrossMarginSummary} whose figures may already be numbers.\n */\nexport type CrossMarginSummaryInput = Record<\n keyof CrossMarginSummary,\n number | string\n>;\n\n/**\n * Account figures derived from the venue's cross margin. Each is undefined\n * while its inputs are unknown.\n */\nexport type PerpsAccountSummary = {\n /** Maintenance margin the cross positions require, in USD. */\n maintenanceMargin: number | undefined;\n /**\n * Cross maintenance margin over cross collateral (0.5 = 50%). Cross\n * positions are liquidated when it reaches 1.\n */\n accountRatio: number | undefined;\n /** Cross notional as a multiple of total account equity. */\n accountLeverage: number | undefined;\n /** Cross margin used over cross collateral (0.25 = 25%). */\n crossMarginUsage: number | undefined;\n};\n\n/**\n * Inputs to {@link calculateAccountSummary}.\n */\nexport type CalculateAccountSummaryParams = {\n /**\n * Total account equity in USD, isolated margin included\n * (`AccountState.totalBalance`).\n */\n totalEquity: number | string;\n /** The venue's cross margin figures; undefined until read. */\n crossMarginSummary: CrossMarginSummaryInput | undefined;\n /**\n * Spot USDC the venue counts as cross collateral (0 when it counts none);\n * undefined until read.\n */\n spotCollateral: number | string | undefined;\n};\n\nfunction toFiniteNumber(\n value: number | string | undefined,\n): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n const parsed = typeof value === 'number' ? value : Number.parseFloat(value);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction share(\n part: number | undefined,\n whole: number | undefined,\n): number | undefined {\n return part === undefined || whole === undefined || whole <= 0\n ? undefined\n : part / whole;\n}\n\n/**\n * The collateral the cross positions draw on: the cross account value plus\n * the spot USDC the venue counts as collateral.\n *\n * @param crossMarginSummary - The venue's cross margin figures.\n * @param spotCollateral - Spot USDC counted as cross collateral.\n * @returns The cross collateral in USD, or undefined until both are known.\n */\nexport function getCrossCollateral(\n crossMarginSummary: CrossMarginSummaryInput | undefined,\n spotCollateral: number | string | undefined,\n): number | undefined {\n const accountValue = toFiniteNumber(crossMarginSummary?.accountValue);\n const spot = toFiniteNumber(spotCollateral);\n return accountValue === undefined || spot === undefined\n ? undefined\n : accountValue + spot;\n}\n\n/**\n * Derive the account summary from the venue's cross margin figures, matching\n * Hyperliquid: account leverage divides cross notional by total account\n * equity, isolated margin included; account ratio and cross margin usage\n * divide the cross maintenance margin and margin used by cross collateral.\n *\n * @param params - The account inputs.\n * @param params.totalEquity - Total account equity in USD.\n * @param params.crossMarginSummary - The venue's cross margin figures.\n * @param params.spotCollateral - Spot USDC counted as cross collateral.\n * @returns The summary; a ratio is undefined while its inputs are unknown or\n * its denominator is not positive.\n */\nexport function calculateAccountSummary({\n totalEquity,\n crossMarginSummary,\n spotCollateral,\n}: CalculateAccountSummaryParams): PerpsAccountSummary {\n const collateral = getCrossCollateral(crossMarginSummary, spotCollateral);\n const maintenanceMargin = toFiniteNumber(\n crossMarginSummary?.maintenanceMargin,\n );\n return {\n maintenanceMargin,\n accountRatio: share(maintenanceMargin, collateral),\n accountLeverage: share(\n toFiniteNumber(crossMarginSummary?.notional),\n toFiniteNumber(totalEquity),\n ),\n crossMarginUsage: share(\n toFiniteNumber(crossMarginSummary?.marginUsed),\n collateral,\n ),\n };\n}\n\n/**\n * Aggregate multiple per-DEX AccountState objects into one by summing numeric\n * fields exactly. Collateral subtotals retain their full precision so spot\n * collateral can be added before the final USDC canonicalization.\n * ROE is recalculated as (totalUnrealizedPnl / totalMarginUsed) * 100.\n *\n * @param states - The array of per-DEX account states to aggregate.\n * @returns The combined account state with summed balances and recalculated ROE.\n */\nexport function aggregateAccountStates(states: AccountState[]): AccountState {\n const fallback: AccountState = {\n spendableBalance: PERPS_CONSTANTS.FallbackDataDisplay,\n withdrawableBalance: PERPS_CONSTANTS.FallbackDataDisplay,\n totalBalance: PERPS_CONSTANTS.FallbackDataDisplay,\n marginUsed: PERPS_CONSTANTS.FallbackDataDisplay,\n unrealizedPnl: PERPS_CONSTANTS.FallbackDataDisplay,\n returnOnEquity: PERPS_CONSTANTS.FallbackDataDisplay,\n };\n\n if (states.length === 0) {\n return fallback;\n }\n\n const aggregated = states.reduce<AccountState>((acc, state, index) => {\n if (index === 0) {\n return { ...state };\n }\n\n return {\n spendableBalance: addDecimalValues(\n acc.spendableBalance,\n state.spendableBalance,\n ),\n withdrawableBalance: addDecimalValues(\n acc.withdrawableBalance,\n state.withdrawableBalance,\n ),\n totalBalance: addDecimalValues(acc.totalBalance, state.totalBalance),\n marginUsed: (\n parseFloat(acc.marginUsed) + parseFloat(state.marginUsed)\n ).toString(),\n unrealizedPnl: (\n parseFloat(acc.unrealizedPnl) + parseFloat(state.unrealizedPnl)\n ).toString(),\n returnOnEquity: '0',\n };\n }, fallback);\n\n aggregated.spendableBalance = normalizeDecimalString(\n aggregated.spendableBalance,\n );\n aggregated.withdrawableBalance = normalizeDecimalString(\n aggregated.withdrawableBalance,\n );\n aggregated.totalBalance = normalizeDecimalString(aggregated.totalBalance);\n\n // Recalculate ROE across all DEXs\n const totalMarginUsed = parseFloat(aggregated.marginUsed);\n const totalUnrealizedPnl = parseFloat(aggregated.unrealizedPnl);\n if (totalMarginUsed > 0) {\n aggregated.returnOnEquity = (\n (totalUnrealizedPnl / totalMarginUsed) *\n 100\n ).toString();\n } else {\n aggregated.returnOnEquity = '0';\n }\n\n const crossMarginSummary = sumCrossMarginSummaries(states);\n if (crossMarginSummary) {\n aggregated.crossMarginSummary = crossMarginSummary;\n }\n\n return aggregated;\n}\n\nfunction addDecimalValues(left: string, right: string): string {\n return new BigNumber(left).plus(right).toFixed();\n}\n\nfunction normalizeDecimalString(value: string): string {\n const amount = new BigNumber(value);\n return amount.isFinite() ? amount.toFixed() : value;\n}\n"]}
|
|
@@ -50,39 +50,6 @@ export declare function markProviderErrorReported<Result extends object>(result:
|
|
|
50
50
|
* @returns Whether the provider owns reporting for the failure.
|
|
51
51
|
*/
|
|
52
52
|
export declare function wasProviderErrorReported(result: object): boolean;
|
|
53
|
-
declare const PROVIDER_ERROR_CAUSE: unique symbol;
|
|
54
|
-
type ProviderErrorCause = {
|
|
55
|
-
[PROVIDER_ERROR_CAUSE]?: Error;
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* Attach the normalized error behind a failed provider result.
|
|
59
|
-
*
|
|
60
|
-
* Results carry only stable codes, so a service that owns reporting would
|
|
61
|
-
* otherwise rebuild an error from `result.error` and lose the original
|
|
62
|
-
* failure. The property is non-enumerable so the result's observable shape is
|
|
63
|
-
* unchanged; a wrapper that spreads a result must carry it over with
|
|
64
|
-
* `carryProviderErrorCause`.
|
|
65
|
-
*
|
|
66
|
-
* @param result - Provider result to annotate.
|
|
67
|
-
* @param error - Normalized error whose `cause` holds the original failure.
|
|
68
|
-
* @returns The same result with internal reporting metadata.
|
|
69
|
-
*/
|
|
70
|
-
export declare function attachProviderErrorCause<Result extends object>(result: Result, error: Error): Result & ProviderErrorCause;
|
|
71
|
-
/**
|
|
72
|
-
* Copy an attached provider error from one result onto a derived result.
|
|
73
|
-
*
|
|
74
|
-
* @param source - Result that may carry an attached error.
|
|
75
|
-
* @param target - Result derived from `source`, typically by spreading it.
|
|
76
|
-
* @returns `target`, annotated with the source's error when it had one.
|
|
77
|
-
*/
|
|
78
|
-
export declare function carryProviderErrorCause<Result extends object>(source: object, target: Result): Result;
|
|
79
|
-
/**
|
|
80
|
-
* Read the normalized error a provider attached to a failed result.
|
|
81
|
-
*
|
|
82
|
-
* @param result - Provider result returned to a service.
|
|
83
|
-
* @returns The attached error, or undefined when the provider attached none.
|
|
84
|
-
*/
|
|
85
|
-
export declare function getProviderErrorCause(result: object): Error | undefined;
|
|
86
53
|
/**
|
|
87
54
|
* Build a Perps logger payload with bounded, searchable Sentry tags.
|
|
88
55
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorContext.d.ts","sourceRoot":"","sources":["../../src/utils/errorContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,qBAAqB;aAChC,oBAAoB,EAAE,uBAAuB;aAC7C,eAAe,EAAE,kBAAkB;aACnC,kBAAkB,EAAE,qBAAqB;aACzC,mBAAmB,EAAE,sBAAsB;CACnC,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAC7B,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC;AAErE,eAAO,MAAM,kBAAkB;aAC7B,oBAAoB,EAAE,uBAAuB;aAC7C,gBAAgB,EAAE,mBAAmB;aACrC,mBAAmB,EAAE,sBAAsB;aAC3C,UAAU,EAAE,aAAa;aACzB,SAAS,EAAE,YAAY;aACvB,WAAW,EAAE,cAAc;aAC3B,aAAa,EAAE,gBAAgB;aAC/B,kBAAkB,EAAE,sBAAsB;aAC1C,YAAY,EAAE,eAAe;aAC7B,YAAY,EAAE,eAAe;CACrB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAC1B,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE/D,eAAO,MAAM,qBAAqB;aAChC,iBAAiB,EAAE,wBAAwB;CACnC,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAC7B,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC;AAErE,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,mBAAmB,CAAC;IAC/B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAC1C,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CACpC,CAAC;AAEF,QAAA,MAAM,uBAAuB,eAAkC,CAAC;AAEhE,KAAK,qBAAqB,GAAG;IAC3B,CAAC,uBAAuB,CAAC,CAAC,EAAE,IAAI,CAAC;CAClC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,SAAS,MAAM,EAC7D,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,qBAAqB,CAEhC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED
|
|
1
|
+
{"version":3,"file":"errorContext.d.ts","sourceRoot":"","sources":["../../src/utils/errorContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,eAAO,MAAM,qBAAqB;aAChC,oBAAoB,EAAE,uBAAuB;aAC7C,eAAe,EAAE,kBAAkB;aACnC,kBAAkB,EAAE,qBAAqB;aACzC,mBAAmB,EAAE,sBAAsB;CACnC,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAC7B,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC;AAErE,eAAO,MAAM,kBAAkB;aAC7B,oBAAoB,EAAE,uBAAuB;aAC7C,gBAAgB,EAAE,mBAAmB;aACrC,mBAAmB,EAAE,sBAAsB;aAC3C,UAAU,EAAE,aAAa;aACzB,SAAS,EAAE,YAAY;aACvB,WAAW,EAAE,cAAc;aAC3B,aAAa,EAAE,gBAAgB;aAC/B,kBAAkB,EAAE,sBAAsB;aAC1C,YAAY,EAAE,eAAe;aAC7B,YAAY,EAAE,eAAe;CACrB,CAAC;AAEX,MAAM,MAAM,gBAAgB,GAC1B,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE/D,eAAO,MAAM,qBAAqB;aAChC,iBAAiB,EAAE,wBAAwB;CACnC,CAAC;AAEX,MAAM,MAAM,mBAAmB,GAC7B,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC;AAErE,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,mBAAmB,CAAC;IAC/B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,SAAS,CAAC,EAAE,mBAAmB,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAC1C,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CACpC,CAAC;AAEF,QAAA,MAAM,uBAAuB,eAAkC,CAAC;AAEhE,KAAK,qBAAqB,GAAG;IAC3B,CAAC,uBAAuB,CAAC,CAAC,EAAE,IAAI,CAAC;CAClC,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,SAAS,MAAM,EAC7D,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,qBAAqB,CAEhC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC,GAAG,kBAAkB,CAgCrB"}
|
|
@@ -42,47 +42,6 @@ export function markProviderErrorReported(result) {
|
|
|
42
42
|
export function wasProviderErrorReported(result) {
|
|
43
43
|
return result[PROVIDER_ERROR_REPORTED] === true;
|
|
44
44
|
}
|
|
45
|
-
const PROVIDER_ERROR_CAUSE = Symbol('providerErrorCause');
|
|
46
|
-
/**
|
|
47
|
-
* Attach the normalized error behind a failed provider result.
|
|
48
|
-
*
|
|
49
|
-
* Results carry only stable codes, so a service that owns reporting would
|
|
50
|
-
* otherwise rebuild an error from `result.error` and lose the original
|
|
51
|
-
* failure. The property is non-enumerable so the result's observable shape is
|
|
52
|
-
* unchanged; a wrapper that spreads a result must carry it over with
|
|
53
|
-
* `carryProviderErrorCause`.
|
|
54
|
-
*
|
|
55
|
-
* @param result - Provider result to annotate.
|
|
56
|
-
* @param error - Normalized error whose `cause` holds the original failure.
|
|
57
|
-
* @returns The same result with internal reporting metadata.
|
|
58
|
-
*/
|
|
59
|
-
export function attachProviderErrorCause(result, error) {
|
|
60
|
-
return Object.defineProperty(result, PROVIDER_ERROR_CAUSE, {
|
|
61
|
-
value: error,
|
|
62
|
-
enumerable: false,
|
|
63
|
-
configurable: true,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Copy an attached provider error from one result onto a derived result.
|
|
68
|
-
*
|
|
69
|
-
* @param source - Result that may carry an attached error.
|
|
70
|
-
* @param target - Result derived from `source`, typically by spreading it.
|
|
71
|
-
* @returns `target`, annotated with the source's error when it had one.
|
|
72
|
-
*/
|
|
73
|
-
export function carryProviderErrorCause(source, target) {
|
|
74
|
-
const error = getProviderErrorCause(source);
|
|
75
|
-
return error ? attachProviderErrorCause(target, error) : target;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Read the normalized error a provider attached to a failed result.
|
|
79
|
-
*
|
|
80
|
-
* @param result - Provider result returned to a service.
|
|
81
|
-
* @returns The attached error, or undefined when the provider attached none.
|
|
82
|
-
*/
|
|
83
|
-
export function getProviderErrorCause(result) {
|
|
84
|
-
return result[PROVIDER_ERROR_CAUSE];
|
|
85
|
-
}
|
|
86
45
|
/**
|
|
87
46
|
* Build a Perps logger payload with bounded, searchable Sentry tags.
|
|
88
47
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorContext.js","sourceRoot":"","sources":["../../src/utils/errorContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAG9D,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,oBAAoB,EAAE,uBAAuB;IAC7C,eAAe,EAAE,kBAAkB;IACnC,kBAAkB,EAAE,qBAAqB;IACzC,mBAAmB,EAAE,sBAAsB;CACnC,CAAC;AAKX,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,oBAAoB,EAAE,uBAAuB;IAC7C,gBAAgB,EAAE,mBAAmB;IACrC,mBAAmB,EAAE,sBAAsB;IAC3C,UAAU,EAAE,aAAa;IACzB,SAAS,EAAE,YAAY;IACvB,WAAW,EAAE,cAAc;IAC3B,aAAa,EAAE,gBAAgB;IAC/B,kBAAkB,EAAE,sBAAsB;IAC1C,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;CACrB,CAAC;AAKX,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,iBAAiB,EAAE,wBAAwB;CACnC,CAAC;AAeX,MAAM,uBAAuB,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAMhE;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,uBAAuB,CAAC,EAAE,IAAa,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAQ,MAAgC,CAAC,uBAAuB,CAAC,KAAK,IAAI,CAAC;AAC7E,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"errorContext.js","sourceRoot":"","sources":["../../src/utils/errorContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAG9D,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,oBAAoB,EAAE,uBAAuB;IAC7C,eAAe,EAAE,kBAAkB;IACnC,kBAAkB,EAAE,qBAAqB;IACzC,mBAAmB,EAAE,sBAAsB;CACnC,CAAC;AAKX,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,oBAAoB,EAAE,uBAAuB;IAC7C,gBAAgB,EAAE,mBAAmB;IACrC,mBAAmB,EAAE,sBAAsB;IAC3C,UAAU,EAAE,aAAa;IACzB,SAAS,EAAE,YAAY;IACvB,WAAW,EAAE,cAAc;IAC3B,aAAa,EAAE,gBAAgB;IAC/B,kBAAkB,EAAE,sBAAsB;IAC1C,YAAY,EAAE,eAAe;IAC7B,YAAY,EAAE,eAAe;CACrB,CAAC;AAKX,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,iBAAiB,EAAE,wBAAwB;CACnC,CAAC;AAeX,MAAM,uBAAuB,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC;AAMhE;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAc;IAEd,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,uBAAuB,CAAC,EAAE,IAAa,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAc;IACrD,OAAQ,MAAgC,CAAC,uBAAuB,CAAC,KAAK,IAAI,CAAC;AAC7E,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB,CAAC,OAOvC;IACC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAC5E,MAAM,IAAI,GAAoC;QAC5C,OAAO,EAAE,eAAe,CAAC,WAAW;KACrC,CAAC;IAEF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IACD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;QACrC,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACnC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;QACjC,CAAC;QACD,IAAI,SAAS,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACtC,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;QACvC,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,OAAO,EAAE;YACP,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE;gBACJ,MAAM;gBACN,GAAG,IAAI;aACR;SACF;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { PERPS_CONSTANTS } from '../constants/perpsConfig.js';\nimport type { PerpsLogger } from '../types/index.js';\n\nexport const PERPS_ERROR_OPERATION = {\n ConnectionManagement: 'connection_management',\n OrderManagement: 'order_management',\n PositionManagement: 'position_management',\n FinancialOperations: 'financial_operations',\n} as const;\n\nexport type PerpsErrorOperation =\n (typeof PERPS_ERROR_OPERATION)[keyof typeof PERPS_ERROR_OPERATION];\n\nexport const PERPS_ERROR_ACTION = {\n ConnectionConnection: 'connection_connection',\n FinancialDeposit: 'financial_deposit',\n FinancialWithdrawal: 'financial_withdrawal',\n PlaceOrder: 'place_order',\n EditOrder: 'edit_order',\n CancelOrder: 'cancel_order',\n ClosePosition: 'close_position',\n PositionTpslUpdate: 'position_tpsl_update',\n UpdateMargin: 'update_margin',\n FlipPosition: 'flip_position',\n} as const;\n\nexport type PerpsErrorAction =\n (typeof PERPS_ERROR_ACTION)[keyof typeof PERPS_ERROR_ACTION];\n\nexport const PERPS_ERROR_COMPONENT = {\n ConnectionManager: 'PerpsConnectionManager',\n} as const;\n\nexport type PerpsErrorComponent =\n (typeof PERPS_ERROR_COMPONENT)[keyof typeof PERPS_ERROR_COMPONENT];\n\nexport type PerpsErrorTags = {\n operation: PerpsErrorOperation;\n action?: PerpsErrorAction;\n component?: PerpsErrorComponent;\n};\n\nexport type PerpsLoggerOptions = NonNullable<\n Parameters<PerpsLogger['error']>[1]\n>;\n\nconst PROVIDER_ERROR_REPORTED = Symbol('providerErrorReported');\n\ntype ProviderErrorReported = {\n [PROVIDER_ERROR_REPORTED]?: true;\n};\n\n/**\n * Mark a failed provider result whose underlying error was already reported.\n *\n * The symbol remains internal to Core and survives object spreads used by the\n * aggregated provider without adding a consumer-facing result field.\n *\n * @param result - Provider result to mark.\n * @returns The same result with internal reporting metadata.\n */\nexport function markProviderErrorReported<Result extends object>(\n result: Result,\n): Result & ProviderErrorReported {\n return Object.assign(result, { [PROVIDER_ERROR_REPORTED]: true as const });\n}\n\n/**\n * Check whether a provider already reported the error represented by a result.\n *\n * @param result - Provider result returned to a service.\n * @returns Whether the provider owns reporting for the failure.\n */\nexport function wasProviderErrorReported(result: object): boolean {\n return (result as ProviderErrorReported)[PROVIDER_ERROR_REPORTED] === true;\n}\n\n/**\n * Build a Perps logger payload with bounded, searchable Sentry tags.\n *\n * Diagnostic data is deliberately kept separate from tags so identifiers,\n * symbols, messages, and method names cannot accidentally create unbounded\n * tag cardinality.\n *\n * @param options - Error context options.\n * @param options.contextName - Stable name for the diagnostic context.\n * @param options.method - Method or boundary where the error occurred.\n * @param options.provider - Optional provider identifier.\n * @param options.network - Optional network environment.\n * @param options.errorTags - Optional bounded dashboard tags.\n * @param options.data - Optional diagnostic data retained in context.\n * @returns Logger options accepted by the platform-provided Perps logger.\n */\nexport function createPerpsErrorContext(options: {\n contextName: string;\n method: string;\n provider?: string;\n network?: 'mainnet' | 'testnet';\n errorTags?: PerpsErrorTags;\n data?: Record<string, unknown>;\n}): PerpsLoggerOptions {\n const { contextName, method, provider, network, errorTags, data } = options;\n const tags: Record<string, string | number> = {\n feature: PERPS_CONSTANTS.FeatureName,\n };\n\n if (provider !== undefined) {\n tags.provider = provider;\n }\n if (network !== undefined) {\n tags.network = network;\n }\n if (errorTags !== undefined) {\n tags.operation = errorTags.operation;\n if (errorTags.action !== undefined) {\n tags.action = errorTags.action;\n }\n if (errorTags.component !== undefined) {\n tags.component = errorTags.component;\n }\n }\n\n return {\n tags,\n context: {\n name: contextName,\n data: {\n method,\n ...data,\n },\n },\n };\n}\n"]}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { PerpsControllerError } from '../errors.js';
|
|
2
1
|
/**
|
|
3
2
|
* Detects expected cancellation/abort errors that should not be reported to Sentry.
|
|
4
3
|
* These occur during normal navigation or view teardown when in-flight fetch requests
|
|
@@ -48,21 +47,4 @@ export declare function isHyperLiquidUserNotFoundError(error: unknown): boolean;
|
|
|
48
47
|
* @returns True if the error indicates multi-sig signing is required.
|
|
49
48
|
*/
|
|
50
49
|
export declare function isHyperLiquidMultiSigRequiredError(error: unknown): boolean;
|
|
51
|
-
/**
|
|
52
|
-
* Normalize a Hyperliquid failure into the stable error contract exposed to
|
|
53
|
-
* clients.
|
|
54
|
-
*
|
|
55
|
-
* Known venue messages receive actionable codes. Unknown messages are replaced
|
|
56
|
-
* by `UNKNOWN_ERROR`, while every newly normalized error keeps the original
|
|
57
|
-
* failure in `cause` for telemetry and debugging.
|
|
58
|
-
*
|
|
59
|
-
* A `PerpsControllerError` that already carries details is returned as is, as
|
|
60
|
-
* is `PRICE_MOVED`, whose numeric payload is computed locally. Any other
|
|
61
|
-
* structured error without details is tagged with the provider so every
|
|
62
|
-
* Hyperliquid result exposes the same details contract.
|
|
63
|
-
*
|
|
64
|
-
* @param error - Failure thrown by Hyperliquid or its SDK.
|
|
65
|
-
* @returns A structured error safe to expose through provider results.
|
|
66
|
-
*/
|
|
67
|
-
export declare function normalizeHyperLiquidError(error: unknown): PerpsControllerError;
|
|
68
50
|
//# sourceMappingURL=errorUtils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorUtils.d.ts","sourceRoot":"","sources":["../../src/utils/errorUtils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errorUtils.d.ts","sourceRoot":"","sources":["../../src/utils/errorUtils.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CASpD;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAK5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAkBnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kCAAkC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAK1E"}
|
package/dist/utils/errorUtils.js
CHANGED
|
@@ -3,91 +3,8 @@
|
|
|
3
3
|
* Includes generic error helpers and Perps error classification helpers.
|
|
4
4
|
*/
|
|
5
5
|
import { hasProperty } from '@metamask/utils';
|
|
6
|
-
import {
|
|
7
|
-
import { isPerpsErrorCode, PERPS_ERROR_CODES } from '../perpsErrorCodes.js';
|
|
6
|
+
import { PERPS_ERROR_CODES } from '../perpsErrorCodes.js';
|
|
8
7
|
import { hasErrorInCauseChain } from '../services/causeChain.js';
|
|
9
|
-
const HYPERLIQUID_ERROR_MAPPINGS = [
|
|
10
|
-
// Post-only rejections come first: the would-cross message embeds raw bbo
|
|
11
|
-
// prices that the generic patterns below (e.g. status 503) could match.
|
|
12
|
-
{
|
|
13
|
-
pattern: /post only order would have immediately matched/iu,
|
|
14
|
-
code: PERPS_ERROR_CODES.ORDER_POST_ONLY_WOULD_CROSS,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
pattern: /only post-only orders allowed immediately after network upgrade/iu,
|
|
18
|
-
code: PERPS_ERROR_CODES.ORDER_POST_ONLY_WINDOW,
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
pattern: /isolated position does not have sufficient margin available to decrease leverage|cannot reduce.*leverage|leverage reduction/iu,
|
|
22
|
-
code: PERPS_ERROR_CODES.ORDER_LEVERAGE_REDUCTION_FAILED,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
pattern: /multi-sig required|multisig required/iu,
|
|
26
|
-
code: PERPS_ERROR_CODES.EXCHANGE_MULTI_SIG_REQUIRED,
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
pattern: /must deposit before performing actions/iu,
|
|
30
|
-
code: PERPS_ERROR_CODES.EXCHANGE_ACCOUNT_NOT_FOUND,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
pattern: /invalid nonce/iu,
|
|
34
|
-
code: PERPS_ERROR_CODES.EXCHANGE_INVALID_NONCE,
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
pattern: /order(?: \d+)?: order must have minimum value|order must have minimum value/iu,
|
|
38
|
-
code: PERPS_ERROR_CODES.ORDER_SIZE_MIN,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
pattern: /timeout|timed out/iu,
|
|
42
|
-
code: PERPS_ERROR_CODES.CONNECTION_TIMEOUT,
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
pattern: /service unavailable|temporarily unavailable|\bstatus(?: code)?\s*[:=]?\s*503\b|\bhttp(?:\/\d(?:\.\d)?)?\s+503\b/iu,
|
|
46
|
-
code: PERPS_ERROR_CODES.SERVICE_UNAVAILABLE,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
pattern: /network error|fetch failed|connection failed/iu,
|
|
50
|
-
code: PERPS_ERROR_CODES.NETWORK_ERROR,
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
pattern: /rate limit|too many .*requests|throttl/iu,
|
|
54
|
-
code: PERPS_ERROR_CODES.RATE_LIMIT_EXCEEDED,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
pattern: /margin available|not enough margin|insufficient margin/iu,
|
|
58
|
-
code: PERPS_ERROR_CODES.INSUFFICIENT_MARGIN,
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
// "builder has insufficient balance" is the venue's builder-fee refusal,
|
|
62
|
-
// not the user's balance. It stays UNKNOWN_ERROR.
|
|
63
|
-
pattern: /(?<!builder has )insufficient balance|not enough balance/iu,
|
|
64
|
-
code: PERPS_ERROR_CODES.INSUFFICIENT_BALANCE,
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
pattern: /reduce only|reduce-only|reduceOnly/iu,
|
|
68
|
-
code: PERPS_ERROR_CODES.REDUCE_ONLY_VIOLATION,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
pattern: /position would flip|would flip position/iu,
|
|
72
|
-
code: PERPS_ERROR_CODES.POSITION_WOULD_FLIP,
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
pattern: /no position found/iu,
|
|
76
|
-
code: PERPS_ERROR_CODES.POSITION_NOT_FOUND,
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
pattern: /slippage|price moved too much/iu,
|
|
80
|
-
code: PERPS_ERROR_CODES.SLIPPAGE_EXCEEDED,
|
|
81
|
-
},
|
|
82
|
-
{
|
|
83
|
-
pattern: /could not immediately match|insufficient liquidity|no liquidity|IOC.*cancel/iu,
|
|
84
|
-
code: PERPS_ERROR_CODES.IOC_CANCEL,
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
pattern: /order rejected|rejected order/iu,
|
|
88
|
-
code: PERPS_ERROR_CODES.ORDER_REJECTED,
|
|
89
|
-
},
|
|
90
|
-
];
|
|
91
8
|
/**
|
|
92
9
|
* Detects expected cancellation/abort errors that should not be reported to Sentry.
|
|
93
10
|
* These occur during normal navigation or view teardown when in-flight fetch requests
|
|
@@ -168,47 +85,4 @@ export function isHyperLiquidMultiSigRequiredError(error) {
|
|
|
168
85
|
const lower = ensureError(error).message.toLowerCase();
|
|
169
86
|
return (lower.includes('multi-sig required') || lower.includes('multisig required'));
|
|
170
87
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Normalize a Hyperliquid failure into the stable error contract exposed to
|
|
173
|
-
* clients.
|
|
174
|
-
*
|
|
175
|
-
* Known venue messages receive actionable codes. Unknown messages are replaced
|
|
176
|
-
* by `UNKNOWN_ERROR`, while every newly normalized error keeps the original
|
|
177
|
-
* failure in `cause` for telemetry and debugging.
|
|
178
|
-
*
|
|
179
|
-
* A `PerpsControllerError` that already carries details is returned as is, as
|
|
180
|
-
* is `PRICE_MOVED`, whose numeric payload is computed locally. Any other
|
|
181
|
-
* structured error without details is tagged with the provider so every
|
|
182
|
-
* Hyperliquid result exposes the same details contract.
|
|
183
|
-
*
|
|
184
|
-
* @param error - Failure thrown by Hyperliquid or its SDK.
|
|
185
|
-
* @returns A structured error safe to expose through provider results.
|
|
186
|
-
*/
|
|
187
|
-
export function normalizeHyperLiquidError(error) {
|
|
188
|
-
if (error instanceof PerpsControllerError) {
|
|
189
|
-
const { errorCode: structuredCode } = error;
|
|
190
|
-
if (error.errorDetails !== undefined ||
|
|
191
|
-
structuredCode === PERPS_ERROR_CODES.PRICE_MOVED) {
|
|
192
|
-
return error;
|
|
193
|
-
}
|
|
194
|
-
return new PerpsControllerError(structuredCode, structuredCode, { code: structuredCode, provider: 'hyperliquid' }, { cause: error.cause ?? error });
|
|
195
|
-
}
|
|
196
|
-
const cause = ensureError(error, 'normalizeHyperLiquidError');
|
|
197
|
-
let errorCode;
|
|
198
|
-
if (isHyperLiquidUserNotFoundError(cause)) {
|
|
199
|
-
errorCode = PERPS_ERROR_CODES.EXCHANGE_ACCOUNT_NOT_FOUND;
|
|
200
|
-
}
|
|
201
|
-
else if (isPerpsErrorCode(cause.message) &&
|
|
202
|
-
cause.message !== PERPS_ERROR_CODES.PRICE_MOVED) {
|
|
203
|
-
errorCode = cause.message;
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
errorCode = HYPERLIQUID_ERROR_MAPPINGS.find(({ pattern }) => pattern.test(cause.message))?.code;
|
|
207
|
-
}
|
|
208
|
-
const normalizedCode = errorCode ?? PERPS_ERROR_CODES.UNKNOWN_ERROR;
|
|
209
|
-
return new PerpsControllerError(normalizedCode, normalizedCode, {
|
|
210
|
-
code: normalizedCode,
|
|
211
|
-
provider: 'hyperliquid',
|
|
212
|
-
}, { cause });
|
|
213
|
-
}
|
|
214
88
|
//# sourceMappingURL=errorUtils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorUtils.js","sourceRoot":"","sources":["../../src/utils/errorUtils.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Utility functions for error handling across Perps controller code.\n * Includes generic error helpers and Perps error classification helpers.\n */\nimport { hasProperty } from '@metamask/utils';\n\nimport { PerpsControllerError } from '../errors.js';\nimport type { HyperLiquidErrorDetails } from '../errors.js';\nimport { isPerpsErrorCode, PERPS_ERROR_CODES } from '../perpsErrorCodes.js';\nimport { hasErrorInCauseChain } from '../services/causeChain.js';\n\nconst HYPERLIQUID_ERROR_MAPPINGS: readonly {\n pattern: RegExp;\n code: HyperLiquidErrorDetails['code'];\n}[] = [\n // Post-only rejections come first: the would-cross message embeds raw bbo\n // prices that the generic patterns below (e.g. status 503) could match.\n {\n pattern: /post only order would have immediately matched/iu,\n code: PERPS_ERROR_CODES.ORDER_POST_ONLY_WOULD_CROSS,\n },\n {\n pattern:\n /only post-only orders allowed immediately after network upgrade/iu,\n code: PERPS_ERROR_CODES.ORDER_POST_ONLY_WINDOW,\n },\n {\n pattern:\n /isolated position does not have sufficient margin available to decrease leverage|cannot reduce.*leverage|leverage reduction/iu,\n code: PERPS_ERROR_CODES.ORDER_LEVERAGE_REDUCTION_FAILED,\n },\n {\n pattern: /multi-sig required|multisig required/iu,\n code: PERPS_ERROR_CODES.EXCHANGE_MULTI_SIG_REQUIRED,\n },\n {\n pattern: /must deposit before performing actions/iu,\n code: PERPS_ERROR_CODES.EXCHANGE_ACCOUNT_NOT_FOUND,\n },\n {\n pattern: /invalid nonce/iu,\n code: PERPS_ERROR_CODES.EXCHANGE_INVALID_NONCE,\n },\n {\n pattern:\n /order(?: \\d+)?: order must have minimum value|order must have minimum value/iu,\n code: PERPS_ERROR_CODES.ORDER_SIZE_MIN,\n },\n {\n pattern: /timeout|timed out/iu,\n code: PERPS_ERROR_CODES.CONNECTION_TIMEOUT,\n },\n {\n pattern:\n /service unavailable|temporarily unavailable|\\bstatus(?: code)?\\s*[:=]?\\s*503\\b|\\bhttp(?:\\/\\d(?:\\.\\d)?)?\\s+503\\b/iu,\n code: PERPS_ERROR_CODES.SERVICE_UNAVAILABLE,\n },\n {\n pattern: /network error|fetch failed|connection failed/iu,\n code: PERPS_ERROR_CODES.NETWORK_ERROR,\n },\n {\n pattern: /rate limit|too many .*requests|throttl/iu,\n code: PERPS_ERROR_CODES.RATE_LIMIT_EXCEEDED,\n },\n {\n pattern: /margin available|not enough margin|insufficient margin/iu,\n code: PERPS_ERROR_CODES.INSUFFICIENT_MARGIN,\n },\n {\n // \"builder has insufficient balance\" is the venue's builder-fee refusal,\n // not the user's balance. It stays UNKNOWN_ERROR.\n pattern: /(?<!builder has )insufficient balance|not enough balance/iu,\n code: PERPS_ERROR_CODES.INSUFFICIENT_BALANCE,\n },\n {\n pattern: /reduce only|reduce-only|reduceOnly/iu,\n code: PERPS_ERROR_CODES.REDUCE_ONLY_VIOLATION,\n },\n {\n pattern: /position would flip|would flip position/iu,\n code: PERPS_ERROR_CODES.POSITION_WOULD_FLIP,\n },\n {\n pattern: /no position found/iu,\n code: PERPS_ERROR_CODES.POSITION_NOT_FOUND,\n },\n {\n pattern: /slippage|price moved too much/iu,\n code: PERPS_ERROR_CODES.SLIPPAGE_EXCEEDED,\n },\n {\n pattern:\n /could not immediately match|insufficient liquidity|no liquidity|IOC.*cancel/iu,\n code: PERPS_ERROR_CODES.IOC_CANCEL,\n },\n {\n pattern: /order rejected|rejected order/iu,\n code: PERPS_ERROR_CODES.ORDER_REJECTED,\n },\n];\n\n/**\n * Detects expected cancellation/abort errors that should not be reported to Sentry.\n * These occur during normal navigation or view teardown when in-flight fetch requests\n * are cancelled via AbortController.\n *\n * @param error - The error to check.\n * @returns True if the error is an expected abort/cancellation.\n */\nexport function isAbortError(error: unknown): boolean {\n if (error instanceof Error) {\n return (\n error.name === 'AbortError' ||\n error.message.includes('signal is aborted') ||\n error.message.includes('The operation was aborted')\n );\n }\n return false;\n}\n\n/**\n * Detects keyring-locked errors, including SDK-wrapped errors that preserve the\n * original error in `cause`.\n *\n * @param error - The error to check.\n * @returns True if any error in the cause chain is KEYRING_LOCKED.\n */\nexport function isKeyringLockedError(error: unknown): boolean {\n return hasErrorInCauseChain(\n error,\n (current) => current.message === PERPS_ERROR_CODES.KEYRING_LOCKED,\n );\n}\n\n/**\n * Ensures we have a proper Error object for logging.\n * Converts unknown/string errors to proper Error instances.\n * Handles undefined/null specially for better Sentry context.\n *\n * @param error - The caught error (could be Error, string, or unknown)\n * @param context - Optional context string to help identify the source of the error\n * @returns A proper Error instance\n */\nexport function ensureError(error: unknown, context?: string): Error {\n if (error instanceof Error) {\n return error;\n }\n // Handle undefined/null specifically for better error context\n // e.g. Hyperliquid SDK may reject with undefined when AbortSignal.reason is not set\n if (error === undefined || error === null) {\n const baseMessage = 'Unknown error (no details provided)';\n return new Error(context ? `${baseMessage} [${context}]` : baseMessage);\n }\n if (typeof error === 'string') {\n return new Error(error);\n }\n return new Error(\n typeof error === 'object' && error !== null && hasProperty(error, 'message')\n ? String(error.message)\n : 'Unknown error',\n );\n}\n\n/**\n * Hyperliquid rejects user-scoped exchange writes (`agentSetAbstraction`,\n * `setReferrer`, ...) with this exact message when the\n * wallet has never funded a Hyperliquid account. It is a benign pre-account\n * state, not an error we should forward to Sentry.\n *\n * @param error - The caught error.\n * @returns True if the error matches the Hyperliquid \"user not on chain yet\" rejection.\n */\nexport function isHyperLiquidUserNotFoundError(error: unknown): boolean {\n const lower = ensureError(error).message.toLowerCase();\n return (\n lower.includes('user or api wallet') && lower.includes('does not exist')\n );\n}\n\n/**\n * Hyperliquid rejects every single-signer exchange write for an account that\n * has been converted to multi-sig (`ApiRequestError: Multi-sig required`).\n * MetaMask signs Perps actions with a single agent/user wallet, so this is a\n * permanent account-shape condition rather than a failure we should retry or\n * forward to Sentry. Only the hyphenated spelling has been observed from the\n * venue; the unhyphenated variant is matched defensively.\n *\n * @param error - The caught error.\n * @returns True if the error indicates multi-sig signing is required.\n */\nexport function isHyperLiquidMultiSigRequiredError(error: unknown): boolean {\n const lower = ensureError(error).message.toLowerCase();\n return (\n lower.includes('multi-sig required') || lower.includes('multisig required')\n );\n}\n\n/**\n * Normalize a Hyperliquid failure into the stable error contract exposed to\n * clients.\n *\n * Known venue messages receive actionable codes. Unknown messages are replaced\n * by `UNKNOWN_ERROR`, while every newly normalized error keeps the original\n * failure in `cause` for telemetry and debugging.\n *\n * A `PerpsControllerError` that already carries details is returned as is, as\n * is `PRICE_MOVED`, whose numeric payload is computed locally. Any other\n * structured error without details is tagged with the provider so every\n * Hyperliquid result exposes the same details contract.\n *\n * @param error - Failure thrown by Hyperliquid or its SDK.\n * @returns A structured error safe to expose through provider results.\n */\nexport function normalizeHyperLiquidError(\n error: unknown,\n): PerpsControllerError {\n if (error instanceof PerpsControllerError) {\n const { errorCode: structuredCode } = error;\n if (\n error.errorDetails !== undefined ||\n structuredCode === PERPS_ERROR_CODES.PRICE_MOVED\n ) {\n return error;\n }\n return new PerpsControllerError(\n structuredCode,\n structuredCode,\n { code: structuredCode, provider: 'hyperliquid' },\n { cause: error.cause ?? error },\n );\n }\n\n const cause = ensureError(error, 'normalizeHyperLiquidError');\n let errorCode: HyperLiquidErrorDetails['code'] | undefined;\n\n if (isHyperLiquidUserNotFoundError(cause)) {\n errorCode = PERPS_ERROR_CODES.EXCHANGE_ACCOUNT_NOT_FOUND;\n } else if (\n isPerpsErrorCode(cause.message) &&\n cause.message !== PERPS_ERROR_CODES.PRICE_MOVED\n ) {\n errorCode = cause.message;\n } else {\n errorCode = HYPERLIQUID_ERROR_MAPPINGS.find(({ pattern }) =>\n pattern.test(cause.message),\n )?.code;\n }\n\n const normalizedCode = errorCode ?? PERPS_ERROR_CODES.UNKNOWN_ERROR;\n return new PerpsControllerError(\n normalizedCode,\n normalizedCode,\n {\n code: normalizedCode,\n provider: 'hyperliquid',\n },\n { cause },\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"errorUtils.js","sourceRoot":"","sources":["../../src/utils/errorUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,CACL,KAAK,CAAC,IAAI,KAAK,YAAY;YAC3B,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;YAC3C,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CACpD,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,OAAO,oBAAoB,CACzB,KAAK,EACL,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,KAAK,iBAAiB,CAAC,cAAc,CAClE,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc,EAAE,OAAgB;IAC1D,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,8DAA8D;IAC9D,oFAAoF;IACpF,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,MAAM,WAAW,GAAG,qCAAqC,CAAC;QAC1D,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,WAAW,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,IAAI,KAAK,CACd,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC;QAC1E,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;QACvB,CAAC,CAAC,eAAe,CACpB,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,8BAA8B,CAAC,KAAc;IAC3D,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IACvD,OAAO,CACL,KAAK,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CACzE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kCAAkC,CAAC,KAAc;IAC/D,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IACvD,OAAO,CACL,KAAK,CAAC,QAAQ,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAC5E,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Utility functions for error handling across Perps controller code.\n * Includes generic error helpers and Perps error classification helpers.\n */\nimport { hasProperty } from '@metamask/utils';\n\nimport { PERPS_ERROR_CODES } from '../perpsErrorCodes.js';\nimport { hasErrorInCauseChain } from '../services/causeChain.js';\n\n/**\n * Detects expected cancellation/abort errors that should not be reported to Sentry.\n * These occur during normal navigation or view teardown when in-flight fetch requests\n * are cancelled via AbortController.\n *\n * @param error - The error to check.\n * @returns True if the error is an expected abort/cancellation.\n */\nexport function isAbortError(error: unknown): boolean {\n if (error instanceof Error) {\n return (\n error.name === 'AbortError' ||\n error.message.includes('signal is aborted') ||\n error.message.includes('The operation was aborted')\n );\n }\n return false;\n}\n\n/**\n * Detects keyring-locked errors, including SDK-wrapped errors that preserve the\n * original error in `cause`.\n *\n * @param error - The error to check.\n * @returns True if any error in the cause chain is KEYRING_LOCKED.\n */\nexport function isKeyringLockedError(error: unknown): boolean {\n return hasErrorInCauseChain(\n error,\n (current) => current.message === PERPS_ERROR_CODES.KEYRING_LOCKED,\n );\n}\n\n/**\n * Ensures we have a proper Error object for logging.\n * Converts unknown/string errors to proper Error instances.\n * Handles undefined/null specially for better Sentry context.\n *\n * @param error - The caught error (could be Error, string, or unknown)\n * @param context - Optional context string to help identify the source of the error\n * @returns A proper Error instance\n */\nexport function ensureError(error: unknown, context?: string): Error {\n if (error instanceof Error) {\n return error;\n }\n // Handle undefined/null specifically for better error context\n // e.g. Hyperliquid SDK may reject with undefined when AbortSignal.reason is not set\n if (error === undefined || error === null) {\n const baseMessage = 'Unknown error (no details provided)';\n return new Error(context ? `${baseMessage} [${context}]` : baseMessage);\n }\n if (typeof error === 'string') {\n return new Error(error);\n }\n return new Error(\n typeof error === 'object' && error !== null && hasProperty(error, 'message')\n ? String(error.message)\n : 'Unknown error',\n );\n}\n\n/**\n * Hyperliquid rejects user-scoped exchange writes (`agentSetAbstraction`,\n * `setReferrer`, ...) with this exact message when the\n * wallet has never funded a Hyperliquid account. It is a benign pre-account\n * state, not an error we should forward to Sentry.\n *\n * @param error - The caught error.\n * @returns True if the error matches the Hyperliquid \"user not on chain yet\" rejection.\n */\nexport function isHyperLiquidUserNotFoundError(error: unknown): boolean {\n const lower = ensureError(error).message.toLowerCase();\n return (\n lower.includes('user or api wallet') && lower.includes('does not exist')\n );\n}\n\n/**\n * Hyperliquid rejects every single-signer exchange write for an account that\n * has been converted to multi-sig (`ApiRequestError: Multi-sig required`).\n * MetaMask signs Perps actions with a single agent/user wallet, so this is a\n * permanent account-shape condition rather than a failure we should retry or\n * forward to Sentry. Only the hyphenated spelling has been observed from the\n * venue; the unhyphenated variant is matched defensively.\n *\n * @param error - The caught error.\n * @returns True if the error indicates multi-sig signing is required.\n */\nexport function isHyperLiquidMultiSigRequiredError(error: unknown): boolean {\n const lower = ensureError(error).message.toLowerCase();\n return (\n lower.includes('multi-sig required') || lower.includes('multisig required')\n );\n}\n"]}
|