From 2900d4a6746e68f1e3eb0c7c4bae035ae20510c7 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Fri, 14 Jul 2023 06:03:41 +0200 Subject: [PATCH 01/15] feat: optimism transaction receipt formatter --- .changeset/wicked-pumas-relax.md | 5 +++++ src/chains/formatters/index.test.ts | 12 ++++++++++++ src/chains/formatters/index.ts | 23 +++++++++++++++++++++++ src/chains/serializers/index.test.ts | 11 +++++++++++ src/chains/serializers/index.ts | 4 ++++ 5 files changed, 55 insertions(+) create mode 100644 .changeset/wicked-pumas-relax.md create mode 100644 src/chains/formatters/index.test.ts create mode 100644 src/chains/formatters/index.ts create mode 100644 src/chains/serializers/index.test.ts create mode 100644 src/chains/serializers/index.ts diff --git a/.changeset/wicked-pumas-relax.md b/.changeset/wicked-pumas-relax.md new file mode 100644 index 0000000000..f839a24198 --- /dev/null +++ b/.changeset/wicked-pumas-relax.md @@ -0,0 +1,5 @@ +--- +"viem": minor +--- + +Added a `viem/chains/serializers` entrypoint to export chain-specific transaction serializers. diff --git a/src/chains/formatters/index.test.ts b/src/chains/formatters/index.test.ts new file mode 100644 index 0000000000..3405597b27 --- /dev/null +++ b/src/chains/formatters/index.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from 'vitest' + +import * as actions from './index.js' + +test('exports', () => { + expect(Object.keys(actions)).toMatchInlineSnapshot(` + [ + "formattersCelo", + "formattersOptimism", + ] + `) +}) diff --git a/src/chains/formatters/index.ts b/src/chains/formatters/index.ts new file mode 100644 index 0000000000..922234fbdc --- /dev/null +++ b/src/chains/formatters/index.ts @@ -0,0 +1,23 @@ +export { + type CeloBlock, + type CeloRpcBlock, + type CeloRpcTransaction, + type CeloRpcTransactionReceipt, + type CeloRpcTransactionRequest, + type CeloTransaction, + type CeloTransactionReceipt, + type CeloTransactionRequest, + formattersCelo, +} from './celo.js' + +export { + type OptimismBlock, + type OptimismDepositTransaction, + type OptimismRpcBlock, + type OptimismRpcDepositTransaction, + type OptimismRpcTransaction, + type OptimismRpcTransactionReceipt, + type OptimismTransaction, + type OptimismTransactionReceipt, + formattersOptimism, +} from './optimism.js' diff --git a/src/chains/serializers/index.test.ts b/src/chains/serializers/index.test.ts new file mode 100644 index 0000000000..e4068ca01e --- /dev/null +++ b/src/chains/serializers/index.test.ts @@ -0,0 +1,11 @@ +import { expect, test } from 'vitest' + +import * as actions from './index.js' + +test('exports', () => { + expect(Object.keys(actions)).toMatchInlineSnapshot(` + [ + "serializeTransactionCelo", + ] + `) +}) diff --git a/src/chains/serializers/index.ts b/src/chains/serializers/index.ts new file mode 100644 index 0000000000..4d3fe773ce --- /dev/null +++ b/src/chains/serializers/index.ts @@ -0,0 +1,4 @@ +export { + type CeloTransactionSerializable, + serializeTransactionCelo, +} from './celo.js' From 83685d7858e6fa36e9622c5fad5a44bee7edc598 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Sun, 6 Aug 2023 17:35:19 +0200 Subject: [PATCH 02/15] refactor: restructure --- .changeset/wicked-pumas-relax.md | 5 ----- src/chains/formatters/index.test.ts | 12 ------------ src/chains/formatters/index.ts | 23 ----------------------- src/chains/serializers/index.test.ts | 11 ----------- src/chains/serializers/index.ts | 4 ---- 5 files changed, 55 deletions(-) delete mode 100644 .changeset/wicked-pumas-relax.md delete mode 100644 src/chains/formatters/index.test.ts delete mode 100644 src/chains/formatters/index.ts delete mode 100644 src/chains/serializers/index.test.ts delete mode 100644 src/chains/serializers/index.ts diff --git a/.changeset/wicked-pumas-relax.md b/.changeset/wicked-pumas-relax.md deleted file mode 100644 index f839a24198..0000000000 --- a/.changeset/wicked-pumas-relax.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"viem": minor ---- - -Added a `viem/chains/serializers` entrypoint to export chain-specific transaction serializers. diff --git a/src/chains/formatters/index.test.ts b/src/chains/formatters/index.test.ts deleted file mode 100644 index 3405597b27..0000000000 --- a/src/chains/formatters/index.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { expect, test } from 'vitest' - -import * as actions from './index.js' - -test('exports', () => { - expect(Object.keys(actions)).toMatchInlineSnapshot(` - [ - "formattersCelo", - "formattersOptimism", - ] - `) -}) diff --git a/src/chains/formatters/index.ts b/src/chains/formatters/index.ts deleted file mode 100644 index 922234fbdc..0000000000 --- a/src/chains/formatters/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -export { - type CeloBlock, - type CeloRpcBlock, - type CeloRpcTransaction, - type CeloRpcTransactionReceipt, - type CeloRpcTransactionRequest, - type CeloTransaction, - type CeloTransactionReceipt, - type CeloTransactionRequest, - formattersCelo, -} from './celo.js' - -export { - type OptimismBlock, - type OptimismDepositTransaction, - type OptimismRpcBlock, - type OptimismRpcDepositTransaction, - type OptimismRpcTransaction, - type OptimismRpcTransactionReceipt, - type OptimismTransaction, - type OptimismTransactionReceipt, - formattersOptimism, -} from './optimism.js' diff --git a/src/chains/serializers/index.test.ts b/src/chains/serializers/index.test.ts deleted file mode 100644 index e4068ca01e..0000000000 --- a/src/chains/serializers/index.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { expect, test } from 'vitest' - -import * as actions from './index.js' - -test('exports', () => { - expect(Object.keys(actions)).toMatchInlineSnapshot(` - [ - "serializeTransactionCelo", - ] - `) -}) diff --git a/src/chains/serializers/index.ts b/src/chains/serializers/index.ts deleted file mode 100644 index 4d3fe773ce..0000000000 --- a/src/chains/serializers/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { - type CeloTransactionSerializable, - serializeTransactionCelo, -} from './celo.js' From 5a86a5a6849217705e8ec357734c55df8461224e Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 06:55:57 +0200 Subject: [PATCH 03/15] feat: chain fees config --- src/actions/public/estimateGas.ts | 5 +- src/actions/public/simulateContract.ts | 4 +- src/actions/wallet/deployContract.ts | 2 +- src/actions/wallet/sendTransaction.ts | 15 +++--- src/actions/wallet/writeContract.ts | 18 ++++--- src/chains/index.ts | 23 +++++++-- src/chains/optimism/fees.ts | 5 ++ src/clients/decorators/public.ts | 6 +-- src/types/chain.ts | 14 ++++++ src/types/formatter.ts | 12 ++--- src/utils/chain.ts | 3 +- src/utils/formatters/block.ts | 4 +- src/utils/formatters/transaction.ts | 4 +- src/utils/transaction/prepareRequest.test.ts | 2 +- src/utils/transaction/prepareRequest.ts | 50 +++++++++++++------- 15 files changed, 116 insertions(+), 51 deletions(-) create mode 100644 src/chains/optimism/fees.ts diff --git a/src/actions/public/estimateGas.ts b/src/actions/public/estimateGas.ts index 107b296e86..f773ddfde5 100644 --- a/src/actions/public/estimateGas.ts +++ b/src/actions/public/estimateGas.ts @@ -102,7 +102,10 @@ export async function estimateGas< to, value, ...rest - } = account.type === 'local' ? await prepareRequest(client, args) : args + } = + account.type === 'local' + ? await prepareRequest(client, args as any) + : args const blockNumberHex = blockNumber ? numberToHex(blockNumber) : undefined const block = blockNumberHex || blockTag diff --git a/src/actions/public/simulateContract.ts b/src/actions/public/simulateContract.ts index acaf0afe24..c1b37412ff 100644 --- a/src/actions/public/simulateContract.ts +++ b/src/actions/public/simulateContract.ts @@ -29,7 +29,7 @@ export type SimulateContractParameters< TAbi extends Abi | readonly unknown[] = Abi, TFunctionName extends string = any, TChain extends Chain | undefined = Chain | undefined, - TChainOverride extends Chain | undefined = undefined, + TChainOverride extends Chain | undefined = Chain | undefined, > = { chain?: TChainOverride /** Data to append to the end of the calldata. Useful for adding a ["domain" tag](https://opensea.notion.site/opensea/Seaport-Order-Attributions-ec2d69bf455041a5baa490941aad307f). */ @@ -51,7 +51,7 @@ export type SimulateContractReturnType< TAbi extends Abi | readonly unknown[] = Abi, TFunctionName extends string = string, TChain extends Chain | undefined = Chain | undefined, - TChainOverride extends Chain | undefined = undefined, + TChainOverride extends Chain | undefined = Chain | undefined, > = { result: ContractFunctionResult request: UnionOmit< diff --git a/src/actions/wallet/deployContract.ts b/src/actions/wallet/deployContract.ts index ca525a49b9..3560777a0b 100644 --- a/src/actions/wallet/deployContract.ts +++ b/src/actions/wallet/deployContract.ts @@ -19,7 +19,7 @@ export type DeployContractParameters< TAbi extends Abi | readonly unknown[] = Abi, TChain extends Chain | undefined = Chain | undefined, TAccount extends Account | undefined = Account | undefined, - TChainOverride extends Chain | undefined = undefined, + TChainOverride extends Chain | undefined = Chain | undefined, > = UnionOmit< SendTransactionParameters, 'accessList' | 'chain' | 'to' | 'data' diff --git a/src/actions/wallet/sendTransaction.ts b/src/actions/wallet/sendTransaction.ts index 5be80a1baf..8027e3c2a4 100644 --- a/src/actions/wallet/sendTransaction.ts +++ b/src/actions/wallet/sendTransaction.ts @@ -11,7 +11,7 @@ import type { TransactionRequest, TransactionSerializable, } from '../../types/transaction.js' -import type { IsUndefined, UnionOmit } from '../../types/utils.js' +import type { UnionOmit } from '../../types/utils.js' import { assertCurrentChain } from '../../utils/chain.js' import { getTransactionError } from '../../utils/errors/getTransactionError.js' import { extract } from '../../utils/formatters/extract.js' @@ -19,17 +19,20 @@ import { type FormattedTransactionRequest, formatTransactionRequest, } from '../../utils/formatters/transactionRequest.js' -import { assertRequest } from '../../utils/transaction/assertRequest.js' +import { + type AssertRequestParameters, + assertRequest, +} from '../../utils/transaction/assertRequest.js' import { prepareRequest } from '../../utils/transaction/prepareRequest.js' import { getChainId } from '../public/getChainId.js' export type SendTransactionParameters< TChain extends Chain | undefined = Chain | undefined, TAccount extends Account | undefined = Account | undefined, - TChainOverride extends Chain | undefined = Chain, + TChainOverride extends Chain | undefined = Chain | undefined, > = UnionOmit< FormattedTransactionRequest< - IsUndefined extends true ? TChainOverride : TChain + TChainOverride extends Chain ? TChainOverride : TChain >, 'from' > & @@ -113,7 +116,7 @@ export async function sendTransaction< const account = parseAccount(account_) try { - assertRequest(args) + assertRequest(args as AssertRequestParameters) let chainId if (chain !== null) { @@ -139,7 +142,7 @@ export async function sendTransaction< to, value, ...rest, - }) + } as any) if (!chainId) chainId = await getChainId(client) diff --git a/src/actions/wallet/writeContract.ts b/src/actions/wallet/writeContract.ts index 47afd9e22f..647dcce4ea 100644 --- a/src/actions/wallet/writeContract.ts +++ b/src/actions/wallet/writeContract.ts @@ -3,6 +3,7 @@ import type { Abi } from 'abitype' import type { Account } from '../../accounts/types.js' import type { Client } from '../../clients/createClient.js' import type { Transport } from '../../clients/transports/createTransport.js' +import type { GetAccountParameter } from '../../types/account.js' import type { Chain, GetChain } from '../../types/chain.js' import type { ContractFunctionConfig, GetValue } from '../../types/contract.js' import type { Hex } from '../../types/misc.js' @@ -11,7 +12,7 @@ import { type EncodeFunctionDataParameters, encodeFunctionData, } from '../../utils/abi/encodeFunctionData.js' - +import type { FormattedTransactionRequest } from '../../utils/formatters/transactionRequest.js' import { type SendTransactionParameters, type SendTransactionReturnType, @@ -22,14 +23,17 @@ export type WriteContractParameters< TAbi extends Abi | readonly unknown[] = Abi, TFunctionName extends string = string, TChain extends Chain | undefined = Chain, - TAccount extends Account | undefined = undefined, - TChainOverride extends Chain | undefined = undefined, + TAccount extends Account | undefined = Account | undefined, + TChainOverride extends Chain | undefined = Chain | undefined, > = ContractFunctionConfig & + GetAccountParameter & + GetChain & UnionOmit< - SendTransactionParameters, - 'chain' | 'to' | 'data' | 'value' + FormattedTransactionRequest< + TChainOverride extends Chain ? TChainOverride : TChain + >, + 'from' | 'to' | 'data' | 'value' > & - GetChain & GetValue< TAbi, TFunctionName, @@ -102,7 +106,7 @@ export async function writeContract< TAccount extends Account | undefined, TAbi extends Abi | readonly unknown[], TFunctionName extends string, - TChainOverride extends Chain | undefined = undefined, + TChainOverride extends Chain | undefined, >( client: Client, { diff --git a/src/chains/index.ts b/src/chains/index.ts index e858a93466..f0840e833d 100644 --- a/src/chains/index.ts +++ b/src/chains/index.ts @@ -3,6 +3,7 @@ import * as chains from '@wagmi/chains' import { defineChain } from '../utils/chain.js' import { formattersCelo } from './celo/formatters.js' import { serializersCelo } from './celo/serializers.js' +import { feesOptimism } from './optimism/fees.js' import { formattersOptimism } from './optimism/formatters.js' export const arbitrum = /*#__PURE__*/ defineChain(chains.arbitrum) @@ -11,8 +12,14 @@ export const aurora = /*#__PURE__*/ defineChain(chains.aurora) export const auroraTestnet = /*#__PURE__*/ defineChain(chains.auroraTestnet) export const avalanche = /*#__PURE__*/ defineChain(chains.avalanche) export const avalancheFuji = /*#__PURE__*/ defineChain(chains.avalancheFuji) -export const base = /*#__PURE__*/ defineChain(chains.base) -export const baseGoerli = /*#__PURE__*/ defineChain(chains.baseGoerli) +export const base = /*#__PURE__*/ defineChain(chains.base, { + fees: feesOptimism, + formatters: formattersOptimism, +}) +export const baseGoerli = /*#__PURE__*/ defineChain(chains.baseGoerli, { + fees: feesOptimism, + formatters: formattersOptimism, +}) export const boba = /*#__PURE__*/ defineChain(chains.boba) export const bronos = /*#__PURE__*/ defineChain(chains.bronos) export const bronosTestnet = /*#__PURE__*/ defineChain(chains.bronosTestnet) @@ -72,9 +79,11 @@ export const moonriver = /*#__PURE__*/ defineChain(chains.moonriver) export const nexi = /*#__PURE__*/ defineChain(chains.nexi) export const okc = /*#__PURE__*/ defineChain(chains.okc) export const optimism = /*#__PURE__*/ defineChain(chains.optimism, { + fees: feesOptimism, formatters: formattersOptimism, }) export const optimismGoerli = /*#__PURE__*/ defineChain(chains.optimismGoerli, { + fees: feesOptimism, formatters: formattersOptimism, }) export const polygon = /*#__PURE__*/ defineChain(chains.polygon) @@ -136,7 +145,13 @@ export const xdcTestnet = /*#__PURE__*/ defineChain(chains.xdcTestnet) export const zhejiang = /*#__PURE__*/ defineChain(chains.zhejiang) export const zkSync = /*#__PURE__*/ defineChain(chains.zkSync) export const zkSyncTestnet = /*#__PURE__*/ defineChain(chains.zkSyncTestnet) -export const zora = /*#__PURE__*/ defineChain(chains.zora) -export const zoraTestnet = /*#__PURE__*/ defineChain(chains.zoraTestnet) +export const zora = /*#__PURE__*/ defineChain(chains.zora, { + fees: feesOptimism, + formatters: formattersOptimism, +}) +export const zoraTestnet = /*#__PURE__*/ defineChain(chains.zoraTestnet, { + fees: feesOptimism, + formatters: formattersOptimism, +}) export type { Chain } from '../types/chain.js' diff --git a/src/chains/optimism/fees.ts b/src/chains/optimism/fees.ts new file mode 100644 index 0000000000..d388db0d28 --- /dev/null +++ b/src/chains/optimism/fees.ts @@ -0,0 +1,5 @@ +import type { ChainFees } from '../../types/chain.js' + +export const feesOptimism = { + getDefaultPriorityFee: () => 1_000_000n, // 0.001 gwei +} as const satisfies ChainFees diff --git a/src/clients/decorators/public.ts b/src/clients/decorators/public.ts index 1070c0d5db..0f72f8a178 100644 --- a/src/clients/decorators/public.ts +++ b/src/clients/decorators/public.ts @@ -1206,9 +1206,9 @@ export type PublicActions< * }) */ simulateContract: < - TAbi extends Abi | readonly unknown[] = Abi, - TFunctionName extends string = any, - TChainOverride extends Chain | undefined = undefined, + TAbi extends Abi | readonly unknown[], + TFunctionName extends string, + TChainOverride extends Chain | undefined, >( args: SimulateContractParameters< TAbi, diff --git a/src/types/chain.ts b/src/types/chain.ts index 5ad43cd6b7..0317b38355 100644 --- a/src/types/chain.ts +++ b/src/types/chain.ts @@ -1,5 +1,7 @@ import type { Address } from 'abitype' +import type { FormattedBlock } from '../utils/formatters/block.js' +import type { PrepareRequestParameters } from '../utils/transaction/prepareRequest.js' import type { Formatters } from './formatter.js' import type { Serializers } from './serializer.js' import type { IsUndefined } from './utils.js' @@ -12,6 +14,7 @@ export type Chain< > = import('@wagmi/chains').Chain & { formatters?: formatters | undefined serializers?: serializers | undefined + fees?: ChainFees | undefined } export type ChainContract = { @@ -19,6 +22,17 @@ export type ChainContract = { blockCreated?: number } +export type ChainFees< + formatters extends Formatters | undefined = Formatters | undefined, +> = { + getDefaultPriorityFee(args: { + block: FormattedBlock<{ formatters: formatters }> + request: PrepareRequestParameters< + Omit & { formatters: formatters } + > + }): Promise | bigint +} + export type GetChain< TChain extends Chain | undefined, TChainOverride extends Chain | undefined = undefined, diff --git a/src/types/formatter.ts b/src/types/formatter.ts index 3674a55fa9..9bb1c4fb55 100644 --- a/src/types/formatter.ts +++ b/src/types/formatter.ts @@ -13,29 +13,29 @@ export type Formatters = { } export type ExtractFormatterExclude< - TChain extends Chain | undefined, + TChain extends { formatters?: Chain['formatters'] } | undefined, TType extends keyof Formatters, -> = TChain extends Chain +> = TChain extends { formatters?: infer _Formatters extends Formatters } ? _Formatters[TType] extends { exclude: infer Exclude } ? Extract[number] : '' : '' export type ExtractFormatterParameters< - TChain extends Chain | undefined, + TChain extends { formatters?: Chain['formatters'] } | undefined, TType extends keyof Formatters, TFallback, -> = TChain extends Chain +> = TChain extends { formatters?: infer _Formatters extends Formatters } ? _Formatters[TType] extends Formatter ? Parameters<_Formatters[TType]['format']>[0] : TFallback : TFallback export type ExtractFormatterReturnType< - TChain extends Chain | undefined, + TChain extends { formatters?: Chain['formatters'] } | undefined, TType extends keyof Formatters, TFallback, -> = TChain extends Chain +> = TChain extends { formatters?: infer _Formatters extends Formatters } ? _Formatters[TType] extends Formatter ? ReturnType<_Formatters[TType]['format']> : TFallback diff --git a/src/utils/chain.ts b/src/utils/chain.ts index fdb501fd0b..9df74df893 100644 --- a/src/utils/chain.ts +++ b/src/utils/chain.ts @@ -25,10 +25,11 @@ export function defineChain< TFormatters extends Formatters, >( chain: TChain, - config?: Pick, 'formatters' | 'serializers'>, + config?: Pick, 'fees' | 'formatters' | 'serializers'>, ) { return { ...chain, + fees: config?.fees, formatters: config?.formatters, serializers: config?.serializers, } diff --git a/src/utils/formatters/block.ts b/src/utils/formatters/block.ts index 44a191c9e9..2ab885ad0f 100644 --- a/src/utils/formatters/block.ts +++ b/src/utils/formatters/block.ts @@ -14,7 +14,9 @@ import { type FormattedTransaction, formatTransaction } from './transaction.js' type BlockPendingDependencies = 'hash' | 'logsBloom' | 'nonce' | 'number' export type FormattedBlock< - TChain extends Chain | undefined = Chain | undefined, + TChain extends { formatters?: Chain['formatters'] } | undefined = + | { formatters?: Chain['formatters'] } + | undefined, TIncludeTransactions extends boolean = boolean, TBlockTag extends BlockTag = BlockTag, _FormatterReturnType = ExtractFormatterReturnType< diff --git a/src/utils/formatters/transaction.ts b/src/utils/formatters/transaction.ts index fedc966b94..b3e89f35d5 100644 --- a/src/utils/formatters/transaction.ts +++ b/src/utils/formatters/transaction.ts @@ -16,7 +16,9 @@ type TransactionPendingDependencies = | 'transactionIndex' export type FormattedTransaction< - TChain extends Chain | undefined = Chain | undefined, + TChain extends { formatters?: Chain['formatters'] } | undefined = + | { formatters?: Chain['formatters'] } + | undefined, TBlockTag extends BlockTag = BlockTag, _FormatterReturnType = ExtractFormatterReturnType< TChain, diff --git a/src/utils/transaction/prepareRequest.test.ts b/src/utils/transaction/prepareRequest.test.ts index 3bba8ba721..3557c96d4d 100644 --- a/src/utils/transaction/prepareRequest.test.ts +++ b/src/utils/transaction/prepareRequest.test.ts @@ -437,8 +437,8 @@ describe('prepareRequest', () => { await setup() await expect(() => + // @ts-expect-error prepareRequest(walletClient, { - // @ts-expect-error to: targetAccount.address, value: parseEther('1'), }), diff --git a/src/utils/transaction/prepareRequest.ts b/src/utils/transaction/prepareRequest.ts index c8ae972f6e..0e467ad856 100644 --- a/src/utils/transaction/prepareRequest.ts +++ b/src/utils/transaction/prepareRequest.ts @@ -15,24 +15,34 @@ import type { Transport } from '../../clients/transports/createTransport.js' import { AccountNotFoundError } from '../../errors/account.js' import { BaseError } from '../../errors/base.js' import type { GetAccountParameter } from '../../types/account.js' -import type { Chain } from '../../types/chain.js' - +import type { Chain, GetChain } from '../../types/chain.js' +import type { UnionOmit } from '../../types/utils.js' +import type { FormattedTransactionRequest } from '../index.js' import { type AssertRequestParameters, assertRequest } from './assertRequest.js' export type PrepareRequestParameters< - TAccount extends Account | undefined = undefined, -> = GetAccountParameter & { - gas?: SendTransactionParameters['gas'] - gasPrice?: SendTransactionParameters['gasPrice'] - maxFeePerGas?: SendTransactionParameters['maxFeePerGas'] - maxPriorityFeePerGas?: SendTransactionParameters['maxPriorityFeePerGas'] - nonce?: SendTransactionParameters['nonce'] -} + TChain extends Chain | undefined = Chain | undefined, + TAccount extends Account | undefined = Account | undefined, + TChainOverride extends Chain | undefined = Chain | undefined, +> = UnionOmit< + FormattedTransactionRequest< + TChainOverride extends Chain ? TChainOverride : TChain + >, + 'from' +> & + GetAccountParameter & + GetChain export type PrepareRequestReturnType< - TAccount extends Account | undefined = undefined, - TParameters extends PrepareRequestParameters = PrepareRequestParameters, -> = TParameters & { + TChain extends Chain | undefined = Chain | undefined, + TAccount extends Account | undefined = Account | undefined, + TChainOverride extends Chain | undefined = Chain | undefined, + TArgs extends PrepareRequestParameters< + TChain, + TAccount, + TChainOverride + > = PrepareRequestParameters, +> = TArgs & { from: Address gas: SendTransactionParameters['gas'] gasPrice?: SendTransactionParameters['gasPrice'] @@ -46,11 +56,12 @@ export const defaultTip = 1_500_000_000n // 1.5 gwei export async function prepareRequest< TChain extends Chain | undefined, TAccount extends Account | undefined, - TParameters extends PrepareRequestParameters, + TChainOverride extends Chain | undefined, + TArgs extends PrepareRequestParameters, >( client: Client, - args: TParameters, -): Promise> { + args: TArgs, +): Promise> { const { account: account_, gas, @@ -113,5 +124,10 @@ export async function prepareRequest< assertRequest(request as AssertRequestParameters) - return request as PrepareRequestReturnType + return request as PrepareRequestReturnType< + TChain, + TAccount, + TChainOverride, + TArgs + > } From e2cd44338d4c45186f9e4609d7505f05e262999d Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 07:02:48 +0200 Subject: [PATCH 04/15] chore: changeset --- .changeset/sweet-lemons-explain.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .changeset/sweet-lemons-explain.md diff --git a/.changeset/sweet-lemons-explain.md b/.changeset/sweet-lemons-explain.md new file mode 100644 index 0000000000..f165be2d5b --- /dev/null +++ b/.changeset/sweet-lemons-explain.md @@ -0,0 +1,17 @@ +--- +"viem": minor +--- + +Added `fees` to `chain` config that includes a `getDefaultPriorityFee` for setting a default priority fee for a chain. + +```ts +import type { Chain } from 'viem' + +export const example = { + // ... + fees: { + getDefaultPriorityFee: () => 1_000_000n, // 0.001 gwei + }, + // ... +} as const satifies Chain +``` From 9aad513028e5c1c9dcba4cc6f4edfaba9c9ad7b0 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 07:08:23 +0200 Subject: [PATCH 05/15] chore: update test --- src/utils/transaction/prepareRequest.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/transaction/prepareRequest.test.ts b/src/utils/transaction/prepareRequest.test.ts index 3557c96d4d..e78dc8ebd2 100644 --- a/src/utils/transaction/prepareRequest.test.ts +++ b/src/utils/transaction/prepareRequest.test.ts @@ -383,6 +383,7 @@ describe('prepareRequest', () => { await setup() await expect(() => + // @ts-expect-error prepareRequest(walletClient, { account: privateKeyToAccount(sourceAccount.privateKey), to: targetAccount.address, @@ -410,6 +411,7 @@ describe('prepareRequest', () => { await setup() await expect(() => + // @ts-expect-error prepareRequest(walletClient, { account: privateKeyToAccount(sourceAccount.privateKey), to: targetAccount.address, From 2cd5d010828d0999b03be9eaf8d420fb6822d4ad Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 07:19:24 +0200 Subject: [PATCH 06/15] chore: impl --- src/utils/transaction/prepareRequest.test.ts | 85 +++++++++++++++++++- src/utils/transaction/prepareRequest.ts | 15 ++-- 2 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/utils/transaction/prepareRequest.test.ts b/src/utils/transaction/prepareRequest.test.ts index e78dc8ebd2..140cd870c8 100644 --- a/src/utils/transaction/prepareRequest.test.ts +++ b/src/utils/transaction/prepareRequest.test.ts @@ -1,16 +1,19 @@ import { describe, expect, test, vi } from 'vitest' -import { accounts } from '../../_test/constants.js' +import { accounts, localHttpUrl } from '../../_test/constants.js' import { publicClient, testClient, walletClient } from '../../_test/utils.js' import { privateKeyToAccount } from '../../accounts/privateKeyToAccount.js' import * as getBlock from '../../actions/public/getBlock.js' import { mine } from '../../actions/test/mine.js' import { setBalance } from '../../actions/test/setBalance.js' import { setNextBlockBaseFeePerGas } from '../../actions/test/setNextBlockBaseFeePerGas.js' +import { createWalletClient } from '../../clients/createWalletClient.js' +import { http } from '../../clients/transports/http.js' import { parseEther } from '../unit/parseEther.js' import { parseGwei } from '../unit/parseGwei.js' -import { defaultTip, prepareRequest } from './prepareRequest.js' +import { anvilChain } from '../../_test/utils.js' +import { prepareRequest } from './prepareRequest.js' const sourceAccount = accounts[0] const targetAccount = accounts[1] @@ -30,8 +33,6 @@ async function setup() { await mine(testClient, { blocks: 1 }) } -test('defaultTip', () => expect(defaultTip).toBe(parseGwei('1.5'))) - describe('prepareRequest', () => { test('default', async () => { await setup() @@ -435,6 +436,82 @@ describe('prepareRequest', () => { `) }) + test('chain default priority fee', async () => { + await setup() + + const block = await getBlock.getBlock(publicClient) + + // client chain + const client_1 = createWalletClient({ + chain: { + ...anvilChain, + fees: { + getDefaultPriorityFee: () => parseGwei('69'), + }, + }, + transport: http(localHttpUrl), + }) + const request_1 = await prepareRequest(client_1, { + account: privateKeyToAccount(sourceAccount.privateKey), + to: targetAccount.address, + value: parseEther('1'), + }) + expect(request_1.maxFeePerGas).toEqual( + (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), + ) + + // client chain (async) + const client_2 = createWalletClient({ + chain: { + ...anvilChain, + fees: { + getDefaultPriorityFee: async () => parseGwei('69'), + }, + }, + transport: http(localHttpUrl), + }) + const request_2 = await prepareRequest(client_2, { + account: privateKeyToAccount(sourceAccount.privateKey), + to: targetAccount.address, + value: parseEther('1'), + }) + expect(request_2.maxFeePerGas).toEqual( + (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), + ) + + // chain override + const request_3 = await prepareRequest(walletClient, { + account: privateKeyToAccount(sourceAccount.privateKey), + chain: { + ...anvilChain, + fees: { + getDefaultPriorityFee: () => parseGwei('69'), + }, + }, + to: targetAccount.address, + value: parseEther('1'), + }) + expect(request_3.maxFeePerGas).toEqual( + (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), + ) + + // chain override (async) + const request_4 = await prepareRequest(walletClient, { + account: privateKeyToAccount(sourceAccount.privateKey), + chain: { + ...anvilChain, + fees: { + getDefaultPriorityFee: async () => parseGwei('69'), + }, + }, + to: targetAccount.address, + value: parseEther('1'), + }) + expect(request_4.maxFeePerGas).toEqual( + (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), + ) + }) + test('no account', async () => { await setup() diff --git a/src/utils/transaction/prepareRequest.ts b/src/utils/transaction/prepareRequest.ts index 0e467ad856..b7ac07190d 100644 --- a/src/utils/transaction/prepareRequest.ts +++ b/src/utils/transaction/prepareRequest.ts @@ -51,8 +51,6 @@ export type PrepareRequestReturnType< nonce: SendTransactionParameters['nonce'] } -export const defaultTip = 1_500_000_000n // 1.5 gwei - export async function prepareRequest< TChain extends Chain | undefined, TAccount extends Account | undefined, @@ -64,6 +62,7 @@ export async function prepareRequest< ): Promise> { const { account: account_, + chain = client.chain, gas, gasPrice, maxFeePerGas, @@ -87,22 +86,28 @@ export async function prepareRequest< typeof block.baseFeePerGas === 'bigint' && typeof gasPrice === 'undefined' ) { + const defaultPriorityFee = + (await chain?.fees?.getDefaultPriorityFee({ + block, + request: request as PrepareRequestParameters, + })) || 1_500_000_000n // 1.5 gwei + // EIP-1559 fees if (typeof maxFeePerGas === 'undefined') { // Set a buffer of 1.2x on top of the base fee to account for fluctuations. - request.maxPriorityFeePerGas = maxPriorityFeePerGas ?? defaultTip + request.maxPriorityFeePerGas = maxPriorityFeePerGas ?? defaultPriorityFee request.maxFeePerGas = (block.baseFeePerGas * 120n) / 100n + request.maxPriorityFeePerGas } else { if ( typeof maxPriorityFeePerGas === 'undefined' && - maxFeePerGas < defaultTip + maxFeePerGas < defaultPriorityFee ) throw new BaseError( '`maxFeePerGas` cannot be less than the default `maxPriorityFeePerGas` (1.5 gwei).', ) request.maxFeePerGas = maxFeePerGas - request.maxPriorityFeePerGas = maxPriorityFeePerGas ?? defaultTip + request.maxPriorityFeePerGas = maxPriorityFeePerGas ?? defaultPriorityFee } } else if (typeof gasPrice === 'undefined') { // Legacy fees From 968b599d0b8d6ae3624f9c2732817c861897096c Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 07:38:39 +0200 Subject: [PATCH 07/15] refactor: cleanup formatters & serializers types --- src/chains/celo/formatters.ts | 4 +- src/chains/celo/serializers.ts | 4 +- src/chains/optimism/formatters.ts | 4 +- src/index.ts | 32 +++++++--- src/types/chain.ts | 72 ++++++++++++++++++++-- src/types/formatter.ts | 42 ------------- src/types/serializer.ts | 19 ------ src/utils/chain.ts | 5 +- src/utils/formatters/block.ts | 10 +-- src/utils/formatters/extract.ts | 4 +- src/utils/formatters/transaction.ts | 10 +-- src/utils/formatters/transactionReceipt.ts | 12 +++- src/utils/formatters/transactionRequest.ts | 12 +++- 13 files changed, 128 insertions(+), 102 deletions(-) delete mode 100644 src/types/formatter.ts delete mode 100644 src/types/serializer.ts diff --git a/src/chains/celo/formatters.ts b/src/chains/celo/formatters.ts index 13b12fd69f..b60516cc4b 100644 --- a/src/chains/celo/formatters.ts +++ b/src/chains/celo/formatters.ts @@ -1,4 +1,4 @@ -import { type Formatters } from '../../types/formatter.js' +import { type ChainFormatters } from '../../types/chain.js' import type { Hash } from '../../types/misc.js' import { hexToBigInt } from '../../utils/encoding/fromHex.js' import { numberToHex } from '../../utils/encoding/toHex.js' @@ -82,4 +82,4 @@ export const formattersCelo = { } }, }), -} as const satisfies Formatters +} as const satisfies ChainFormatters diff --git a/src/chains/celo/serializers.ts b/src/chains/celo/serializers.ts index 0c30b6e83f..9c685b7abe 100644 --- a/src/chains/celo/serializers.ts +++ b/src/chains/celo/serializers.ts @@ -4,9 +4,9 @@ import { InvalidAddressError } from '../../errors/address.js' import { BaseError } from '../../errors/base.js' import { InvalidChainIdError } from '../../errors/chain.js' import { FeeCapTooHighError, TipAboveFeeCapError } from '../../errors/node.js' +import type { ChainSerializers } from '../../types/chain.js' import type { FeeValuesEIP1559 } from '../../types/fee.js' import type { Signature } from '../../types/misc.js' -import type { Serializers } from '../../types/serializer.js' import type { AccessList, TransactionSerializable, @@ -39,7 +39,7 @@ export const serializeTransactionCelo: SerializeTransactionFn< export const serializersCelo = { transaction: serializeTransactionCelo, -} as const satisfies Serializers +} as const satisfies ChainSerializers ////////////////////////////////////////////////////////////////////////////// // Types diff --git a/src/chains/optimism/formatters.ts b/src/chains/optimism/formatters.ts index c732aff514..2fbfebfd24 100644 --- a/src/chains/optimism/formatters.ts +++ b/src/chains/optimism/formatters.ts @@ -1,4 +1,4 @@ -import { type Formatters } from '../../types/formatter.js' +import { type ChainFormatters } from '../../types/chain.js' import type { Hash } from '../../types/misc.js' import { type RpcTransaction } from '../../types/rpc.js' import { hexToBigInt } from '../../utils/encoding/fromHex.js' @@ -71,4 +71,4 @@ export const formattersOptimism = { } }, }), -} as const satisfies Formatters +} as const satisfies ChainFormatters diff --git a/src/index.ts b/src/index.ts index 54c0b49c61..9032168231 100644 --- a/src/index.ts +++ b/src/index.ts @@ -571,7 +571,30 @@ export type { Signature, SignableMessage, } from './types/misc.js' -export type { Chain } from './types/chain.js' +export type { + Chain, + ChainContract, + ChainFees, + ChainFormatter, + /** @deprecated use `ChainFormatter` instead. */ + ChainFormatter as Formatter, + ChainFormatters, + /** @deprecated use `ChainFormatters` instead. */ + ChainFormatters as Formatters, + ChainSerializers, + /** @deprecated use `ChainSerializers` instead. */ + ChainSerializers as Serializers, + ExtractChainFormatterExclude, + /** @deprecated use `ExtractChainFormatterExclude` instead. */ + ExtractChainFormatterExclude as ExtractFormatterExclude, + ExtractChainFormatterParameters, + /** @deprecated use `ExtractChainFormatterParameters` instead. */ + ExtractChainFormatterParameters as ExtractFormatterParameters, + ExtractChainFormatterReturnType, + /** @deprecated use `ExtractChainFormatterReturnType` instead. */ + ExtractChainFormatterReturnType as ExtractFormatterReturnType, + GetChain, +} from './types/chain.js' export type { AddEthereumChainParameter, EIP1193Events, @@ -599,13 +622,6 @@ export type { FeeValuesLegacy, } from './types/fee.js' export type { Filter } from './types/filter.js' -export type { - Formatter, - Formatters, - ExtractFormatterParameters, - ExtractFormatterReturnType, -} from './types/formatter.js' -export type { Serializers } from './types/serializer.js' export type { GetTypedDataDomain, GetTypedDataMessage, diff --git a/src/types/chain.ts b/src/types/chain.ts index 0317b38355..c26808e5c4 100644 --- a/src/types/chain.ts +++ b/src/types/chain.ts @@ -2,14 +2,17 @@ import type { Address } from 'abitype' import type { FormattedBlock } from '../utils/formatters/block.js' import type { PrepareRequestParameters } from '../utils/transaction/prepareRequest.js' -import type { Formatters } from './formatter.js' -import type { Serializers } from './serializer.js' +import type { SerializeTransactionFn } from '../utils/transaction/serializeTransaction.js' +import type { + TransactionSerializable, + TransactionSerializableGeneric, +} from './transaction.js' import type { IsUndefined } from './utils.js' export type Chain< - formatters extends Formatters | undefined = Formatters | undefined, - serializers extends Serializers | undefined = - | Serializers + formatters extends ChainFormatters | undefined = ChainFormatters | undefined, + serializers extends ChainSerializers | undefined = + | ChainSerializers | undefined, > = import('@wagmi/chains').Chain & { formatters?: formatters | undefined @@ -23,7 +26,7 @@ export type ChainContract = { } export type ChainFees< - formatters extends Formatters | undefined = Formatters | undefined, + formatters extends ChainFormatters | undefined = ChainFormatters | undefined, > = { getDefaultPriorityFee(args: { block: FormattedBlock<{ formatters: formatters }> @@ -33,6 +36,63 @@ export type ChainFees< }): Promise | bigint } +export type ChainFormatters = { + block?: ChainFormatter<'block'> + transaction?: ChainFormatter<'transaction'> + transactionReceipt?: ChainFormatter<'transactionReceipt'> + transactionRequest?: ChainFormatter<'transactionRequest'> +} + +export type ChainFormatter = { + format: (args: any) => any + type: TType +} + +export type ChainSerializers< + TFormatters extends ChainFormatters | undefined = undefined, +> = { + transaction?: SerializeTransactionFn< + TFormatters extends ChainFormatters + ? TFormatters['transactionRequest'] extends ChainFormatter + ? TransactionSerializableGeneric & + Parameters[0] + : TransactionSerializable + : TransactionSerializable + > +} + +///////////////////////////////////////////////////////////////////// +// Utils + +export type ExtractChainFormatterExclude< + TChain extends { formatters?: Chain['formatters'] } | undefined, + TType extends keyof ChainFormatters, +> = TChain extends { formatters?: infer _Formatters extends ChainFormatters } + ? _Formatters[TType] extends { exclude: infer Exclude } + ? Extract[number] + : '' + : '' + +export type ExtractChainFormatterParameters< + TChain extends { formatters?: Chain['formatters'] } | undefined, + TType extends keyof ChainFormatters, + TFallback, +> = TChain extends { formatters?: infer _Formatters extends ChainFormatters } + ? _Formatters[TType] extends ChainFormatter + ? Parameters<_Formatters[TType]['format']>[0] + : TFallback + : TFallback + +export type ExtractChainFormatterReturnType< + TChain extends { formatters?: Chain['formatters'] } | undefined, + TType extends keyof ChainFormatters, + TFallback, +> = TChain extends { formatters?: infer _Formatters extends ChainFormatters } + ? _Formatters[TType] extends ChainFormatter + ? ReturnType<_Formatters[TType]['format']> + : TFallback + : TFallback + export type GetChain< TChain extends Chain | undefined, TChainOverride extends Chain | undefined = undefined, diff --git a/src/types/formatter.ts b/src/types/formatter.ts deleted file mode 100644 index 9bb1c4fb55..0000000000 --- a/src/types/formatter.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { Chain } from './chain.js' - -export type Formatter = { - format: (args: any) => any - type: TType -} - -export type Formatters = { - block?: Formatter<'block'> - transaction?: Formatter<'transaction'> - transactionReceipt?: Formatter<'transactionReceipt'> - transactionRequest?: Formatter<'transactionRequest'> -} - -export type ExtractFormatterExclude< - TChain extends { formatters?: Chain['formatters'] } | undefined, - TType extends keyof Formatters, -> = TChain extends { formatters?: infer _Formatters extends Formatters } - ? _Formatters[TType] extends { exclude: infer Exclude } - ? Extract[number] - : '' - : '' - -export type ExtractFormatterParameters< - TChain extends { formatters?: Chain['formatters'] } | undefined, - TType extends keyof Formatters, - TFallback, -> = TChain extends { formatters?: infer _Formatters extends Formatters } - ? _Formatters[TType] extends Formatter - ? Parameters<_Formatters[TType]['format']>[0] - : TFallback - : TFallback - -export type ExtractFormatterReturnType< - TChain extends { formatters?: Chain['formatters'] } | undefined, - TType extends keyof Formatters, - TFallback, -> = TChain extends { formatters?: infer _Formatters extends Formatters } - ? _Formatters[TType] extends Formatter - ? ReturnType<_Formatters[TType]['format']> - : TFallback - : TFallback diff --git a/src/types/serializer.ts b/src/types/serializer.ts deleted file mode 100644 index cbc6ab07b5..0000000000 --- a/src/types/serializer.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { - TransactionSerializable, - TransactionSerializableGeneric, -} from '../types/transaction.js' -import type { SerializeTransactionFn } from '../utils/transaction/serializeTransaction.js' -import type { Formatter, Formatters } from './formatter.js' - -export type Serializers< - TFormatters extends Formatters | undefined = undefined, -> = { - transaction?: SerializeTransactionFn< - TFormatters extends Formatters - ? TFormatters['transactionRequest'] extends Formatter - ? TransactionSerializableGeneric & - Parameters[0] - : TransactionSerializable - : TransactionSerializable - > -} diff --git a/src/utils/chain.ts b/src/utils/chain.ts index 9df74df893..69462f6d4d 100644 --- a/src/utils/chain.ts +++ b/src/utils/chain.ts @@ -3,8 +3,7 @@ import { ChainMismatchError, ChainNotFoundError, } from '../errors/chain.js' -import type { Chain, ChainContract } from '../types/chain.js' -import type { Formatters } from '../types/formatter.js' +import type { Chain, ChainContract, ChainFormatters } from '../types/chain.js' export type AssertCurrentChainParameters = { chain?: Chain @@ -22,7 +21,7 @@ export function assertCurrentChain({ export function defineChain< TChain extends Chain, - TFormatters extends Formatters, + TFormatters extends ChainFormatters, >( chain: TChain, config?: Pick, 'fees' | 'formatters' | 'serializers'>, diff --git a/src/utils/formatters/block.ts b/src/utils/formatters/block.ts index 2ab885ad0f..c7f7f4554c 100644 --- a/src/utils/formatters/block.ts +++ b/src/utils/formatters/block.ts @@ -1,9 +1,9 @@ import type { Block, BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' import type { - ExtractFormatterExclude, - ExtractFormatterReturnType, -} from '../../types/formatter.js' + ExtractChainFormatterExclude, + ExtractChainFormatterReturnType, +} from '../../types/chain.js' import type { Hash } from '../../types/misc.js' import type { RpcBlock } from '../../types/rpc.js' import type { Prettify } from '../../types/utils.js' @@ -19,13 +19,13 @@ export type FormattedBlock< | undefined, TIncludeTransactions extends boolean = boolean, TBlockTag extends BlockTag = BlockTag, - _FormatterReturnType = ExtractFormatterReturnType< + _FormatterReturnType = ExtractChainFormatterReturnType< TChain, 'block', Block >, _ExcludedPendingDependencies extends string = BlockPendingDependencies & - ExtractFormatterExclude, + ExtractChainFormatterExclude, _Formatted = Omit<_FormatterReturnType, BlockPendingDependencies> & { [K in _ExcludedPendingDependencies]: never } & Pick< diff --git a/src/utils/formatters/extract.ts b/src/utils/formatters/extract.ts index 9cd5aaa701..904eeff3ee 100644 --- a/src/utils/formatters/extract.ts +++ b/src/utils/formatters/extract.ts @@ -1,11 +1,11 @@ -import type { Formatter } from '../../types/formatter.js' +import type { ChainFormatter } from '../../types/chain.js' /** * @description Picks out the keys from `value` that exist in the formatter. */ export function extract( value: Record, - { format }: { format?: Formatter['format'] }, + { format }: { format?: ChainFormatter['format'] }, ) { if (!format) return {} const keys = Object.keys(format({})) diff --git a/src/utils/formatters/transaction.ts b/src/utils/formatters/transaction.ts index b3e89f35d5..31d5bb7585 100644 --- a/src/utils/formatters/transaction.ts +++ b/src/utils/formatters/transaction.ts @@ -1,9 +1,9 @@ import type { BlockTag } from '../../types/block.js' import type { Chain } from '../../types/chain.js' import type { - ExtractFormatterExclude, - ExtractFormatterReturnType, -} from '../../types/formatter.js' + ExtractChainFormatterExclude, + ExtractChainFormatterReturnType, +} from '../../types/chain.js' import type { RpcTransaction } from '../../types/rpc.js' import type { Transaction } from '../../types/transaction.js' import type { UnionOmit } from '../../types/utils.js' @@ -20,13 +20,13 @@ export type FormattedTransaction< | { formatters?: Chain['formatters'] } | undefined, TBlockTag extends BlockTag = BlockTag, - _FormatterReturnType = ExtractFormatterReturnType< + _FormatterReturnType = ExtractChainFormatterReturnType< TChain, 'transaction', Transaction >, _ExcludedPendingDependencies extends string = TransactionPendingDependencies & - ExtractFormatterExclude, + ExtractChainFormatterExclude, > = UnionOmit<_FormatterReturnType, TransactionPendingDependencies> & { [K in _ExcludedPendingDependencies]: never } & Pick< diff --git a/src/utils/formatters/transactionReceipt.ts b/src/utils/formatters/transactionReceipt.ts index c8a6554523..52ae66ddd8 100644 --- a/src/utils/formatters/transactionReceipt.ts +++ b/src/utils/formatters/transactionReceipt.ts @@ -1,5 +1,7 @@ -import type { Chain } from '../../types/chain.js' -import type { ExtractFormatterReturnType } from '../../types/formatter.js' +import type { + Chain, + ExtractChainFormatterReturnType, +} from '../../types/chain.js' import type { RpcTransactionReceipt } from '../../types/rpc.js' import type { TransactionReceipt } from '../../types/transaction.js' import { hexToNumber } from '../encoding/fromHex.js' @@ -10,7 +12,11 @@ import { transactionType } from './transaction.js' export type FormattedTransactionReceipt< TChain extends Chain | undefined = Chain | undefined, -> = ExtractFormatterReturnType +> = ExtractChainFormatterReturnType< + TChain, + 'transactionReceipt', + TransactionReceipt +> const statuses = { '0x0': 'reverted', diff --git a/src/utils/formatters/transactionRequest.ts b/src/utils/formatters/transactionRequest.ts index abf3861354..c376220c8a 100644 --- a/src/utils/formatters/transactionRequest.ts +++ b/src/utils/formatters/transactionRequest.ts @@ -1,5 +1,7 @@ -import type { Chain } from '../../types/chain.js' -import type { ExtractFormatterParameters } from '../../types/formatter.js' +import type { + Chain, + ExtractChainFormatterParameters, +} from '../../types/chain.js' import type { RpcTransactionRequest } from '../../types/rpc.js' import type { TransactionRequest } from '../../types/transaction.js' import { numberToHex } from '../encoding/toHex.js' @@ -7,7 +9,11 @@ import { defineFormatter } from './formatter.js' export type FormattedTransactionRequest< TChain extends Chain | undefined = Chain | undefined, -> = ExtractFormatterParameters +> = ExtractChainFormatterParameters< + TChain, + 'transactionRequest', + TransactionRequest +> export function formatTransactionRequest( transactionRequest: Partial, From 992766afb5fd61afe7650fc89b3cbfec3e0749c7 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 07:44:55 +0200 Subject: [PATCH 08/15] chore: fix --- src/actions/public/estimateGas.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/actions/public/estimateGas.ts b/src/actions/public/estimateGas.ts index f773ddfde5..c71174d7a9 100644 --- a/src/actions/public/estimateGas.ts +++ b/src/actions/public/estimateGas.ts @@ -20,7 +20,10 @@ import { type AssertRequestParameters, assertRequest, } from '../../utils/transaction/assertRequest.js' -import { prepareRequest } from '../../utils/transaction/prepareRequest.js' +import { + type PrepareRequestParameters, + prepareRequest, +} from '../../utils/transaction/prepareRequest.js' export type FormattedEstimateGas< TChain extends Chain | undefined = Chain | undefined, @@ -104,7 +107,10 @@ export async function estimateGas< ...rest } = account.type === 'local' - ? await prepareRequest(client, args as any) + ? ((await prepareRequest( + client, + args as PrepareRequestParameters, + )) as EstimateGasParameters) : args const blockNumberHex = blockNumber ? numberToHex(blockNumber) : undefined From fd10d30dd78ed850e89c095cb8c06c7c554d53d8 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 16:14:05 +0200 Subject: [PATCH 09/15] refactor --- src/types/chain.ts | 66 +++++++++++++++++++++++----------------------- src/utils/chain.ts | 8 +++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/types/chain.ts b/src/types/chain.ts index c26808e5c4..bd8e3d2134 100644 --- a/src/types/chain.ts +++ b/src/types/chain.ts @@ -7,7 +7,7 @@ import type { TransactionSerializable, TransactionSerializableGeneric, } from './transaction.js' -import type { IsUndefined } from './utils.js' +import type { IsUndefined, Prettify } from './utils.js' export type Chain< formatters extends ChainFormatters | undefined = ChainFormatters | undefined, @@ -29,7 +29,7 @@ export type ChainFees< formatters extends ChainFormatters | undefined = ChainFormatters | undefined, > = { getDefaultPriorityFee(args: { - block: FormattedBlock<{ formatters: formatters }> + block: Prettify> request: PrepareRequestParameters< Omit & { formatters: formatters } > @@ -43,19 +43,19 @@ export type ChainFormatters = { transactionRequest?: ChainFormatter<'transactionRequest'> } -export type ChainFormatter = { +export type ChainFormatter = { format: (args: any) => any - type: TType + type: type } export type ChainSerializers< - TFormatters extends ChainFormatters | undefined = undefined, + formatters extends ChainFormatters | undefined = undefined, > = { transaction?: SerializeTransactionFn< - TFormatters extends ChainFormatters - ? TFormatters['transactionRequest'] extends ChainFormatter + formatters extends ChainFormatters + ? formatters['transactionRequest'] extends ChainFormatter ? TransactionSerializableGeneric & - Parameters[0] + Parameters[0] : TransactionSerializable : TransactionSerializable > @@ -65,37 +65,37 @@ export type ChainSerializers< // Utils export type ExtractChainFormatterExclude< - TChain extends { formatters?: Chain['formatters'] } | undefined, - TType extends keyof ChainFormatters, -> = TChain extends { formatters?: infer _Formatters extends ChainFormatters } - ? _Formatters[TType] extends { exclude: infer Exclude } + chain extends { formatters?: Chain['formatters'] } | undefined, + type extends keyof ChainFormatters, +> = chain extends { formatters?: infer _Formatters extends ChainFormatters } + ? _Formatters[type] extends { exclude: infer Exclude } ? Extract[number] : '' : '' export type ExtractChainFormatterParameters< - TChain extends { formatters?: Chain['formatters'] } | undefined, - TType extends keyof ChainFormatters, - TFallback, -> = TChain extends { formatters?: infer _Formatters extends ChainFormatters } - ? _Formatters[TType] extends ChainFormatter - ? Parameters<_Formatters[TType]['format']>[0] - : TFallback - : TFallback + chain extends { formatters?: Chain['formatters'] } | undefined, + type extends keyof ChainFormatters, + fallback, +> = chain extends { formatters?: infer _Formatters extends ChainFormatters } + ? _Formatters[type] extends ChainFormatter + ? Parameters<_Formatters[type]['format']>[0] + : fallback + : fallback export type ExtractChainFormatterReturnType< - TChain extends { formatters?: Chain['formatters'] } | undefined, - TType extends keyof ChainFormatters, - TFallback, -> = TChain extends { formatters?: infer _Formatters extends ChainFormatters } - ? _Formatters[TType] extends ChainFormatter - ? ReturnType<_Formatters[TType]['format']> - : TFallback - : TFallback + chain extends { formatters?: Chain['formatters'] } | undefined, + type extends keyof ChainFormatters, + fallback, +> = chain extends { formatters?: infer _Formatters extends ChainFormatters } + ? _Formatters[type] extends ChainFormatter + ? ReturnType<_Formatters[type]['format']> + : fallback + : fallback export type GetChain< - TChain extends Chain | undefined, - TChainOverride extends Chain | undefined = undefined, -> = IsUndefined extends true - ? { chain: TChainOverride | null } - : { chain?: TChainOverride | null } + chain extends Chain | undefined, + chainOverride extends Chain | undefined = undefined, +> = IsUndefined extends true + ? { chain: chainOverride | null } + : { chain?: chainOverride | null } diff --git a/src/utils/chain.ts b/src/utils/chain.ts index 69462f6d4d..e213adb970 100644 --- a/src/utils/chain.ts +++ b/src/utils/chain.ts @@ -20,11 +20,11 @@ export function assertCurrentChain({ } export function defineChain< - TChain extends Chain, - TFormatters extends ChainFormatters, + chain extends Chain, + formatters extends ChainFormatters, >( - chain: TChain, - config?: Pick, 'fees' | 'formatters' | 'serializers'>, + chain: chain, + config?: Pick, 'fees' | 'formatters' | 'serializers'>, ) { return { ...chain, From 08a5a8ba4de3b943171ddc46edacf46b7be42bcb Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 16:17:27 +0200 Subject: [PATCH 10/15] chore: snapshots --- src/clients/createClient.test.ts | 3 +++ src/clients/createPublicClient.test.ts | 3 +++ src/clients/createTestClient.test.ts | 4 ++++ src/clients/createWalletClient.test.ts | 2 ++ src/utils/chain.test.ts | 1 + 5 files changed, 13 insertions(+) diff --git a/src/clients/createClient.test.ts b/src/clients/createClient.test.ts index c3171db7ce..0631ea009c 100644 --- a/src/clients/createClient.test.ts +++ b/src/clients/createClient.test.ts @@ -64,6 +64,7 @@ describe('transports', () => { "batch": undefined, "cacheTime": 4000, "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -120,6 +121,7 @@ describe('transports', () => { "batch": undefined, "cacheTime": 4000, "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -422,6 +424,7 @@ describe('extends', () => { "cacheTime": 4000, "call": [Function], "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", diff --git a/src/clients/createPublicClient.test.ts b/src/clients/createPublicClient.test.ts index 1c126c83ea..b03cb13d00 100644 --- a/src/clients/createPublicClient.test.ts +++ b/src/clients/createPublicClient.test.ts @@ -143,6 +143,7 @@ describe('transports', () => { "cacheTime": 4000, "call": [Function], "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -239,6 +240,7 @@ describe('transports', () => { "cacheTime": 4000, "call": [Function], "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -410,6 +412,7 @@ test('extend', () => { "cacheTime": 4000, "call": [Function], "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", diff --git a/src/clients/createTestClient.test.ts b/src/clients/createTestClient.test.ts index f7ec50d095..573cd3b096 100644 --- a/src/clients/createTestClient.test.ts +++ b/src/clients/createTestClient.test.ts @@ -33,6 +33,7 @@ test('creates', () => { "batch": undefined, "cacheTime": 4000, "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -119,6 +120,7 @@ describe('transports', () => { "batch": undefined, "cacheTime": 4000, "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -205,6 +207,7 @@ describe('transports', () => { "batch": undefined, "cacheTime": 4000, "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -300,6 +303,7 @@ test('extend', () => { "cacheTime": 4000, "call": [Function], "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", diff --git a/src/clients/createWalletClient.test.ts b/src/clients/createWalletClient.test.ts index 9f288b5229..6212fb9e82 100644 --- a/src/clients/createWalletClient.test.ts +++ b/src/clients/createWalletClient.test.ts @@ -280,6 +280,7 @@ describe('args: transport', () => { "batch": undefined, "cacheTime": 4000, "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", @@ -357,6 +358,7 @@ test('extend', () => { "cacheTime": 4000, "call": [Function], "chain": { + "fees": undefined, "formatters": undefined, "id": 1337, "name": "Localhost", diff --git a/src/utils/chain.test.ts b/src/utils/chain.test.ts index c78bbaea62..1f2280b8db 100644 --- a/src/utils/chain.test.ts +++ b/src/utils/chain.test.ts @@ -72,6 +72,7 @@ describe('defineChain', () => { }), ).toMatchInlineSnapshot(` { + "fees": undefined, "formatters": undefined, "id": 42220, "name": "Celo", From 4ba6bb113a9c388077ac76d9ebaf69dc0e49e597 Mon Sep 17 00:00:00 2001 From: jxom Date: Wed, 9 Aug 2023 16:20:08 +0200 Subject: [PATCH 11/15] Update sweet-lemons-explain.md --- .changeset/sweet-lemons-explain.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.changeset/sweet-lemons-explain.md b/.changeset/sweet-lemons-explain.md index f165be2d5b..67130dc2d8 100644 --- a/.changeset/sweet-lemons-explain.md +++ b/.changeset/sweet-lemons-explain.md @@ -5,13 +5,12 @@ Added `fees` to `chain` config that includes a `getDefaultPriorityFee` for setting a default priority fee for a chain. ```ts -import type { Chain } from 'viem' +import { defineChain } from 'viem' +import { zora } from 'viem/chains' -export const example = { - // ... +export const example = defineChain(zora, { fees: { getDefaultPriorityFee: () => 1_000_000n, // 0.001 gwei }, - // ... -} as const satifies Chain +}) ``` From 29a43176b28f7fe4533d997a913b393c398f8523 Mon Sep 17 00:00:00 2001 From: jxom Date: Wed, 9 Aug 2023 16:22:29 +0200 Subject: [PATCH 12/15] Update sweet-lemons-explain.md --- .changeset/sweet-lemons-explain.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.changeset/sweet-lemons-explain.md b/.changeset/sweet-lemons-explain.md index 67130dc2d8..f165be2d5b 100644 --- a/.changeset/sweet-lemons-explain.md +++ b/.changeset/sweet-lemons-explain.md @@ -5,12 +5,13 @@ Added `fees` to `chain` config that includes a `getDefaultPriorityFee` for setting a default priority fee for a chain. ```ts -import { defineChain } from 'viem' -import { zora } from 'viem/chains' +import type { Chain } from 'viem' -export const example = defineChain(zora, { +export const example = { + // ... fees: { getDefaultPriorityFee: () => 1_000_000n, // 0.001 gwei }, -}) + // ... +} as const satifies Chain ``` From 951c8dab1c09cbee7673212643fa93e5c013c1af Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Wed, 9 Aug 2023 16:36:28 +0200 Subject: [PATCH 13/15] refactor --- src/types/chain.ts | 11 ++++++----- src/utils/chain.ts | 25 ++++++++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/types/chain.ts b/src/types/chain.ts index bd8e3d2134..97aa95f3a2 100644 --- a/src/types/chain.ts +++ b/src/types/chain.ts @@ -11,12 +11,13 @@ import type { IsUndefined, Prettify } from './utils.js' export type Chain< formatters extends ChainFormatters | undefined = ChainFormatters | undefined, - serializers extends ChainSerializers | undefined = - | ChainSerializers - | undefined, -> = import('@wagmi/chains').Chain & { +> = import('@wagmi/chains').Chain & ChainConfig + +export type ChainConfig< + formatters extends ChainFormatters | undefined = ChainFormatters | undefined, +> = { formatters?: formatters | undefined - serializers?: serializers | undefined + serializers?: ChainSerializers | undefined fees?: ChainFees | undefined } diff --git a/src/utils/chain.ts b/src/utils/chain.ts index e213adb970..31057c2099 100644 --- a/src/utils/chain.ts +++ b/src/utils/chain.ts @@ -3,7 +3,13 @@ import { ChainMismatchError, ChainNotFoundError, } from '../errors/chain.js' -import type { Chain, ChainContract, ChainFormatters } from '../types/chain.js' +import type { + Chain, + ChainConfig, + ChainContract, + ChainFormatters, +} from '../types/chain.js' +import type { Assign } from '../types/utils.js' export type AssertCurrentChainParameters = { chain?: Chain @@ -24,14 +30,19 @@ export function defineChain< formatters extends ChainFormatters, >( chain: chain, - config?: Pick, 'fees' | 'formatters' | 'serializers'>, -) { + config?: ChainConfig, +): Assign> { + const { + fees = chain.fees, + formatters = chain.formatters, + serializers = chain.serializers, + } = config || {} return { ...chain, - fees: config?.fees, - formatters: config?.formatters, - serializers: config?.serializers, - } + fees, + formatters, + serializers, + } as unknown as Assign> } export function getChainContractAddress({ From ee18f349ce658d1bd95d2af1cc778cac787d5691 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Thu, 10 Aug 2023 05:40:27 +0200 Subject: [PATCH 14/15] refactor --- .changeset/sweet-lemons-explain.md | 8 ++- src/chains/optimism/fees.ts | 2 +- src/types/chain.ts | 14 +++--- src/utils/transaction/prepareRequest.test.ts | 51 +++++++++++++++++--- src/utils/transaction/prepareRequest.ts | 15 ++++-- 5 files changed, 68 insertions(+), 22 deletions(-) diff --git a/.changeset/sweet-lemons-explain.md b/.changeset/sweet-lemons-explain.md index f165be2d5b..889f2f819c 100644 --- a/.changeset/sweet-lemons-explain.md +++ b/.changeset/sweet-lemons-explain.md @@ -2,7 +2,7 @@ "viem": minor --- -Added `fees` to `chain` config that includes a `getDefaultPriorityFee` for setting a default priority fee for a chain. +Added `fees` to `chain` config that includes a `defaultPriorityFee` for setting a default priority fee (`maxFeePerGas`) for a chain. ```ts import type { Chain } from 'viem' @@ -10,7 +10,11 @@ import type { Chain } from 'viem' export const example = { // ... fees: { - getDefaultPriorityFee: () => 1_000_000n, // 0.001 gwei + defaultPriorityFee: 1_000_000n, // 0.001 gwei + // or + async defaultPriorityFee() { + // ... some async behavior to derive the fee. + } }, // ... } as const satifies Chain diff --git a/src/chains/optimism/fees.ts b/src/chains/optimism/fees.ts index d388db0d28..bf5966a3c9 100644 --- a/src/chains/optimism/fees.ts +++ b/src/chains/optimism/fees.ts @@ -1,5 +1,5 @@ import type { ChainFees } from '../../types/chain.js' export const feesOptimism = { - getDefaultPriorityFee: () => 1_000_000n, // 0.001 gwei + defaultPriorityFee: 1_000_000n, // 0.001 gwei } as const satisfies ChainFees diff --git a/src/types/chain.ts b/src/types/chain.ts index 97aa95f3a2..cf0cf06c35 100644 --- a/src/types/chain.ts +++ b/src/types/chain.ts @@ -29,12 +29,14 @@ export type ChainContract = { export type ChainFees< formatters extends ChainFormatters | undefined = ChainFormatters | undefined, > = { - getDefaultPriorityFee(args: { - block: Prettify> - request: PrepareRequestParameters< - Omit & { formatters: formatters } - > - }): Promise | bigint + defaultPriorityFee: + | bigint + | ((args: { + block: Prettify> + request: PrepareRequestParameters< + Omit & { formatters: formatters } + > + }) => Promise | bigint) } export type ChainFormatters = { diff --git a/src/utils/transaction/prepareRequest.test.ts b/src/utils/transaction/prepareRequest.test.ts index 140cd870c8..05b0b2900f 100644 --- a/src/utils/transaction/prepareRequest.test.ts +++ b/src/utils/transaction/prepareRequest.test.ts @@ -446,7 +446,7 @@ describe('prepareRequest', () => { chain: { ...anvilChain, fees: { - getDefaultPriorityFee: () => parseGwei('69'), + defaultPriorityFee: () => parseGwei('69'), }, }, transport: http(localHttpUrl), @@ -465,7 +465,7 @@ describe('prepareRequest', () => { chain: { ...anvilChain, fees: { - getDefaultPriorityFee: async () => parseGwei('69'), + defaultPriorityFee: async () => parseGwei('69'), }, }, transport: http(localHttpUrl), @@ -479,15 +479,18 @@ describe('prepareRequest', () => { (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), ) - // chain override - const request_3 = await prepareRequest(walletClient, { - account: privateKeyToAccount(sourceAccount.privateKey), + // client chain (bigint) + const client_3 = createWalletClient({ chain: { ...anvilChain, fees: { - getDefaultPriorityFee: () => parseGwei('69'), + defaultPriorityFee: parseGwei('69'), }, }, + transport: http(localHttpUrl), + }) + const request_3 = await prepareRequest(client_3, { + account: privateKeyToAccount(sourceAccount.privateKey), to: targetAccount.address, value: parseEther('1'), }) @@ -495,13 +498,13 @@ describe('prepareRequest', () => { (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), ) - // chain override (async) + // chain override (bigint) const request_4 = await prepareRequest(walletClient, { account: privateKeyToAccount(sourceAccount.privateKey), chain: { ...anvilChain, fees: { - getDefaultPriorityFee: async () => parseGwei('69'), + defaultPriorityFee: () => parseGwei('69'), }, }, to: targetAccount.address, @@ -510,6 +513,38 @@ describe('prepareRequest', () => { expect(request_4.maxFeePerGas).toEqual( (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), ) + + // chain override (async) + const request_5 = await prepareRequest(walletClient, { + account: privateKeyToAccount(sourceAccount.privateKey), + chain: { + ...anvilChain, + fees: { + defaultPriorityFee: async () => parseGwei('69'), + }, + }, + to: targetAccount.address, + value: parseEther('1'), + }) + expect(request_5.maxFeePerGas).toEqual( + (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), + ) + + // chain override (bigint) + const request_6 = await prepareRequest(walletClient, { + account: privateKeyToAccount(sourceAccount.privateKey), + chain: { + ...anvilChain, + fees: { + defaultPriorityFee: parseGwei('69'), + }, + }, + to: targetAccount.address, + value: parseEther('1'), + }) + expect(request_6.maxFeePerGas).toEqual( + (block.baseFeePerGas! * 120n) / 100n + parseGwei('69'), + ) }) test('no account', async () => { diff --git a/src/utils/transaction/prepareRequest.ts b/src/utils/transaction/prepareRequest.ts index b7ac07190d..62e5f7b318 100644 --- a/src/utils/transaction/prepareRequest.ts +++ b/src/utils/transaction/prepareRequest.ts @@ -86,11 +86,16 @@ export async function prepareRequest< typeof block.baseFeePerGas === 'bigint' && typeof gasPrice === 'undefined' ) { - const defaultPriorityFee = - (await chain?.fees?.getDefaultPriorityFee({ - block, - request: request as PrepareRequestParameters, - })) || 1_500_000_000n // 1.5 gwei + let defaultPriorityFee = 1_500_000_000n // 1.5 gwei + if (typeof chain?.fees?.defaultPriorityFee !== 'undefined') { + defaultPriorityFee = + typeof chain.fees.defaultPriorityFee === 'bigint' + ? chain.fees.defaultPriorityFee + : await chain.fees.defaultPriorityFee({ + block, + request: request as PrepareRequestParameters, + }) + } // EIP-1559 fees if (typeof maxFeePerGas === 'undefined') { From 14e61407bf67fd3f5dba4876888846cceca788c8 Mon Sep 17 00:00:00 2001 From: "moxey.eth" Date: Thu, 10 Aug 2023 05:41:05 +0200 Subject: [PATCH 15/15] refactor --- src/utils/chain.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/chain.ts b/src/utils/chain.ts index 31057c2099..132af5d514 100644 --- a/src/utils/chain.ts +++ b/src/utils/chain.ts @@ -30,13 +30,13 @@ export function defineChain< formatters extends ChainFormatters, >( chain: chain, - config?: ChainConfig, + config: ChainConfig = {}, ): Assign> { const { fees = chain.fees, formatters = chain.formatters, serializers = chain.serializers, - } = config || {} + } = config return { ...chain, fees,