Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding autofill gas option sendtransaction #6265

Merged
merged 23 commits into from
Jul 24, 2023
Merged
2 changes: 2 additions & 0 deletions packages/web3-errors/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ Documentation:
### Added

- `RpcErrorMessages` that contains mapping for standard RPC Errors and their messages. (#6230)
- created `TransactionGasMismatchInnerError` for clarity on the error in `TransactionGasMismatchError` (#6215)
- created `MissingGasInnerError` for clarity on the error in `MissingGasError` (#6215)

### Fixed

Expand Down
2 changes: 2 additions & 0 deletions packages/web3-errors/src/error_codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export const ERR_TX_INVALID_RECEIVER = 437;
export const ERR_TX_REVERT_TRANSACTION_CUSTOM_ERROR = 438;
export const ERR_TX_INVALID_PROPERTIES_FOR_TYPE = 439;

export const ERR_TX_MISSING_GAS_INNER_ERROR = 440;
export const ERR_TX_GAS_MISMATCH_INNER_ERROR = 441;
// Connection error codes
export const ERR_CONN = 500;
export const ERR_CONN_INVALID = 501;
Expand Down
24 changes: 24 additions & 0 deletions packages/web3-errors/src/errors/transaction_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import {
ERR_TX_UNSUPPORTED_TYPE,
ERR_TX_REVERT_TRANSACTION_CUSTOM_ERROR,
ERR_TX_INVALID_PROPERTIES_FOR_TYPE,
ERR_TX_MISSING_GAS_INNER_ERROR,
ERR_TX_GAS_MISMATCH_INNER_ERROR,
} from '../error_codes.js';
import { InvalidValueError, BaseWeb3Error } from '../web3_error_base.js';

Expand Down Expand Up @@ -312,6 +314,16 @@ export class MissingChainOrHardforkError extends InvalidValueError {
}
}

export class MissingGasInnerError extends BaseWeb3Error {
public code = ERR_TX_MISSING_GAS_INNER_ERROR;

public constructor() {
super(
'Missing properties in transaction, either define "gas" and "gasPrice" for type 0 transactions or "gas", "maxPriorityFeePerGas" and "maxFeePerGas" for type 2 transactions',
);
}
}

export class MissingGasError extends InvalidValueError {
public code = ERR_TX_MISSING_GAS;

Expand All @@ -329,6 +341,17 @@ export class MissingGasError extends InvalidValueError {
}`,
'"gas" is missing',
);
this.innerError = new MissingGasInnerError();
}
}

export class TransactionGasMismatchInnerError extends BaseWeb3Error {
public code = ERR_TX_GAS_MISMATCH_INNER_ERROR;

public constructor() {
super(
'Missing properties in transaction, either define "gas" and "gasPrice" for type 0 transactions or "gas", "maxPriorityFeePerGas" and "maxFeePerGas" for type 2 transactions, not both',
);
}
}

Expand All @@ -349,6 +372,7 @@ export class TransactionGasMismatchError extends InvalidValueError {
}`,
'transaction must specify legacy or fee market gas properties, not both',
);
this.innerError = new TransactionGasMismatchInnerError();
}
}

Expand Down
13 changes: 9 additions & 4 deletions packages/web3-eth/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,16 @@ Documentation:

## [Unreleased]

### Fixed

- sendTransaction will have gas filled by default using method `estimateGas` unless transaction builder `options.fillGas` is false. (#6249)
- Missing `blockHeaderSchema` properties causing some properties to not appear in response of `newHeads` subscription (#6243)

### Changed

- `MissingGasError` error message changed for clarity (#6215)
-
### Added

- A `rpc_method_wrapper` (`signTypedData`) for the rpc calls `eth_signTypedData` and `eth_signTypedData_v4` (#6286)
- A `signTypedData` method to the `Web3Eth` class (#6286)

### Fixed

- Missing `blockHeaderSchema` properties causing some properties to not appear in response of `newHeads` subscription (#6243)
1 change: 1 addition & 0 deletions packages/web3-eth/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ export interface SendTransactionOptions<ResolveType = TransactionReceipt> {
transactionResolver?: (receipt: TransactionReceipt) => ResolveType;
contractAbi?: ContractAbi;
checkRevertBeforeSending?: boolean;
ignoreFillingGasLimit?: boolean;
}

export interface SendSignedTransactionOptions<ResolveType = TransactionReceipt> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,19 @@ export const prepareTransactionForSigning = async (
web3Context: Web3Context<EthExecutionAPI>,
privateKey?: HexString | Uint8Array,
fillGasPrice = false,
fillGasLimit = true,
) => {
const populatedTransaction = (await transactionBuilder({
transaction,
web3Context,
privateKey,
fillGasPrice,
fillGasLimit,
})) as unknown as PopulatedUnsignedTransaction;

const formattedTransaction = formatTransaction(
populatedTransaction,
ETH_DATA_FORMAT,
) as unknown as FormatType<PopulatedUnsignedTransaction, typeof ETH_DATA_FORMAT>;

validateTransactionForSigning(
formattedTransaction as unknown as FormatType<Transaction, typeof ETH_DATA_FORMAT>,
);
Expand Down
24 changes: 19 additions & 5 deletions packages/web3-eth/src/utils/transaction_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import {
import { bytesToHex, format } from 'web3-utils';
import { NUMBER_DATA_FORMAT } from '../constants.js';
// eslint-disable-next-line import/no-cycle
import { getChainId, getTransactionCount } from '../rpc_method_wrappers.js';
import { getChainId, getTransactionCount, estimateGas } from '../rpc_method_wrappers.js';
import { detectTransactionType } from './detect_transaction_type.js';
import { transactionSchema } from '../schemas.js';
import { InternalTransaction } from '../types.js';
Expand Down Expand Up @@ -129,8 +129,8 @@ export async function defaultTransactionBuilder<ReturnType = Transaction>(option
web3Context: Web3Context<EthExecutionAPI & Web3NetAPI>;
privateKey?: HexString | Uint8Array;
fillGasPrice?: boolean;
fillGasLimit?: boolean;
}): Promise<ReturnType> {
// let populatedTransaction = { ...options.transaction } as unknown as InternalTransaction;
let populatedTransaction = format(
transactionSchema,
options.transaction,
Expand Down Expand Up @@ -221,14 +221,12 @@ export async function defaultTransactionBuilder<ReturnType = Transaction>(option
}

populatedTransaction.type = getTransactionType(populatedTransaction, options.web3Context);

if (
isNullish(populatedTransaction.accessList) &&
(populatedTransaction.type === '0x1' || populatedTransaction.type === '0x2')
) {
populatedTransaction.accessList = [];
}

if (options.fillGasPrice)
populatedTransaction = {
...populatedTransaction,
Expand All @@ -238,7 +236,22 @@ export async function defaultTransactionBuilder<ReturnType = Transaction>(option
ETH_DATA_FORMAT,
)),
};

if (
isNullish(populatedTransaction.gas) &&
isNullish(populatedTransaction.gasLimit) &&
options.fillGasLimit
) {
const fillGasLimit = await estimateGas(
options.web3Context,
populatedTransaction,
'latest',
ETH_DATA_FORMAT,
);
populatedTransaction = {
...populatedTransaction,
gas: format({ format: 'uint' }, fillGasLimit as Numbers, ETH_DATA_FORMAT),
};
}
return populatedTransaction as ReturnType;
}

Expand All @@ -248,6 +261,7 @@ export const transactionBuilder = async <ReturnType = Transaction>(
web3Context: Web3Context<EthExecutionAPI>;
privateKey?: HexString | Uint8Array;
fillGasPrice?: boolean;
fillGasLimit?: boolean;
},
// eslint-disable-next-line @typescript-eslint/require-await
) =>
Expand Down
6 changes: 3 additions & 3 deletions packages/web3-eth/test/integration/eth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ describe('eth', () => {

sendOptions = { from: tempAcc.address, gas: '1000000' };

const deoloyedContract = await contract.deploy(deployOptions).send(sendOptions);
const deployedContract = await contract.deploy(deployOptions).send(sendOptions);
const { provider } = web3Eth;
web3Eth.setProvider(deoloyedContract.provider as SupportedProviders);
web3Eth.setProvider(deployedContract.provider as SupportedProviders);

expect(web3Eth.provider).toBe(deoloyedContract.provider);
expect(web3Eth.provider).toBe(deployedContract.provider);
web3Eth.setProvider(provider as SupportedProviders);
});
it('providers', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
TransactionWithToLocalWalletIndex,
TransactionWithFromAndToLocalWalletIndex,
Address,
DEFAULT_RETURN_FORMAT,
} from 'web3-types';
import { Wallet } from 'web3-eth-accounts';
import { isHexStrict } from 'web3-validator';
Expand Down Expand Up @@ -170,6 +171,19 @@ describe('Web3Eth.sendTransaction', () => {
const minedTransactionData = await web3Eth.getTransaction(response.transactionHash);
expect(minedTransactionData).toMatchObject(transaction);
});
it('should send a transaction with data', async () => {
const transaction: Transaction = {
from: tempAcc.address,
to: '0x0000000000000000000000000000000000000000',
data: '0x64edfbf0e2c706ba4a09595315c45355a341a576cc17f3a19f43ac1c02f814ee',
value: BigInt(0),
};
const response = await web3Eth.sendTransaction(transaction);
expect(response.status).toBe(BigInt(1));

const minedTransactionData = await web3Eth.getTransaction(response.transactionHash);
expect(minedTransactionData).toMatchObject(transaction);
});

describe('Deploy and interact with contract', () => {
let greeterContractAddress: string;
Expand Down Expand Up @@ -267,6 +281,47 @@ describe('Web3Eth.sendTransaction', () => {
const minedTransactionData = await web3Eth.getTransaction(response.transactionHash);
expect(minedTransactionData).toMatchObject(transaction);
});

it('should send a successful type 0x0 transaction with data', async () => {
const transaction: Transaction = {
from: tempAcc.address,
to: '0x0000000000000000000000000000000000000000',
data: '0x64edfbf0e2c706ba4a09595315c45355a341a576cc17f3a19f43ac1c02f814ee',
value: BigInt(1),
};
const response = await web3Eth.sendTransaction(transaction, DEFAULT_RETURN_FORMAT);
expect(response.type).toBe(BigInt(0));
expect(response.status).toBe(BigInt(1));
const minedTransactionData = await web3Eth.getTransaction(response.transactionHash);
expect(minedTransactionData).toMatchObject(transaction);
});
});
it('should autofill a successful type 0x2 transaction with only maxFeePerGas passed', async () => {
const transaction: Transaction = {
from: tempAcc.address,
to: '0x0000000000000000000000000000000000000000',
value: BigInt(1),
maxFeePerGas: BigInt(2500000016),
};
const response = await web3Eth.sendTransaction(transaction);
expect(response.type).toBe(BigInt(2));
expect(response.status).toBe(BigInt(1));
const minedTransactionData = await web3Eth.getTransaction(response.transactionHash);
expect(minedTransactionData).toMatchObject(transaction);
});

it('should autofill a successful type 0x2 transaction with only maxPriorityFeePerGas passed', async () => {
const transaction: Transaction = {
from: tempAcc.address,
to: '0x0000000000000000000000000000000000000000',
value: BigInt(1),
maxPriorityFeePerGas: BigInt(100),
};
const response = await web3Eth.sendTransaction(transaction);
expect(response.type).toBe(BigInt(2));
expect(response.status).toBe(BigInt(1));
const minedTransactionData = await web3Eth.getTransaction(response.transactionHash);
expect(minedTransactionData).toMatchObject(transaction);
});

describe('Transaction PromiEvents', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ describe('prepareTransactionForSigning', () => {
expectedR,
expectedS,
) => {
// @ts-expect-error - Mocked implementation doesn't have correct method signature
// (i.e. requestManager, blockNumber, hydrated params), but that doesn't matter for the test
jest.spyOn(ethRpcMethods, 'estimateGas').mockImplementation(
// @ts-expect-error - Mocked implementation doesn't have correct method signature
() => expectedTransaction.gas,
);
// @ts-expect-error - Mocked implementation doesn't have correct method signature
jest.spyOn(ethRpcMethods, 'getBlockByNumber').mockImplementation(() => mockBlock);

const ethereumjsTx = await prepareTransactionForSigning(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ describe('ContractMethodWrappersPlugin', () => {
recipient,
amount,
);

// The first call will be to `eth_gasPrice` and the second is to `eth_blockNumber`. And the third one will be to `eth_sendTransaction`:
expect(requestManagerSendSpy).toHaveBeenNthCalledWith(3, {
method: 'eth_sendTransaction',
Expand Down