Skip to content
Merged
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
11 changes: 10 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,17 @@ pnpm lint:fix # Auto-fix ESLint issues

### Testing Individual Files
```bash
pnpm test -- src/utils/__tests__/retry-with-fallback.test.js
# Run tests in non-interactive mode (CI/automated)
pnpm test -- --no-watch --passWithNoTests --watchAll=false

# Run specific test file in non-interactive mode
pnpm test -- --no-watch --passWithNoTests --watchAll=false src/utils/__tests__/retry-with-fallback.test.js

# Run tests by pattern
pnpm test -- --testNamePattern="test name pattern"

# Watch mode (interactive)
pnpm test -- src/utils/__tests__/retry-with-fallback.test.js
```

## Architecture Overview
Expand Down
45 changes: 5 additions & 40 deletions src/components/AssignNewManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useSettings } from '../contexts/SettingsContext';
import { ethers } from 'ethers';
import toast from 'react-hot-toast';
import { motion } from 'framer-motion';
import { handleTransactionError } from '../utils/error-handler';

const AssignNewManager = ({ assistant, onClose, onSuccess }) => {
const { signer } = useWeb3();
Expand Down Expand Up @@ -122,46 +123,10 @@ const AssignNewManager = ({ assistant, onClose, onSuccess }) => {
toast.error('Transaction failed', { id: 'assign-manager' });
}
} catch (error) {
console.error('Error assigning new manager:', error);

// Handle user rejection gracefully
if (error.code === 'ACTION_REJECTED' || error.code === 'USER_REJECTED' ||
error.message?.includes('user rejected') || error.message?.includes('User denied')) {
toast.error('Transaction cancelled by user', { id: 'assign-manager' });
return; // Don't show additional error messages for user cancellation
}

// Handle insufficient funds
if (error.code === 'INSUFFICIENT_FUNDS' || error.message?.includes('insufficient funds')) {
toast.error('Insufficient funds for gas fees', { id: 'assign-manager' });
}
// Handle network issues
else if (error.code === 'NETWORK_ERROR' || error.message?.includes('network')) {
toast.error('Network error. Please check your connection and try again', { id: 'assign-manager' });
}
// Handle contract-specific errors
else if (error.message?.includes('zero address')) {
toast.error('Cannot assign zero address as manager', { id: 'assign-manager' });
} else if (error.message?.includes('P3D precompile cannot be manager')) {
toast.error('P3D precompile cannot be assigned as manager', { id: 'assign-manager' });
} else if (error.message?.includes('ERC20 precompile cannot be manager')) {
toast.error('ERC20 precompile cannot be assigned as manager', { id: 'assign-manager' });
} else if (error.message?.includes('onlyManager')) {
toast.error('Only the current manager can assign a new manager', { id: 'assign-manager' });
}
// Handle gas estimation errors
else if (error.message?.includes('gas') || error.code === 'UNPREDICTABLE_GAS_LIMIT') {
toast.error('Gas estimation failed. Please try again', { id: 'assign-manager' });
}
// Handle timeout errors
else if (error.message?.includes('timeout') || error.code === 'TIMEOUT') {
toast.error('Transaction timeout. Please try again', { id: 'assign-manager' });
}
// Generic error handling
else {
const errorMessage = error.message || error.reason || 'Unknown error occurred';
toast.error(`Failed to assign new manager: ${errorMessage}`, { id: 'assign-manager' });
}
handleTransactionError(error, {
messagePrefix: 'Failed to assign new manager: ',
toastOptions: { id: 'assign-manager' }
});
} finally {
setLoading(false);
}
Expand Down
39 changes: 7 additions & 32 deletions src/components/Challenge.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import toast from 'react-hot-toast';
import { handleTransactionError } from '../utils/error-handler';

// Get maximum allowance value (2^256 - 1)
const getMaxAllowance = () => {
Expand Down Expand Up @@ -571,23 +572,9 @@ const Challenge = ({ claim, onChallengeSuccess, onClose }) => {

onClose();
} catch (error) {
console.error('Error challenging claim:', error);

// Handle different types of errors gracefully
if (error.code === 'ACTION_REJECTED' || error.message?.includes('user rejected')) {
toast.error('Transaction was cancelled by user');
} else if (error.code === 'INSUFFICIENT_FUNDS') {
toast.error('Insufficient funds for transaction');
} else if (error.code === 'NETWORK_ERROR' || error.message?.includes('network')) {
toast.error('Network error. Please check your connection');
} else if (error.message?.includes('gas')) {
toast.error('Transaction failed due to gas issues');
} else if (error.message?.includes('revert')) {
toast.error('Transaction was reverted by the contract');
} else {
// For other errors, show a generic message but log the full error
toast.error('Transaction failed. Please try again');
}
handleTransactionError(error, {
messagePrefix: 'Failed to challenge: '
});
} finally {
setLoading(false);
}
Expand Down Expand Up @@ -657,21 +644,9 @@ const Challenge = ({ claim, onChallengeSuccess, onClose }) => {
}
);
} catch (error) {
console.error('❌ Error revoking allowance:', error);

if (error.code === 'ACTION_REJECTED' || error.message?.includes('user rejected')) {
toast.error('Transaction was cancelled by user');
} else if (error.code === 'INSUFFICIENT_FUNDS') {
toast.error('Insufficient funds for transaction');
} else if (error.code === 'NETWORK_ERROR' || error.message?.includes('network')) {
toast.error('Network error. Please check your connection');
} else if (error.message?.includes('gas')) {
toast.error('Transaction failed due to gas issues');
} else if (error.message?.includes('revert')) {
toast.error('Transaction was reverted by the contract');
} else {
toast.error('Transaction failed. Please try again');
}
handleTransactionError(error, {
messagePrefix: 'Failed to revoke allowance: '
});
} finally {
setIsRevoking(false);
}
Expand Down
45 changes: 7 additions & 38 deletions src/components/CreateNewAssistant.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { motion, AnimatePresence } from 'framer-motion';
import toast from 'react-hot-toast';
import { ethers } from 'ethers';
import { handleTransactionError } from '../utils/error-handler';

const CreateNewAssistant = ({ networkKey, onClose, onAssistantCreated }) => {
const { signer, account } = useWeb3();
Expand Down Expand Up @@ -358,25 +359,9 @@ const CreateNewAssistant = ({ networkKey, onClose, onAssistantCreated }) => {
}

} catch (error) {
console.error('Error creating assistant:', error);

// Handle different types of errors gracefully
if (error.code === 'ACTION_REJECTED' || error.message?.includes('user rejected')) {
toast.error('Transaction was cancelled by user');
} else if (error.code === 'INSUFFICIENT_FUNDS') {
toast.error('Insufficient funds to pay for gas fees');
} else if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
toast.error('Gas estimation failed. Please try again or increase gas limit');
} else if (error.message?.includes('execution reverted')) {
// Extract revert reason if available
const revertReason = error.message.match(/execution reverted: (.+)/)?.[1] || 'Transaction failed';
toast.error(`Transaction failed: ${revertReason}`);
} else if (error.message?.includes('network')) {
toast.error('Network error. Please check your connection and try again');
} else {
// Generic error message for other cases
toast.error(`Failed to create assistant: ${error.message || 'Unknown error'}`);
}
handleTransactionError(error, {
messagePrefix: 'Failed to create assistant: '
});
} finally {
setIsCreating(false);
}
Expand Down Expand Up @@ -451,25 +436,9 @@ const CreateNewAssistant = ({ networkKey, onClose, onAssistantCreated }) => {
toast.success('Precompile approved successfully');

} catch (error) {
console.error('Error approving precompile:', error);

// Handle different types of errors gracefully
if (error.code === 'ACTION_REJECTED' || error.message?.includes('user rejected')) {
toast.error('Approval transaction was cancelled by user');
} else if (error.code === 'INSUFFICIENT_FUNDS') {
toast.error('Insufficient funds to pay for gas fees');
} else if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
toast.error('Gas estimation failed. Please try again or increase gas limit');
} else if (error.message?.includes('execution reverted')) {
// Extract revert reason if available
const revertReason = error.message.match(/execution reverted: (.+)/)?.[1] || 'Transaction failed';
toast.error(`Approval failed: ${revertReason}`);
} else if (error.message?.includes('network')) {
toast.error('Network error. Please check your connection and try again');
} else {
// Generic error message for other cases
toast.error(`Failed to approve precompile: ${error.message || 'Unknown error'}`);
}
handleTransactionError(error, {
messagePrefix: 'Failed to approve precompile: '
});
} finally {
setIsApproving(false);
}
Expand Down
58 changes: 4 additions & 54 deletions src/components/CreateNewBridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { motion, AnimatePresence } from 'framer-motion';
import toast from 'react-hot-toast';
import { ethers } from 'ethers';
import { handleTransactionError } from '../utils/error-handler';

// Constants
const DEFAULT_COUNTERSTAKE_COEF = '160'; // 1.6%
Expand Down Expand Up @@ -673,61 +674,10 @@ const CreateNewBridge = ({ networkKey, onClose, onBridgeCreated }) => {
}

} catch (error) {
console.error('Error creating bridge:', error);

// Dismiss any loading toasts first
toast.dismiss();

// Handle different types of errors gracefully
if (error.code === 4001 ||
error.code === 'ACTION_REJECTED' ||
error.message?.includes('User denied transaction') ||
error.message?.includes('user rejected transaction') ||
error.message?.includes('User rejected')) {
// User cancelled the transaction in MetaMask
toast.error('Transaction cancelled by user');
} else if (error.code === -32603 ||
error.message?.includes('insufficient funds') ||
error.message?.includes('insufficient balance')) {
// Insufficient funds
toast.error('Insufficient funds for transaction. Please check your wallet balance.');
} else if (error.message?.includes('gas') ||
error.message?.includes('Gas') ||
error.code === -32000) {
// Gas related errors
toast.error('Transaction failed due to gas issues. Please try again or increase gas limit.');
} else if (error.message?.includes('revert') ||
error.message?.includes('execution reverted')) {
// Contract revert - provide more specific error messages
if (error.message?.includes('bad oracle') || error.message?.includes('no price from oracle')) {
toast.error('Oracle validation failed. Please ensure the oracle has price data for the required token pairs.');
} else if (error.message?.includes('bad stake token') || error.message?.includes('symbol')) {
toast.error('Stake token validation failed. Please select a different stake token that implements the symbol() function properly.');
} else {
toast.error('Transaction failed. Please check your inputs and try again.');
}
} else if (error.message?.includes('network') ||
error.message?.includes('Network')) {
// Network related errors
toast.error('Network error. Please check your connection and try again.');
} else if (error.message?.includes('timeout') ||
error.message?.includes('Timeout')) {
// Timeout errors
toast.error('Transaction timed out. Please try again.');
} else if (error.message?.includes('nonce')) {
// Nonce errors
toast.error('Transaction nonce error. Please try again.');
} else {
// Generic error - show a more user-friendly message
const errorMessage = error.message || 'Unknown error occurred';
console.error('Unhandled error details:', {
code: error.code,
message: error.message,
reason: error.reason,
action: error.action
});
toast.error(`Failed to create bridge: ${errorMessage}`);
}
handleTransactionError(error, {
messagePrefix: 'Failed to create bridge: '
});
} finally {
setIsCreating(false);
}
Expand Down
52 changes: 4 additions & 48 deletions src/components/DeployNewOracle.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { motion, AnimatePresence } from 'framer-motion';
import toast from 'react-hot-toast';
import { ethers } from 'ethers';
import { handleTransactionError } from '../utils/error-handler';

// Oracle contract ABI (complete ABI for deployment and interaction)
const ORACLE_ABI = [
Expand Down Expand Up @@ -232,55 +233,10 @@ const DeployNewOracle = ({ networkKey, onClose, onOracleCreated }) => {
}

} catch (error) {
console.error('Error deploying oracle:', error);

// Dismiss any loading toasts first
toast.dismiss();

// Handle different types of errors gracefully
if (error.code === 4001 ||
error.code === 'ACTION_REJECTED' ||
error.message?.includes('User denied transaction') ||
error.message?.includes('user rejected transaction') ||
error.message?.includes('User rejected')) {
// User cancelled the transaction in MetaMask
toast.error('Transaction cancelled by user');
} else if (error.code === -32603 ||
error.message?.includes('insufficient funds') ||
error.message?.includes('insufficient balance')) {
// Insufficient funds
toast.error('Insufficient funds for transaction. Please check your wallet balance.');
} else if (error.message?.includes('gas') ||
error.message?.includes('Gas') ||
error.code === -32000) {
// Gas related errors
toast.error('Transaction failed due to gas issues. Please try again or increase gas limit.');
} else if (error.message?.includes('revert') ||
error.message?.includes('execution reverted')) {
// Contract revert
toast.error('Transaction failed. Please check your inputs and try again.');
} else if (error.message?.includes('network') ||
error.message?.includes('Network')) {
// Network related errors
toast.error('Network error. Please check your connection and try again.');
} else if (error.message?.includes('timeout') ||
error.message?.includes('Timeout')) {
// Timeout errors
toast.error('Transaction timed out. Please try again.');
} else if (error.message?.includes('nonce')) {
// Nonce errors
toast.error('Transaction nonce error. Please try again.');
} else {
// Generic error - show a more user-friendly message
const errorMessage = error.message || 'Unknown error occurred';
console.error('Unhandled error details:', {
code: error.code,
message: error.message,
reason: error.reason,
action: error.action
});
toast.error(`Failed to deploy oracle: ${errorMessage}`);
}
handleTransactionError(error, {
messagePrefix: 'Failed to deploy oracle: '
});
} finally {
setIsDeploying(false);
}
Expand Down
Loading