Skip to content

Commit

Permalink
Errors not caught in 4.x (#6623)
Browse files Browse the repository at this point in the history
* catch TransactionPollingTimeoutError

* add contract tests

* eslint fix

* text fixes

* fix

* increase coverage

* fix

* changelog
  • Loading branch information
avkos authored Dec 4, 2023
1 parent 7461c8e commit 119dfa5
Show file tree
Hide file tree
Showing 8 changed files with 248 additions and 10 deletions.
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2254,3 +2254,36 @@ If there are any bugs, improvements, optimizations or any new feature proposal f
- Will populate `data` for transactions in contract for metamask provider instead of `input` (#6534)

## [Unreleased]

### Added

#### web3


#### web3-eth

- Catch `TransactionPollingTimeoutError` was added to send transaction events (#6623)

#### web3-utils

- `SocketProvider` now contains public function `getPendingRequestQueueSize`, `getSentRequestsQueueSize` and `clearQueues` (#6479)
- Added `safeDisconnect` as a `SocketProvider` method to disconnect only when request queue size and send request queue size is 0 (#6479)
- Add `isContractInitOptions` method (#6555)

### Changed

#### web3-core


#### web3-eth-contract


### Fixed

#### web3-rpc-methods

- Fix web3-types import #6590 (#6589)

#### web3-utils

- Fix unecessary array copy when pack encoding (#6553)
10 changes: 6 additions & 4 deletions packages/web3-eth-accounts/src/tx/baseTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,12 @@ export abstract class BaseTransaction<TransactionObject> {
}
// No chain ID provided
// -> return Common provided or create new default Common
return (
common?.copy() ??
new Common({ chain: this.DEFAULT_CHAIN, hardfork: this.DEFAULT_HARDFORK })
);

if (common?.copy && typeof common?.copy === 'function') {
return common.copy();
}

return new Common({ chain: this.DEFAULT_CHAIN, hardfork: this.DEFAULT_HARDFORK });
}

/**
Expand Down
60 changes: 60 additions & 0 deletions packages/web3-eth-contract/test/integration/contract_erc20.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,66 @@ describe('contract', () => {
value,
);
});

it('send tokens from the account that does not have ether', async () => {
const tempAccount = await createTempAccount();
const test = await createNewAccount({
unlock: true,
refill: false,
});

let catchError = false;
let catchErrorPromise;
try {
const promiEvent = contractDeployed.methods
.transfer(tempAccount.address, '0x1')
.send({ ...sendOptions, from: test.address });

catchErrorPromise = new Promise(resolve => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
promiEvent.on('error', err => {
// Returned error: insufficient funds for gas * price + value: balance 0, tx cost 25000327300000000, overshot 25000327300000000
resolve(err);
});
});
await promiEvent;
} catch (e) {
// Returned error: insufficient funds for gas * price + value: balance 0, tx cost 25000327300000000, overshot 25000327300000000
catchError = true;
}
expect(await catchErrorPromise).toBeDefined();
expect(catchError).toBe(true);
});
it('send tokens from the account that does not have tokens', async () => {
const tempAccount = await createTempAccount();
const test = await createNewAccount({
unlock: true,
refill: true,
});

let catchError = false;
let catchErrorPromise;
try {
const promiEvent = contractDeployed.methods
.transfer(tempAccount.address, '0x1')
.send({ ...sendOptions, from: test.address });

catchErrorPromise = new Promise(resolve => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
promiEvent.on('error', err => {
// Transaction has been reverted by the EVM
resolve(err);
});
});
await promiEvent;
} catch (e) {
// Transaction has been reverted by the EVM
catchError = true;
}
expect(await catchErrorPromise).toBeDefined();
expect(catchError).toBe(true);
});

it.each([signAndSendContractMethodEIP1559, signAndSendContractMethodEIP2930])(
'should transfer tokens with local wallet %p',
async signAndSendContractMethod => {
Expand Down
6 changes: 5 additions & 1 deletion packages/web3-eth/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,8 @@ Documentation:

- Dependencies updated

## [Unreleased]
## [Unreleased]

### Added

- Catch `TransactionPollingTimeoutError` was added to send transaction events (#6623)
2 changes: 2 additions & 0 deletions packages/web3-eth/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
TransactionRevertInstructionError,
TransactionRevertWithCustomError,
InvalidResponseError,
TransactionPollingTimeoutError,
} from 'web3-errors';
import {
FormatType,
Expand Down Expand Up @@ -50,6 +51,7 @@ export type SendTransactionEventsBase<ReturnFormat extends DataFormat, TxType> =
| TransactionRevertedWithoutReasonError<FormatType<TransactionReceipt, ReturnFormat>>
| TransactionRevertInstructionError<FormatType<TransactionReceipt, ReturnFormat>>
| TransactionRevertWithCustomError<FormatType<TransactionReceipt, ReturnFormat>>
| TransactionPollingTimeoutError
| InvalidResponseError
| ContractExecutionError;
};
Expand Down
4 changes: 3 additions & 1 deletion packages/web3-eth/src/utils/send_tx_helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { isNullish } from 'web3-validator';
import {
ContractExecutionError,
InvalidResponseError,
TransactionPollingTimeoutError,
TransactionRevertedWithoutReasonError,
TransactionRevertInstructionError,
TransactionRevertWithCustomError,
Expand Down Expand Up @@ -243,7 +244,8 @@ export class SendTxHelper<
_error instanceof ContractExecutionError ||
_error instanceof TransactionRevertWithCustomError ||
_error instanceof TransactionRevertedWithoutReasonError ||
_error instanceof TransactionRevertInstructionError) &&
_error instanceof TransactionRevertInstructionError ||
_error instanceof TransactionPollingTimeoutError) &&
this.promiEvent.listenerCount('error') > 0
) {
this.promiEvent.emit('error', _error);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { isHexStrict } from 'web3-validator';
import { Web3Eth, InternalTransaction, transactionSchema } from '../../../src';
import {
closeOpenConnection,
createNewAccount,
createTempAccount,
getSystemTestBackend,
getSystemTestProvider,
Expand All @@ -55,6 +56,74 @@ describe('Web3Eth.sendSignedTransaction', () => {
await closeOpenConnection(web3Eth);
});

describe('Should catch errors', () => {
it('send ether from the account that does not have ether', async () => {
let onErrorReceived = false;
let catchErrorReceived = false;
const from = await createNewAccount({
unlock: true,
refill: false,
});
let pr;
try {
const promiEvent = web3Eth.sendTransaction({
to: tempAcc.address,
from: from.address,
});
pr = new Promise(resolve => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
promiEvent.on('error', () => {
onErrorReceived = true;
resolve(true);
});
});
await promiEvent;
} catch (e) {
catchErrorReceived = true;
}
await pr;
expect(onErrorReceived).toBe(true);
expect(catchErrorReceived).toBe(true);
});
it('send and wait timeout', async () => {
let onErrorReceived = false;
let catchErrorReceived = false;
const from = await createNewAccount({
unlock: true,
refill: true,
});

web3Eth.setConfig({
transactionReceiptPollingInterval: 10,
transactionPollingTimeout: 1000,
});

const currentNonce = await web3Eth.getTransactionCount(from.address);
let pr;
try {
const promiEvent = web3Eth.sendTransaction({
to: tempAcc.address,
from: from.address,
value: '0x1',
nonce: currentNonce + BigInt(1000),
});
pr = new Promise(resolve => {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
promiEvent.on('error', () => {
onErrorReceived = true;
resolve(true);
});
});
await promiEvent;
} catch (e) {
catchErrorReceived = true;
}
await pr;
expect(onErrorReceived).toBe(true);
expect(catchErrorReceived).toBe(true);
});
});

describe('Transaction Types', () => {
it('should send a signed simple value transfer - type 0x0', async () => {
const temp = await createTempAccount();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,25 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

import { EthExecutionAPI } from 'web3-types';
import {
Common,
EthExecutionAPI,
HexString,
Web3NetAPI,
Transaction as TransactionType,
} from 'web3-types';
import { Web3Context } from 'web3-core';
import HttpProvider from 'web3-providers-http';
import { isNullish } from 'web3-validator';

import { ethRpcMethods } from 'web3-rpc-methods';

import { bytesToHex, hexToBytes } from 'web3-utils';
import {
AccessListEIP2930Transaction,
FeeMarketEIP1559Transaction,
Transaction,
} from 'web3-eth-accounts';
import { ethRpcMethods } from 'web3-rpc-methods';

import { bytesToHex, hexToBytes } from 'web3-utils';
import { prepareTransactionForSigning } from '../../src/utils/prepare_transaction_for_signing';
import { validTransactions } from '../fixtures/prepare_transaction_for_signing';

Expand All @@ -36,6 +43,65 @@ describe('prepareTransactionForSigning', () => {
config: { defaultNetworkId: '0x1' },
});

describe('default', () => {
it('use default common', async () => {
const context = new Web3Context<EthExecutionAPI>({
provider: new HttpProvider('http://127.0.0.1'),
config: { defaultNetworkId: '0x1' },
});
context.defaultChain = 'mainnet';
context.defaultCommon = {
customChain: {
name: 'test',
networkId: 457,
chainId: 1458,
},
baseChain: 'mainnet',
};

async function transactionBuilder<ReturnType = TransactionType>(options: {
transaction: TransactionType;
web3Context: Web3Context<EthExecutionAPI & Web3NetAPI>;
privateKey?: HexString | Uint8Array;
fillGasPrice?: boolean;
fillGasLimit?: boolean;
}): Promise<ReturnType> {
const tx = { ...options.transaction };

if (isNullish(tx.common)) {
if (options.web3Context.defaultCommon) {
const common = options.web3Context.defaultCommon as unknown as Common;
const chainId = common.customChain.chainId as string;
const networkId = common.customChain.networkId as string;
const name = common.customChain.name as string;
tx.common = {
...common,
customChain: { chainId, networkId, name },
};
}
}
return tx as unknown as ReturnType;
}

context.transactionBuilder = transactionBuilder;

const ethereumjsTx = await prepareTransactionForSigning(
{
chainId: 1458,
nonce: 1,
gasPrice: BigInt(20000000000),
gas: BigInt(21000),
to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55',
from: '0x2c7536E3605D9C16a7a3D7b1898e529396a65c23',
value: '1000000000',
input: '',
},
context,
);
expect(Number(ethereumjsTx.common.networkId())).toBe(457);
expect(ethereumjsTx.common.chainName()).toBe('test');
});
});
describe('should return an web3-utils/tx instance with expected properties', () => {
it.each(validTransactions)(
'mockBlock: %s\nexpectedTransaction: %s\nexpectedPrivateKey: %s\nexpectedAddress: %s\nexpectedRlpEncodedTransaction: %s\nexpectedTransactionHash: %s\nexpectedMessageToSign: %s\nexpectedV: %s\nexpectedR: %s\nexpectedS: %s',
Expand Down

0 comments on commit 119dfa5

Please sign in to comment.