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

Nikos/5711/transaction with local wallet index should support to as a wallet index #5731

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -945,3 +945,19 @@ should use 4.0.1-alpha.0 for testing.
- These types were moved from `web3-eth-accounts` to `web3-types` package: Cipher, CipherOptions, ScryptParams, PBKDF2SHA256Params, KeyStore (#5581 )

## [Unreleased]

### Fixed

#### web3-eth

- Enable transaction with local wallet index in the `to` field (#5731)
nikoulai marked this conversation as resolved.
Show resolved Hide resolved

#### web3-eth

- Add error for invalid transaction receiver (#5731)

### Added

#### web3-types

- Add `TransactionWithFromLocalWalletIndex`, `TransactionWithToLocalWalletIndex` and `TransactionWithFromAndToLocalWalletIndex` types (#5731)
1 change: 1 addition & 0 deletions packages/web3-errors/src/error_codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export const ERR_TX_GAS_MISMATCH = 434;

export const ERR_TX_CHAIN_MISMATCH = 435;
export const ERR_TX_HARDFORK_MISMATCH = 436;
export const ERR_TX_INVALID_RECEIVER = 437;

// Connection error codes
export const ERR_CONN = 500;
Expand Down
7 changes: 7 additions & 0 deletions packages/web3-errors/src/errors/transaction_errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
ERR_TX_INVALID_NONCE_OR_CHAIN_ID,
ERR_TX_INVALID_OBJECT,
ERR_TX_INVALID_SENDER,
ERR_TX_INVALID_RECEIVER,
ERR_TX_LOCAL_WALLET_NOT_AVAILABLE,
ERR_TX_MISSING_CHAIN_INFO,
ERR_TX_MISSING_CUSTOM_CHAIN,
Expand Down Expand Up @@ -168,7 +169,13 @@ export class InvalidTransactionWithSender extends InvalidValueError {
super(value, 'invalid transaction with sender');
}
}
export class InvalidTransactionWithReceiver extends InvalidValueError {
public code = ERR_TX_INVALID_RECEIVER;

public constructor(value: unknown) {
super(value, 'invalid transaction with receiver');
}
}
export class InvalidTransactionCall extends InvalidValueError {
public code = ERR_TX_INVALID_CALL;

Expand Down
4 changes: 4 additions & 0 deletions packages/web3-eth/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated Web3.js dependencies (#5664)

## [Unreleased]

### Fixed

- Enable transaction with local wallet index in the `to` field (#5731)
17 changes: 12 additions & 5 deletions packages/web3-eth/src/rpc_method_wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ import {
TransactionReceipt,
Transaction,
TransactionCall,
TransactionWithLocalWalletIndex,
Web3EthExecutionAPI,
TransactionWithFromLocalWalletIndex,
TransactionWithToLocalWalletIndex,
TransactionWithFromAndToLocalWalletIndex,
} from 'web3-types';
import { Web3Context, Web3PromiEvent } from 'web3-core';
import { ETH_DATA_FORMAT, FormatType, DataFormat, DEFAULT_RETURN_FORMAT, format } from 'web3-utils';
Expand All @@ -63,7 +65,7 @@ import {
SendTransactionOptions,
} from './types';
// eslint-disable-next-line import/no-cycle
import { getTransactionFromAttr } from './utils/transaction_builder';
import { getTransactionFromOrToAttr } from './utils/transaction_builder';
import { formatTransaction } from './utils/format_transaction';
// eslint-disable-next-line import/no-cycle
import { getTransactionGasPricing } from './utils/get_transaction_gas_pricing';
Expand Down Expand Up @@ -940,7 +942,7 @@ export async function getTransactionCount<ReturnFormat extends DataFormat>(

/**
* @param web3Context ({@link Web3Context}) Web3 configuration object that contains things such as the provider, request manager, wallet, etc.
* @param transaction The {@link Transaction} or {@link TransactionWithLocalWalletIndex} to send.
* @param transaction The {@link Transaction}, {@link TransactionWithFromLocalWalletIndex}, {@link TransactionWithToLocalWalletIndex}, or {@link TransactionWithFromAndToLocalWalletIndex} to send.
* @param returnFormat ({@link DataFormat} defaults to {@link DEFAULT_RETURN_FORMAT}) Specifies how the return data should be formatted.
* @param options A configuration object used to change the behavior of the `sendTransaction` method.
* @returns If `await`ed or `.then`d (i.e. the promise resolves), the transaction hash is returned.
Expand Down Expand Up @@ -1050,7 +1052,11 @@ export function sendTransaction<
ResolveType = FormatType<TransactionReceipt, ReturnFormat>,
>(
web3Context: Web3Context<EthExecutionAPI>,
transaction: Transaction | TransactionWithLocalWalletIndex,
transaction:
| Transaction
| TransactionWithFromLocalWalletIndex
| TransactionWithToLocalWalletIndex
| TransactionWithFromAndToLocalWalletIndex,
returnFormat: ReturnFormat,
options?: SendTransactionOptions<ResolveType>,
): Web3PromiEvent<ResolveType, SendTransactionEvents<ReturnFormat>> {
Expand All @@ -1062,7 +1068,8 @@ export function sendTransaction<
let transactionFormatted = formatTransaction(
{
...transaction,
from: getTransactionFromAttr(web3Context, transaction),
from: getTransactionFromOrToAttr('from', web3Context, transaction),
to: getTransactionFromOrToAttr('to', web3Context, transaction),
},
ETH_DATA_FORMAT,
);
Expand Down
41 changes: 28 additions & 13 deletions packages/web3-eth/src/utils/transaction_builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,20 @@ import {
ValidChains,
Hardfork,
Transaction,
TransactionWithLocalWalletIndex,
TransactionWithFromLocalWalletIndex,
TransactionWithToLocalWalletIndex,
TransactionWithFromAndToLocalWalletIndex,
Common,
Web3NetAPI,
Numbers,
} from 'web3-types';
import { Web3Context } from 'web3-core';
import { privateKeyToAddress } from 'web3-eth-accounts';
import { getId } from 'web3-net';
import { isNullish, isNumber } from 'web3-validator';
import {
InvalidTransactionWithSender,
InvalidTransactionWithReceiver,
LocalWalletNotAvailableError,
TransactionDataAndInputError,
UnableToPopulateNonceError,
Expand All @@ -53,19 +57,24 @@ import { getTransactionGasPricing } from './get_transaction_gas_pricing';
import { transactionSchema } from '../schemas';
import { InternalTransaction } from '../types';

export const getTransactionFromAttr = (
export const getTransactionFromOrToAttr = (
attr: 'from' | 'to',
web3Context: Web3Context<EthExecutionAPI>,
transaction?: Transaction | TransactionWithLocalWalletIndex,
transaction?:
| Transaction
| TransactionWithFromLocalWalletIndex
| TransactionWithToLocalWalletIndex
| TransactionWithFromAndToLocalWalletIndex,
privateKey?: HexString | Buffer,
) => {
if (transaction?.from !== undefined) {
if (typeof transaction.from === 'string' && isAddress(transaction.from)) {
return transaction.from;
): Address | undefined => {
if (transaction !== undefined && attr in transaction && transaction[attr] !== undefined) {
if (typeof transaction[attr] === 'string' && isAddress(transaction[attr] as string)) {
return transaction[attr] as Address;
}
if (isNumber(transaction.from)) {
if (isNumber(transaction[attr] as Numbers)) {
if (web3Context.wallet) {
const account = web3Context.wallet.get(
format({ eth: 'uint' }, transaction.from, NUMBER_DATA_FORMAT),
format({ eth: 'uint' }, transaction[attr] as Numbers, NUMBER_DATA_FORMAT),
);

if (!isNullish(account)) {
Expand All @@ -76,11 +85,16 @@ export const getTransactionFromAttr = (
}
throw new LocalWalletNotAvailableError();
} else {
throw new InvalidTransactionWithSender(transaction.from);
throw attr === 'from'
? new InvalidTransactionWithSender(transaction.from)
: // eslint-disable-next-line @typescript-eslint/no-unsafe-call
new InvalidTransactionWithReceiver(transaction.to);
}
}
if (!isNullish(privateKey)) return privateKeyToAddress(privateKey);
if (!isNullish(web3Context.defaultAccount)) return web3Context.defaultAccount;
if (attr === 'from') {
if (!isNullish(privateKey)) return privateKeyToAddress(privateKey);
if (!isNullish(web3Context.defaultAccount)) return web3Context.defaultAccount;
}

return undefined;
};
Expand Down Expand Up @@ -126,7 +140,8 @@ export async function defaultTransactionBuilder<ReturnType = Record<string, unkn
) as InternalTransaction;

if (isNullish(populatedTransaction.from)) {
populatedTransaction.from = getTransactionFromAttr(
populatedTransaction.from = getTransactionFromOrToAttr(
'from',
options.web3Context,
undefined,
options.privateKey,
Expand Down
12 changes: 9 additions & 3 deletions packages/web3-eth/src/web3_eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,10 @@ import {
LogsOutput,
Transaction,
TransactionCall,
TransactionWithLocalWalletIndex,
Web3EthExecutionAPI,
TransactionWithFromLocalWalletIndex,
TransactionWithToLocalWalletIndex,
TransactionWithFromAndToLocalWalletIndex,
} from 'web3-types';
import { isSupportedProvider, Web3Context, Web3ContextInitOptions } from 'web3-core';
import { TransactionNotFound } from 'web3-errors';
Expand Down Expand Up @@ -822,7 +824,7 @@ export class Web3Eth extends Web3Context<Web3EthExecutionAPI, RegisteredSubscrip
}

/**
* @param transaction The {@link Transaction} or {@link TransactionWithLocalWalletIndex} to send.
* @param transaction The {@link Transaction}, {@link TransactionWithFromLocalWalletIndex}, {@link TransactionWithToLocalWalletIndex} or {@link TransactionWithFromAndToLocalWalletIndex} to send.
* @param returnFormat ({@link DataFormat} defaults to {@link DEFAULT_RETURN_FORMAT}) Specifies how the return data should be formatted.
* @param options A configuration object used to change the behavior of the `sendTransaction` method.
* @returns If `await`ed or `.then`d (i.e. the promise resolves), the transaction hash is returned.
Expand Down Expand Up @@ -928,7 +930,11 @@ export class Web3Eth extends Web3Context<Web3EthExecutionAPI, RegisteredSubscrip
* ```
*/
public sendTransaction<ReturnFormat extends DataFormat = typeof DEFAULT_RETURN_FORMAT>(
transaction: Transaction | TransactionWithLocalWalletIndex,
transaction:
| Transaction
| TransactionWithFromLocalWalletIndex
| TransactionWithToLocalWalletIndex
| TransactionWithFromAndToLocalWalletIndex,
returnFormat: ReturnFormat = DEFAULT_RETURN_FORMAT as ReturnFormat,
options?: SendTransactionOptions,
) {
Expand Down
4 changes: 2 additions & 2 deletions packages/web3-eth/test/integration/defaults.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {

import {
defaultTransactionBuilder,
getTransactionFromAttr,
getTransactionFromOrToAttr,
getTransactionType,
} from '../../src/utils';
import { BasicAbi, BasicBytecode } from '../shared_fixtures/build/Basic';
Expand Down Expand Up @@ -113,7 +113,7 @@ describe('defaults', () => {
expect(eth2.defaultAccount).toBe(tempAcc3.address);

// check utils
expect(getTransactionFromAttr(eth2)).toBe(tempAcc3.address);
expect(getTransactionFromOrToAttr('from', eth2)).toBe(tempAcc3.address);
// TODO: after handleRevert implementation https://github.com/ChainSafe/web3.js/issues/5069 add following tests in future release
// set handleRevert true and test following functions with invalid input tx data and see revert reason present in error details:
contractMsgFrom.setConfig({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ 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 { Transaction, TransactionWithLocalWalletIndex } from 'web3-types';
import {
Transaction,
TransactionWithFromLocalWalletIndex,
TransactionWithToLocalWalletIndex,
TransactionWithFromAndToLocalWalletIndex,
} from 'web3-types';
import { Wallet } from 'web3-eth-accounts';
import { isHexStrict } from 'web3-validator';

Expand Down Expand Up @@ -63,7 +68,7 @@ describe('Web3Eth.sendTransaction', () => {

web3EthWithWallet.wallet?.add(tempAcc.privateKey);

const transaction: TransactionWithLocalWalletIndex = {
const transaction: TransactionWithFromLocalWalletIndex = {
from: 0,
to: '0x0000000000000000000000000000000000000000',
gas: 21000,
Expand All @@ -83,6 +88,69 @@ describe('Web3Eth.sendTransaction', () => {
});
});

it('should make a simple value transfer - with local wallet indexed receiver', async () => {
const web3EthWithWallet = new Web3Eth(getSystemTestProvider());
const accountProvider = createAccountProvider(web3Eth);
const wallet = new Wallet(accountProvider);

web3EthWithWallet['_accountProvider'] = accountProvider;
web3EthWithWallet['_wallet'] = wallet;

web3EthWithWallet.wallet?.add(tempAcc.privateKey);

const transaction: TransactionWithToLocalWalletIndex = {
from: tempAcc.address,
to: 0,
gas: 21000,
value: BigInt(1),
};
const response = await web3EthWithWallet.sendTransaction(transaction);
expect(response.status).toBe(BigInt(1));

const minedTransactionData = await web3EthWithWallet.getTransaction(
response.transactionHash,
);

expect(minedTransactionData).toMatchObject({
from: tempAcc.address,
to: wallet.get(0)?.address.toLowerCase(),
value: BigInt(1),
});
});

it('should make a simple value transfer - with local wallet indexed sender and receiver', async () => {
const web3EthWithWallet = new Web3Eth(getSystemTestProvider());
const accountProvider = createAccountProvider(web3Eth);
const wallet = new Wallet(accountProvider);

web3EthWithWallet['_accountProvider'] = accountProvider;
web3EthWithWallet['_wallet'] = wallet;

const tempAcc2 = await createTempAccount();

web3EthWithWallet.wallet?.add(tempAcc.privateKey);

web3EthWithWallet.wallet?.add(tempAcc2.privateKey);

const transaction: TransactionWithFromAndToLocalWalletIndex = {
from: 0,
to: 1,
gas: 21000,
value: BigInt(1),
};
const response = await web3EthWithWallet.sendTransaction(transaction);
expect(response.status).toBe(BigInt(1));

const minedTransactionData = await web3EthWithWallet.getTransaction(
response.transactionHash,
);

expect(minedTransactionData).toMatchObject({
from: tempAcc.address,
to: wallet.get(1)?.address.toLowerCase(),
value: BigInt(1),
});
});
it('should make a transaction with no value transfer', async () => {
const transaction: Transaction = {
from: tempAcc.address,
Expand Down
4 changes: 4 additions & 0 deletions packages/web3-types/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Make the `request` method of `EIP1193Provider` class, compatible with EIP 1193 (#5591)

## [Unreleased]

### Added

- Add `TransactionWithFromLocalWalletIndex`, `TransactionWithToLocalWalletIndex` and `TransactionWithFromAndToLocalWalletIndex` types (#5731)
17 changes: 13 additions & 4 deletions packages/web3-types/src/eth_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,6 @@ interface TransactionBase {
value?: Numbers;
accessList?: AccessList;
common?: Common;
// eslint-disable-next-line @typescript-eslint/ban-types
to?: Address | null;
gas?: Numbers;
gasPrice?: Numbers;
type?: Numbers;
Expand All @@ -361,14 +359,25 @@ interface TransactionBase {

export interface Transaction extends TransactionBase {
from?: Address;
// eslint-disable-next-line @typescript-eslint/ban-types
to?: Address | null;
}

export interface TransactionCall extends Transaction {
to: Address;
}

export interface TransactionWithLocalWalletIndex extends TransactionBase {
from?: Numbers;
export interface TransactionWithFromLocalWalletIndex extends Omit<Transaction, 'from'> {
from: Numbers;
}

export interface TransactionWithToLocalWalletIndex extends Omit<Transaction, 'to'> {
to: Numbers;
}

export interface TransactionWithFromAndToLocalWalletIndex extends Omit<Transaction, 'from' | 'to'> {
from: Numbers;
to: Numbers;
}

export interface TransactionInfo extends Transaction {
Expand Down