Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .changeset/wallet-add-chain-unsupported.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@relayprotocol/relay-sdk': patch
'@relayprotocol/relay-kit-ui': patch
---

Show an unsupported chain error instead of the locked-wallet message when a wallet rejects wallet_addEthereumChain
78 changes: 78 additions & 0 deletions packages/sdk/src/utils/viemWallet.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
import { describe, it, expect, vi } from 'vitest'
import { createWalletClient, custom } from 'viem'
import { adaptViemWallet } from './viemWallet'

const INK_CHAIN_ID = 57073

vi.mock('../client.js', () => ({
getClient: () => ({
chains: [
{
id: 57073,
viemChain: {
id: 57073,
name: 'Ink',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: { default: { http: ['https://rpc-gel.inkonchain.com'] } }
}
}
],
log: vi.fn()
})
}))

const buildWallet = (overrides: Record<string, any> = {}) =>
({
account: { address: '0x0000000000000000000000000000000000000001' },
Expand Down Expand Up @@ -48,3 +68,61 @@ describe('adaptViemWallet disableCapabilitiesCheck', () => {
expect(hangingGetCapabilities).not.toHaveBeenCalled()
})
})

describe('adaptViemWallet switchChain', () => {
// EIP-1193 provider that doesn't know the chain and responds to
// wallet_addEthereumChain with the given error (or success when undefined)
const buildProvider = (addChainError?: { code: number; message: string }) => ({
request: vi.fn(async ({ method }: { method: string }) => {
if (method === 'wallet_switchEthereumChain') {
throw Object.assign(new Error('Unrecognized chain ID "0xdef1".'), {
code: 4902
})
}
if (method === 'wallet_addEthereumChain') {
if (addChainError) {
throw Object.assign(new Error(addChainError.message), {
code: addChainError.code
})
}
return null
}
return null
})
})

const adapt = (provider: ReturnType<typeof buildProvider>) =>
adaptViemWallet(
createWalletClient({ transport: custom(provider, { retryCount: 0 }) })
)

it('maps a rejected wallet_addEthereumChain to an unsupported chain error (Coinbase Wallet, Ink origin)', async () => {
const provider = buildProvider({
code: -32004,
message: 'Method "wallet_addEthereumChain" is not supported.'
})

await expect(adapt(provider).switchChain(INK_CHAIN_ID)).rejects.toThrow(
'Wallet does not support chain'
)
expect(provider.request).toHaveBeenCalledWith(
expect.objectContaining({ method: 'wallet_addEthereumChain' })
)
})

it('rethrows other addChain errors unchanged', async () => {
const provider = buildProvider({ code: 4100, message: 'Wallet is locked' })

await expect(adapt(provider).switchChain(INK_CHAIN_ID)).rejects.toThrow(
'Wallet is locked'
)
})

it('resolves when addChain succeeds', async () => {
const provider = buildProvider()

await expect(
adapt(provider).switchChain(INK_CHAIN_ID)
).resolves.toBeUndefined()
})
})
6 changes: 5 additions & 1 deletion packages/sdk/src/utils/viemWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
custom,
fallback,
hexToBigInt,
http
http,
MethodNotSupportedRpcError
} from 'viem'

// Cache for expensive RPC calls (code, balance, tx count)
Expand Down Expand Up @@ -212,6 +213,9 @@ export const adaptViemWallet = (
LogLevel.Verbose
)
return
} else if (e?.code === MethodNotSupportedRpcError.code) {
// Wallet rejects wallet_addEthereumChain (EIP-1474 code -32004)
throw new Error('Wallet does not support chain')
} else {
throw e
}
Expand Down
8 changes: 5 additions & 3 deletions packages/ui/src/components/common/ErrorWell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ const ErrorWell: React.FC<Props> = ({ error, hasTxHashes, fromChain }) => {
} else if (
error?.name &&
(error?.message?.includes('does not support chain') ||
error?.message?.match(/Chain \d+ not supported/))
error?.message?.match(/Chain \d+ not supported/) ||
error?.message?.match(/wallet_addEthereumChain.*not supported/))
) {
return `Your wallet does not support ${fromChain?.displayName ?? 'this chain'}`
const chainName = fromChain?.displayName ?? 'this chain'
return `Your wallet does not support ${chainName}. Add ${chainName} to your wallet manually or connect a different wallet, then try again.`
}
if (
!hasTxHashes ||
Expand All @@ -40,7 +42,7 @@ const ErrorWell: React.FC<Props> = ({ error, hasTxHashes, fromChain }) => {
return 'Transaction Failed. Try adjusting slippage or gas limits and try again.'
}
return error?.message
}, [error?.message, hasTxHashes])
}, [error?.message, hasTxHashes, fromChain?.displayName])

const shouldScrollErrorMessage =
typeof renderedErrorMessage === 'string' &&
Expand Down
Loading