Skip to content

Commit

Permalink
Fix and error when trying to get past events by calling `contract.get…
Browse files Browse the repository at this point in the history
…PastEvents` or `contract.events.allEvents()` if there is no matching events. (#6647)

* handle the case when getPastEvents does not have any event to return

* add unit test for contract log subscription

* update CHANGELOG.md

* update jest at web3-eth-ens
  • Loading branch information
Muhammad-Altabba authored Dec 12, 2023
1 parent 80c2e1d commit b35eca1
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 144 deletions.
6 changes: 5 additions & 1 deletion packages/web3-eth-contract/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,4 +353,8 @@ Documentation:

- By default, contracts will fill `data` instead of `input` within method calls (#6622)

## [Unreleased]
## [Unreleased]

### Fixed

- Fix and error that happen when trying to get past events by calling `contract.getPastEvents` or `contract.events.allEvents()`, if there is no matching events. (#6647)
3 changes: 2 additions & 1 deletion packages/web3-eth-contract/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"prettier": "^2.7.1",
"ts-jest": "^28.0.7",
"typescript": "^4.7.4",
"web3-eth-accounts": "^4.1.0"
"web3-eth-accounts": "^4.1.0",
"web3-providers-ws": "^4.0.7"
}
}
286 changes: 145 additions & 141 deletions packages/web3-eth-contract/src/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,140 +191,140 @@ const contractSubscriptions = {
};

/**
* The `web3.eth.Contract` makes it easy to interact with smart contracts on the ethereum blockchain.
* For using contract package, first install Web3 package using: `npm i web3` or `yarn add web3` based on your package manager, after that contracts features can be used as mentioned in following snippet.
* ```ts
*
* import { Web3 } from 'web3';
*
* const web3 = new Web3('https://127.0.0.1:4545');
* const abi = [...] as const; // your contract ABI
*
* let contract = new web3.eth.Contract(abi,'0xdAC17F958D2ee523a2206206994597C13D831ec7');
* await contract.methods.balanceOf('0xdAC17F958D2ee523a2206206994597C13D831ec7').call();
* ```
* For using individual package install `web3-eth-contract` and `web3-core` packages using: `npm i web3-eth-contract web3-core` or `yarn add web3-eth-contract web3-core`. This is more efficient approach for building lightweight applications.
* ```ts
*
* import { Web3Context } from 'web3-core';
* import { Contract } from 'web3-eth-contract';
*
* const abi = [...] as const; // your contract ABI
*
* let contract = new web3.eth.Contract(
* abi,
* '0xdAC17F958D2ee523a2206206994597C13D831ec7'
* new Web3Context('http://127.0.0.1:8545'));
*
* await contract.methods.balanceOf('0xdAC17F958D2ee523a2206206994597C13D831ec7').call();
* ```
* ## Generated Methods
* Following methods are generated by web3.js contract object for each of contract functions by using its ABI.
*
* ### send
* This is used to send a transaction to the smart contract and execute its method. Note this can alter the smart contract state.
*
* #### Parameters
* options?: PayableTxOptions | NonPayableTxOptions
*
* #### Returns
* [Web3PromiEvent](/api/web3/namespace/core#Web3PromiEvent) : Web3 Promi Event
*
* ```ts
* // using the promise
* myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
* .then(function(receipt){
* // other parts of code to use receipt
* });
*
*
* // using the event emitter
* myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
* .on('transactionHash', function(hash){
* // ...
* })
* .on('confirmation', function(confirmationNumber, receipt){
* // ...
* })
* .on('receipt', function(receipt){
* // ...
* })
* .on('error', function(error, receipt) {
* // ...
* });
*
* ```
*
* ### call
* This will execute smart contract method in the EVM without sending any transaction. Note calling cannot alter the smart contract state.
*
* #### Parameters
* options?: PayableCallOptions | NonPayableCallOptions,
* block?: BlockNumberOrTag,
*
* #### Returns
* Promise : having results of call
*
* ```ts
*
* let myContract = new web3.eth.Contract(abi, address);
*
* myContract.methods.myFunction().call()
* .then(console.log);
*
* ```
* ### estimateGas
* Returns the amount of gas consumed by executing the method in EVM without creating a new transaction on the blockchain. The returned amount can be used as a gas estimate for executing the transaction publicly. The actual gas used can be different when sending the transaction later, as the state of the smart contract can be different at that time.
*
* #### Parameters
* options?: PayableCallOptions,
* returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat,
*
* #### Returns
* Promise: The gas amount estimated.
*
* ```ts
* const estimatedGas = await contract.methods.approve('0xdAC17F958D2ee523a2206206994597C13D831ec7', 300)
* .estimateGas();
*
* ```
*
* ### encodeABI
* Encodes the ABI for this method. The resulting hex string is 32-bit function signature hash plus the passed parameters in Solidity tightly packed format. This can be used to send a transaction, call a method, or pass it into another smart contract’s method as arguments. Set the data field on web3.eth.sendTransaction options as the encodeABI() result and it is the same as calling the contract method with contract.myMethod.send().
*
* Some use cases for encodeABI() include: preparing a smart contract transaction for a multisignature wallet, working with offline wallets and cold storage and creating transaction payload for complex smart contract proxy calls.
*
* #### Parameters
* None
*
* #### Returns
* String: The encoded ABI.
*
* ```ts
* const encodedABI = await contract.methods.approve('0xdAC17F958D2ee523a2206206994597C13D831ec7', 300)
* .encodeABI();
*
* ```
*
* ### createAccessList
* This will create an access list a method execution will access when executed in the EVM.
* Note: You must specify a from address and gas if it’s not specified in options when instantiating parent contract object.
*
* #### Parameters
* options?: PayableCallOptions | NonPayableCallOptions,
* block?: BlockNumberOrTag,
*
* #### Returns
* Promise: The generated access list for transaction.
*
* ```ts
* const accessList = await contract.methods.approve('0xbEe634C21c16F05B03B704BaE071536121e6cFeA', 300)
* .createAccessList({
* from: "0x9992695e1053bb737d3cfae4743dcfc4b94f203d"
* });
* ```
*
*/
* The `web3.eth.Contract` makes it easy to interact with smart contracts on the ethereum blockchain.
* For using contract package, first install Web3 package using: `npm i web3` or `yarn add web3` based on your package manager, after that contracts features can be used as mentioned in following snippet.
* ```ts
*
* import { Web3 } from 'web3';
*
* const web3 = new Web3('https://127.0.0.1:4545');
* const abi = [...] as const; // your contract ABI
*
* let contract = new web3.eth.Contract(abi,'0xdAC17F958D2ee523a2206206994597C13D831ec7');
* await contract.methods.balanceOf('0xdAC17F958D2ee523a2206206994597C13D831ec7').call();
* ```
* For using individual package install `web3-eth-contract` and `web3-core` packages using: `npm i web3-eth-contract web3-core` or `yarn add web3-eth-contract web3-core`. This is more efficient approach for building lightweight applications.
* ```ts
*
* import { Web3Context } from 'web3-core';
* import { Contract } from 'web3-eth-contract';
*
* const abi = [...] as const; // your contract ABI
*
* let contract = new web3.eth.Contract(
* abi,
* '0xdAC17F958D2ee523a2206206994597C13D831ec7'
* new Web3Context('http://127.0.0.1:8545'));
*
* await contract.methods.balanceOf('0xdAC17F958D2ee523a2206206994597C13D831ec7').call();
* ```
* ## Generated Methods
* Following methods are generated by web3.js contract object for each of contract functions by using its ABI.
*
* ### send
* This is used to send a transaction to the smart contract and execute its method. Note this can alter the smart contract state.
*
* #### Parameters
* options?: PayableTxOptions | NonPayableTxOptions
*
* #### Returns
* [Web3PromiEvent](/api/web3/namespace/core#Web3PromiEvent) : Web3 Promi Event
*
* ```ts
* // using the promise
* myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
* .then(function(receipt){
* // other parts of code to use receipt
* });
*
*
* // using the event emitter
* myContract.methods.myMethod(123).send({from: '0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe'})
* .on('transactionHash', function(hash){
* // ...
* })
* .on('confirmation', function(confirmationNumber, receipt){
* // ...
* })
* .on('receipt', function(receipt){
* // ...
* })
* .on('error', function(error, receipt) {
* // ...
* });
*
* ```
*
* ### call
* This will execute smart contract method in the EVM without sending any transaction. Note calling cannot alter the smart contract state.
*
* #### Parameters
* options?: PayableCallOptions | NonPayableCallOptions,
* block?: BlockNumberOrTag,
*
* #### Returns
* Promise : having results of call
*
* ```ts
*
* let myContract = new web3.eth.Contract(abi, address);
*
* myContract.methods.myFunction().call()
* .then(console.log);
*
* ```
* ### estimateGas
* Returns the amount of gas consumed by executing the method in EVM without creating a new transaction on the blockchain. The returned amount can be used as a gas estimate for executing the transaction publicly. The actual gas used can be different when sending the transaction later, as the state of the smart contract can be different at that time.
*
* #### Parameters
* options?: PayableCallOptions,
* returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat,
*
* #### Returns
* Promise: The gas amount estimated.
*
* ```ts
* const estimatedGas = await contract.methods.approve('0xdAC17F958D2ee523a2206206994597C13D831ec7', 300)
* .estimateGas();
*
* ```
*
* ### encodeABI
* Encodes the ABI for this method. The resulting hex string is 32-bit function signature hash plus the passed parameters in Solidity tightly packed format. This can be used to send a transaction, call a method, or pass it into another smart contract’s method as arguments. Set the data field on web3.eth.sendTransaction options as the encodeABI() result and it is the same as calling the contract method with contract.myMethod.send().
*
* Some use cases for encodeABI() include: preparing a smart contract transaction for a multisignature wallet, working with offline wallets and cold storage and creating transaction payload for complex smart contract proxy calls.
*
* #### Parameters
* None
*
* #### Returns
* String: The encoded ABI.
*
* ```ts
* const encodedABI = await contract.methods.approve('0xdAC17F958D2ee523a2206206994597C13D831ec7', 300)
* .encodeABI();
*
* ```
*
* ### createAccessList
* This will create an access list a method execution will access when executed in the EVM.
* Note: You must specify a from address and gas if it’s not specified in options when instantiating parent contract object.
*
* #### Parameters
* options?: PayableCallOptions | NonPayableCallOptions,
* block?: BlockNumberOrTag,
*
* #### Returns
* Promise: The generated access list for transaction.
*
* ```ts
* const accessList = await contract.methods.approve('0xbEe634C21c16F05B03B704BaE071536121e6cFeA', 300)
* .createAccessList({
* from: "0x9992695e1053bb737d3cfae4743dcfc4b94f203d"
* });
* ```
*
*/
export class Contract<Abi extends ContractAbi>
extends Web3Context<EthExecutionAPI, typeof contractSubscriptions>
implements Web3EventEmitter<ContractEventEmitterInterface<Abi>>
Expand Down Expand Up @@ -663,7 +663,7 @@ export class Contract<Abi extends ContractAbi>
*
* ```ts
* myContract.deploy({
* input: '0x12345...', // data keyword can be used, too.
* input: '0x12345...', // data keyword can be used, too.
* arguments: [123, 'My String']
* })
* .send({
Expand Down Expand Up @@ -898,11 +898,13 @@ export class Contract<Abi extends ContractAbi>
);

const logs = await getLogs(this, { fromBlock, toBlock, topics, address }, returnFormat);
const decodedLogs = logs.map(log =>
typeof log === 'string'
? log
: decodeEventABI(abi, log as LogsInput, this._jsonInterface, returnFormat),
);
const decodedLogs = logs
? logs.map(log =>
typeof log === 'string'
? log
: decodeEventABI(abi, log as LogsInput, this._jsonInterface, returnFormat),
)
: [];

const filter = options?.filter ?? {};
const filterKeys = Object.keys(filter);
Expand Down Expand Up @@ -1322,7 +1324,9 @@ export class Contract<Abi extends ContractAbi>
// emit past events when fromBlock is defined
this.getPastEvents(abi.name, { fromBlock, topics }, returnFormat)
.then(logs => {
logs.forEach(log => sub.emit('data', log as EventLog));
if (logs) {
logs.forEach(log => sub.emit('data', log as EventLog));
}
})
.catch((error: Error) => {
sub.emit(
Expand Down
Loading

0 comments on commit b35eca1

Please sign in to comment.