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

Commit 60fc197

Browse files
Refactor some parts of contract and accounts packages (#7197)
* add `DeployerMethodClass` * set `extraTxTypes` using global * update CHANGELOG.md files
1 parent 4f8e8cc commit 60fc197

File tree

7 files changed

+310
-200
lines changed

7 files changed

+310
-200
lines changed

packages/web3-eth-accounts/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,6 @@ Documentation:
172172
### Added
173173

174174
- Added public function `signMessageWithPrivateKey` (#7174)
175+
176+
### Fixed
177+
- Fix `TransactionFactory.registerTransactionType` not working, if there is a version mistatch between `web3-eth` and `web3-eth-accounts` by saving `extraTxTypes` at `globals`. (#7197)

packages/web3-eth-accounts/src/tx/transactionFactory.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@ import type {
3131
} from './types.js';
3232
import { BaseTransaction } from './baseTransaction.js';
3333

34-
const extraTxTypes: Map<Numbers, typeof BaseTransaction<unknown>> = new Map();
34+
let extraTxTypes: Map<Numbers, typeof BaseTransaction<unknown>>;
35+
// use the global object, to work fine even if web3-eth and web3-eth-accounts was on a different versions:
36+
const typedGlobal = global as unknown as {extraTxTypes: Map<Numbers, typeof BaseTransaction<unknown>>}
37+
if (!typedGlobal.extraTxTypes) {
38+
extraTxTypes = new Map();
39+
typedGlobal.extraTxTypes = extraTxTypes;
40+
} else {
41+
extraTxTypes = typedGlobal.extraTxTypes;
42+
}
3543

3644
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
3745
export class TransactionFactory {

packages/web3-eth-contract/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,3 +395,11 @@ Documentation:
395395

396396
## [Unreleased]
397397

398+
### Added
399+
400+
- Added `populateTransaction` to the `contract.deploy(...)` properties. (#7197)
401+
402+
### Changed
403+
404+
- The returnred properties of `contract.deploy(...)` are structured with a newly created class named `DeployerMethodClass`. (#7197)
405+
- Add a missed accepted type for the `abi` parameter, at `dataInputEncodeMethodHelper` and `getSendTxParams`. (#7197)
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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 {
19+
Web3ContractError,
20+
} from 'web3-errors';
21+
import {
22+
sendTransaction,
23+
SendTransactionEvents,
24+
SendTransactionOptions,
25+
} from 'web3-eth';
26+
import {
27+
AbiConstructorFragment,
28+
AbiFunctionFragment,
29+
ContractAbi,
30+
ContractConstructorArgs,
31+
Bytes,
32+
HexString,
33+
PayableCallOptions,
34+
DataFormat,
35+
DEFAULT_RETURN_FORMAT,
36+
ContractOptions,
37+
TransactionReceipt,
38+
TransactionCall,
39+
} from 'web3-types';
40+
import {
41+
format,
42+
} from 'web3-utils';
43+
import {
44+
isNullish,
45+
} from 'web3-validator';
46+
import { Web3PromiEvent } from 'web3-core';
47+
import {
48+
decodeMethodParams,
49+
encodeMethodABI,
50+
} from './encoding.js';
51+
import {
52+
NonPayableTxOptions,
53+
PayableTxOptions,
54+
} from './types.js';
55+
import {
56+
getSendTxParams,
57+
} from './utils.js';
58+
// eslint-disable-next-line import/no-cycle
59+
import { Contract } from './contract.js';
60+
61+
export type ContractDeploySend<Abi extends ContractAbi> = Web3PromiEvent<
62+
// eslint-disable-next-line no-use-before-define
63+
Contract<Abi>,
64+
SendTransactionEvents<DataFormat>
65+
>;
66+
67+
/*
68+
* This class is only supposed to be used for the return of `new Contract(...).deploy(...)` method.
69+
*/
70+
export class DeployerMethodClass<FullContractAbi extends ContractAbi> {
71+
72+
protected readonly args: never[] | ContractConstructorArgs<FullContractAbi>;
73+
protected readonly constructorAbi: AbiConstructorFragment;
74+
protected readonly contractOptions: ContractOptions;
75+
protected readonly deployData?: string;
76+
77+
protected _contractMethodDeploySend(
78+
tx: TransactionCall,
79+
) {
80+
// eslint-disable-next-line no-use-before-define
81+
const returnTxOptions: SendTransactionOptions<Contract<FullContractAbi>> = {
82+
transactionResolver: (receipt: TransactionReceipt) => {
83+
if (receipt.status === BigInt(0)) {
84+
throw new Web3ContractError("code couldn't be stored", receipt);
85+
}
86+
87+
const newContract = this.parent.clone();
88+
89+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
90+
newContract.options.address = receipt.contractAddress;
91+
return newContract;
92+
},
93+
94+
contractAbi: this.parent.options.jsonInterface,
95+
// TODO Should make this configurable by the user
96+
checkRevertBeforeSending: false,
97+
};
98+
99+
return isNullish(this.parent.getTransactionMiddleware())
100+
? sendTransaction(this.parent, tx, this.parent.defaultReturnFormat, returnTxOptions) // not calling this with undefined Middleware because it will not break if Eth package is not updated
101+
: sendTransaction(
102+
this.parent,
103+
tx,
104+
this.parent.defaultReturnFormat,
105+
returnTxOptions,
106+
this.parent.getTransactionMiddleware(),
107+
);
108+
}
109+
110+
public constructor(
111+
// eslint-disable-next-line no-use-before-define
112+
public parent: Contract<FullContractAbi>,
113+
public deployOptions:
114+
| {
115+
/**
116+
* The byte code of the contract.
117+
*/
118+
data?: HexString;
119+
input?: HexString;
120+
/**
121+
* The arguments which get passed to the constructor on deployment.
122+
*/
123+
arguments?: ContractConstructorArgs<FullContractAbi>;
124+
}
125+
| undefined,
126+
) {
127+
128+
const { args, abi, contractOptions, deployData} = this.calculateDeployParams();
129+
130+
this.args = args;
131+
this.constructorAbi = abi;
132+
this.contractOptions = contractOptions;
133+
this.deployData = deployData;
134+
}
135+
136+
public send(options?: PayableTxOptions): ContractDeploySend<FullContractAbi> {
137+
const modifiedOptions = { ...options };
138+
139+
const tx = this.populateTransaction(modifiedOptions);
140+
141+
return this._contractMethodDeploySend(tx);
142+
}
143+
144+
public populateTransaction(
145+
txOptions?: PayableTxOptions | NonPayableTxOptions,
146+
) {
147+
const modifiedContractOptions = {
148+
...this.contractOptions,
149+
from: this.contractOptions.from ?? this.parent.defaultAccount ?? undefined,
150+
};
151+
152+
// args, abi, contractOptions, deployData
153+
154+
const tx = getSendTxParams({
155+
abi: this.constructorAbi,
156+
params: this.args as unknown[],
157+
options: { ...txOptions, dataInputFill: this.parent.contractDataInputFill },
158+
contractOptions: modifiedContractOptions,
159+
});
160+
161+
// @ts-expect-error remove unnecessary field
162+
if (tx.dataInputFill) {
163+
// @ts-expect-error remove unnecessary field
164+
delete tx.dataInputFill;
165+
}
166+
return tx;
167+
}
168+
169+
protected calculateDeployParams() {
170+
let abi = this.parent.options.jsonInterface.find(
171+
j => j.type === 'constructor',
172+
) as AbiConstructorFragment;
173+
if (!abi) {
174+
abi = {
175+
type: 'constructor',
176+
stateMutability: '',
177+
} as AbiConstructorFragment;
178+
}
179+
180+
const _input = format(
181+
{ format: 'bytes' },
182+
this.deployOptions?.input ?? this.parent.options.input,
183+
DEFAULT_RETURN_FORMAT,
184+
);
185+
186+
const _data = format(
187+
{ format: 'bytes' },
188+
this.deployOptions?.data ?? this.parent.options.data,
189+
DEFAULT_RETURN_FORMAT,
190+
);
191+
192+
if ((!_input || _input.trim() === '0x') && (!_data || _data.trim() === '0x')) {
193+
throw new Web3ContractError('contract creation without any data provided.');
194+
}
195+
196+
const args = this.deployOptions?.arguments ?? [];
197+
198+
const contractOptions: ContractOptions = {
199+
...this.parent.options,
200+
input: _input,
201+
data: _data,
202+
};
203+
const deployData = _input ?? _data;
204+
205+
return { args, abi, contractOptions, deployData}
206+
}
207+
208+
public async estimateGas<ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT>(
209+
options?: PayableCallOptions,
210+
returnFormat: ReturnFormat = this.parent.defaultReturnFormat as ReturnFormat,
211+
) {
212+
const modifiedOptions = { ...options };
213+
return this.parent.contractMethodEstimateGas({
214+
abi: this.constructorAbi as AbiFunctionFragment,
215+
params: this.args as unknown[],
216+
returnFormat,
217+
options: modifiedOptions,
218+
contractOptions: this.contractOptions,
219+
});
220+
}
221+
222+
public encodeABI() {
223+
return encodeMethodABI(
224+
this.constructorAbi,
225+
this.args as unknown[],
226+
format(
227+
{ format: 'bytes' },
228+
this.deployData as Bytes,
229+
this.parent.defaultReturnFormat as typeof DEFAULT_RETURN_FORMAT,
230+
),
231+
);
232+
}
233+
234+
public decodeData(data: HexString) {
235+
return {
236+
...decodeMethodParams(this.constructorAbi, data.replace(this.deployData as string, ''), false),
237+
__method__: this.constructorAbi.type,
238+
};
239+
}
240+
};

0 commit comments

Comments
 (0)