Skip to content
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

feat: fail dropped jupiter transactions #553

Merged
merged 7 commits into from
Dec 4, 2024

Conversation

NickKelly1
Copy link
Contributor

@NickKelly1 NickKelly1 commented Nov 13, 2024

If a Solana Jupiter swap transaction fails to be mined within 3 minutes then consider it dropped

Sets up ability to add the same functionality to Solana transactions from Rango and Changelly too.

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced swap functionality with improved transaction handling and user input validation.
    • Added support for tracking transaction statuses, including a new "dropped" status.
    • Introduced methods for managing activity data, including updating, retrieving, and deleting activities.
  • Bug Fixes

    • Improved error handling for transaction status checks across various providers.
  • Documentation

    • Updated comments and JSDoc for better clarity on method functionalities.
  • Style

    • Code formatting improvements for better readability and consistency.
  • Tests

    • Adjusted test cases to align with new method signatures and transaction structures.

Copy link

coderabbitai bot commented Nov 13, 2024

Walkthrough

The changes in this pull request focus on enhancing the transaction handling and status management within the swap functionality of the application. Key updates include the transition from using transactionHashes to transactions, which are now structured as objects containing both a transaction hash and a timestamp. This change affects various components, including Vue files and provider classes, improving how transactions are processed, validated, and displayed. Additional methods have been introduced for managing activity states, and the user interface has been updated to reflect new transaction statuses, including "dropped."

Changes

File Change Summary
packages/extension/src/ui/action/views/swap/index.vue Updated sendAction method to change transaction handling from transactionHashes to transactions. Modified input handling with debounce for address resolution and improved validation logic.
packages/extension/src/ui/action/views/swap/libs/send-transactions.ts Changed return type of executeSwap from Promise<string[]> to Promise<{ hash: string, sentAt: number }[]>. Updated transaction handling to include timestamps.
packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue Adjusted sendAction method to reflect changes in transaction structure, replacing hashes with txs.
packages/swap/src/providers/changelly/index.ts Added getStatus method to retrieve swap transaction status and a utility function fixBaseAndTrimDecimals for handling decimal precision.
packages/swap/src/providers/jupiter/index.ts Updated getStatus method to accept transactions instead of transactionHashes, enhancing error handling for transaction statuses.
packages/swap/src/providers/oneInch/index.ts Modified getStatus method to use transactions instead of transactionHashes, improving error handling.
packages/swap/src/providers/paraswap/index.ts Updated getStatus method to reflect new transactions structure.
packages/swap/src/providers/rango/index.ts Changed getStatus method to use transactions, enhancing logic for determining transaction status.
packages/swap/src/providers/zerox/index.ts Modified getStatus method to handle transactions instead of transactionHashes.
packages/swap/src/types/index.ts Introduced new type StatusOptionTransaction and updated StatusOptions interface to use transactions.
packages/swap/tests/changelly.test.ts Updated tests to reflect changes in input structure for getStatusObject.
packages/extension/src/libs/activity-state/index.ts Added new methods for managing activity data, including updateActivity, setCacheTime, and others.
packages/extension/src/providers/solana/libs/api.ts Refactored getTransactionStatus method to use async/await for improved readability.
packages/extension/src/types/activity.ts Added new enum value dropped to ActivityStatus and reformatted rawInfo property for clarity.
packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue Enhanced transaction status handling to include ActivityStatus.dropped.
packages/extension/src/ui/action/views/network-activity/index.vue Improved activity checking logic to handle "dropped" statuses and streamlined provider checks.

Possibly related PRs

  • Fix: Rango supported network check #549: The changes in the Rango class's getStatus method to handle transaction data structures align with the main PR's updates to transaction handling in the swap functionality, particularly regarding the transition from transactionHashes to transactions.

Warning

Rate limit exceeded

@NickKelly1 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 20 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between c0bbe9a and 216798b.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Nov 13, 2024

💼 Build Files
chrome: enkrypt-chrome-216798ba.zip
firefox: enkrypt-firefox-216798ba.zip

💉 Virus total analysis
chrome: 216798ba
firefox: 216798ba

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (17)
packages/swap/tests/zerox.test.ts (1)

28-31: LGTM! Consider documenting the CI skip reason.

The placeholder test for CI environments is correctly implemented. However, it would be helpful to add a comment explaining why Zerox tests are skipped in CI (e.g., if it requires specific network conditions, API keys, or live endpoints).

 if (process.env.CI) {
+    // Skip Zerox tests in CI as they require live network access
     // We need at-least one test otherwise vitest reports failure
     it("No ZeroX swap tests in CI", function () {
       expect(true).toBeTruthy();
     });
     return;
   }
packages/swap/src/types/index.ts (1)

224-229: LGTM! Consider adding validation utilities.

The new StatusOptionTransaction type is well-structured and documented. It provides the necessary fields to track transaction timing, which aligns with the PR objective of handling transaction timeouts.

Consider adding a utility type or const for the timeout duration:

/** 3 minutes in milliseconds */
export const TRANSACTION_TIMEOUT_MS = 3 * 60 * 1000;

export type TransactionTimeoutStatus = {
  isTimedOut: boolean;
  remainingMs: number;
};

export const getTransactionTimeoutStatus = (
  transaction: StatusOptionTransaction
): TransactionTimeoutStatus => ({
  isTimedOut: Date.now() - transaction.sentAt > TRANSACTION_TIMEOUT_MS,
  remainingMs: Math.max(0, TRANSACTION_TIMEOUT_MS - (Date.now() - transaction.sentAt))
});
packages/swap/src/providers/zerox/index.ts (1)

Add timeout handling for consistency with Jupiter provider

The review comment is valid. The Jupiter provider already implements the 3-minute timeout handling, while ZeroX and other EVM-based providers (OneInch, Paraswap) don't handle timeouts yet. Here's how to implement it consistently:

getStatus(options: StatusOptions): Promise<TransactionStatus> {
  const promises = options.transactions.map(({ hash, sentAt }) => {
    // Consider dropped if it's still null after 3 minutes
    if (Date.now() > sentAt + 3 * 60_000) {
      return Promise.resolve(null);
    }
    return this.web3eth.getTransactionReceipt(hash);
  });
  return Promise.all(promises).then((receipts) => {
    for (const receipt of receipts) {
      if (!receipt || (receipt && !receipt.blockNumber)) {
        return TransactionStatus.pending;
      }
      if (receipt && !receipt.status) return TransactionStatus.failed;
    }
    return TransactionStatus.success;
  });
}
  • The same timeout handling should also be added to packages/swap/src/providers/oneInch/index.ts and packages/swap/src/providers/paraswap/index.ts
🔗 Analysis chain

Line range hint 275-286: Add timeout handling for consistency with other providers.

While the parameter structure has been updated to support transaction timestamps, the getStatus method doesn't implement the 3-minute timeout requirement mentioned in the PR objectives. This could lead to inconsistent behavior across different providers.

Consider implementing timeout handling similar to other providers. Here's a suggested implementation:

  getStatus(options: StatusOptions): Promise<TransactionStatus> {
-   const promises = options.transactions.map(({ hash }) =>
+   const promises = options.transactions.map(({ hash, timestamp }) => {
+     // Check for timeout first
+     const now = Date.now();
+     if (now - timestamp > 180000) { // 3 minutes in milliseconds
+       return Promise.resolve(null); // Simulate missing receipt for timeout
+     }
+     // If not timed out, check receipt
      this.web3eth.getTransactionReceipt(hash)
-   );
+   });
    return Promise.all(promises).then((receipts) => {
      // eslint-disable-next-line no-restricted-syntax
      for (const receipt of receipts) {
        if (!receipt || (receipt && !receipt.blockNumber)) {
          return TransactionStatus.pending;
        }
        if (receipt && !receipt.status) return TransactionStatus.failed;
      }
      return TransactionStatus.success;
    });
  }

Let's verify if other providers implement similar timeout handling:


Let me analyze the search results further to understand the timeout implementation across providers.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for timeout handling in other swap providers
rg -A 10 "getStatus.*StatusOptions.*Promise.*TransactionStatus" packages/swap/src/providers/

Length of output: 5952


Script:

#!/bin/bash
# Search for timeout or timestamp handling in Jupiter provider
rg -A 15 "sentAt.*hash.*options\.transactions" packages/swap/src/providers/jupiter/

Length of output: 1410

packages/swap/src/providers/oneInch/index.ts (2)

Line range hint 298-309: Add timeout handling to align with PR objectives.

The current implementation doesn't implement the 3-minute timeout requirement mentioned in the PR objectives. Transactions should be marked as failed if they haven't been mined within 3 minutes.

Here's a suggested implementation:

  getStatus(options: StatusOptions): Promise<TransactionStatus> {
+   const THREE_MINUTES = 3 * 60 * 1000; // 3 minutes in milliseconds
    const promises = options.transactions.map(({ hash }) =>
-     this.web3eth.getTransactionReceipt(hash)
+     Promise.all([
+       this.web3eth.getTransactionReceipt(hash),
+       this.web3eth.getTransaction(hash)
+     ]).then(([receipt, tx]) => {
+       if (!receipt || (receipt && !receipt.blockNumber)) {
+         // Check if transaction is pending for more than 3 minutes
+         if (tx && Date.now() - Number(options.transactions.find(t => t.hash === hash)?.timestamp || 0) > THREE_MINUTES) {
+           return { status: false };
+         }
+         return null;
+       }
+       return receipt;
+     }).catch(error => {
+       console.error(`Failed to fetch transaction status for ${hash}:`, error);
+       return null;
+     })
    );
    return Promise.all(promises).then((receipts) => {
      // eslint-disable-next-line no-restricted-syntax
      for (const receipt of receipts) {
        if (!receipt || (receipt && !receipt.blockNumber)) {
          return TransactionStatus.pending;
        }
        if (receipt && !receipt.status) return TransactionStatus.failed;
      }
      return TransactionStatus.success;
    });
  }

Line range hint 298-309: Add type safety for the transactions array.

The code assumes that the transactions array contains objects with a hash property, but this isn't enforced through TypeScript types.

Consider adding a type definition:

interface Transaction {
  hash: string;
  timestamp: number;
}

interface StatusOptions {
  transactions: Transaction[];
}
packages/swap/src/providers/paraswap/index.ts (1)

Line range hint 353-364: Add timeout handling for pending transactions

The current implementation doesn't implement the 3-minute timeout mentioned in the PR objectives. While this is a ParaSwap provider, we should maintain consistent behavior across all providers.

Consider implementing the timeout like this:

  getStatus(options: StatusOptions): Promise<TransactionStatus> {
+   const THREE_MINUTES = 3 * 60 * 1000;
+   const now = Date.now();
+   
+   // Check for timeout first
+   for (const tx of options.transactions) {
+     if (now - tx.timestamp > THREE_MINUTES) {
+       return Promise.resolve(TransactionStatus.failed);
+     }
+   }
+
    const promises = options.transactions.map(({ hash }) =>
-     this.web3eth.getTransactionReceipt(hash)
+     this.web3eth.getTransactionReceipt(hash).catch(error => {
+       console.error(`Failed to fetch receipt for ${hash}:`, error);
+       return null;
+     })
    );
packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue (1)

Based on the search results, I can now provide a final response as there's enough context about the error handling in the codebase.

Consider using the existing error type system for better error handling

The codebase already has a structured error handling system that we should leverage:

  1. There's a SwapError enum in packages/extension/src/ui/action/views/swap/components/swap-error/types.ts for swap-specific errors
  2. For provider errors, there's an ErrorCodes enum that includes specific error cases like userRejected (4001), unauthorized (4100), etc.
 .catch(err => {
   console.error(err);
   isTXSendError.value = true;
-  TXSendErrorMessage.value = err.error ? err.error.message : err.message;
+  // Map provider error codes to user-friendly messages
+  let errorType = 'UNKNOWN_ERROR';
+  if (err.code && Object.values(ErrorCodes).includes(err.code)) {
+    errorType = err.code === ErrorCodes.userRejected ? 'USER_REJECTED' : 'PROVIDER_ERROR';
+  }
+  TXSendErrorMessage.value = errorType === 'USER_REJECTED' 
+    ? 'Transaction was rejected' 
+    : 'Failed to complete the swap';
   trackSwapEvents(SwapEventType.swapFailed, {
     network: network.value!.name,
     fromToken: swapData.fromToken.name,
     toToken: swapData.toToken.name,
     swapProvider: pickedTrade.value.provider,
-    error: TXSendErrorMessage.value,
+    error: errorType,
+    errorCode: err.code,
   });
 });
🔗 Analysis chain

Line range hint 437-451: Enhance error handling and user feedback

The current error handling implementation could be improved:

  1. Error messages might expose sensitive information directly to the UI.
  2. Generic error handling doesn't distinguish between different types of failures (network, timeout, user rejection, etc.).

Consider applying these changes:

 .catch(err => {
   console.error(err);
   isTXSendError.value = true;
-  TXSendErrorMessage.value = err.error ? err.error.message : err.message;
+  // Sanitize and categorize errors
+  const errorType = err.code ? mapErrorCodeToType(err) : 'UNKNOWN_ERROR';
+  TXSendErrorMessage.value = getUserFriendlyMessage(errorType);
   trackSwapEvents(SwapEventType.swapFailed, {
     network: network.value!.name,
     fromToken: swapData.fromToken.name,
     toToken: swapData.toToken.name,
     swapProvider: pickedTrade.value.provider,
-    error: TXSendErrorMessage.value,
+    error: errorType,
+    errorDetails: err.message, // Log full error details for debugging
   });
 });

Let's check for existing error handling patterns:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for error handling patterns in swap-related code
# Expected: Find error mapping implementations and user-friendly messages

# Search for error handling in swap-related files
rg -A 5 "catch\s*\(.*\)\s*{" "packages/extension/src/ui/action/views/swap"

# Look for error type definitions
ast-grep --pattern 'enum $_ {
  $$$
}'

Length of output: 165


Script:

#!/bin/bash
# Let's try a broader search for error handling patterns

# Search for error-related code in the swap functionality
rg -l "error|Error" "packages/extension/src/ui/action/views/swap"

# Look for any error type definitions or mappings
rg -A 5 "type.*Error|enum.*Error|interface.*Error" packages/extension

# Check for existing error message handling or mapping functions
rg -A 5 "message.*error|error.*message" "packages/extension/src/ui/action/views/swap"

# Search for any error tracking or analytics
rg -A 5 "trackSwapEvents.*error|SwapEventType" packages/extension

Length of output: 15988

packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (4)

Line range hint 342-352: Consider extracting retry configuration.

The backoff retry logic uses magic numbers. Consider extracting these into a configuration object for better maintainability.

-              const backoff = [0, 500, 1_000, 2_000];
+              const BLOCKHASH_UPDATE_CONFIG = {
+                retryDelays: [0, 500, 1_000, 2_000],
+                maxAttempts: 4,
+              };
+              const backoff = BLOCKHASH_UPDATE_CONFIG.retryDelays;

241-249: Improve logging consistency.

Consider using a structured logging approach instead of concatenating messages with template literals. This will make it easier to parse and analyze logs.

-                    console.warn(
-                      `Failed to get latest blockhash after ${backoffi} attempts,` +
-                      ` continuing with old block hash for legacy transaction...`,
-                    );
+                    console.warn('Failed to update blockhash', {
+                      attempts: backoffi,
+                      transactionType: 'legacy',
+                      action: 'continuing with old blockhash'
+                    });

Also applies to: 352-360

🧰 Tools
🪛 Biome

[error] 243-243: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)


Line range hint 236-243: Remove unnecessary labels.

The update_block_hash labels are unnecessary and can be removed as flagged by static analysis.

-              update_block_hash: while (true) {
+              while (true) {
                 if (backoffi >= backoff.length) {
-                  break update_block_hash;
+                  break;
                 }
                 // ... rest of the code
                 try {
                   const latest = await conn.getLatestBlockhash();
                   legacyTx.recentBlockhash = latest.blockhash;
-                  break update_block_hash;
+                  break;
                 }

Also applies to: 347-354

🧰 Tools
🪛 Biome

[error] 243-243: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)


78-78: Implementation of transaction timeout logic needed.

While the changes add transaction timestamps, the actual logic to fail transactions after 3 minutes is not implemented. Consider adding a transaction monitor that:

  1. Tracks transaction status using the sentAt timestamp
  2. Marks transactions as failed if not mined within 3 minutes
  3. Handles cleanup of failed transactions

Would you like me to help design and implement the transaction timeout monitoring system?

packages/swap/src/providers/jupiter/index.ts (1)

590-590: Consider extracting the magic number to a named constant

The timeout value 3 * 60_000 would be more maintainable as a named constant at the top of the file.

+ /** Time after which a pending transaction is considered failed (3 minutes) */
+ const TRANSACTION_TIMEOUT_MS = 3 * 60_000;

- if (Date.now() > sentAt + 3 * 60_000) {
+ if (Date.now() > sentAt + TRANSACTION_TIMEOUT_MS) {
packages/swap/src/providers/changelly/index.ts (3)

1074-1076: TODO comment aligns with PR objectives but needs implementation.

The TODO comment correctly identifies the need to handle dropped Solana transactions after 3 minutes, which aligns with the PR objectives. However, the implementation is missing.

Would you like me to help implement the 3-minute timeout functionality? Here's a suggested approach:

async getStatus(options: StatusOptions): Promise<TransactionStatus> {
-    // TODO: If a Solana transaction hasn't been found after 3 minutes then consider dropping it
-    // I'm not sure how Rango's API handles Solana transactions being dropped...
+    // Check if this is a Solana transaction
+    if (options.network === NetworkType.Solana && options.transactions?.[0]?.timestamp) {
+      const currentTime = Date.now();
+      const transactionTime = options.transactions[0].timestamp;
+      const threeMinutesInMs = 3 * 60 * 1000;
+      
+      // If transaction is older than 3 minutes, consider it failed
+      if (currentTime - transactionTime > threeMinutesInMs) {
+        return TransactionStatus.failed;
+      }
+    }

    const params: ChangellyApiGetStatusParams = {

Line range hint 142-207: Enhance error handling and type safety in changellyRequest method.

The changellyRequest method could benefit from improved error handling and type safety:

  1. Network errors should be handled explicitly
  2. Response type validation should be added
  3. Timeout handling could be more robust

Consider applying these improvements:

  private async changellyRequest<T>(
    method: string,
    params: any,
    context?: { signal?: AbortSignal }
  ): Promise<ChangellyApiResponse<T>> {
    const signal = context?.signal;
    const aborter = new AbortController();
+    const timeoutMs = 30_000;
+    
+    // Define error types
+    class ChangellyTimeoutError extends Error {
+      constructor() {
+        super(`Changelly API request timed out after ${timeoutMs}ms: ${method}`);
+      }
+    }
+    
    function onAbort() {
      aborter.abort(signal!.reason);
    }
    function onTimeout() {
-      aborter.abort(
-        new Error(`Changelly API request timed out ${BASE_URL} ${method}`)
-      );
+      aborter.abort(new ChangellyTimeoutError());
    }
    function cleanup() {
      clearTimeout(timeout);
      signal?.removeEventListener("abort", onAbort);
    }
-    const timeout = setTimeout(onTimeout, 30_000);
+    const timeout = setTimeout(onTimeout, timeoutMs);
    signal?.addEventListener("abort", onAbort);
    try {
      const response = await fetch(BASE_URL, {
        method: "POST",
        signal: aborter.signal,
        body: JSON.stringify({
          id: uuidv4(),
          jsonrpc: "2.0",
          method,
          params,
        }),
        headers: [
          ["Content-Type", "application/json"],
          ["Accept", "application/json"],
        ],
      });
+      
+      // Handle non-200 responses
+      if (!response.ok) {
+        throw new Error(`Changelly API returned ${response.status}: ${response.statusText}`);
+      }
+      
      const json = (await response.json()) as ChangellyApiResponse<T>;
+      
+      // Validate response structure
+      if (!json || (typeof json !== 'object')) {
+        throw new Error('Invalid response format from Changelly API');
+      }
+      
      return json;
    } catch (error) {
+      if (error instanceof ChangellyTimeoutError) {
+        throw error;
+      }
+      throw new Error(`Changelly API request failed: ${error.message}`);
    } finally {
      cleanup();
    }
  }

1074-1076: Consider security implications of transaction timeout handling.

While implementing the 3-minute timeout for Solana transactions, consider these security aspects:

  1. Ensure transaction status is properly persisted to prevent loss of transaction history
  2. Consider edge cases where network delays might cause false timeouts
  3. Implement proper logging for security audit trails

Consider implementing these security measures:

  1. Add detailed logging of transaction status changes
  2. Implement a recovery mechanism for edge cases
  3. Consider making the timeout duration configurable
  4. Add monitoring for timed-out transactions
packages/swap/src/providers/rango/index.ts (2)

Line range hint 1283-1293: Add error handling for Solana transaction status checks

The current implementation should handle potential network errors when checking Solana transaction statuses.

Apply this improvement:

- const sigStatuses = await (
-   this.web3 as Connection
- ).getSignatureStatuses(transactions.map(({ hash }) => hash));
+ try {
+   const sigStatuses = await (
+     this.web3 as Connection
+   ).getSignatureStatuses(transactions.map(({ hash }) => hash));
+   // ... rest of the status checking logic
+ } catch (error) {
+   console.error('Failed to check Solana transaction status:', error);
+   return TransactionStatus.pending;
+ }

1300-1302: Optimize EVM transaction status checks

The current implementation could be more resilient to individual transaction failures.

Consider using Promise.allSettled to handle individual transaction failures:

- const receipts = await Promise.all(
-   transactions.map(({ hash }) =>
-     (this.web3 as Web3Eth).getTransactionReceipt(hash)
-   )
- );

+ const receiptResults = await Promise.allSettled(
+   transactions.map(({ hash }) =>
+     (this.web3 as Web3Eth).getTransactionReceipt(hash)
+   )
+ );
+ const receipts = receiptResults.map(result => 
+   result.status === 'fulfilled' ? result.value : null
+ );
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 76fb11b and 87d808c.

📒 Files selected for processing (12)
  • packages/extension/src/ui/action/views/swap/index.vue (1 hunks)
  • packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (16 hunks)
  • packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue (2 hunks)
  • packages/swap/src/providers/changelly/index.ts (1 hunks)
  • packages/swap/src/providers/jupiter/index.ts (1 hunks)
  • packages/swap/src/providers/oneInch/index.ts (1 hunks)
  • packages/swap/src/providers/paraswap/index.ts (1 hunks)
  • packages/swap/src/providers/rango/index.ts (4 hunks)
  • packages/swap/src/providers/zerox/index.ts (1 hunks)
  • packages/swap/src/types/index.ts (1 hunks)
  • packages/swap/tests/changelly.test.ts (1 hunks)
  • packages/swap/tests/zerox.test.ts (1 hunks)
🧰 Additional context used
🪛 Biome
packages/extension/src/ui/action/views/swap/libs/send-transactions.ts

[error] 243-243: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)


[error] 354-354: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)

🔇 Additional comments (11)
packages/swap/tests/changelly.test.ts (1)

55-55: LGTM! Parameter structure updated correctly.

The change from transactionHashes to transactions aligns with the updated method signature.

packages/swap/src/types/index.ts (1)

233-233: Verify the interface change impact.

The change from transactionHashes to transactions is well-aligned with the new timing functionality. However, this is a breaking change that requires verification.

Let's verify the usage of this interface across the codebase:

✅ Verification successful

Let me try a different search strategy to verify the interface change impact.


Based on the search results, I can now provide a definitive response about the interface change impact:

Interface change is properly implemented across the codebase

The search results confirm that:

  • All providers (Jupiter, Paraswap, 0x, OneInch, Changelly, Rango) have been updated to use the new transactions array property
  • The implementations correctly handle the new array structure:
    • Jupiter checks for single transaction: options.transactions.length !== 1
    • Paraswap/0x/OneInch map over transactions: options.transactions.map(({ hash }) =>
    • Rango/Changelly destructure and use transactions array: const { requestId, transactions } = options
  • No remaining references to the old transactionHashes property exist in the codebase

The breaking change has been properly propagated throughout all dependent code.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining uses of the old transactionHashes property
# and verify that all implementations properly handle the new transactions array.

echo "Checking for old transactionHashes usage..."
rg "transactionHashes" --type ts

echo "Verifying implementations of StatusOptions interface..."
ast-grep --pattern 'interface $_ implements StatusOptions {
  $$$
}'

echo "Checking getStatus method implementations..."
ast-grep --pattern 'getStatus(options: StatusOptions) {
  $$$
}'

Length of output: 487


Script:

#!/bin/bash
# Search for any usage of StatusOptions type/interface
echo "Checking StatusOptions usage..."
rg "StatusOptions" -A 3 --type ts

# Search for any property access with 'transactions' or 'transactionHashes'
echo -e "\nChecking property access patterns..."
ast-grep --pattern 'options.$_'

# Search for type definitions containing 'transactions' array
echo -e "\nChecking transactions array type definitions..."
ast-grep --pattern 'transactions: $_[]'

Length of output: 62398

packages/swap/src/providers/oneInch/index.ts (1)

Line range hint 298-309: Verify transaction receipt fetching across providers.

Let's verify if other swap providers (Jupiter, ParaSwap, Rango, ZeroX) have consistent transaction status checking implementations.

✅ Verification successful

Let me gather more specific information about the transaction status checking implementations in these files.


Transaction status checking is consistent across providers, no changes needed

The verification shows that OneInch, ParaSwap, and ZeroX providers have identical transaction receipt checking implementations:

  • All check for missing receipts or missing block numbers to determine pending status
  • All verify receipt.status to determine failed transactions
  • All use the same pattern: !receipt || (receipt && !receipt.blockNumber) for pending and receipt && !receipt.status for failed

Rango has a slightly different implementation due to its unique API requirements, but follows the same validation principles.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check transaction status implementations across providers
# Expected: Find status checking implementations that might need similar updates

echo "Searching for getStatus method implementations across providers..."
ast-grep --pattern 'getStatus(options: $_) {
  $$$
}'

echo "\nSearching for transaction receipt checking patterns..."
rg -l 'getTransactionReceipt|blockNumber|receipt.status' packages/swap/src/providers/

Length of output: 632


Script:

#!/bin/bash
# Get detailed implementation of getStatus and transaction receipt checking in each provider

echo "OneInch implementation:"
rg -A 10 "getStatus" packages/swap/src/providers/oneInch/index.ts

echo "\nParaswap implementation:"
rg -A 10 "getStatus" packages/swap/src/providers/paraswap/index.ts

echo "\nRango implementation:"
rg -A 10 "getStatus" packages/swap/src/providers/rango/index.ts

echo "\nZeroX implementation:"
rg -A 10 "getStatus" packages/swap/src/providers/zerox/index.ts

# Also check for any transaction receipt validation patterns
echo "\nTransaction receipt validation patterns:"
rg "receipt.status|!receipt|receipt &&" packages/swap/src/providers/

Length of output: 4445

packages/swap/src/providers/paraswap/index.ts (1)

Line range hint 353-364: Verify parameter type changes across providers

The change from transactionHashes to transactions objects is a breaking change that needs to be consistently implemented.

Let's verify the parameter structure across other providers:

packages/extension/src/ui/action/views/swap/views/swap-best-offer/index.vue (1)

Line range hint 398-424: Critical: Implement timeout handling and add array bounds check

The current implementation has several issues that need to be addressed:

  1. Missing timeout handling: According to PR objectives, transactions should fail after 3 minutes.
  2. No bounds checking: Accessing txs[0] without verifying array length could cause runtime errors.
  3. Transaction timing: The status tracking doesn't include transaction timestamp for timeout calculations.

Consider applying these changes:

 .then(txs => {
+  if (!txs.length) {
+    throw new Error('No transaction hash received');
+  }
+
   pickedTrade.value.status!.options.transactions = txs;
   const swapRaw: SwapRawInfo = {
     fromToken: swapData.fromToken,
     toToken: swapData.toToken,
     status: pickedTrade.value.status!,
   };
   const swapActivity: Activity = {
     from: network.value!.displayAddress(account.value!.address),
     to: swapData.toAddress,
     token: {
       decimals: swapData.toToken.decimals,
       icon: swapData.toToken.logoURI,
       name: swapData.toToken.name,
       symbol: swapData.toToken.symbol,
       coingeckoID: swapData.toToken.cgId,
       price: swapData.toToken.price
         ? swapData.toToken.price.toString()
         : '0',
     },
     isIncoming: account.value!.address === swapData.toAddress,
     network: network.value!.name,
-    status: ActivityStatus.pending,
+    status: ActivityStatus.pending,
+    createdAt: Date.now(),
+    timeoutAt: Date.now() + 180000, // 3 minutes in milliseconds
     timestamp: new Date().getTime(),
     type: ActivityType.swap,
     value: pickedTrade.value.toTokenAmount.toString(),
     transactionHash: `${txs[0].hash}-swap`,
     rawInfo: JSON.parse(JSON.stringify(swapRaw)),
   };

Let's verify if the activity state implementation supports timeout tracking:

#!/bin/bash
# Description: Check if ActivityState handles transaction timeouts
# Expected: Find implementation details of transaction timeout handling

# Search for timeout-related code in ActivityState
rg -A 5 "timeoutAt|timeout" "packages/extension/src/libs/activity-state"

# Search for status update mechanisms
ast-grep --pattern 'class ActivityState {
  $$$
  updateStatus($$$) {
    $$$
  }
  $$$
}'
packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (4)

119-119: LGTM! Bitcoin transaction handling updated correctly.

The Bitcoin transaction handling has been properly updated to include the sentAt timestamp.


176-176: LGTM! Substrate transaction handling updated correctly.

The Substrate transaction handling has been properly updated to include the sentAt timestamp.


Line range hint 507-569: LGTM! EVM transaction handling updated correctly.

The EVM transaction handling has been properly updated to include the sentAt timestamp while maintaining the existing transaction broadcasting logic.


78-78: LGTM! Return type change enables transaction timing.

The new return type including sentAt timestamp aligns with the PR objective to fail transactions after 3 minutes.

Let's verify that all consumers handle the new return type correctly:

✅ Verification successful

Return type change is correctly handled throughout the codebase

The new return type { hash: string, sentAt: number }[] aligns perfectly with the StatusOptionTransaction type definition in packages/swap/src/types/index.ts, which already expects both the hash and sentAt fields. The consumer in swap-best-offer/index.vue assigns the transactions directly to pickedTrade.value.status!.options.transactions, which expects this exact type.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for places where executeSwap result is used
rg -A 3 "executeSwap\(" 

Length of output: 457


Script:

#!/bin/bash
# Get more context around executeSwap usage
rg -B 5 -A 10 "executeSwap\(" 

# Also search for any handling of the returned hash
rg "const.*=.*executeSwap" -A 3

Length of output: 1793


Script:

#!/bin/bash
# Let's check the actual usage of the transaction result with more context
rg -B 10 -A 10 "pickedTrade\.value\.status.*options\.transactions = txs" 

# Also check the type definition of the status options
ast-grep --pattern 'interface $_ {
  $$$
  transactions: $_;
  $$$
}'

Length of output: 4834


Script:

#!/bin/bash
# Let's check the StatusOptionTransaction type definition
ast-grep --pattern 'type StatusOptionTransaction = $_'

# And check for any existing usage of sentAt in the status handling
rg "sentAt.*status" -A 3

Length of output: 454

packages/swap/src/providers/jupiter/index.ts (2)

577-582: LGTM: Parameter validation and transaction extraction

The validation logic correctly ensures exactly one transaction is provided and properly extracts the transaction hash and timestamp using the new structure.


588-594: LGTM: Transaction timeout handling implementation

The implementation correctly fulfills the PR objective by failing transactions after 3 minutes, with good documentation explaining the timeout choice (2 minutes block hash expiry + 1 minute leeway).

packages/swap/tests/changelly.test.ts Show resolved Hide resolved
packages/swap/src/providers/rango/index.ts Show resolved Hide resolved
packages/swap/src/providers/rango/index.ts Show resolved Hide resolved
@NickKelly1 NickKelly1 changed the title [wip] feat: fail dropped jupiter transactions feat: fail dropped jupiter transactions Nov 14, 2024
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Outside diff range and nitpick comments (20)
packages/extension/src/types/activity.ts (1)

125-131: Consider adding JSDoc comments to document the union type.

The formatting change improves readability, and the inclusion of SOLRawInfo aligns with the Solana transaction handling requirements.

Consider adding JSDoc comments to document when each type is used:

+/**
+ * Raw information specific to different blockchain transactions:
+ * - EthereumRawInfo: For Ethereum transactions
+ * - SubstrateRawInfo: For Substrate-based chain transactions
+ * - SubscanExtrinsicInfo: For Subscan API responses
+ * - BTCRawInfo: For Bitcoin transactions
+ * - SwapRawInfo: For cross-chain swap transactions
+ * - KadenaRawInfo: For Kadena transactions
+ * - SOLRawInfo: For Solana transactions, includes timestamp for timeout tracking
+ */
rawInfo:
  | EthereumRawInfo
  | SubstrateRawInfo
  | SubscanExtrinsicInfo
  | BTCRawInfo
  | SwapRawInfo
  | KadenaRawInfo
  | SOLRawInfo;
packages/extension/src/providers/solana/libs/api.ts (3)

26-26: Consider adding timeout configuration initialization.

Given that this PR implements transaction timeout handling, consider initializing timeout-related configurations (e.g., 3-minute threshold) in this method.


33-36: Document version limitation.

The maxSupportedTransactionVersion: 0 parameter might need documentation explaining why version 0 is used and any implications for newer transaction versions.


44-49: Simplify boolean conversion.

The status conversion can be simplified.

-    status: tx.meta?.err ? false : true,
+    status: !tx.meta?.err,
🧰 Tools
🪛 Biome

[error] 48-48: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

packages/extension/src/libs/activity-state/index.ts (4)

Line range hint 64-79: Consider improving error handling and validation

The updateActivity method could benefit from several improvements:

  1. Input validation for activity and options parameters
  2. Error handling for storage operations
  3. More functional approach to array manipulation

Consider this refactor:

  async updateActivity(
    activity: Activity,
    options: ActivityOptions,
  ): Promise<void> {
+   if (!activity?.transactionHash) {
+     throw new Error('Invalid activity: missing transaction hash');
+   }
    const activities = await this.getActivitiesById(
      this.getActivityId(options),
    );
-   const clone = [...activities];
-   activities.forEach((act, idx) => {
-     if (act.transactionHash === activity.transactionHash)
-       clone[idx] = activity;
-   });
+   const clone = activities.map(act => 
+     act.transactionHash === activity.transactionHash ? activity : act
+   );
    await this.setActivitiesById(clone, this.getActivityId(options));
  }

80-85: Add error handling for storage operations

The storage operation could fail and should be handled appropriately.

Consider adding error handling:

  async setCacheTime(options: ActivityOptions): Promise<void> {
+   if (!options?.address || !options?.network) {
+     throw new Error('Invalid options: missing required fields');
+   }
+   try {
      await this.#storage.set(this.getActivityCacheId(options), {
        [STORAGE_KEY]: new Date().getTime(),
      });
+   } catch (error) {
+     throw new Error(`Failed to set cache time: ${error.message}`);
+   }
  }

94-97: Consider adding filtering and pagination

The method could benefit from:

  1. Filtering out expired transactions
  2. Pagination for large datasets
  3. Error handling

Consider this enhancement:

  async getAllActivities(options: ActivityOptions): Promise<Activity[]> {
+   if (!options?.address || !options?.network) {
+     throw new Error('Invalid options: missing required fields');
+   }
+   try {
-     return this.getActivitiesById(this.getActivityId(options));
+     const activities = await this.getActivitiesById(this.getActivityId(options));
+     const currentTime = new Date().getTime();
+     return activities.filter(a => 
+       a.status !== ActivityStatus.pending || 
+       a.timestamp > currentTime - MAX_PENDING_TIME
+     );
+   } catch (error) {
+     throw new Error(`Failed to get activities: ${error.message}`);
+   }
  }

Line range hint 102-116: Enhance security and validation in storage operations

The private methods need better input validation and error handling to prevent potential security issues.

Consider these enhancements:

  private async setActivitiesById(
    activities: Activity[],
    id: string,
  ): Promise<void> {
+   if (!id?.trim()) {
+     throw new Error('Invalid storage ID');
+   }
+   if (!Array.isArray(activities)) {
+     throw new Error('Invalid activities array');
+   }
+   try {
    await this.#storage.set(id, {
      [STORAGE_KEY]: activities,
    });
+   } catch (error) {
+     throw new Error(`Storage operation failed: ${error.message}`);
+   }
  }

  private async getActivitiesById(id: string): Promise<Activity[]> {
+   if (!id?.trim()) {
+     throw new Error('Invalid storage ID');
+   }
+   try {
    const allStates: Record<string, Activity[]> = await this.#storage.get(id);
    if (!allStates || !allStates[STORAGE_KEY]) return [];
    return allStates[STORAGE_KEY];
+   } catch (error) {
+     throw new Error(`Storage operation failed: ${error.message}`);
+   }
  }
packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue (3)

40-43: Consider visually distinguishing dropped vs failed states.

The class bindings for both transaction and swap sections correctly handle the new dropped status. However, there's currently no visual distinction between failed and dropped states, which might confuse users about why their transaction didn't complete.

Consider adding distinct visual styles for dropped transactions to help users understand the difference between a failed transaction and one that timed out.

Also applies to: 94-97


267-269: Address the TODO comment with specific color suggestions.

The dropped status should have a distinct color to differentiate it from failed transactions. Consider using:

  • Orange (#FFA500) to indicate timeout/dropped status
  • Red (#FF0000) for failed transactions

Would you like me to provide a complete CSS implementation for these color states?


Line range hint 40-269: Overall implementation feedback.

The changes successfully implement support for dropped transactions, aligning with the PR objectives. The code structure is clean and follows Vue.js best practices. However, consider these improvements:

  1. Add visual distinction between failed and dropped states
  2. Validate the 3-minute timeout duration
  3. Add tooltips or help text to explain why transactions are marked as dropped

These enhancements would improve the user experience by making it clearer why transactions are in specific states.

packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (5)

119-119: Consider implementing timeout handling for Bitcoin transactions

While the timestamp is now tracked, there's no implementation of the 3-minute timeout handling for Bitcoin transactions. Consider extending the timeout feature to Bitcoin transactions for consistency across networks.


176-176: Consider implementing timeout handling for Substrate transactions

While the timestamp is now tracked, there's no implementation of the 3-minute timeout handling for Substrate transactions. Consider extending the timeout feature to Substrate transactions for consistency across networks.


Line range hint 235-264: Refactor duplicated block hash update logic

The block hash update logic is duplicated between legacy and versioned transactions. Consider extracting this into a shared helper function.

+ async function updateBlockHash(conn: Connection, backoff: number[], kind: string) {
+   let backoffi = 0;
+   while (true) {
+     if (backoffi >= backoff.length) {
+       console.warn(
+         `Failed to get latest blockhash after ${backoffi} attempts,` +
+         ` continuing with old block hash for ${kind} transaction...`
+       );
+       return null;
+     }
+     const backoffMs = backoff[backoffi];
+     if (backoffMs > 0) {
+       console.warn(
+         `Waiting ${backoffMs}ms before retrying latest block` +
+         ` hash for ${kind} transaction...`
+       );
+       await new Promise(res => setTimeout(res, backoffMs));
+     }
+     try {
+       return await conn.getLatestBlockhash();
+     } catch (err) {
+       console.warn(
+         `Failed to get latest blockhash on attempt` +
+         ` ${backoffi + 1}: ${String(err)}`
+       );
+     }
+     backoffi++;
+   }
+ }

Also applies to: 346-375

🧰 Tools
🪛 Biome

[error] 243-243: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)


Line range hint 507-569: Consider implementing timeout handling for EVM transactions

While timestamps are now tracked, there's no implementation of the 3-minute timeout handling for EVM transactions. Consider extending the timeout feature to EVM transactions for consistency across networks.

Additionally, consider implementing a transaction monitoring mechanism that:

  1. Tracks transaction status
  2. Marks transactions as failed if not mined within 3 minutes
  3. Provides feedback to the user about transaction timeouts

Line range hint 78-569: Implement a unified transaction timeout handler

While the changes successfully add timestamp tracking across all networks, consider implementing a unified transaction timeout handler that:

  1. Accepts the network type and transaction details
  2. Monitors transaction status across different networks
  3. Implements the 3-minute timeout logic consistently
  4. Updates the activity state when transactions timeout
  5. Provides appropriate user feedback

This would centralize the timeout logic and ensure consistent behavior across all supported networks.

Would you like me to help design this unified timeout handler?

🧰 Tools
🪛 Biome

[error] 243-243: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)

packages/extension/src/ui/action/views/network-activity/index.vue (4)

207-207: Remove unnecessary debug console.log statement

The console.log at line 207 appears to be a leftover debug statement:

console.log('[[ ??? doing something ??? ]]', info);

Consider removing it to clean up the console output.


150-236: Refactor duplicated code in handleActivityUpdate for maintainability

The handleActivityUpdate function contains repetitive code blocks for each provider when updating the activity status and calling activityState.updateActivity. Consider refactoring to eliminate duplication and improve readability.

One approach is to extract common logic into a helper function. Here's an example:

const updateActivityStatus = (
  activity: Activity,
  status: ActivityStatus,
  rawInfo: any,
  timer: ReturnType<typeof setInterval>,
) => {
  activity.status = status;
  activity.rawInfo = rawInfo;
  activityState
    .updateActivity(activity, {
      address: activityAddress.value,
      network: props.network.name,
    })
    .then(() => updateVisibleActivity(activity));
  // Clear the interval as we've reached a terminal status
  clearInterval(timer);
};

Then, in each provider case, you can use this helper function:

// Example for Ethereum provider
if (props.network.provider === ProviderName.ethereum) {
  if (!info) return;
  const evmInfo = info as EthereumRawInfo;
  const status = evmInfo.status
    ? ActivityStatus.success
    : ActivityStatus.failed;
  updateActivityStatus(activity, status, evmInfo, timer);
}

308-309: Re-evaluate clearing timers in onMounted

In the onMounted() hook, you are clearing intervals and resetting the activityCheckTimers array:

activityCheckTimers.forEach(timer => clearInterval(timer));
activityCheckTimers.length = 0;

Since the component is newly mounted, there shouldn't be any existing timers. This code may be unnecessary and could be removed unless there's a specific reason for it.


150-236: Ensure proper typing for function parameters

In the handleActivityUpdate function, the timer parameter is typed as any. For better type safety, specify the parameter type explicitly.

Update the function signature to:

const handleActivityUpdate = (
  activity: Activity,
  info: any,
  timer: ReturnType<typeof setInterval>,
) => { ... }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 87d808c and c0bbe9a.

📒 Files selected for processing (8)
  • packages/extension/src/libs/activity-state/index.ts (3 hunks)
  • packages/extension/src/providers/solana/libs/api.ts (1 hunks)
  • packages/extension/src/types/activity.ts (2 hunks)
  • packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue (4 hunks)
  • packages/extension/src/ui/action/views/network-activity/index.vue (4 hunks)
  • packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (17 hunks)
  • packages/swap/src/providers/jupiter/index.ts (1 hunks)
  • packages/swap/src/types/index.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/swap/src/providers/jupiter/index.ts
  • packages/swap/src/types/index.ts
🧰 Additional context used
🪛 Biome
packages/extension/src/providers/solana/libs/api.ts

[error] 48-48: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

packages/extension/src/ui/action/views/swap/libs/send-transactions.ts

[error] 243-243: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)


[error] 354-354: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)

🔇 Additional comments (7)
packages/extension/src/types/activity.ts (1)

98-98: LGTM! Verify all enum consumers are updated.

The new dropped status aligns with the PR objective to handle failed Jupiter transactions. The naming is clear and consistent with existing patterns.

Let's verify all consumers of this enum are updated:

✅ Verification successful

Let's gather more specific information about how ActivityStatus is used in these files to ensure the new 'dropped' status is properly handled.


The new dropped status is properly integrated across the codebase

Based on the search results, the new dropped status is already being handled appropriately in all relevant places:

  • UI components properly handle the status in conditional rendering (network-activity-transaction.vue)
  • Status updates include the new dropped state in activity tracking (network-activity/index.vue)
  • All activity handlers across different providers (ethereum, bitcoin, kadena, etc.) maintain consistent status handling
  • The status is properly used in transaction verification components
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all files using ActivityStatus to ensure they handle the new 'dropped' status
# Test: Search for ActivityStatus usage. Expect: All consumers properly handle the 'dropped' case

# Find direct enum usage
rg -l "ActivityStatus\." 

# Find switch/case statements that might need updating
ast-grep --pattern 'switch ($value) {
  $$$
  case $_: {
    $$$
  }
  $$$
}'

Length of output: 2215


Script:

#!/bin/bash
# Search for switch statements specifically handling ActivityStatus
rg -U "switch.*ActivityStatus.*\{.*\}" -A 10

# Search for direct string comparisons with ActivityStatus
rg "ActivityStatus\.[a-zA-Z]+" -A 1

# Look for any conditional checks that might need updating
rg "if.*ActivityStatus\." -A 2

Length of output: 16795

packages/extension/src/providers/solana/libs/api.ts (2)

28-31: LGTM! Clear documentation of null cases.

The JSDoc comment clearly documents when null is returned, which is crucial for handling dropped transactions.


32-52: Verify consistent timeout handling across providers.

Let's ensure other Solana transaction providers (Rango, Changelly) implement consistent timeout handling.

🧰 Tools
🪛 Biome

[error] 48-48: Unnecessary use of boolean literals in conditional expression.

Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with

(lint/complexity/noUselessTernary)

packages/extension/src/libs/activity-state/index.ts (1)

86-93: 🛠️ Refactor suggestion

Verify cache time implementation for failed transactions

This method is crucial for the PR's objective of failing transactions after 3 minutes. However, it needs better error handling and validation.

Let's verify the implementation aligns with the PR objectives:

Consider this enhancement:

  async getCacheTime(options: ActivityOptions): Promise<number> {
+   if (!options?.address || !options?.network) {
+     throw new Error('Invalid options: missing required fields');
+   }
+   try {
    const cacheTime: Record<string, number> = await this.#storage.get(
      this.getActivityCacheId(options),
    );
    if (!cacheTime || !cacheTime[STORAGE_KEY]) return 0;
    return cacheTime[STORAGE_KEY];
+   } catch (error) {
+     throw new Error(`Failed to get cache time: ${error.message}`);
+   }
  }
packages/extension/src/ui/action/views/network-activity/components/network-activity-transaction.vue (1)

195-197: Verify transaction timeout duration.

While the status handling for dropped transactions is implemented, there's no validation to ensure that transactions are marked as dropped after the specified 3-minute timeout mentioned in the PR objectives.

Consider adding a check to validate the timeout duration:

  } else if (props.activity.status === ActivityStatus.dropped) {
+   const timeoutDuration = 3 * 60 * 1000; // 3 minutes in milliseconds
+   const currentTime = Date.now();
+   const transactionAge = currentTime - props.activity.timestamp;
+   if (transactionAge < timeoutDuration) {
+     console.warn('Transaction marked as dropped before 3-minute timeout');
+   }
    status.value = 'Dropped';
packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (1)

78-78: LGTM: Return type enhancement supports transaction timing tracking

The modification to include sentAt timestamp is essential for implementing the 3-minute transaction timeout feature.

packages/extension/src/ui/action/views/network-activity/index.vue (1)

286-291: Verify necessity of monitoring activities with status dropped

In the setActivities function, you're initiating checkActivity for activities with status pending or dropped:

if (
  (act.status === ActivityStatus.pending ||
    act.status === ActivityStatus.dropped) &&
  act.type === ActivityType.transaction
) {
  checkActivity(act);
}

Consider whether it's necessary to continue monitoring activities already marked as dropped, as they may have reached a terminal status.

@NickKelly1 NickKelly1 requested a review from kvhnuke November 14, 2024 21:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
packages/swap/src/providers/jupiter/index.ts (1)

588-593: Add validation for sentAt timestamp

While the 3-minute timeout logic is sound, consider adding validation for the sentAt timestamp to protect against:

  • Invalid future timestamps
  • Very old timestamps
  • Clock skew between nodes
 if (txResponse == null) {
+  // Validate sentAt timestamp
+  const now = Date.now();
+  if (sentAt > now) {
+    throw new Error('Invalid sentAt timestamp: cannot be in the future');
+  }
+  if (sentAt < now - 24 * 60 * 60 * 1000) {
+    throw new Error('Invalid sentAt timestamp: too old (max 24h)');
+  }
   // Consider dropped (/failed) if it's still null after 3 minutes
   if (Date.now() > sentAt + 3 * 60_000) {
     return TransactionStatus.dropped;
   }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c0bbe9a and 216798b.

📒 Files selected for processing (8)
  • packages/swap/src/providers/changelly/index.ts (1 hunks)
  • packages/swap/src/providers/jupiter/index.ts (1 hunks)
  • packages/swap/src/providers/oneInch/index.ts (1 hunks)
  • packages/swap/src/providers/paraswap/index.ts (1 hunks)
  • packages/swap/src/providers/rango/index.ts (4 hunks)
  • packages/swap/src/providers/zerox/index.ts (1 hunks)
  • packages/swap/src/types/index.ts (2 hunks)
  • packages/swap/tests/changelly.test.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/swap/src/providers/changelly/index.ts
  • packages/swap/src/providers/oneInch/index.ts
  • packages/swap/src/providers/paraswap/index.ts
  • packages/swap/src/providers/rango/index.ts
  • packages/swap/src/providers/zerox/index.ts
  • packages/swap/src/types/index.ts
  • packages/swap/tests/changelly.test.ts
🔇 Additional comments (3)
packages/swap/src/providers/jupiter/index.ts (3)

577-581: LGTM: Proper validation of transaction count

The check ensures exactly one transaction is provided, which is correct as Jupiter only supports single transactions.


582-582: LGTM: Clean destructuring of transaction data

Properly extracts both the timestamp and transaction hash needed for status checking.


583-586: LGTM: Proper transaction retrieval configuration

Correctly retrieves transaction details with appropriate version configuration for Solana compatibility.

@kvhnuke kvhnuke changed the base branch from develop to prep/release December 4, 2024 19:22
@kvhnuke kvhnuke merged commit 021167d into prep/release Dec 4, 2024
4 checks passed
@kvhnuke kvhnuke deleted the feat/fail-dropped-jupiter-transactions branch December 4, 2024 19:22
@coderabbitai coderabbitai bot mentioned this pull request Dec 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants