-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/add support for hyperliquid transactions #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: next
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import type { | ||
| SwapActionTypes, | ||
| SwapQueueContext, | ||
| SwapStorage, | ||
| } from '../../types'; | ||
| import type { NextTransactionStateError } from '../common/produceNextStateForTransaction'; | ||
| import type { ExecuterActions } from '@rango-dev/queue-manager-core'; | ||
| import type { Err } from 'ts-results'; | ||
|
|
||
| import { warn } from '@rango-dev/logging-core'; | ||
|
|
||
| import { | ||
| delay, | ||
| getCurrentStep, | ||
| getCurrentStepTx, | ||
| handleSuccessfulSign, | ||
| } from '../../helpers'; | ||
| import { getCurrentAddressOf } from '../../shared'; | ||
| import { onNextStateError } from '../common/produceNextStateForTransaction'; | ||
| import { ensureHyperliquidTransactionIsValid } from '../executeHyperliquidTransaction'; | ||
|
|
||
| import { | ||
| GetHyperliquidTransactionHashError, | ||
| INTERVAL_FOR_CHECK_HYPERLIQUID_TRANSACTION_STATUS, | ||
| } from './constants'; | ||
| import { getHyperliquidTransactionHash } from './utils'; | ||
|
|
||
| export async function checkHyperliquidTransactionStatus( | ||
| actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext> | ||
| ): Promise<void> { | ||
| const { failed, getStorage, retry } = actions; | ||
|
|
||
| const swap = getStorage().swapDetails; | ||
| const currentStep = getCurrentStep(swap)!; | ||
|
|
||
| const onFinish = () => { | ||
| if (actions.context.resetClaimedBy) { | ||
| actions.context.resetClaimedBy(); | ||
| } | ||
| }; | ||
|
|
||
| const handleErr = (err: Err<NextTransactionStateError>) => { | ||
| onNextStateError(actions, err.val); | ||
| failed(); | ||
| onFinish(); | ||
| }; | ||
|
|
||
| const currentTransactionFromStorage = getCurrentStepTx(currentStep); | ||
| const walletAddress = getCurrentAddressOf(swap, currentStep); | ||
|
|
||
| const hyperliquidTransactionResult = ensureHyperliquidTransactionIsValid( | ||
| currentTransactionFromStorage | ||
| ); | ||
| if (hyperliquidTransactionResult.err) { | ||
| handleErr(hyperliquidTransactionResult); | ||
| return; | ||
| } | ||
|
|
||
| const hyperliquidTransaction = hyperliquidTransactionResult.val; | ||
| const nonce = hyperliquidTransaction.nonce; | ||
|
|
||
| const hyperliquidTransactionHash = await getHyperliquidTransactionHash( | ||
| walletAddress, | ||
| nonce | ||
| ); | ||
|
|
||
| if (hyperliquidTransactionHash.err) { | ||
| if ( | ||
| hyperliquidTransactionHash.val === | ||
| GetHyperliquidTransactionHashError.FETCH_ERROR || | ||
| hyperliquidTransactionHash.val === | ||
| GetHyperliquidTransactionHashError.RESPONSE_PARSING_ERROR | ||
| ) { | ||
| warn(new Error('check Hyperliquid transaction status Error'), { | ||
| tags: { | ||
| type: 'request-error', | ||
| requestBody: { type: 'userDetails', user: walletAddress, nonce }, | ||
| pendingSwap: swap, | ||
| }, | ||
| }); | ||
| } | ||
|
|
||
| await delay(INTERVAL_FOR_CHECK_HYPERLIQUID_TRANSACTION_STATUS); | ||
| retry(); | ||
| return; | ||
| } | ||
|
|
||
| handleSuccessfulSign(actions, { | ||
| isApproval: false, | ||
| })({ | ||
| hash: hyperliquidTransactionHash.val, | ||
| }); | ||
| onFinish(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| export const HYPERLIQUID_EXPLORER_API_URL = | ||
| 'https://rpc.hyperliquid.xyz/explorer'; | ||
|
|
||
| export const INTERVAL_FOR_CHECK_HYPERLIQUID_TRANSACTION_STATUS = 5_000; | ||
|
|
||
| export enum GetHyperliquidTransactionHashError { | ||
| FETCH_ERROR = 'FETCH_ERROR', | ||
| RESPONSE_PARSING_ERROR = 'RESPONSE_PARSING_ERROR', | ||
| TRANSACTION_NOT_FOUND = 'TRANSACTION_NOT_FOUND', | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { checkHyperliquidTransactionStatus } from './checkHyperliquidTransactionStatus.js'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| export type UserDetailsItem = { | ||
| time: number; | ||
| user: string; | ||
| action: { | ||
| type: string; | ||
| signatureChainId: `0x${string}`; | ||
| hyperliquidChain: 'Mainnet'; | ||
| destination: string; | ||
| amount: string; | ||
| time: number; | ||
| }; | ||
| block: number; | ||
| hash: string; | ||
| error: null; | ||
| }; | ||
|
|
||
| export type UserDetailsResponse = { | ||
| type: 'userDetails'; | ||
| txs: UserDetailsItem[]; | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||||||||||||||||||||||||
| import type { UserDetailsResponse } from './types'; | ||||||||||||||||||||||||||||
| import type { Result } from 'ts-results'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { Err, Ok } from 'ts-results'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
| GetHyperliquidTransactionHashError, | ||||||||||||||||||||||||||||
| HYPERLIQUID_EXPLORER_API_URL, | ||||||||||||||||||||||||||||
| } from './constants'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| export async function getHyperliquidTransactionHash( | ||||||||||||||||||||||||||||
| walletAddress: string, | ||||||||||||||||||||||||||||
| nonce: number | ||||||||||||||||||||||||||||
| ): Promise<Result<string, GetHyperliquidTransactionHashError>> { | ||||||||||||||||||||||||||||
| let userDetailsResponse: Response; | ||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||
| userDetailsResponse = await fetch(HYPERLIQUID_EXPLORER_API_URL, { | ||||||||||||||||||||||||||||
| method: 'POST', | ||||||||||||||||||||||||||||
| headers: { | ||||||||||||||||||||||||||||
| 'Content-Type': 'application/json', | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| body: JSON.stringify({ type: 'userDetails', user: walletAddress }), | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
Comment on lines
+17
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add timeout and HTTP status handling to explorer request Line 17 awaits Proposed fix let userDetailsResponse: Response;
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), 10_000);
try {
userDetailsResponse = await fetch(HYPERLIQUID_EXPLORER_API_URL, {
method: 'POST',
+ signal: controller.signal,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ type: 'userDetails', user: walletAddress }),
});
+ if (!userDetailsResponse.ok) {
+ return new Err(GetHyperliquidTransactionHashError.FETCH_ERROR);
+ }
} catch {
return new Err(GetHyperliquidTransactionHashError.FETCH_ERROR);
+ } finally {
+ clearTimeout(timeoutId);
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||
| return new Err(GetHyperliquidTransactionHashError.FETCH_ERROR); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| let userDetailsJson: UserDetailsResponse; | ||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||
| userDetailsJson = await userDetailsResponse.json(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (!Array.isArray(userDetailsJson.txs)) { | ||||||||||||||||||||||||||||
| return new Err(GetHyperliquidTransactionHashError.RESPONSE_PARSING_ERROR); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||
| return new Err(GetHyperliquidTransactionHashError.RESPONSE_PARSING_ERROR); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+28
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid unsafe This issue appears in multiple locations:
let userDetailsJson: UserDetailsResponse;
try {
const data: unknown = await userDetailsResponse.json();
if (
!data ||
typeof data !== 'object' ||
!('txs' in data) ||
!Array.isArray((data as { txs: unknown }).txs)
) {
return new Err(GetHyperliquidTransactionHashError.RESPONSE_PARSING_ERROR);
}
userDetailsJson = data as UserDetailsResponse;
} catch {
return new Err(GetHyperliquidTransactionHashError.RESPONSE_PARSING_ERROR);
}Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| const currentUserDetailsItem = userDetailsJson.txs.find( | ||||||||||||||||||||||||||||
| (item) => | ||||||||||||||||||||||||||||
| (item.action.type === 'withdraw3' || item.action.type === 'usdSend') && | ||||||||||||||||||||||||||||
| item.action.time === nonce | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard malformed explorer items before nested property access Line 41/42 can throw at runtime if any Proposed fix- const currentUserDetailsItem = userDetailsJson.txs.find(
- (item) =>
- (item.action.type === 'withdraw3' || item.action.type === 'usdSend') &&
- item.action.time === nonce
- );
+ const currentUserDetailsItem = userDetailsJson.txs.find((item) => {
+ const action = item?.action;
+ return (
+ (action?.type === 'withdraw3' || action?.type === 'usdSend') &&
+ action?.time === nonce &&
+ typeof item?.hash === 'string'
+ );
+ });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (!currentUserDetailsItem) { | ||||||||||||||||||||||||||||
| return new Err(GetHyperliquidTransactionHashError.TRANSACTION_NOT_FOUND); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return Ok(currentUserDetailsItem.hash); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export const HYPERLIQUID_EXCHANGE_API_URL = | ||
| 'https://api.hyperliquid.xyz/exchange'; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,166 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { SwapQueueContext, SwapStorage } from '../../types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { NextTransactionStateError } from '../common/produceNextStateForTransaction'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { ExecuterActions } from '@rango-dev/queue-manager-core'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { EvmTransaction } from 'rango-sdk'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { type GenericSigner, TransactionType } from 'rango-types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Err } from 'ts-results'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getCurrentStep, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getCurrentStepTx, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleRejectedSign, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '../../helpers'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getCurrentAddressOf, getRelatedWallet } from '../../shared'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { SwapActionTypes } from '../../types'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { checkEnvironmentBeforeExecuteTransaction } from '../common/checkEnvironmentBeforeExecuteTransaction'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onNextStateError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onNextStateOk, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| produceNextStateForTransaction, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from '../common/produceNextStateForTransaction'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { requestBlockQueue } from '../common/utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ensureHyperliquidTransactionIsValid, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getEthersV6CompatibleTypedDataFromMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initiateWithdrawalRequest, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splitSignature, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from './utils'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function executeHyperliquidTransaction( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actions: ExecuterActions<SwapStorage, SwapActionTypes, SwapQueueContext> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): Promise<void> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const checkResult = await checkEnvironmentBeforeExecuteTransaction(actions); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (checkResult.err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestBlockQueue(actions, checkResult.val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { failed, getStorage, context, schedule, next } = actions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { getSigners } = context; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const swap = getStorage().swapDetails; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const currentStep = getCurrentStep(swap)!; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onFinish = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (actions.context.resetClaimedBy) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| actions.context.resetClaimedBy(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const onSuccessfulFinish = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schedule(SwapActionTypes.CHECK_HYPERLIQUID_TRANSACTION_STATUS); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| next(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFinish(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const handleErr = (err: Err<NextTransactionStateError>) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onNextStateError(actions, err.val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| failed(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFinish(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Checking the current transaction state to determine the next step. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * It will either be Err, indicating process should stop, or Ok, indicating process should continu. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const nextStateResult = produceNextStateForTransaction(actions); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (nextStateResult.err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleErr(nextStateResult); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // On sucess, we should update Swap object and also call notifier | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onNextStateOk(actions, nextStateResult.val); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // when we are producing next step, it will check to tx shouldn't be null. So ! here is safe. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const currentTransactionFromStorage = getCurrentStepTx(currentStep)!; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hyperliquidTransactionResult = ensureHyperliquidTransactionIsValid( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| currentTransactionFromStorage | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (hyperliquidTransactionResult.err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleErr(hyperliquidTransactionResult); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const hyperliquidTransaction = hyperliquidTransactionResult.val; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const sourceWallet = getRelatedWallet(swap, currentStep); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const walletAddress = getCurrentAddressOf(swap, currentStep); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let signer: GenericSigner<EvmTransaction>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const walletSigners = await getSigners(sourceWallet.walletType); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signer = walletSigners.getSigner(TransactionType.EVM); // We need EVM signer for Hyperliquid transactions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleRejectedSign(actions)(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFinish(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!signer?.signTypedData) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleErr( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new Err({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nextStatus: 'failed', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nextStepStatus: 'failed', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: 'Unexpected Error: Signer does not support signTypedData.', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| details: undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorCode: 'CLIENT_UNEXPECTED_BEHAVIOUR', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const typedData = getEthersV6CompatibleTypedDataFromMessage( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hyperliquidTransaction.message | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (typedData.err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleErr(typedData); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let signature: `0x${string}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| signature = (await signer.signTypedData( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typedData.val, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| walletAddress, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hyperliquidTransaction.action.signatureChainId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) as `0x${string}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleRejectedSign(actions)(error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onFinish(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const splitSignatureResult = splitSignature(signature); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (splitSignatureResult.err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleErr(splitSignatureResult); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const splittedSignature = splitSignatureResult.val; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const initiateWithdrawalResponse = await initiateWithdrawalRequest( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hyperliquidTransaction.action, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| splittedSignature, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hyperliquidTransaction.nonce | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (initiateWithdrawalResponse.err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleErr(initiateWithdrawalResponse); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (initiateWithdrawalResponse.val.status !== 'ok') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| handleErr( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| new Err({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nextStatus: 'failed', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nextStepStatus: 'failed', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: 'Unexpected Error: Failed to initiate withdrawal.', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| details: undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errorCode: 'CLIENT_UNEXPECTED_BEHAVIOUR', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| onSuccessfulFinish(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+153
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stop flow after failed withdrawal initiation status Line 153-163 marks an error, but execution still reaches Line 165 ( Proposed fix if (initiateWithdrawalResponse.val.status !== 'ok') {
handleErr(
new Err({
nextStatus: 'failed',
nextStepStatus: 'failed',
message: 'Unexpected Error: Failed to initiate withdrawal.',
details: undefined,
errorCode: 'CLIENT_UNEXPECTED_BEHAVIOUR',
})
);
+ return;
}
onSuccessfulFinish();📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
Comment on lines
+153
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Logic error causes inconsistent state on failed withdrawal. When if (initiateWithdrawalResponse.val.status !== 'ok') {
handleErr(
new Err({
nextStatus: 'failed',
nextStepStatus: 'failed',
message: 'Unexpected Error: Failed to initiate withdrawal.',
details: undefined,
errorCode: 'CLIENT_UNEXPECTED_BEHAVIOUR',
})
);
return;
}
onSuccessfulFinish();Prompt for LLMTalk to Kody by mentioning @kody Was this suggestion helpful? React with 👍 or 👎 to help Kody learn from this interaction. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export { executeHyperliquidTransaction } from './executeHyperliquidTransaction.js'; | ||
| export { ensureHyperliquidTransactionIsValid } from './utils.js'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export type EthersV6CompatibleTypedData = { | ||
| domain: unknown; | ||
| types: Record<string, unknown>; | ||
| value: Record<string, unknown>; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid logging full swap payload in warning tags
Line 78 logs
pendingSwap: swap, which can expose user identifiers and transaction metadata in logs.Proposed fix
warn(new Error('check Hyperliquid transaction status Error'), { tags: { type: 'request-error', - requestBody: { type: 'userDetails', user: walletAddress, nonce }, - pendingSwap: swap, + requestType: 'userDetails', + nonce, + walletAddressSuffix: walletAddress.slice(-6), }, });🤖 Prompt for AI Agents