web3-eth
Version:
Web3 module to interact with the Ethereum blockchain and smart contracts.
260 lines (257 loc) • 10.8 kB
JavaScript
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
import { ETH_DATA_FORMAT, } from 'web3-types';
import { isAddress, isHexStrict, isHexString32Bytes, isNullish, isUInt } from 'web3-validator';
import { ChainMismatchError, HardforkMismatchError, ChainIdMismatchError, CommonOrChainAndHardforkError, Eip1559GasPriceError, InvalidGasOrGasPrice, InvalidMaxPriorityFeePerGasOrMaxFeePerGas, InvalidNonceOrChainIdError, InvalidTransactionCall, InvalidTransactionObjectError, InvalidTransactionWithSender, MissingChainOrHardforkError, MissingCustomChainError, MissingCustomChainIdError, MissingGasError, TransactionGasMismatchError, UnsupportedFeeMarketError, } from 'web3-errors';
import { formatTransaction } from './utils/format_transaction.js';
export function isBaseTransaction(value) {
if (!isNullish(value.to) && !isAddress(value.to))
return false;
if (!isHexStrict(value.type) && !isNullish(value.type) && value.type.length !== 2)
return false;
if (!isHexStrict(value.nonce))
return false;
if (!isHexStrict(value.gas))
return false;
if (!isHexStrict(value.value))
return false;
if (!isHexStrict(value.input))
return false;
if (value.chainId && !isHexStrict(value.chainId))
return false;
return true;
}
export function isAccessListEntry(value) {
if (!isNullish(value.address) && !isAddress(value.address))
return false;
if (!isNullish(value.storageKeys) &&
!value.storageKeys.every(storageKey => isHexString32Bytes(storageKey)))
return false;
return true;
}
export function isAccessList(value) {
if (!Array.isArray(value) ||
!value.every(accessListEntry => isAccessListEntry(accessListEntry)))
return false;
return true;
}
export function isTransaction1559Unsigned(value) {
if (!isBaseTransaction(value))
return false;
if (!isHexStrict(value.maxFeePerGas))
return false;
if (!isHexStrict(value.maxPriorityFeePerGas))
return false;
if (!isAccessList(value.accessList))
return false;
return true;
}
export function isTransaction2930Unsigned(value) {
if (!isBaseTransaction(value))
return false;
if (!isHexStrict(value.gasPrice))
return false;
if (!isAccessList(value.accessList))
return false;
return true;
}
export function isTransactionLegacyUnsigned(value) {
if (!isBaseTransaction(value))
return false;
if (!isHexStrict(value.gasPrice))
return false;
return true;
}
export function isTransactionWithSender(value) {
if (!isAddress(value.from))
return false;
if (!isBaseTransaction(value))
return false;
if (!isTransaction1559Unsigned(value) &&
!isTransaction2930Unsigned(value) &&
!isTransactionLegacyUnsigned(value))
return false;
return true;
}
export function validateTransactionWithSender(value) {
if (!isTransactionWithSender(value))
throw new InvalidTransactionWithSender(value);
}
export function isTransactionCall(value) {
if (!isNullish(value.from) && !isAddress(value.from))
return false;
if (!isAddress(value.to))
return false;
if (!isNullish(value.gas) && !isHexStrict(value.gas))
return false;
if (!isNullish(value.gasPrice) && !isHexStrict(value.gasPrice))
return false;
if (!isNullish(value.value) && !isHexStrict(value.value))
return false;
if (!isNullish(value.data) && !isHexStrict(value.data))
return false;
if (!isNullish(value.input) && !isHexStrict(value.input))
return false;
if (!isNullish(value.type))
return false;
if (isTransaction1559Unsigned(value))
return false;
if (isTransaction2930Unsigned(value))
return false;
return true;
}
export function validateTransactionCall(value) {
if (!isTransactionCall(value))
throw new InvalidTransactionCall(value);
}
export const validateCustomChainInfo = (transaction) => {
if (!isNullish(transaction.common)) {
if (isNullish(transaction.common.customChain))
throw new MissingCustomChainError();
if (isNullish(transaction.common.customChain.chainId))
throw new MissingCustomChainIdError();
if (!isNullish(transaction.chainId) &&
transaction.chainId !== transaction.common.customChain.chainId)
throw new ChainIdMismatchError({
txChainId: transaction.chainId,
customChainId: transaction.common.customChain.chainId,
});
}
};
export const validateChainInfo = (transaction) => {
if (!isNullish(transaction.common) &&
!isNullish(transaction.chain) &&
!isNullish(transaction.hardfork)) {
throw new CommonOrChainAndHardforkError();
}
if ((!isNullish(transaction.chain) && isNullish(transaction.hardfork)) ||
(!isNullish(transaction.hardfork) && isNullish(transaction.chain)))
throw new MissingChainOrHardforkError({
chain: transaction.chain,
hardfork: transaction.hardfork,
});
};
export const validateBaseChain = (transaction) => {
if (!isNullish(transaction.common))
if (!isNullish(transaction.common.baseChain))
if (!isNullish(transaction.chain) &&
transaction.chain !== transaction.common.baseChain) {
throw new ChainMismatchError({
txChain: transaction.chain,
baseChain: transaction.common.baseChain,
});
}
};
export const validateHardfork = (transaction) => {
if (!isNullish(transaction.common))
if (!isNullish(transaction.common.hardfork))
if (!isNullish(transaction.hardfork) &&
transaction.hardfork !== transaction.common.hardfork) {
throw new HardforkMismatchError({
txHardfork: transaction.hardfork,
commonHardfork: transaction.common.hardfork,
});
}
};
export const validateLegacyGas = (transaction) => {
if (
// This check is verifying gas and gasPrice aren't less than 0.
isNullish(transaction.gas) ||
!isUInt(transaction.gas) ||
isNullish(transaction.gasPrice) ||
!isUInt(transaction.gasPrice))
throw new InvalidGasOrGasPrice({
gas: transaction.gas,
gasPrice: transaction.gasPrice,
});
if (!isNullish(transaction.maxFeePerGas) || !isNullish(transaction.maxPriorityFeePerGas))
throw new UnsupportedFeeMarketError({
maxFeePerGas: transaction.maxFeePerGas,
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
});
};
export const validateFeeMarketGas = (transaction) => {
// These errors come from 1.x, so they must be checked before
// InvalidMaxPriorityFeePerGasOrMaxFeePerGas to throw the same error
// for the same code executing in 1.x
if (!isNullish(transaction.gasPrice) && transaction.type === '0x2')
throw new Eip1559GasPriceError(transaction.gasPrice);
if (transaction.type === '0x0' || transaction.type === '0x1')
throw new UnsupportedFeeMarketError({
maxFeePerGas: transaction.maxFeePerGas,
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
});
if (isNullish(transaction.maxFeePerGas) ||
!isUInt(transaction.maxFeePerGas) ||
isNullish(transaction.maxPriorityFeePerGas) ||
!isUInt(transaction.maxPriorityFeePerGas))
throw new InvalidMaxPriorityFeePerGasOrMaxFeePerGas({
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
maxFeePerGas: transaction.maxFeePerGas,
});
};
/**
* This method checks if all required gas properties are present for either
* legacy gas (type 0x0 and 0x1) OR fee market transactions (0x2)
*/
export const validateGas = (transaction) => {
const gasPresent = !isNullish(transaction.gas) || !isNullish(transaction.gasLimit);
const legacyGasPresent = gasPresent && !isNullish(transaction.gasPrice);
const feeMarketGasPresent = gasPresent &&
!isNullish(transaction.maxPriorityFeePerGas) &&
!isNullish(transaction.maxFeePerGas);
if (!legacyGasPresent && !feeMarketGasPresent)
throw new MissingGasError({
gas: transaction.gas,
gasPrice: transaction.gasPrice,
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
maxFeePerGas: transaction.maxFeePerGas,
});
if (legacyGasPresent && feeMarketGasPresent)
throw new TransactionGasMismatchError({
gas: transaction.gas,
gasPrice: transaction.gasPrice,
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas,
maxFeePerGas: transaction.maxFeePerGas,
});
(legacyGasPresent ? validateLegacyGas : validateFeeMarketGas)(transaction);
(!isNullish(transaction.type) && transaction.type > '0x1'
? validateFeeMarketGas
: validateLegacyGas)(transaction);
};
export const validateTransactionForSigning = (transaction, overrideMethod, options = { transactionSchema: undefined }) => {
if (!isNullish(overrideMethod)) {
overrideMethod(transaction);
return;
}
if (typeof transaction !== 'object' || isNullish(transaction))
throw new InvalidTransactionObjectError(transaction);
validateCustomChainInfo(transaction);
validateChainInfo(transaction);
validateBaseChain(transaction);
validateHardfork(transaction);
const formattedTransaction = formatTransaction(transaction, ETH_DATA_FORMAT, {
transactionSchema: options.transactionSchema,
});
validateGas(formattedTransaction);
if (isNullish(formattedTransaction.nonce) ||
isNullish(formattedTransaction.chainId) ||
formattedTransaction.nonce.startsWith('-') ||
formattedTransaction.chainId.startsWith('-'))
throw new InvalidNonceOrChainIdError({
nonce: transaction.nonce,
chainId: transaction.chainId,
});
};
//# sourceMappingURL=validation.js.map