Skip to content

Commit

Permalink
adding autofill gas option sendtransaction (#6265)
Browse files Browse the repository at this point in the history
* adding autofill gas option sendtransaction

* update wrappers test

* update transaction tests

* update send transaction test

* update test cases

* update testcase

* update sign transaction

* update tests

* update tests

* update tests

* update config

* update testdcase

* gas is now filled by default

* fix tests

* fix eslint error

* update errors

* fix formatting

* update changelog

* addres feedback and fix broken test cases

* update error
  • Loading branch information
Alex committed Jul 24, 2023
1 parent 6da17e1 commit ed2770c
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 16 deletions.
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

0 comments on commit ed2770c

Please sign in to comment.