Skip to content

Commit

Permalink
fix: .extend performance (#2328)
Browse files Browse the repository at this point in the history
* fix: .extend performance

* chore: format

* test(types): bench

* chore: ignore

* chore: up

* chore: up

---------

Co-authored-by: tmm <tmm@users.noreply.github.com>
  • Loading branch information
tmm and tmm committed May 29, 2024
1 parent 2583af9 commit d946d55
Show file tree
Hide file tree
Showing 22 changed files with 192 additions and 31 deletions.
5 changes: 5 additions & 0 deletions .changeset/stale-dryers-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Improved `.extend` performance with `publicActions` and other large types.
2 changes: 0 additions & 2 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
### Description

<!-- What is this PR solving? Write a clear description or reference the issues it solves (e.g. `fixes #123`). What other alternatives have you explored? Are there any parts you think require more attention from reviewers? -->

<!----------------------------------------------------------------------
Expand Down
19 changes: 12 additions & 7 deletions .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
timeout-minutes: 5
strategy:
matrix:
typescript-version: ['5.3.2', 'latest']
version: ['5.3.3', '5.4.5']

steps:
- name: Clone repository
Expand All @@ -65,7 +65,7 @@ jobs:
- name: Install dependencies
uses: ./.github/actions/install-dependencies

- run: bun i -d typescript@${{ matrix.typescript-version }}
- run: bun i -d typescript@${{ matrix.version }}

- name: Build contracts
shell: bash
Expand All @@ -74,10 +74,15 @@ jobs:
- name: Check types
run: bun run typecheck

- name: Test types
run: bun run test:typecheck
env:
VITE_ANVIL_BLOCK_NUMBER: ${{ vars.VITE_ANVIL_BLOCK_NUMBER }}
- name: Bench types
run: bun run typebench

# Redundant with `pnpm typecheck`
# If Vitest adds special features in the future, e.g. type coverage, can add this back!
# - name: Test types
# run: bun run test:typecheck
# env:
# VITE_ANVIL_BLOCK_NUMBER: ${{ vars.VITE_ANVIL_BLOCK_NUMBER }}

test:
name: Test
Expand Down Expand Up @@ -211,7 +216,7 @@ jobs:
timeout-minutes: 10
strategy:
matrix:
typescript-version: ['5.4.2', 'latest']
typescript-version: ['5.4.5']

steps:
- name: Clone repository
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ _esm
_types
*.local
.DS_Store
.attest
.eslintcache
.next
bench
Expand Down
12 changes: 12 additions & 0 deletions .size-limit.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,17 @@
"path": "./src/_esm/ens/index.js",
"limit": "19 kB",
"import": "{ getEnsAvatar }"
},
{
"name": "viem/siwe",
"path": "./src/_esm/siwe/index.js",
"limit": "28 kB",
"import": "*"
},
{
"name": "viem/siwe (tree-shaking)",
"path": "./src/_esm/siwe/index.js",
"limit": "27 kB",
"import": "{ verifySiweMessage }"
}
]
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
".vercel",
"**/node_modules",
"CHANGELOG.md",
"bun.lockb",
"cache",
"test/contracts",
"test/kzg/*.json",
Expand Down
Binary file modified bun.lockb
Binary file not shown.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@
"test:env:vite": "cd environments/vite && bun run test",
"test:typecheck": "SKIP_GLOBAL_SETUP=true vitest --typecheck.only -c ./test/vitest.config.ts",
"test:ui": "vitest dev -c ./test/vitest.config.ts --ui",
"typebench": "tsx test/typebench.ts --benchPercentThreshold 10 --benchErrorOnThresholdExceeded",
"typecheck": "tsc --noEmit",
"vectors": "bun test vectors/**/*.test.ts",
"vectors:generate": "bun vectors/generate.ts",
"version:update": "bun scripts/updateVersion.ts"
},
"devDependencies": {
"@actions/core": "^1.10.0",
"@actions/github": "^5.1.1",
"@arktype/attest": "0.7.5",
"@biomejs/biome": "^1.7.3",
"@changesets/changelog-github": "^0.4.5",
"@changesets/cli": "^2.23.2",
Expand All @@ -63,6 +63,7 @@
"rimraf": "^4.4.1",
"simple-git-hooks": "^2.8.1",
"size-limit": "^11.1.2",
"tsx": "^4.11.0",
"typescript": "5.4.2",
"vite": "^5.0.7",
"vitest": "^1.0.4"
Expand Down
33 changes: 33 additions & 0 deletions src/actions/public/mulitcall.bench-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { bench } from '@arktype/attest'

import {
baycContractConfig,
usdcContractConfig,
} from '../../../test/src/abis.js'
import { anvil } from '../../chains/index.js'
import { createClient } from '../../clients/createClient.js'
import { http } from '../../clients/transports/http.js'
import { multicall } from './multicall.js'

bench('multicall return type', async () => {
const client = createClient({ chain: anvil, transport: http() })
const res = multicall(client, {
allowFailure: false,
contracts: [
{
...usdcContractConfig,
functionName: 'totalSupply',
},
{
...usdcContractConfig,
functionName: 'balanceOf',
args: ['0x...'],
},
{
...baycContractConfig,
functionName: 'name',
},
],
})
return {} as typeof res
}).types([192503, 'instantiations'])
1 change: 0 additions & 1 deletion src/actions/public/multicall.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,6 @@ test('MulticallParameters', async () => {
| readonly [`0x${string}`]
| readonly [`0x${string}`, `0x${string}`]
| undefined
value?: undefined
}>()
})

Expand Down
17 changes: 17 additions & 0 deletions src/actions/public/readContract.bench-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { bench } from '@arktype/attest'

import { usdcContractConfig } from '../../../test/src/abis.js'
import { anvil } from '../../chains/index.js'
import { createClient } from '../../clients/createClient.js'
import { http } from '../../clients/transports/http.js'
import { readContract } from './readContract.js'

bench('readContract return type', async () => {
const client = createClient({ chain: anvil, transport: http() })
const res = readContract(client, {
...usdcContractConfig,
functionName: 'balanceOf',
args: ['0x...'],
})
return {} as typeof res
}).types([152288, 'instantiations'])
16 changes: 16 additions & 0 deletions src/clients/createPublicClient.bench-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { bench } from '@arktype/attest'

import { createClient } from './createClient.js'
import { createPublicClient } from './createPublicClient.js'
import { publicActions } from './decorators/public.js'
import { http } from './transports/http.js'

bench('createPublicClient', () => {
const client = createPublicClient({ transport: http() })
return {} as typeof client
}).types([13470, 'instantiations'])

bench('createClient.extend + publicActions', () => {
const client = createClient({ transport: http() }).extend(publicActions)
return {} as typeof client
}).types([246356, 'instantiations'])
18 changes: 18 additions & 0 deletions src/clients/createTestClient.bench-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { bench } from '@arktype/attest'

import { createClient } from './createClient.js'
import { createTestClient } from './createTestClient.js'
import { testActions } from './decorators/test.js'
import { http } from './transports/http.js'

bench('createTestClient', () => {
const client = createTestClient({ mode: 'anvil', transport: http() })
return {} as typeof client
}).types([668, 'instantiations'])

bench('createClient.extend + testActions', () => {
const client = createClient({ transport: http() }).extend(
testActions({ mode: 'anvil' }),
)
return {} as typeof client
}).types([6275, 'instantiations'])
16 changes: 16 additions & 0 deletions src/clients/createWalletClient.bench-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { bench } from '@arktype/attest'

import { createClient } from './createClient.js'
import { createWalletClient } from './createWalletClient.js'
import { walletActions } from './decorators/wallet.js'
import { http } from './transports/http.js'

bench('createWalletClient', () => {
const client = createWalletClient({ transport: http() })
return {} as typeof client
}).types([1384, 'instantiations'])

bench('createClient.extend + walletActions', () => {
const client = createClient({ transport: http() }).extend(walletActions)
return {} as typeof client
}).types([193153, 'instantiations'])
42 changes: 38 additions & 4 deletions src/experimental/eip5792/actions/writeContracts.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import type { AbiStateMutability, Narrow } from 'abitype'
import type { Abi, AbiStateMutability, Address, Narrow } from 'abitype'

import type { Client } from '../../../clients/createClient.js'
import type { Transport } from '../../../clients/transports/createTransport.js'
import type { ErrorType } from '../../../errors/utils.js'
import type { Account, GetAccountParameter } from '../../../types/account.js'
import type { Chain, GetChainParameter } from '../../../types/chain.js'
import type { ContractFunctionParameters } from '../../../types/contract.js'
import type {
ContractFunctionArgs,
ContractFunctionName,
GetValue,
UnionWiden,
Widen,
} from '../../../types/contract.js'
import type { MulticallContracts } from '../../../types/multicall.js'
import {
type EncodeFunctionDataErrorType,
Expand All @@ -19,7 +25,8 @@ import {
} from './sendCalls.js'

export type WriteContractsParameters<
contracts extends readonly unknown[] = readonly ContractFunctionParameters[],
contracts extends
readonly unknown[] = readonly WriteContractFunctionParameters[],
chain extends Chain | undefined = Chain | undefined,
account extends Account | undefined = Account | undefined,
chainOverride extends Chain | undefined = Chain | undefined,
Expand Down Expand Up @@ -98,7 +105,7 @@ export async function writeContracts<
chainOverride
>,
): Promise<WriteContractsReturnType> {
const contracts = parameters.contracts as ContractFunctionParameters[]
const contracts = parameters.contracts as WriteContractFunctionParameters[]
const calls = contracts.map((contract) => {
const { address, abi, functionName, args, value } = contract
return {
Expand All @@ -113,3 +120,30 @@ export async function writeContracts<
})
return sendCalls(client, { ...parameters, calls } as SendCallsParameters)
}

export type WriteContractFunctionParameters<
abi extends Abi | readonly unknown[] = Abi,
mutability extends AbiStateMutability = AbiStateMutability,
functionName extends ContractFunctionName<
abi,
mutability
> = ContractFunctionName<abi, mutability>,
args extends ContractFunctionArgs<
abi,
mutability,
functionName
> = ContractFunctionArgs<abi, mutability, functionName>,
///
allFunctionNames = ContractFunctionName<abi, mutability>,
allArgs = ContractFunctionArgs<abi, mutability, functionName>,
// when `args` is inferred to `readonly []` ("inputs": []) or `never` (`abi` declared as `Abi` or not inferrable), allow `args` to be optional.
// important that both branches return same structural type
> = {
address: Address
abi: abi
functionName:
| allFunctionNames // show all options
| (functionName extends allFunctionNames ? functionName : never) // infer value
args?: (abi extends Abi ? UnionWiden<args> : never) | allArgs | undefined
} & (readonly [] extends allArgs ? {} : { args: Widen<args> }) &
GetValue<abi, functionName>
1 change: 1 addition & 0 deletions src/experimental/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export {
type WriteContractsErrorType,
type WriteContractsParameters,
type WriteContractsReturnType,
type WriteContractFunctionParameters,
writeContracts,
} from './eip5792/actions/writeContracts.js'
export {
Expand Down
7 changes: 6 additions & 1 deletion src/jsr.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
},
"publish": {
"include": ["LICENSE", "README.md", "CHANGELOG.md", "**/*.ts"],
"exclude": ["**/*.bench.ts", "**/*.test.ts", "**/*.test-d.ts"]
"exclude": [
"**/*.bench.ts",
"**/*.bench-d.ts",
"**/*.test.ts",
"**/*.test-d.ts"
]
}
}
5 changes: 3 additions & 2 deletions src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
"sideEffects": false,
"files": [
"*",
"!**/*.tsbuildinfo",
"!**/*.bench.ts",
"!**/*.bench-d.ts",
"!**/*.test.ts",
"!**/*.test.ts.snap",
"!**/*.test-d.ts",
"!**/*.bench.ts",
"!**/*.tsbuildinfo",
"!tsconfig.build.json",
"!jsr.json"
],
Expand Down
4 changes: 1 addition & 3 deletions src/types/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,9 +209,7 @@ export type ContractFunctionParameters<
| allFunctionNames // show all options
| (functionName extends allFunctionNames ? functionName : never) // infer value
args?: (abi extends Abi ? UnionWiden<args> : never) | allArgs | undefined
} & (readonly [] extends allArgs ? {} : { args: Widen<args> }) &
// TODO: Remove `GetValue` from here (should be applied to top-level type as separate utility)
GetValue<abi, functionName>
} & (readonly [] extends allArgs ? {} : { args: Widen<args> })

export type ContractFunctionReturnType<
abi extends Abi | readonly unknown[] = Abi,
Expand Down
7 changes: 7 additions & 0 deletions test/typebench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { globby } from 'globby'
import { tsImport } from 'tsx/esm/api'

const paths = await globby(['src/**/*.bench-d.ts'])
for (const path of paths) {
await tsImport(`../${path}`, import.meta.url)
}
3 changes: 2 additions & 1 deletion tsconfig.build.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
"extends": "./tsconfig.base.json",
"include": ["src"],
"exclude": [
"src/**/*.bench.ts",
"src/**/*.bench-d.ts",
"src/**/*.test.ts",
"src/**/*.test-d.ts",
"src/**/*.bench.ts",
"src/node/trustedSetups_esm.ts"
],
"compilerOptions": {
Expand Down
8 changes: 0 additions & 8 deletions vercel.json

This file was deleted.

0 comments on commit d946d55

Please sign in to comment.