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
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: '24'
registry-url: 'https://registry.npmjs.org'
- name: Update npm to latest
run: npm install -g npm@latest
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
### Added

- [MINOR] Added `useWeb3WritableContract` to get a signer-bound (write-capable) contract instance directly, without a separate `useWeb3Contract` call or a manual `.connect()`
- [MINOR] Added optional `abi` param to `useWeb3Transaction` and a new `errorMessage` field on `Web3TransactionDetails`, decoding a transaction's custom-error revert data via the ABI when available
- [MINOR] Added optional `abi` param to `useWeb3Transaction` and a new `errorMessage` field on `Web3TransactionDetails`
- [MINOR] Added `web3Units` helpers: `Web3MaxUint256`, `formatWeb3Units` (null-safe `formatUnits`), and `parseWeb3Units` (safe-fallback or throwing `parseUnits`)

### Changed
Expand Down
24 changes: 22 additions & 2 deletions src/useWeb3Transaction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import { Interface, isCallException } from 'ethers';
import { ErrorCode, Interface, isCallException, isError } from 'ethers';

import { Web3ContractInterface, Web3TransactionReceipt, Web3TransactionResponse } from './model';

Expand All @@ -14,15 +14,35 @@ export interface Web3TransactionDetails {
receipt: Web3TransactionReceipt | null;
}

const ETHERS_ERROR_MESSAGES_BY_CODE: Partial<Record<ErrorCode, string>> = {
ACTION_REJECTED: 'Transaction cancelled.',
INSUFFICIENT_FUNDS: 'Your wallet does not have enough funds to cover this transaction.',
NONCE_EXPIRED: 'This transaction has already been submitted. Refresh and try again.',
REPLACEMENT_UNDERPRICED: 'Could not replace the pending transaction — try again with a higher fee.',
TIMEOUT: 'The request timed out. Please try again.',
NETWORK_ERROR: 'A network error occurred. Please try again.',
SERVER_ERROR: 'A network error occurred. Please try again.',
};

const decodeErrorMessage = (newError: unknown, abi?: Web3ContractInterface): string => {
if (isError(newError, 'TRANSACTION_REPLACED')) {
if (newError.reason === 'repriced') {
return 'Your transaction was sped up by your wallet — check your wallet for the latest status.';
}
return newError.reason === 'cancelled' ? 'Transaction cancelled.' : 'Your transaction was replaced by another transaction.';
}
const code = (newError as { code?: ErrorCode } | null)?.code;
if (code && ETHERS_ERROR_MESSAGES_BY_CODE[code]) {
return ETHERS_ERROR_MESSAGES_BY_CODE[code];
}
if (abi && isCallException(newError) && newError.data) {
try {
const parsedError = new Interface(abi).parseError(newError.data);
if (parsedError) {
return parsedError.name;
}
} catch {
// Revert data present but not decodable with this ABI (e.g. selector from a different contract)
// Revert data present but not decodable with this ABI
}
}
return newError instanceof Error ? newError.message : String(newError);
Expand Down
Loading