Skip to content

Commit

Permalink
wip: callout
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Oct 20, 2023
1 parent ea4323b commit f387653
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 18 deletions.
30 changes: 30 additions & 0 deletions playground/pages/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,32 @@ Combined emphasis with **asterisks and _underscores_**.

Strikethrough uses two tildes. ~~Scratch this.~~

## Callouts

:::note
This is a note callout.
:::

:::info
This is an info callout.
:::

:::warning
This is a warning callout.
:::

:::danger
This is a danger callout.
:::

:::tip
This is a tip callout.
:::

:::success
This is a success callout.
:::

## Lists

### Ordered list
Expand Down Expand Up @@ -207,6 +233,10 @@ function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
let a = createLabel("typescript");
```

:::tip[Tip:]
Make sure to do the thing that does stuff.
:::

## Code group

:::code-group
Expand Down
2 changes: 1 addition & 1 deletion playground/pages/public-client.mdx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Public Client
# Public Client

A Public Client is an interface to "public" [JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/) methods such as retrieving block numbers, transactions, reading from smart contracts, etc through [Public Actions](/docs/actions/public/introduction).

Expand Down
4 changes: 2 additions & 2 deletions playground/pages/viem.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ const client = createPublicClient({ // [!code hl]
}) // [!code hl]
```

> TODO: Callout component for below
:::callout
In a production app, it is highly recommended to pass through your authenticated RPC provider URL (Alchemy, Infura, Ankr, etc). If no URL is provided, viem will default to a public RPC provider. [Read more](/docs/clients/transports/http.html#usage).
:::

### 2. Consume Actions

Expand Down
229 changes: 229 additions & 0 deletions site/pages/public-client.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# Public Client

A Public Client is an interface to "public" [JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/) methods such as retrieving block numbers, transactions, reading from smart contracts, etc through [Public Actions](/docs/actions/public/introduction).

The `createPublicClient` function sets up a Public Client with a given [Transport](/docs/clients/intro) configured for a [Chain](/docs/clients/chains).

## Import

```ts
import { createPublicClient } from 'viem'
```

## Usage

Initialize a Client with your desired [Chain](/docs/clients/chains) (e.g. `mainnet`) and [Transport](/docs/clients/intro) (e.g. `http`).

```ts
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
chain: mainnet,
transport: http()
})
```

Then you can consume [Public Actions](/docs/actions/public/introduction):

```ts
const blockNumber = await client.getBlockNumber() // [!code focus:10]
```

## Optimization

The Public Client also supports [`eth_call` Aggregation](#multicall) for improved performance.

### `eth_call` Aggregation (via Multicall)

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.

The Public Client schedules the aggregation of `eth_call` requests over a given time period. By default, it executes the batch request at the end of the current [JavaScript message queue](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop#queue) (a [zero delay](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop#zero_delays)), however, consumers can specify a custom `wait` period (in ms).

You can enable `eth_call` aggregation by setting the `batch.multicall` flag to `true`:

```ts
const client = createPublicClient({
batch: {
multicall: true, // [!code focus]
},
chain: mainnet,
transport: http(),
})
```

> You can also [customize the `multicall` options](#batch-multicall-batchsize-optional).
Now, when you start to utilize `readContract` Actions, the Public Client will batch and send over those requests at the end of the message queue (or custom time period) in a single `eth_call` multicall request:

```ts
const contract = getContract({ address, abi })

// The below will send a single request to the RPC Provider.
const [name, totalSupply, symbol, tokenUri, balance] = await Promise.all([
contract.read.name(),
contract.read.totalSupply(),
contract.read.symbol(),
contract.read.tokenURI([420n]),
contract.read.balanceOf([address]),
])
```

> Read more on [Contract Instances](/docs/contract/getContract.html).
## Parameters

### transport

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

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

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

### chain (optional)

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

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

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

### batch (optional)

Flags for batch settings.

### batch.multicall (optional)

- **Type:** `boolean | MulticallBatchOptions`
- **Default:** `false`

Toggle to enable `eth_call` multicall aggregation.

```ts
const client = createPublicClient({
batch: {
multicall: true, // [!code focus]
},
chain: mainnet,
transport: http(),
})
```

### batch.multicall.batchSize (optional)

- **Type:** `number`
- **Default:** `1_024`

The maximum size (in bytes) for each multicall (`aggregate3`) calldata chunk.

> Note: Some RPC Providers limit the amount of calldata that can be sent in a single request. It is best to check with your RPC Provider to see if there are any calldata size limits to `eth_call` requests.
```ts
const client = createPublicClient({
batch: {
multicall: {
batchSize: 512, // [!code focus]
},
},
chain: mainnet,
transport: http(),
})
```

### batch.multicall.wait (optional)

- **Type:** `number`
- **Default:** `0` ([zero delay](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop#zero_delays))

The maximum number of milliseconds to wait before sending a batch.

```ts
const client = createPublicClient({
batch: {
multicall: {
wait: 16, // [!code focus]
},
},
chain: mainnet,
transport: http(),
})
```

### 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`
- **Default:** `"public"`

A key for the Client.

```ts
const client = createPublicClient({
chain: mainnet,
key: 'public', // [!code focus]
transport: http(),
})
```

### name (optional)

- **Type:** `string`
- **Default:** `"Public Client"`

A name for the Client.

```ts
const client = createPublicClient({
chain: mainnet,
name: 'Public Client', // [!code focus]
transport: http(),
})
```

### pollingInterval (optional)

- **Type:** `number`
- **Default:** `4_000`

Frequency (in ms) for polling enabled Actions.

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

## Live Example

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>
21 changes: 11 additions & 10 deletions site/pages/viem.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
title: Viem Example
---

# Getting Started

# Getting Started [How to get up and running with Viem]

## Overview

Expand Down Expand Up @@ -39,25 +40,25 @@ bun i viem

Firstly, set up your [Client](/docs/clients/intro) with a desired [Transport](/docs/clients/intro) & [Chain](/docs/clients/chains).

```tsx {4-7}
```tsx
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

const client = createPublicClient({
chain: mainnet,
transport: http(),
})
const client = createPublicClient({ // [!code hl]
chain: mainnet, // [!code hl]
transport: http(), // [!code hl]
}) // [!code hl]
```

> TODO: Callout component for below
:::callout
In a production app, it is highly recommended to pass through your authenticated RPC provider URL (Alchemy, Infura, Ankr, etc). If no URL is provided, viem will default to a public RPC provider. [Read more](/docs/clients/transports/http.html#usage).
:::

### 2. Consume Actions

Now that you have a Client set up, you can now interact with Ethereum and consume [Actions](/docs/actions/public/introduction)!

```tsx {9}
```tsx
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'

Expand All @@ -66,5 +67,5 @@ const client = createPublicClient({
transport: http(),
})

const blockNumber = await client.getBlockNumber()
const blockNumber = await client.getBlockNumber() // [!code focus]
```
46 changes: 46 additions & 0 deletions src/remark-plugins/callout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/// <reference types="mdast-util-to-hast" />
/// <reference types="mdast-util-directive" />

import { h } from 'hastscript'
import type { Root } from 'mdast'
import { visit } from 'unist-util-visit'

export function remarkCallout() {
return (tree: Root) => {
visit(tree, (node) => {
if (node.type !== 'containerDirective') return
if (
node.name !== 'callout' &&
node.name !== 'info' &&
node.name !== 'warning' &&
node.name !== 'danger' &&
node.name !== 'tip' &&
node.name !== 'success' &&
node.name !== 'note'
)
return

// @ts-expect-error
const label = node.children.find((child) => child.data?.directiveLabel)?.children[0].value

const data = node.data || (node.data = {})
const tagName = 'aside'

node.attributes = {
...node.attributes,
class: `callout${node.name !== 'callout' ? ` ${node.name}` : ''}`,
}

if (label) {
node.children = node.children.filter((child: any) => !child.data?.directiveLabel)
;(node.children[0] as any).children.unshift({
type: 'strong',
children: [{ type: 'text', value: `${label} ` }],
})
}

data.hName = tagName
data.hProperties = h(tagName, node.attributes || {}).properties
})
}
}
Loading

0 comments on commit f387653

Please sign in to comment.