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

feat: add custom transaction schema to formatTransaction #7227

Merged
merged 19 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 13 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
21 changes: 21 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2702,3 +2702,24 @@ If there are any bugs, improvements, optimizations or any new feature proposal f
- The callback function provided to the static `Web3.onNewProviderDiscovered` function expects a parameter of type `EIP6963ProvidersMapUpdateEvent` as opposed to `EIP6963AnnounceProviderEvent`. (#7242)

## [Unreleased]

### Added

#### web3-core

- Adds a new property (`customTransactionSchema`) to `Web3ConfigOptions`
- Adds a new property (`config`) to `Web3RequestManager`

#### web3-eth

- Adds the same `{transactionSchema?: ValidationSchemaInput}` that exists in `formatTransaction` to `validateTransactionForSigning`

### Changed

#### web3-eth

- Forwards the new `web3Context.config.customTransactionSchema` to `formatTransaction`

#### web3-eth-personal

- Forwards the new `web3Context.config.customTransactionSchema` to `formatTransaction`
4 changes: 4 additions & 0 deletions docs/docs/guides/web3_config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ There is list of configuration params that can be set for modifying behavior of
- [defaultCommon](/guides/web3_config/#defaultcommon)
- [defaultTransactionType](/guides/web3_config/#defaulttransactiontype)
- [defaultMaxPriorityFeePerGas](/guides/web3_config/#defaultmaxpriorityfeepergas)
- [customTransactionSchema](/guides/web3_config/#customTransactionSchema)
- [defaultReturnFormat](/guides/web3_config/#defaultreturnformat)

## Global level Config
Expand Down Expand Up @@ -411,6 +412,9 @@ The `defaultMaxPriorityFeePerGas` option is used to set the [`defaultMaxPriority

The default value of `defaultMaxPriorityFeePerGas` is 2500000000 (2.5gwei) in hexstring format.

### [customTransactionSchema](/api/web3-core/class/Web3Config#customTransactionSchema)
The `customTransactionSchema` option is used to allow [`formatTransaction`](/api/web3-eth/function/formatTransaction) to accept a custom schema to validate transactions. A use-case could be: your chain has an extra field in its transactions and you want to write a plugin that makes sending these transactions easier.

### [defaultReturnFormat](/api/web3-core/class/Web3Config#defaultReturnFormat)
The `defaultReturnFormat` option allows users to specify the format in which certain types of data should be returned by default. It is a configuration parameter that can be set at the global level and affects how data is returned across the entire library.
```ts
Expand Down
5 changes: 5 additions & 0 deletions packages/web3-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,8 @@ Documentation:
- `setConfig()` fix for `setMaxListenerWarningThreshold` fix (#5079)

## [Unreleased]

### Added

- Adds a new property (`customTransactionSchema`) to `Web3ConfigOptions`
- Adds a new property (`config`) to `Web3RequestManager`
1 change: 1 addition & 0 deletions packages/web3-core/src/web3_batch_request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
import { JsonRpcBatchResponse, JsonRpcOptionalRequest, JsonRpcRequest } from 'web3-types';
import { jsonRpc, Web3DeferredPromise } from 'web3-utils';
import { OperationAbortError, OperationTimeoutError, ResponseError } from 'web3-errors';
// eslint-disable-next-line import/no-cycle
nicolasbrugneaux marked this conversation as resolved.
Show resolved Hide resolved
import { Web3RequestManager } from './web3_request_manager.js';

export const DEFAULT_BATCH_REQUEST_TIMEOUT = 1000;
Expand Down
12 changes: 12 additions & 0 deletions packages/web3-core/src/web3_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
} from 'web3-types';
import { ConfigHardforkMismatchError, ConfigChainMismatchError } from 'web3-errors';
import { isNullish, toHex } from 'web3-utils';
import { ValidationSchemaInput } from 'web3-validator';
import { TransactionTypeParser } from './types.js';
// eslint-disable-next-line import/no-cycle
import { TransactionBuilder } from './web3_context.js';
Expand Down Expand Up @@ -59,6 +60,7 @@ export interface Web3ConfigOptions {
};
transactionBuilder?: TransactionBuilder;
transactionTypeParser?: TransactionTypeParser;
customTransactionSchema?: ValidationSchemaInput;
defaultReturnFormat: DataFormat;
}

Expand Down Expand Up @@ -101,6 +103,7 @@ export abstract class Web3Config
},
transactionBuilder: undefined,
transactionTypeParser: undefined,
customTransactionSchema: undefined,
defaultReturnFormat: DEFAULT_RETURN_FORMAT,
};

Expand Down Expand Up @@ -520,6 +523,15 @@ export abstract class Web3Config
this.config.transactionTypeParser = val;
}

public get customTransactionSchema(): ValidationSchemaInput | undefined {
return this.config.customTransactionSchema;
}

public set customTransactionSchema(schema: ValidationSchemaInput | undefined) {
this._triggerConfigChange('customTransactionSchema', schema);
this.config.customTransactionSchema = schema;
}

private _triggerConfigChange<K extends keyof Web3ConfigOptions>(
config: K,
newValue: Web3ConfigOptions[K],
Expand Down
6 changes: 6 additions & 0 deletions packages/web3-core/src/web3_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { ExtensionObject, RequestManagerMiddleware } from './types.js';
import { Web3BatchRequest } from './web3_batch_request.js';
// eslint-disable-next-line import/no-cycle
import { Web3Config, Web3ConfigEvent, Web3ConfigOptions } from './web3_config.js';
// eslint-disable-next-line import/no-cycle
nicolasbrugneaux marked this conversation as resolved.
Show resolved Hide resolved
import { Web3RequestManager } from './web3_request_manager.js';
import { Web3SubscriptionConstructor } from './web3_subscriptions.js';
import { Web3SubscriptionManager } from './web3_subscription_manager.js';
Expand Down Expand Up @@ -148,6 +149,7 @@ export class Web3Context<
provider,
config?.enableExperimentalFeatures?.useSubscriptionWhenCheckingBlockTimeout,
requestManagerMiddleware,
config,
);

if (subscriptionManager) {
Expand Down Expand Up @@ -224,6 +226,8 @@ export class Web3Context<
this.on(Web3ConfigEvent.CONFIG_CHANGE, event => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
newContextChild.setConfig({ [event.name]: event.newValue });
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this._requestManager.setConfig({ [event.name]: event.newValue });
});

// @ts-expect-error No index signature with a parameter of type 'string' was found on type 'Web3Context<API, RegisteredSubs>'
Expand All @@ -247,6 +251,8 @@ export class Web3Context<
parentContext.on(Web3ConfigEvent.CONFIG_CHANGE, event => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this.setConfig({ [event.name]: event.newValue });
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this._requestManager.setConfig({ [event.name]: event.newValue });
});
}

Expand Down
9 changes: 9 additions & 0 deletions packages/web3-core/src/web3_request_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
} from './utils.js';
import { Web3EventEmitter } from './web3_event_emitter.js';
import { RequestManagerMiddleware } from './types.js';
import { type Web3ConfigOptions } from './web3_config.js';

export enum Web3RequestManagerEvent {
PROVIDER_CHANGED = 'PROVIDER_CHANGED',
Expand All @@ -74,15 +75,19 @@ export class Web3RequestManager<
}> {
private _provider?: SupportedProviders<API>;
private readonly useRpcCallSpecification?: boolean;
public config: Partial<Web3ConfigOptions>;
nicolasbrugneaux marked this conversation as resolved.
Show resolved Hide resolved
public middleware?: RequestManagerMiddleware<API>;

public constructor(
provider?: SupportedProviders<API> | string,
useRpcCallSpecification?: boolean,
requestManagerMiddleware?: RequestManagerMiddleware<API>,
config?: Partial<Web3ConfigOptions>,
) {
super();

this.config = config ?? {};

if (!isNullish(provider)) {
this.setProvider(provider);
}
Expand Down Expand Up @@ -148,6 +153,10 @@ export class Web3RequestManager<
return true;
}

public setConfig(options: Partial<Web3ConfigOptions>) {
Object.assign(this.config, options);
}

public setMiddleware(requestManagerMiddleware: RequestManagerMiddleware<API>) {
this.middleware = requestManagerMiddleware;
}
Expand Down
1 change: 1 addition & 0 deletions packages/web3-core/src/web3_subscription_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import { ProviderError, SubscriptionError } from 'web3-errors';
import { isNullish } from 'web3-utils';
import { isSupportSubscriptions } from './utils.js';
// eslint-disable-next-line import/no-cycle
import { Web3RequestManager, Web3RequestManagerEvent } from './web3_request_manager.js';
// eslint-disable-next-line import/no-cycle
import { Web3SubscriptionConstructor } from './web3_subscriptions.js';
Expand Down
1 change: 1 addition & 0 deletions packages/web3-core/src/web3_subscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { jsonRpc } from 'web3-utils';
// eslint-disable-next-line import/no-cycle
import { Web3SubscriptionManager } from './web3_subscription_manager.js';
import { Web3EventEmitter, Web3EventMap } from './web3_event_emitter.js';
// eslint-disable-next-line import/no-cycle
import { Web3RequestManager } from './web3_request_manager.js';

type CommonSubscriptionEvents = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ exports[`Web3Context getContextObject should return correct context object 1`] =
"config": {
"blockHeaderTimeout": 10,
"contractDataInputFill": "data",
"customTransactionSchema": undefined,
"defaultAccount": undefined,
"defaultBlock": "latest",
"defaultChain": "mainnet",
Expand Down Expand Up @@ -73,6 +74,7 @@ exports[`Web3Context getContextObject should return correct context object 1`] =
"clientUrl": "http://test/abc",
"httpProviderOptions": undefined,
},
"config": {},
"useRpcCallSpecification": undefined,
},
"subscriptionManager": Web3SubscriptionManager {
Expand Down Expand Up @@ -108,6 +110,7 @@ exports[`Web3Context getContextObject should return correct context object 1`] =
"clientUrl": "http://test/abc",
"httpProviderOptions": undefined,
},
"config": {},
"useRpcCallSpecification": undefined,
},
"tolerateUnlinkedSubscription": false,
Expand Down
1 change: 1 addition & 0 deletions packages/web3-core/test/unit/web3_config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const defaultConfig = {
defaultReturnFormat: DEFAULT_RETURN_FORMAT,
transactionBuilder: undefined,
transactionTypeParser: undefined,
customTransactionSchema: undefined,
};
const setValue = {
string: 'newValue',
Expand Down
3 changes: 3 additions & 0 deletions packages/web3-core/test/unit/web3_context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ describe('Web3Context', () => {
expect(newContext.requestManager).toBeInstanceOf(Web3RequestManager);
expect(newContext.config.defaultHardfork).toEqual(config.defaultHardfork);
expect(newContext.config.defaultNetworkId).toEqual(config.defaultNetworkId);
expect(newContext.requestManager.config.defaultHardfork).toEqual(
config.defaultHardfork,
);
});

describe('accountsProvider', () => {
Expand Down
4 changes: 4 additions & 0 deletions packages/web3-eth-personal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,7 @@ Documentation:
- Dependencies updated

## [Unreleased]

### Changed

- Forwards the new `web3Context.config.customTransactionSchema` to `formatTransaction`
8 changes: 6 additions & 2 deletions packages/web3-eth-personal/src/rpc_method_wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ export const sendTransaction = async (
tx: Transaction,
passphrase: string,
) => {
const formattedTx = formatTransaction(tx, ETH_DATA_FORMAT);
const formattedTx = formatTransaction(tx, ETH_DATA_FORMAT, {
nicolasbrugneaux marked this conversation as resolved.
Show resolved Hide resolved
transactionSchema: requestManager.config.customTransactionSchema,
});

return personalRpcMethods.sendTransaction(requestManager, formattedTx, passphrase);
};
Expand All @@ -83,7 +85,9 @@ export const signTransaction = async (
tx: Transaction,
passphrase: string,
) => {
const formattedTx = formatTransaction(tx, ETH_DATA_FORMAT);
const formattedTx = formatTransaction(tx, ETH_DATA_FORMAT, {
transactionSchema: requestManager.config.customTransactionSchema,
});
nicolasbrugneaux marked this conversation as resolved.
Show resolved Hide resolved

return personalRpcMethods.signTransaction(requestManager, formattedTx, passphrase);
};
Expand Down
8 changes: 8 additions & 0 deletions packages/web3-eth/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,11 @@ Documentation:
- Change method `getTransactionReceipt` to not be casted as `TransactionReceipt` to give proper return type (#7159)

## [Unreleased]

### Changed

- Forwards the new `web3Context.config.customTransactionSchema` to `formatTransaction`

### Added

- Adds the same `{transactionSchema?: ValidationSchemaInput}` that exists in `formatTransaction` to `validateTransactionForSigning`
23 changes: 19 additions & 4 deletions packages/web3-eth/src/rpc_method_wrappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ export async function getTransaction<ReturnFormat extends DataFormat>(
return isNullish(response)
? response
: formatTransaction(response, returnFormat, {
transactionSchema: web3Context.config.customTransactionSchema,
fillInputAndData: true,
});
}
Expand All @@ -448,6 +449,7 @@ export async function getPendingTransactions<ReturnFormat extends DataFormat>(
transaction as unknown as Transaction,
returnFormat ?? web3Context.defaultReturnFormat,
{
transactionSchema: web3Context.config.customTransactionSchema,
fillInputAndData: true,
},
),
Expand Down Expand Up @@ -488,6 +490,7 @@ export async function getTransactionFromBlock<ReturnFormat extends DataFormat>(
return isNullish(response)
? response
: formatTransaction(response, returnFormat ?? web3Context.defaultReturnFormat, {
transactionSchema: web3Context.config.customTransactionSchema,
fillInputAndData: true,
});
}
Expand Down Expand Up @@ -606,6 +609,9 @@ export function sendTransaction<
to: getTransactionFromOrToAttr('to', web3Context, transaction),
},
ETH_DATA_FORMAT,
{
transactionSchema: web3Context.config.customTransactionSchema,
},
);

try {
Expand Down Expand Up @@ -847,7 +853,9 @@ export async function signTransaction<ReturnFormat extends DataFormat>(
) {
const response = await ethRpcMethods.signTransaction(
web3Context.requestManager,
formatTransaction(transaction, ETH_DATA_FORMAT),
formatTransaction(transaction, ETH_DATA_FORMAT, {
transactionSchema: web3Context.config.customTransactionSchema,
}),
);
// Some clients only return the encoded signed transaction (e.g. Ganache)
// while clients such as Geth return the desired SignedTransactionInfoAPI object
Expand All @@ -862,6 +870,7 @@ export async function signTransaction<ReturnFormat extends DataFormat>(
returnFormat,
),
tx: formatTransaction((response as SignedTransactionInfoAPI).tx, returnFormat, {
transactionSchema: web3Context.config.customTransactionSchema,
fillInputAndData: true,
}),
};
Expand All @@ -885,7 +894,9 @@ export async function call<ReturnFormat extends DataFormat>(

const response = await ethRpcMethods.call(
web3Context.requestManager,
formatTransaction(transaction, ETH_DATA_FORMAT),
formatTransaction(transaction, ETH_DATA_FORMAT, {
transactionSchema: web3Context.config.customTransactionSchema,
}),
blockNumberFormatted,
);

Expand All @@ -903,7 +914,9 @@ export async function estimateGas<ReturnFormat extends DataFormat>(
blockNumber: BlockNumberOrTag = web3Context.defaultBlock,
returnFormat: ReturnFormat,
) {
const transactionFormatted = formatTransaction(transaction, ETH_DATA_FORMAT);
const transactionFormatted = formatTransaction(transaction, ETH_DATA_FORMAT, {
transactionSchema: web3Context.config.customTransactionSchema,
});
const blockNumberFormatted = isBlockTag(blockNumber as string)
? (blockNumber as BlockTag)
: format({ format: 'uint' }, blockNumber as Numbers, ETH_DATA_FORMAT);
Expand Down Expand Up @@ -1074,7 +1087,9 @@ export async function createAccessList<ReturnFormat extends DataFormat>(

const response = (await ethRpcMethods.createAccessList(
web3Context.requestManager,
formatTransaction(transaction, ETH_DATA_FORMAT),
formatTransaction(transaction, ETH_DATA_FORMAT, {
transactionSchema: web3Context.config.customTransactionSchema,
}),
blockNumberFormatted,
)) as unknown as AccessListResult;

Expand Down
10 changes: 8 additions & 2 deletions packages/web3-eth/src/utils/decode_signed_transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
} from 'web3-types';
import { bytesToHex, format, hexToBytes, keccak256 } from 'web3-utils';
import { TransactionFactory } from 'web3-eth-accounts';
import { ValidationSchemaInput } from 'web3-validator';
import { detectRawTransactionType } from './detect_transaction_type.js';
import { formatTransaction } from './format_transaction.js';

Expand All @@ -35,7 +36,9 @@ import { formatTransaction } from './format_transaction.js';
export function decodeSignedTransaction<ReturnFormat extends DataFormat>(
encodedSignedTransaction: HexStringBytes,
returnFormat: ReturnFormat,
options: { fillInputAndData?: boolean } = { fillInputAndData: false },
options: { fillInputAndData?: boolean; transactionSchema?: ValidationSchemaInput } = {
fillInputAndData: false,
},
): SignedTransactionInfoAPI {
return {
raw: format({ format: 'bytes' }, encodedSignedTransaction, returnFormat),
Expand All @@ -48,7 +51,10 @@ export function decodeSignedTransaction<ReturnFormat extends DataFormat>(
type: detectRawTransactionType(hexToBytes(encodedSignedTransaction)),
} as TransactionSignedAPI,
returnFormat,
{ fillInputAndData: options.fillInputAndData },
{
fillInputAndData: options.fillInputAndData,
transactionSchema: options.transactionSchema,
},
),
};
}
Loading
Loading