Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brave-radios-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@wagmi/connectors': patch
---

Added `tempoWallet` connector
5 changes: 5 additions & 0 deletions .changeset/empty-donuts-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@wagmi/core': patch
---

Added `tempoWallet` connector
5 changes: 5 additions & 0 deletions .changeset/funny-paws-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'wagmi': patch
---

Added `tempoWallet` connector
4 changes: 3 additions & 1 deletion .github/workflows/verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ jobs:
with:
node-version: 24.5

# TODO: Remove `--ignore-registry-errors` when issue fixed
# https://github.com/pnpm/pnpm/issues/11265
- name: Audit dependencies
run: pnpm audit
run: pnpm audit --ignore-registry-errors

- name: Check repo
run: pnpm check:repo
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"packages/connectors": {
"entry": "src/exports/index.ts!",
"ignoreDependencies": [
"accounts",
"@base-org/account",
"@coinbase/wallet-sdk",
"@metamask/connect-evm",
Expand All @@ -124,6 +125,7 @@
],
"ignoreDependencies": [
"@tanstack/query-core",
"accounts",
"ox"
]
},
Expand Down
14 changes: 8 additions & 6 deletions packages/cli/src/utils/resolveConfig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { defaultConfig } from '../config.js'
import { findConfig } from './findConfig.js'
import { resolveConfig } from './resolveConfig.js'

test.skip('resolves config', async () => {
const configModulePath = new URL('../config.ts', import.meta.url).href

test('resolves config', async () => {
const { paths } = await createFixture({
files: {
'wagmi.config.ts': `
import { defineConfig } from '@wagmi/cli'
import { defineConfig } from '${configModulePath}'

export default defineConfig(${JSON.stringify(defaultConfig)})
`,
Expand All @@ -30,11 +32,11 @@ test.skip('resolves config', async () => {
`)
})

test.skip('resolves function config', async () => {
test('resolves function config', async () => {
const { paths } = await createFixture({
files: {
'wagmi.config.ts': `
import { defineConfig } from '@wagmi/cli'
import { defineConfig } from '${configModulePath}'

export default defineConfig(() => (${JSON.stringify(defaultConfig)}))
`,
Expand All @@ -55,11 +57,11 @@ test.skip('resolves function config', async () => {
`)
})

test.skip('resolves array config', async () => {
test('resolves array config', async () => {
const { paths } = await createFixture({
files: {
'wagmi.config.ts': `
import { defineConfig } from '@wagmi/cli'
import { defineConfig } from '${configModulePath}'

export default defineConfig([${JSON.stringify(defaultConfig)}])
`,
Expand Down
5 changes: 5 additions & 0 deletions packages/connectors/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@safe-global/safe-apps-sdk": "^9.1.0",
"@wagmi/core": "workspace:*",
"@walletconnect/ethereum-provider": "^2.21.1",
"accounts": "catalog:",
"porto": "~0.2.35",
"typescript": ">=5.7.3",
"viem": "2.x"
Expand All @@ -65,6 +66,9 @@
"@walletconnect/ethereum-provider": {
"optional": true
},
"accounts": {
"optional": true
},
"porto": {
"optional": true
},
Expand All @@ -80,6 +84,7 @@
"@safe-global/safe-apps-sdk": "catalog:",
"@wagmi/core": "workspace:*",
"@walletconnect/ethereum-provider": "catalog:",
"accounts": "catalog:",
"msw": "^2.4.9",
"porto": "catalog:"
},
Expand Down
1 change: 1 addition & 0 deletions packages/connectors/src/exports/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ test('exports', () => {
[
"injected",
"mock",
"tempoWallet",
"baseAccount",
"coinbaseWallet",
"metaMask",
Expand Down
1 change: 1 addition & 0 deletions packages/connectors/src/exports/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export {
type MockParameters,
mock,
} from '@wagmi/core'
export { type TempoWalletParameters, tempoWallet } from '@wagmi/core/tempo'
export { type BaseAccountParameters, baseAccount } from '../baseAccount.js'
export {
type CoinbaseWalletParameters,
Expand Down
5 changes: 5 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
},
"peerDependencies": {
"@tanstack/query-core": ">=5.0.0",
"accounts": "catalog:",
"ox": ">=0.11.1",
"typescript": ">=5.7.3",
"viem": "2.x"
Expand All @@ -95,6 +96,9 @@
"@tanstack/query-core": {
"optional": true
},
"accounts": {
"optional": true
},
"ox": {
"optional": true
},
Expand All @@ -109,6 +113,7 @@
},
"devDependencies": {
"@tanstack/query-core": "catalog:",
"accounts": "catalog:",
"ox": "catalog:"
},
"contributors": [
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/actions/call.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ test('zero data', async () => {
`)
})

// TODO: Re-enable
test.skip('parameters: blockNumber', async () => {
test('parameters: blockNumber', async () => {
await expect(
call(config, {
account,
Expand Down
38 changes: 35 additions & 3 deletions packages/core/src/actions/getProof.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,44 @@
import { chain, config } from '@wagmi/test'
import { chain } from '@wagmi/test'
import { custom } from 'viem'
import { expect, test } from 'vitest'

import { createConfig } from '../createConfig.js'
import { getProof } from './getProof.js'

test.skip('default', async () => {
const proofResponse = {
address: '0x4200000000000000000000000000000000000016',
accountProof: ['0x1'],
balance: '0x0',
codeHash: `0x${'0'.repeat(64)}`,
nonce: '0x0',
storageHash: `0x${'0'.repeat(64)}`,
storageProof: [
{
key: '0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99',
proof: ['0x1'],
value: '0x0',
},
],
} as const

const config = createConfig({
chains: [chain.mainnet],
storage: null,
transports: {
[chain.mainnet.id]: custom({
async request({ method }) {
if (method === 'eth_getProof') return proofResponse
if (method === 'eth_chainId')
return `0x${chain.mainnet.id.toString(16)}`
throw new Error(`Unexpected RPC method: ${method}`)
},
}),
},
})

test('default', async () => {
await expect(
getProof(config, {
chainId: chain.optimism.id,
address: '0x4200000000000000000000000000000000000016',
storageKeys: [
'0x4a932049252365b3eedbc5190e18949f2ec11f39d3bef2d259764799a1b27d99',
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/exports/tempo.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { expect, test } from 'vitest'

import * as tempo from './tempo.js'

test('exports', () => {
expect(Object.keys(tempo)).toMatchInlineSnapshot(`
[
"Actions",
"dangerous_secp256k1",
"tempoWallet",
"webAuthn",
]
`)
})
3 changes: 2 additions & 1 deletion packages/core/src/exports/tempo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export * as Actions from '../tempo/actions/index.js'
export {
type Dangerous_Secp256k1Parameters,
dangerous_secp256k1,
type TempoWalletParameters,
tempoWallet,
type WebAuthnParameters,
webAuthn,
} from '../tempo/Connectors.js'
export * as KeyManager from '../tempo/KeyManager.js'
115 changes: 94 additions & 21 deletions packages/core/src/tempo/Connectors.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
/// <reference types="@vitest/browser-playwright" />
import { createConfig, createStorage } from '@wagmi/core'
import { KeyManager, webAuthn } from '@wagmi/core/tempo'
import { tempoLocal } from '@wagmi/test/tempo'

import { connect, createConfig, disconnect, getConnection } from '@wagmi/core'
import { tempoWallet, webAuthn } from '@wagmi/core/tempo'
import { accounts, tempoLocal } from '@wagmi/test/tempo'
import { Storage as AccountsStorage, Dialog } from 'accounts'
import { http } from 'viem'
import { describe, expect, test } from 'vitest'
import { cdp } from 'vitest/browser'
Expand All @@ -28,49 +30,120 @@ async function setupWebAuthn() {
}
}

function createTempoWalletDialog(
address: `0x${string}`,
capabilities: Record<string, unknown> = {},
) {
return Dialog.define({ name: 'test' }, ({ store }: any) => ({
close() {},
destroy() {},
open() {},
async syncRequests(requests) {
for (const queued of requests) {
if (queued.request.method !== 'wallet_connect') continue

store.setState((state: any) => ({
...state,
requestQueue: state.requestQueue.map((request: any) =>
request.request.id === queued.request.id
? {
request: request.request,
result: {
accounts: [{ address, capabilities }],
},
status: 'success',
}
: request,
),
}))
}
},
}))
}

describe('tempoWallet', () => {
test('connect + getAccounts + disconnect', async () => {
const config = createConfig({
chains: [tempoLocal],
connectors: [],
storage: null,
transports: {
[tempoLocal.id]: http(),
},
})

const address = accounts[0]!.address
const connector = config._internal.connectors.setup(
tempoWallet({
dialog: createTempoWalletDialog(address),
storage: AccountsStorage.memory({ key: 'tempo-wallet-test' }),
}),
)

expect(await connector.getAccounts()).toEqual([])
expect(await connector.isAuthorized()).toBe(false)
expect(getConnection(config).status).toBe('disconnected')

const result = await connect(config, { connector })
expect(result).toMatchObject({
accounts: [address],
chainId: tempoLocal.id,
})
expect(await connector.getAccounts()).toEqual([address])
expect(await connector.isAuthorized()).toBe(true)
expect(getConnection(config)).toMatchObject({
address,
addresses: [address],
chainId: tempoLocal.id,
isConnected: true,
status: 'connected',
})

await disconnect(config, { connector })
expect(await connector.getAccounts()).toEqual([])
expect(await connector.isAuthorized()).toBe(false)
})
})

describe('webAuthn', () => {
describe('sign-up with grantAccessKey', () => {
describe('register with authorizeAccessKey', () => {
test('passes chainId to signKeyAuthorization', async (context) => {
const cleanup = await setupWebAuthn()
context.onTestFinished(async () => await cleanup())

const storage = createStorage({ storage: localStorage })
const config = createConfig({
chains: [tempoLocal],
connectors: [],
storage,
storage: null,
transports: {
[tempoLocal.id]: http(),
},
})

const connector = config._internal.connectors.setup(
webAuthn({
grantAccessKey: true,
keyManager: KeyManager.localStorage(),
authorizeAccessKey: () => ({
expiry: Math.floor((Date.now() + 24 * 60 * 60 * 1000) / 1000),
}),
}),
)

const chainId = tempoLocal.id

const result = await connector.connect({
capabilities: { type: 'sign-up', label: 'ChainId Test' },
capabilities: { method: 'register', name: 'ChainId Test' },
chainId,
withCapabilities: true,
})

expect(result.chainId).toBe(chainId)

// Retrieve the pending key authorization from storage to verify chainId.
// The connector stores it at `pendingKeyAuthorization:<address>`.
const address = (result.accounts as readonly string[])[0]!.toLowerCase()
const keyAuth = await storage.getItem(
`pendingKeyAuthorization:${address}` as any,
)

expect(keyAuth).toBeDefined()
// The key authorization should include the chainId passed during connect.
// KeyAuthorization stores chainId as bigint.
expect(BigInt((keyAuth as any).chainId)).toBe(BigInt(chainId))
expect(result.accounts[0]?.capabilities.keyAuthorization).toBeDefined()
expect(
BigInt(
result.accounts[0]?.capabilities.keyAuthorization
?.chainId as unknown as `0x${string}`,
),
).toBe(BigInt(chainId))
})
})
})
Loading
Loading