Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions queue-manager/queue-manager-demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"devDependencies": {
"process": "^0.11.10",
"rango-sdk": "^0.1.74"
"rango-sdk": "^0.1.76"
},
"dependencies": {
"@rango-dev/provider-all": "^0.60.1-next.4",
Expand All @@ -27,7 +27,7 @@
"@rango-dev/wallets-shared": "^0.58.1-next.2",
"bignumber.js": "^9.1.1",
"ethers": "^6.13.2",
"rango-sdk-basic": "^0.1.74",
"rango-types": "^0.1.95"
"rango-sdk-basic": "^0.1.76",
"rango-types": "^0.1.97"
}
}
2 changes: 1 addition & 1 deletion queue-manager/rango-preset/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
},
"dependencies": {
"@rango-dev/logging-core": "^0.12.1",
"rango-types": "^0.1.95",
"rango-types": "^0.1.97",
"ts-results": "^3.3.0",
"uuid": "^9.0.0"
},
Expand Down
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,
},
Comment on lines +74 to +79
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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
Verify each finding against the current code and only fix it if needed.

In
`@queue-manager/rango-preset/src/actions/checkHyperliquidTransactionStatus/checkHyperliquidTransactionStatus.ts`
around lines 74 - 79, The warning call in checkHyperliquidTransactionStatus is
logging the full swap object (pendingSwap: swap) which may contain PII; instead,
modify the warn(...) tags to avoid including the entire swap payload—replace
pendingSwap: swap with a minimal identifier such as pendingSwapId: swap.id (or
pendingSwapNonce: swap.nonce) and remove or redact any direct user identifiers
(e.g., walletAddress) in the tags; update the warn(...) invocation so it only
emits non-sensitive fields and keep the same tag keys for traceability.

});
}

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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add timeout and HTTP status handling to explorer request

Line 17 awaits fetch without timeout, and non-2xx responses are treated like parse issues. This can hang polling or blur failure classification.

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
Verify each finding against the current code and only fix it if needed.

In
`@queue-manager/rango-preset/src/actions/checkHyperliquidTransactionStatus/utils.ts`
around lines 17 - 23, The fetch call that assigns userDetailsResponse should use
a timeout via an AbortController (or equivalent) and must check the HTTP status
before parsing: wrap the POST to HYPERLIQUID_EXPLORER_API_URL in a request that
aborts after a configured timeout, call response.ok (or check status) and
throw/return a clear error for non-2xx responses instead of assuming parse
errors, and ensure the calling code (e.g., whichever function in utils.ts that
awaits userDetailsResponse) handles abort/timeouts and non-2xx errors distinctly
so polling won't hang or misclassify failures.

} 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Kody Rules high

Avoid unsafe any assignment by validating the API response structure before casting to UserDetailsResponse.

This issue appears in multiple locations:

  • queue-manager/rango-preset/src/actions/checkHyperliquidTransactionStatus/utils.ts: Lines 28-37
  • queue-manager/rango-preset/src/actions/executeHyperliquidTransaction/utils.ts: Lines 63-70
  • signers/signer-evm/src/signer.ts: Lines 163-164
    Please fix this Kody Rule violation in all listed 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 LLM

File queue-manager/rango-preset/src/actions/checkHyperliquidTransactionStatus/utils.ts:

Line 28 to 37:

I have a piece of TypeScript code that fetches data from an API. It currently assigns the result of `response.json()`, which is of type `any`, directly to a variable that has a specific interface type. This approach is not type-safe and could lead to runtime errors if the API response structure changes.

My project follows a strict rule that disallows the use of the `any` type to maintain type safety. All external data, such as API responses, must be properly validated before being used with a specific type.

Please refactor the code to handle this safely. The solution should first treat the JSON response as `unknown`, then perform runtime checks to validate its structure (e.g., ensure it's an object and contains the expected `txs` array). Only after successful validation should the data be cast to the `UserDetailsResponse` type.

Suggested Code:

  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);
  }

Talk 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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard malformed explorer items before nested property access

Line 41/42 can throw at runtime if any txs entry is missing action. This crashes polling instead of returning a typed error.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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'
);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@queue-manager/rango-preset/src/actions/checkHyperliquidTransactionStatus/utils.ts`
around lines 39 - 43, The find predicate that computes currentUserDetailsItem
assumes every entry in userDetailsJson.txs has an action and directly accesses
item.action.type/time, which can throw; update the predicate in the function
that uses currentUserDetailsItem to first guard that item and item.action are
present (e.g., check item && item.action or use optional chaining) before
comparing action.type and action.time against 'withdraw3'|'usdSend' and nonce,
and if missing return a typed error/result instead of letting polling crash so
callers can handle malformed explorer items.


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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Stop flow after failed withdrawal initiation status

Line 153-163 marks an error, but execution still reaches Line 165 (onSuccessfulFinish()), advancing the queue on failure.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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();
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 AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@queue-manager/rango-preset/src/actions/executeHyperliquidTransaction/executeHyperliquidTransaction.ts`
around lines 153 - 165, The code calls handleErr when
initiateWithdrawalResponse.val.status !== 'ok' but then continues to call
onSuccessfulFinish(), advancing the queue on failure; modify the control flow in
executeHyperliquidTransaction so that after creating and invoking handleErr for
the failed initiation (reference initiateWithdrawalResponse and handleErr) the
function returns or otherwise stops execution (do not call onSuccessfulFinish)
to prevent advancing the queue on error; ensure any necessary cleanup or error
propagation still occurs before the early return.

Comment on lines +153 to +165
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kody code-review Bug high

Logic error causes inconsistent state on failed withdrawal. When initiateWithdrawalRequest returns a non-'ok' status, the code calls handleErr to mark the transaction as failed but then proceeds to call onSuccessfulFinish. This incorrectly advances the queue to the next step for a failed transaction, leading to state corruption. A return statement is missing after handling the error.

  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 LLM

File queue-manager/rango-preset/src/actions/executeHyperliquidTransaction/executeHyperliquidTransaction.ts:

Line 153 to 165:

I have a TypeScript function that handles Hyperliquid transactions within a queue manager. There's a logic error in the error handling. When a withdrawal initiation request succeeds but returns a non-'ok' status, the code correctly calls an error handler (`handleErr`). However, it fails to stop execution and proceeds to call the success handler (`onSuccessfulFinish`). This results in the transaction being marked as failed while also advancing the queue to the next step, causing an inconsistent state. Explain why this is a problem and how to fix it by adding a `return` statement.

Suggested Code:

  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();

Talk 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>;
};
Loading
Loading