Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit e9fc019

Browse files
authored
Contract Transactions Middleware (#7138)
* contract transactionMiddleware * transactionMiddleware setting for contract from eth * contract Tx middleware plugin * contract tx middleware tests * changelog * fix * test import fix * integration patch fix * lint fix * integration tests * middleware in fixture * unit test * lint fix * fixture fix for lint import err via symlink * unit test coverage push * up coverage
1 parent 280b8f0 commit e9fc019

File tree

12 files changed

+525
-10
lines changed

12 files changed

+525
-10
lines changed

packages/web3-eth-contract/CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,8 @@ Documentation:
386386

387387
- `defaultReturnFormat` was added to all methods that have `ReturnType` param. (#6947)
388388

389-
## [Unreleased]
389+
## [Unreleased]
390+
391+
### Added
392+
393+
- Contract has `setTransactionMiddleware` and `getTransactionMiddleware` for automatically passing to `sentTransaction` for `deploy` and `send` functions (#7138)

packages/web3-eth-contract/src/contract.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ import {
3939
ALL_EVENTS,
4040
ALL_EVENTS_ABI,
4141
SendTransactionEvents,
42+
TransactionMiddleware,
43+
SendTransactionOptions,
4244
} from 'web3-eth';
4345
import {
4446
encodeEventSignature,
@@ -433,7 +435,7 @@ export class Contract<Abi extends ContractAbi>
433435
*/
434436

435437
public readonly options: ContractOptions;
436-
438+
private transactionMiddleware?: TransactionMiddleware;
437439
/**
438440
* Set to true if you want contracts' defaults to sync with global defaults.
439441
*/
@@ -640,6 +642,14 @@ export class Contract<Abi extends ContractAbi>
640642
}
641643
}
642644

645+
public setTransactionMiddleware(transactionMiddleware: TransactionMiddleware) {
646+
this.transactionMiddleware = transactionMiddleware;
647+
}
648+
649+
public getTransactionMiddleware() {
650+
return this.transactionMiddleware;
651+
}
652+
643653
/**
644654
* Subscribe to an event.
645655
*
@@ -1396,11 +1406,17 @@ export class Contract<Abi extends ContractAbi>
13961406
contractOptions: modifiedContractOptions,
13971407
});
13981408

1399-
const transactionToSend = sendTransaction(this, tx, this.defaultReturnFormat, {
1400-
// TODO Should make this configurable by the user
1401-
checkRevertBeforeSending: false,
1402-
contractAbi: this._jsonInterface,
1403-
});
1409+
const transactionToSend = (isNullish(this.transactionMiddleware)) ?
1410+
sendTransaction(this, tx, this.defaultReturnFormat, {
1411+
// TODO Should make this configurable by the user
1412+
checkRevertBeforeSending: false,
1413+
contractAbi: this._jsonInterface, // explicitly not passing middleware so if some one is using old eth package it will not break
1414+
}) :
1415+
sendTransaction(this, tx, this.defaultReturnFormat, {
1416+
// TODO Should make this configurable by the user
1417+
checkRevertBeforeSending: false,
1418+
contractAbi: this._jsonInterface,
1419+
}, this.transactionMiddleware);
14041420

14051421
// eslint-disable-next-line no-void
14061422
void transactionToSend.on('error', (error: unknown) => {
@@ -1429,8 +1445,9 @@ export class Contract<Abi extends ContractAbi>
14291445
options: { ...options, dataInputFill: this.contractDataInputFill },
14301446
contractOptions: modifiedContractOptions,
14311447
});
1432-
return sendTransaction(this, tx, this.defaultReturnFormat, {
1433-
transactionResolver: receipt => {
1448+
1449+
const returnTxOptions: SendTransactionOptions<Contract<Abi>> = {
1450+
transactionResolver: (receipt: TransactionReceipt) => {
14341451
if (receipt.status === BigInt(0)) {
14351452
throw new Web3ContractError("code couldn't be stored", receipt);
14361453
}
@@ -1444,7 +1461,12 @@ export class Contract<Abi extends ContractAbi>
14441461
contractAbi: this._jsonInterface,
14451462
// TODO Should make this configurable by the user
14461463
checkRevertBeforeSending: false,
1447-
});
1464+
};
1465+
1466+
return (
1467+
(isNullish(this.transactionMiddleware)) ?
1468+
sendTransaction(this, tx, this.defaultReturnFormat, returnTxOptions) : // not calling this with undefined Middleware because it will not break if Eth package is not updated
1469+
sendTransaction(this, tx, this.defaultReturnFormat, returnTxOptions, this.transactionMiddleware));
14481470
}
14491471

14501472
private async _contractMethodEstimateGas<
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
This file is part of web3.js.
3+
4+
web3.js is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU Lesser General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
web3.js is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public License
15+
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import { TransactionMiddleware, TransactionMiddlewareData } from "web3-eth";
19+
20+
// Sample Transaction Middleware
21+
export class ContractTransactionMiddleware implements TransactionMiddleware {
22+
23+
// eslint-disable-next-line class-methods-use-this
24+
public async processTransaction(transaction: TransactionMiddlewareData,
25+
_options?: { [key: string]: unknown } | undefined):
26+
27+
Promise<TransactionMiddlewareData> {
28+
29+
// eslint-disable-next-line prefer-const
30+
let txObj = { ...transaction };
31+
32+
// Add your logic here for transaction modification
33+
txObj.data = '0x123';
34+
35+
return Promise.resolve(txObj);
36+
}
37+
38+
}

packages/web3-eth-contract/test/unit/contract.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { getSystemTestProvider } from '../fixtures/system_test_utils';
3535
import { erc721Abi } from '../fixtures/erc721';
3636
import { ERC20TokenAbi } from '../shared_fixtures/build/ERC20Token';
3737
import { processAsync } from '../shared_fixtures/utils';
38+
import { ContractTransactionMiddleware } from "../fixtures/contract_transaction_middleware";
3839

3940
jest.mock('web3-eth', () => {
4041
const allAutoMocked = jest.createMockFromModule('web3-eth');
@@ -275,6 +276,61 @@ describe('Contract', () => {
275276
sendTransactionSpy.mockClear();
276277
});
277278

279+
it('should pass middleware to sendTransaction when middleware is there and deploy().send() is called', async () => {
280+
const contract = new Contract(GreeterAbi);
281+
const middleware = new ContractTransactionMiddleware();
282+
contract.setTransactionMiddleware(middleware)
283+
284+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
285+
const sendTransactionSpy = jest
286+
.spyOn(eth, 'sendTransaction')
287+
.mockImplementation((_objInstance, _tx, _dataFormat, _options, _middleware) => {
288+
289+
expect(_middleware).toBeDefined();
290+
const newContract = contract.clone();
291+
newContract.options.address = deployedAddr;
292+
293+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
294+
return Promise.resolve(newContract) as any;
295+
});
296+
297+
await contract
298+
.deploy({
299+
input: GreeterBytecode,
300+
arguments: ['My Greeting'],
301+
})
302+
.send(sendOptions);
303+
304+
sendTransactionSpy.mockClear();
305+
});
306+
307+
it('should pass middleware to sendTransaction when middleware is there and contract.method.send() is called', async () => {
308+
309+
const contract = new Contract(GreeterAbi, '0x12264916b10Ae90076dDa6dE756EE1395BB69ec2');
310+
const middleware = new ContractTransactionMiddleware();
311+
contract.setTransactionMiddleware(middleware);
312+
313+
const spyTx = jest
314+
.spyOn(eth, 'sendTransaction')
315+
.mockImplementation((_objInstance, _tx, _dataformat, _options, _middleware) => {
316+
317+
expect(_middleware).toBeDefined();
318+
319+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-empty-function
320+
return { status: '0x1', on: () => {} } as any;
321+
322+
});
323+
324+
const receipt = await contract.methods.setGreeting('Hello').send({
325+
from: '0x12364916b10Ae90076dDa6dE756EE1395BB69ec2',
326+
gas: '1000000'
327+
});
328+
329+
expect(receipt.status).toBe('0x1');
330+
331+
spyTx.mockClear();
332+
});
333+
278334
it('should deploy contract with input property with no ABI', async () => {
279335
const input = `${GreeterBytecode}0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000b4d79204772656574696e67000000000000000000000000000000000000000000`;
280336
const contract = new Contract([]);
@@ -788,6 +844,17 @@ describe('Contract', () => {
788844
expect(contract.methods.setGreeting).toBeDefined();
789845
});
790846

847+
it('should be able to set and get transaction middleware', () => {
848+
const contract = new Contract(sampleStorageContractABI);
849+
const middleware = new ContractTransactionMiddleware();
850+
851+
expect(contract.getTransactionMiddleware()).toBeUndefined();
852+
853+
contract.setTransactionMiddleware(middleware);
854+
expect(contract.getTransactionMiddleware()).toBeDefined();
855+
expect(contract.getTransactionMiddleware()).toEqual(middleware);
856+
});
857+
791858
it('defaults set and get should work', () => {
792859
const contract = new Contract([], '0x00000000219ab540356cBB839Cbe05303d7705Fa');
793860

packages/web3/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,9 @@ Documentation:
382382
- `getName` reverse resolution
383383

384384
## [Unreleased]
385+
386+
### Added
387+
388+
#### web3
389+
390+
- `web3.eth.Contract` will get transaction middleware and use it, if `web3.eth` has transaction middleware. (#7138)

packages/web3/src/web3.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,15 @@ export class Web3<
203203

204204
super(jsonInterface, address, options, context, dataFormat);
205205
super.subscribeToContextEvents(self);
206+
207+
// eslint-disable-next-line no-use-before-define
208+
if (!isNullish(eth)) {
209+
// eslint-disable-next-line no-use-before-define
210+
const TxMiddleware = eth.getTransactionMiddleware();
211+
if (!isNullish(TxMiddleware)) {
212+
super.setTransactionMiddleware(TxMiddleware);
213+
}
214+
}
206215
}
207216
}
208217

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
This file is part of web3.js.
3+
4+
web3.js is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU Lesser General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
web3.js is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public License
15+
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import { TransactionMiddleware, TransactionMiddlewareData } from "web3-eth";
19+
20+
// Sample Transaction Middleware
21+
export class CTransactionMiddleware implements TransactionMiddleware {
22+
23+
// eslint-disable-next-line class-methods-use-this
24+
public async processTransaction(transaction: TransactionMiddlewareData,
25+
_options?: { [key: string]: unknown } | undefined):
26+
27+
Promise<TransactionMiddlewareData> {
28+
29+
// eslint-disable-next-line prefer-const
30+
let txObj = { ...transaction };
31+
32+
// Add your logic here for transaction modification
33+
txObj.data = '0x123';
34+
35+
return Promise.resolve(txObj);
36+
}
37+
38+
}

0 commit comments

Comments
 (0)