Skip to content

Commit

Permalink
feat: unstable_connector transport
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Dec 7, 2023
1 parent fda9697 commit 2ff4890
Show file tree
Hide file tree
Showing 24 changed files with 228 additions and 45 deletions.
1 change: 1 addition & 0 deletions packages/connectors/src/coinbaseWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function coinbaseWallet(parameters: CoinbaseWalletParameters) {
return createConnector<Provider, Properties>((config) => ({
id: 'coinbaseWalletSDK',
name: 'Coinbase Wallet',
type: 'coinbaseWallet',
async connect({ chainId } = {}) {
try {
const provider = await this.getProvider()
Expand Down
1 change: 1 addition & 0 deletions packages/connectors/src/ledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function ledger(parameters: LedgerParameters = {}) {
return createConnector<Provider, Properties>((config) => ({
id: 'ledger',
name: 'Ledger',
type: 'ledger',
async setup() {
const provider = await this.getProvider().catch(() => null)
if (!provider) return
Expand Down
1 change: 1 addition & 0 deletions packages/connectors/src/metaMask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function metaMask(parameters: MetaMaskParameters = {}) {
return createConnector<Provider, Properties, StorageItem>((config) => ({
id: 'metaMaskSDK',
name: 'MetaMask',
type: 'metaMask',
async setup() {
const provider = await this.getProvider()
if (provider)
Expand Down
1 change: 1 addition & 0 deletions packages/connectors/src/safe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function safe(parameters: SafeParameters = {}) {
return createConnector<Provider, Properties, StorageItem>((config) => ({
id: 'safe',
name: 'Safe',
type: 'safe',
async connect() {
const provider = await this.getProvider()
if (!provider) throw new ProviderNotFoundError()
Expand Down
1 change: 1 addition & 0 deletions packages/connectors/src/walletConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export function walletConnect(parameters: WalletConnectParameters) {
return createConnector<Provider, Properties, StorageItem>((config) => ({
id: 'walletConnect',
name: 'WalletConnect',
type: 'walletConnect',
async setup() {
const provider = await this.getProvider().catch(() => null)
if (!provider) return
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/actions/connect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test('behavior: user rejected request', async () => {
[UserRejectedRequestError: User rejected the request.
Details: Failed to connect.
Version: [email protected].9]
Version: [email protected].11]
`)
})

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/actions/getToken.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ test('behavior: bogus token', async () => {
function: decimals()
Docs: https://viem.sh/docs/contract/multicall.html
Version: [email protected].9"
Version: [email protected].11"
`)
})
16 changes: 8 additions & 8 deletions packages/core/src/actions/readContracts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ test('throws if allowFailure=false & a contract method fails', async () => {
args: (0xA0Cf798816D4b9b9866b5330EEa46a18382f251e, 69420)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9"
Version: [email protected].11"
`,
)
})
Expand Down Expand Up @@ -450,7 +450,7 @@ test('allowFailure=true & a contract method fails', async () => {
args: (0xA0Cf798816D4b9b9866b5330EEa46a18382f251e, 69420)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9],
Version: [email protected].11],
"result": undefined,
"status": "failure",
},
Expand All @@ -464,7 +464,7 @@ test('allowFailure=true & a contract method fails', async () => {
args: (0xA0Cf798816D4b9b9866b5330EEa46a18382f251e, 69421)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9],
Version: [email protected].11],
"result": undefined,
"status": "failure",
},
Expand Down Expand Up @@ -502,7 +502,7 @@ test('throws if allowFailure=false & encoding contract function data fails', asy
args: (1e+31)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9"
Version: [email protected].11"
`,
)
})
Expand Down Expand Up @@ -561,7 +561,7 @@ test('allowFailure=true & encoding contract function data fails', async () => {
args: (1e+31)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9],
Version: [email protected].11],
"result": undefined,
"status": "failure",
},
Expand All @@ -579,7 +579,7 @@ test('allowFailure=true & encoding contract function data fails', async () => {
args: (1e+31)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9],
Version: [email protected].11],
"result": undefined,
"status": "failure",
},
Expand Down Expand Up @@ -617,7 +617,7 @@ test('should throw if allowFailure=false & a contract has no response', async ()
args: (0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9"
Version: [email protected].11"
`,
)
})
Expand Down Expand Up @@ -669,7 +669,7 @@ test('allowFailure=true & a contract has no response', async () => {
args: (0xa5cc3c03994DB5b0d9A5eEdD10CabaB0813678AC)
Docs: https://viem.sh/docs/contract/readContract.html
Version: [email protected].9],
Version: [email protected].11],
"result": undefined,
"status": "failure",
},
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/actions/signMessage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test('behavior: user rejected request', async () => {
[UserRejectedRequestError: User rejected the request.
Details: Failed to sign message.
Version: [email protected].9]
Version: [email protected].11]
`)
await disconnect(config, { connector: connector_ })
})
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/actions/signTypedData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ test('behavior: user rejected request', async () => {
[UserRejectedRequestError: User rejected the request.
Details: Failed to sign typed data.
Version: [email protected].9]
Version: [email protected].11]
`)
await disconnect(config, { connector: connector_ })
})
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/actions/switchChain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ test('behavior: user rejected request', async () => {
[UserRejectedRequestError: User rejected the request.
Details: Failed to switch chain.
Version: [email protected].9]
Version: [email protected].11]
`)
await disconnect(config, { connector: connector_ })
})
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/connectors/createConnector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ test('default', () => {
return {
id: 'test',
name: 'Test Connector',
type: 'test',
async setup() {},
async connect() {
return { accounts: [] as Address[], chainId: 123 }
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/connectors/createConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type CreateConnectorFn<
readonly icon?: string | undefined
readonly id: string
readonly name: string
readonly type: string

setup?(): Promise<void>
connect(
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/connectors/injected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ export function injected(parameters: InjectedParameters = {}) {
get name() {
return getTarget().name
},
type: 'injected',
async setup() {
const provider = await this.getProvider()
// Only start listening for events if `target` is set, otherwise `injected()` will also receive events
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/connectors/mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function mock(parameters: MockParameters) {
return createConnector<Provider>((config) => ({
id: 'mock',
name: 'Mock Connector',
type: 'mock',
async setup() {
connectedChainId = config.chains[0].id
},
Expand Down
12 changes: 10 additions & 2 deletions packages/core/src/createConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
type Chain,
type Client,
type ClientConfig as viem_ClientConfig,
type Transport,
type Transport as viem_Transport,
createClient,
} from 'viem'
import { persist, subscribeWithSelector } from 'zustand/middleware'
Expand Down Expand Up @@ -155,7 +155,11 @@ export function createConfig<
...properties,
chain,
batch: properties.batch ?? { multicall: true },
transport: rest.transports[chainId],
transport: (parameters) =>
rest.transports[chainId]({
...parameters,
connectors: connectors.getState(),
}),
})
}

Expand Down Expand Up @@ -443,6 +447,10 @@ export type Connector = ReturnType<CreateConnectorFn> & {
uid: string
}

export type Transport = (
params: Parameters<viem_Transport>[0] & { connectors?: Connector[] },
) => ReturnType<viem_Transport>

type ClientConfig = LooseOmit<
viem_ClientConfig,
'account' | 'chain' | 'key' | 'name' | 'transport' | 'type'
Expand Down
9 changes: 5 additions & 4 deletions packages/core/src/exports/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import * as core from './index.js'
test('exports', () => {
expect(Object.keys(core)).toMatchInlineSnapshot(`
[
"custom",
"fallback",
"http",
"webSocket",
"connect",
"disconnect",
"estimateGas",
Expand Down Expand Up @@ -75,6 +71,11 @@ test('exports', () => {
"ConnectorAccountNotFoundError",
"ProviderNotFoundError",
"SwitchChainNotSupportedError",
"custom",
"http",
"webSocket",
"unstable_connector",
"fallback",
"cookieStorage",
"cookieToInitialState",
"parseCookie",
Expand Down
18 changes: 7 additions & 11 deletions packages/core/src/exports/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
////////////////////////////////////////////////////////////////////////////////
// Viem

// extremely commonly used viem exports
export {
custom,
fallback,
http,
webSocket,
} from 'viem'

////////////////////////////////////////////////////////////////////////////////
// Actions

Expand Down Expand Up @@ -374,6 +363,13 @@ export {
SwitchChainNotSupportedError,
} from '../errors/connector.js'

////////////////////////////////////////////////////////////////////////////////
// Transports

export { custom, http, webSocket } from 'viem'
export { unstable_connector } from '../transports/connector.js'
export { fallback } from '../transports/fallback.js'

////////////////////////////////////////////////////////////////////////////////
// Types

Expand Down
20 changes: 20 additions & 0 deletions packages/core/src/transports/connector.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { expect, test } from 'vitest'
import { unstable_connector } from './connector.js'

test('setup', () => {
expect(unstable_connector({ type: 'injected' })({})).toMatchInlineSnapshot(`
{
"config": {
"key": "connector",
"name": "Connector",
"request": [Function],
"retryCount": 3,
"retryDelay": 150,
"timeout": undefined,
"type": "connector",
},
"request": [Function],
"value": undefined,
}
`)
})
84 changes: 84 additions & 0 deletions packages/core/src/transports/connector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {
ChainDisconnectedError,
type EIP1193Provider,
ProviderDisconnectedError,
type TransportConfig,
createTransport,
hexToNumber,
} from 'viem'
import type { Transport } from '../createConfig.js'

export type ConnectorTransportConfig = {
/** The key of the transport. */
key?: TransportConfig['key']
/** The name of the transport. */
name?: TransportConfig['name']
/** The max number of times to retry. */
retryCount?: TransportConfig['retryCount']
/** The base delay (in ms) between retries. */
retryDelay?: TransportConfig['retryDelay']
/** Type of connector to target. */
type?:
| 'injected'
| 'coinbase'
| 'ledger'
| 'metaMask'
| 'safe'
| 'walletConnect'
| (string & {})
}

export type ConnectorTransport = Transport

export function unstable_connector(
config: ConnectorTransportConfig,
): Transport {
const {
key = 'connector',
name = 'Connector',
retryCount,
retryDelay,
type,
} = config || {}
return ({ chain, connectors, retryCount: defaultRetryCount }) => {
const connector = connectors?.find((c) => c.type === type)
return createTransport({
key,
name,
async request({ method, params }) {
if (!connector)
throw new ProviderDisconnectedError(
new Error(
`Could not find connector of type "${type}" in \`connectors\` passed to \`createConfig\`.`,
),
)

const provider = (await connector.getProvider({
chainId: chain?.id,
})) as EIP1193Provider | undefined
if (!provider)
throw new ProviderDisconnectedError(
new Error('Provider is disconnected.'),
)

const chainId = hexToNumber(
await provider.request({ method: 'eth_chainId' }),
)
if (chain && chainId !== chain.id)
throw new ChainDisconnectedError(
new Error(
`The current chain of the connector (id: ${chainId}) does not match the target chain for the request (id: ${chain.id}${chain.name}).`,
),
)

return (await provider.request({
method,
params,
} as any)) as any
},
retryDelay,
retryCount: retryCount ?? defaultRetryCount,
type: 'connector',
})
}
}
Loading

0 comments on commit 2ff4890

Please sign in to comment.