Skip to content

Commit

Permalink
feat: add cacheTime on client (#958)
Browse files Browse the repository at this point in the history
* feat: cacheTime

* chore: changeset
  • Loading branch information
jxom authored Jul 31, 2023
1 parent 7950df8 commit f7976fd
Show file tree
Hide file tree
Showing 20 changed files with 185 additions and 37 deletions.
5 changes: 5 additions & 0 deletions .changeset/cool-goats-approve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": minor
---

Added `cacheTime` as a parameter to `getBlockNumber` & `createClient`.
14 changes: 6 additions & 8 deletions site/docs/actions/public/getBlockNumber.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,24 @@ The number of the block.

## Parameters

### maxAge (optional)
### cacheTime (optional)

- **Type:** `number`
- **Default:** [Client's `pollingInterval`](/docs/clients/public#pollinginterval-optional)
- **Default:** [Client's `cacheTime`](/docs/clients/public#cachetime-optional)

The maximum age (in ms) of the cached value.
Time (in ms) that cached block number will remain in memory.

```ts
const block = await publicClient.getBlockNumber({
maxAge: 4_000 // [!code focus]
cacheTime: 4_000 // [!code focus]
})
```

By default, block numbers are cached for the period of the [Client's `pollingInterval`](/docs/clients/public#pollinginterval-optional).
By default, block numbers are cached for the period of the [Client's `cacheTime`](/docs/clients/public#cacheTime-optional).

- Setting a value of above zero will make block number remain in the cache for that period.
- Setting a value of `0` will disable the cache, and always retrieve a fresh block number.



## Example

Check out the usage of `getBlockNumber` in the live [Fetching Blocks Example](https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/blocks/fetching-blocks) below.
Expand All @@ -75,4 +73,4 @@ Check out the usage of `getBlockNumber` in the live [Fetching Blocks Example](ht

## JSON-RPC Method

[`eth_blockNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber)
[`eth_blockNumber`](https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber)
23 changes: 19 additions & 4 deletions site/docs/clients/public.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The Public Client also supports [`eth_call` Aggregation](#multicall) for improve

### `eth_call` Aggregation (via Multicall)

The Public Client supports the aggregation of `eth_call` requests into a single multicall (`aggregate3`) request.
The Public Client supports the aggregation of `eth_call` requests into a single multicall (`aggregate3`) request.

This means for every Action that utilizes an `eth_call` request (ie. `readContract`), the Public Client will batch the requests (over a timed period) and send it to the RPC Provider in a single multicall request. This can dramatically improve network performance, and decrease the amount of [Compute Units (CU)](https://docs.alchemy.com/reference/compute-units) used by RPC Providers like Alchemy, Infura, etc.

Expand Down Expand Up @@ -93,7 +93,7 @@ const [name, totalSupply, symbol, tokenUri, balance] = await Promise.all([

- **Type:** [Transport](/docs/glossary/types#transport)

The [Transport](/docs/clients/intro) of the Public Client.
The [Transport](/docs/clients/intro) of the Public Client.

```ts
const client = createPublicClient({
Expand All @@ -106,7 +106,7 @@ const client = createPublicClient({

- **Type:** [Chain](/docs/glossary/types#chain)

The [Chain](/docs/clients/chains) of the Public Client.
The [Chain](/docs/clients/chains) of the Public Client.

```ts
const client = createPublicClient({
Expand Down Expand Up @@ -176,6 +176,21 @@ const client = createPublicClient({
})
```

### cacheTime (optional)

- **Type:** `number`
- **Default:** `client.pollingInterval`

Time (in ms) that cached data will remain in memory.

```ts
const client = createPublicClient({
cacheTime: 10_000, // [!code focus]
chain: mainnet,
transport: http(),
})
```

### key (optional)

- **Type:** `string`
Expand Down Expand Up @@ -225,4 +240,4 @@ const client = createPublicClient({

Check out the usage of `createPublicClient` in the live [Public Client Example](https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/clients/public-client) below.

<iframe frameborder="0" width="100%" height="500px" src="https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/clients/public-client?embed=1&file=index.ts&hideNavigation=1&hideDevTools=true&terminalHeight=0&ctl=1"></iframe>
<iframe frameborder="0" width="100%" height="500px" src="https://stackblitz.com/github/wagmi-dev/viem/tree/main/examples/clients/public-client?embed=1&file=index.ts&hideNavigation=1&hideDevTools=true&terminalHeight=0&ctl=1"></iframe>
19 changes: 18 additions & 1 deletion site/docs/clients/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ import { privateKeyToAccount } from 'viem/accounts'

const client = createTestClient({
account: privateKeyToAccount('0x...') // [!code focus]
chain: mainnet,
chain: foundry,
mode: 'anvil',
transport: http(),
})
```
Expand All @@ -128,6 +129,22 @@ const client = createTestClient({
})
```

### cacheTime (optional)

- **Type:** `number`
- **Default:** `client.pollingInterval`

Time (in ms) that cached data will remain in memory.

```ts
const client = createTestClient({
cacheTime: 10_000, // [!code focus]
chain: foundry,
mode: 'anvil',
transport: http(),
})
```

### name (optional)

- **Type:** `string`
Expand Down
15 changes: 15 additions & 0 deletions site/docs/clients/wallet.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,21 @@ const client = createWalletClient({
})
```

### cacheTime (optional)

- **Type:** `number`
- **Default:** `client.pollingInterval`

Time (in ms) that cached data will remain in memory.

```ts
const client = createWalletClient({
cacheTime: 10_000, // [!code focus]
chain: mainnet,
transport: custom(window.ethereum)
})
```

### key (optional)

- **Type:** `string`
Expand Down
8 changes: 5 additions & 3 deletions src/actions/public/getBlockNumber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import type { Chain } from '../../types/chain.js'
import { getCache, withCache } from '../../utils/promise/withCache.js'

export type GetBlockNumberParameters = {
/** The maximum age (in ms) of the cached value. */
/** Time (in ms) that cached block number will remain in memory. */
cacheTime?: number
/** @deprecated use `cacheTime` instead. */
maxAge?: number
}

Expand Down Expand Up @@ -41,14 +43,14 @@ export function getBlockNumberCache(id: string) {
*/
export async function getBlockNumber<TChain extends Chain | undefined>(
client: Client<Transport, TChain>,
{ maxAge = client.pollingInterval }: GetBlockNumberParameters = {},
{ cacheTime = client.cacheTime, maxAge }: GetBlockNumberParameters = {},
): Promise<GetBlockNumberReturnType> {
const blockNumberHex = await withCache(
() =>
client.request({
method: 'eth_blockNumber',
}),
{ cacheKey: cacheKey(client.uid), maxAge },
{ cacheKey: cacheKey(client.uid), cacheTime: maxAge ?? cacheTime },
)
return BigInt(blockNumberHex)
}
2 changes: 1 addition & 1 deletion src/actions/public/watchBlockNumber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export function watchBlockNumber<
poll(
async () => {
try {
const blockNumber = await getBlockNumber(client, { maxAge: 0 })
const blockNumber = await getBlockNumber(client, { cacheTime: 0 })

if (prevBlockNumber) {
// If the current block number is the same as the previous,
Expand Down
12 changes: 8 additions & 4 deletions src/actions/test/mine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,19 @@ import { getBlockNumber } from '../public/getBlockNumber.js'
import { mine } from './mine.js'

test('mines 1 block', async () => {
const currentBlockNumber = await getBlockNumber(publicClient, { maxAge: 0 })
const currentBlockNumber = await getBlockNumber(publicClient, {
cacheTime: 0,
})
await expect(mine(testClient, { blocks: 1 })).resolves.toBeUndefined()
const nextBlockNumber = await getBlockNumber(publicClient, { maxAge: 0 })
const nextBlockNumber = await getBlockNumber(publicClient, { cacheTime: 0 })
expect(nextBlockNumber).toEqual(currentBlockNumber + 1n)
})

test('mines 5 blocks', async () => {
const currentBlockNumber = await getBlockNumber(publicClient, { maxAge: 0 })
const currentBlockNumber = await getBlockNumber(publicClient, {
cacheTime: 0,
})
await mine(testClient, { blocks: 5 })
const nextBlockNumber = await getBlockNumber(publicClient, { maxAge: 0 })
const nextBlockNumber = await getBlockNumber(publicClient, { cacheTime: 0 })
expect(nextBlockNumber).toEqual(currentBlockNumber + 5n)
})
8 changes: 4 additions & 4 deletions src/actions/test/setIntervalMining.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ import { setIntervalMining } from './setIntervalMining.js'
test('sets mining interval', async () => {
await mine(testClient, { blocks: 1 })

const blockNumber1 = await getBlockNumber(publicClient, { maxAge: 0 })
const blockNumber1 = await getBlockNumber(publicClient, { cacheTime: 0 })
await expect(
setIntervalMining(testClient, { interval: 1 }),
).resolves.toBeUndefined()
await wait(2000)
const blockNumber2 = await getBlockNumber(publicClient, { maxAge: 0 })
const blockNumber2 = await getBlockNumber(publicClient, { cacheTime: 0 })
expect(blockNumber2 - blockNumber1).toBe(2n)

await setIntervalMining(testClient, { interval: 2 })
await wait(2000)
const blockNumber3 = await getBlockNumber(publicClient, { maxAge: 0 })
const blockNumber3 = await getBlockNumber(publicClient, { cacheTime: 0 })
expect(blockNumber3 - blockNumber2).toBe(1n)

await setIntervalMining(testClient, { interval: 0 })
await wait(2000)
const blockNumber4 = await getBlockNumber(publicClient, { maxAge: 0 })
const blockNumber4 = await getBlockNumber(publicClient, { cacheTime: 0 })
expect(blockNumber4 - blockNumber3).toBe(0n)

await setIntervalMining(testClient, { interval: 1 })
Expand Down
48 changes: 48 additions & 0 deletions src/clients/createClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ test('creates', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"chain": undefined,
"extend": [Function],
"key": "base",
Expand Down Expand Up @@ -61,6 +62,7 @@ describe('transports', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"chain": {
"formatters": undefined,
"id": 1337,
Expand Down Expand Up @@ -116,6 +118,7 @@ describe('transports', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"chain": {
"formatters": undefined,
"id": 1337,
Expand Down Expand Up @@ -171,6 +174,7 @@ describe('transports', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"chain": undefined,
"extend": [Function],
"key": "base",
Expand All @@ -193,6 +197,45 @@ describe('transports', () => {
})

describe('config', () => {
test('cacheTime', () => {
const mockTransport = () =>
createTransport({
key: 'mock',
name: 'Mock Transport',
request: vi.fn(async () => null) as unknown as EIP1193RequestFn,
type: 'mock',
})
const { uid, ...client } = createClient({
cacheTime: 10_000,
transport: mockTransport,
})

expect(uid).toBeDefined()
expect(client).toMatchInlineSnapshot(`
{
"account": undefined,
"batch": undefined,
"cacheTime": 10000,
"chain": undefined,
"extend": [Function],
"key": "base",
"name": "Base Client",
"pollingInterval": 4000,
"request": [Function],
"transport": {
"key": "mock",
"name": "Mock Transport",
"request": [MockFunction spy],
"retryCount": 3,
"retryDelay": 150,
"timeout": undefined,
"type": "mock",
},
"type": "base",
}
`)
})

test('key', () => {
const mockTransport = () =>
createTransport({
Expand All @@ -212,6 +255,7 @@ describe('config', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"chain": undefined,
"extend": [Function],
"key": "bar",
Expand Down Expand Up @@ -251,6 +295,7 @@ describe('config', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"chain": undefined,
"extend": [Function],
"key": "base",
Expand Down Expand Up @@ -290,6 +335,7 @@ describe('config', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 10000,
"chain": undefined,
"extend": [Function],
"key": "base",
Expand Down Expand Up @@ -329,6 +375,7 @@ describe('config', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"chain": undefined,
"extend": [Function],
"key": "base",
Expand Down Expand Up @@ -372,6 +419,7 @@ describe('extends', () => {
{
"account": undefined,
"batch": undefined,
"cacheTime": 4000,
"call": [Function],
"chain": {
"formatters": undefined,
Expand Down
9 changes: 9 additions & 0 deletions src/clients/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export type ClientConfig<
multicall?: boolean | Prettify<MulticallBatchOptions> | undefined
}
| undefined
/**
* Time (in ms) that cached data will remain in memory.
* @default 4_000
*/
cacheTime?: number | undefined
/** Chain for the client. */
chain?: Chain | undefined | chain
/** A key for the client. */
Expand Down Expand Up @@ -79,6 +84,8 @@ type Client_Base<
account: account
/** Flags for batch settings. */
batch?: ClientConfig['batch']
/** Time (in ms) that cached data will remain in memory. */
cacheTime: number
/** Chain for the client. */
chain: chain
/** A key for the client. */
Expand Down Expand Up @@ -135,6 +142,7 @@ export function createClient<
export function createClient(parameters: ClientConfig): Client {
const {
batch,
cacheTime = parameters.pollingInterval ?? 4_000,
key = 'base',
name = 'Base Client',
pollingInterval = 4_000,
Expand All @@ -154,6 +162,7 @@ export function createClient(parameters: ClientConfig): Client {
const client = {
account,
batch,
cacheTime,
chain,
key,
name,
Expand Down
Loading

1 comment on commit f7976fd

@vercel
Copy link

@vercel vercel bot commented on f7976fd Jul 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.