11import { createStore , type WritableAtom } from 'jotai'
22
3- import type { EIP1193Provider , PublicClient } from 'viem'
43import type { Connector } from 'wagmi'
54
65import { SupportedChainId } from '@cowprotocol/cow-sdk'
98 isAtomicBatchSupportedAtom ,
109 isAtomicBatchSupportedAsyncAtom ,
1110 isAtomicBatchSupportedLoadableAtom ,
12- resolveCapabilitiesForChain ,
1311 walletCapabilitiesAtom ,
1412} from './walletCapabilitiesAtom'
1513
@@ -37,79 +35,34 @@ const MOCK_CHAIN_ID = SupportedChainId.MAINNET
3735const MOCK_CONNECTOR = { type : 'injected' } as Connector
3836
3937const mockGetCapabilities = jest . fn ( )
40- const mockWagmiConfigGetClient = jest . fn ( )
4138
42- jest . mock ( 'viem /actions' , ( ) => ( {
39+ jest . mock ( 'wagmi /actions' , ( ) => ( {
4340 getCapabilities : ( ...args : unknown [ ] ) => mockGetCapabilities ( ...args ) ,
4441} ) )
4542
4643jest . mock ( '../../wagmi/config' , ( ) => ( {
47- wagmiConfig : {
48- getClient : ( ...args : unknown [ ] ) => mockWagmiConfigGetClient ( ...args ) ,
49- } ,
44+ wagmiConfig : { } ,
5045} ) )
5146
52- function createMockEip1193Provider ( request = jest . fn ( ) ) : EIP1193Provider {
53- return {
54- request,
55- on : jest . fn ( ) ,
56- removeListener : jest . fn ( ) ,
57- } as unknown as EIP1193Provider
58- }
59-
6047function setWalletInfo (
6148 store : ReturnType < typeof createStore > ,
6249 overrides : Partial < {
6350 account : string
6451 chainId : SupportedChainId
6552 connector : Connector
66- provider : NonNullable < WalletInfo [ 'provider' ] >
6753 } > = { } ,
6854) : void {
6955 store . set ( walletInfoAtom , {
7056 chainId : overrides . chainId ?? MOCK_CHAIN_ID ,
7157 account : overrides . account ?? MOCK_ACCOUNT ,
7258 connector : overrides . connector ?? MOCK_CONNECTOR ,
73- provider : overrides . provider ?? createMockEip1193Provider ( ) ,
7459 } )
7560}
7661
77- describe ( 'resolveCapabilitiesForChain' , ( ) => {
78- const capabilities : WalletCapabilities = { atomic : { status : 'supported' } }
79-
80- it ( 'matches numeric chain id key' , ( ) => {
81- expect ( resolveCapabilitiesForChain ( { [ MOCK_CHAIN_ID ] : capabilities } , MOCK_CHAIN_ID ) ) . toEqual ( capabilities )
82- } )
83-
84- it ( 'matches hex chain id key' , ( ) => {
85- expect ( resolveCapabilitiesForChain ( { '0x1' : capabilities } , MOCK_CHAIN_ID ) ) . toEqual ( capabilities )
86- } )
87-
88- it ( 'returns null when no chain key matches' , ( ) => {
89- expect ( resolveCapabilitiesForChain ( { '0x64' : capabilities } , MOCK_CHAIN_ID ) ) . toBeNull ( )
90- } )
91-
92- it ( 'returns null for empty capabilities' , ( ) => {
93- expect ( resolveCapabilitiesForChain ( { } , MOCK_CHAIN_ID ) ) . toBeNull ( )
94- } )
95- } )
96-
9762describe ( 'walletCapabilitiesAtom' , ( ) => {
9863 beforeEach ( ( ) => {
9964 jest . clearAllMocks ( )
10065 mockGetCapabilities . mockResolvedValue ( { } )
101- mockWagmiConfigGetClient . mockReturnValue ( { chainId : MOCK_CHAIN_ID } )
102- } )
103-
104- it ( 'returns null when isSafeViaWc is still loading' , async ( ) => {
105- const store = createStore ( )
106- store . set ( writableIsSafeViaWcAtom , null )
107- setWalletInfo ( store )
108-
109- const result = await store . get ( walletCapabilitiesAtom )
110-
111- expect ( result ) . toBeNull ( )
112- expect ( mockGetCapabilities ) . not . toHaveBeenCalled ( )
11366 } )
11467
11568 describe ( 'walletInfoAtom state: missing required fields' , ( ) => {
@@ -118,7 +71,6 @@ describe('walletCapabilitiesAtom', () => {
11871 store . set ( walletInfoAtom , {
11972 chainId : MOCK_CHAIN_ID ,
12073 connector : MOCK_CONNECTOR ,
121- provider : createMockEip1193Provider ( ) ,
12274 } )
12375
12476 const result = await store . get ( walletCapabilitiesAtom )
@@ -132,7 +84,6 @@ describe('walletCapabilitiesAtom', () => {
13284 store . set ( walletInfoAtom , {
13385 account : MOCK_ACCOUNT ,
13486 connector : MOCK_CONNECTOR ,
135- provider : createMockEip1193Provider ( ) ,
13687 } as WalletInfo )
13788
13889 const result = await store . get ( walletCapabilitiesAtom )
@@ -146,21 +97,6 @@ describe('walletCapabilitiesAtom', () => {
14697 store . set ( walletInfoAtom , {
14798 chainId : MOCK_CHAIN_ID ,
14899 account : MOCK_ACCOUNT ,
149- provider : createMockEip1193Provider ( ) ,
150- } )
151-
152- const result = await store . get ( walletCapabilitiesAtom )
153-
154- expect ( result ) . toBeNull ( )
155- expect ( mockGetCapabilities ) . not . toHaveBeenCalled ( )
156- } )
157-
158- it ( 'returns null when provider is missing' , async ( ) => {
159- const store = createStore ( )
160- store . set ( walletInfoAtom , {
161- chainId : MOCK_CHAIN_ID ,
162- account : MOCK_ACCOUNT ,
163- connector : MOCK_CONNECTOR ,
164100 } )
165101
166102 const result = await store . get ( walletCapabilitiesAtom )
@@ -170,7 +106,7 @@ describe('walletCapabilitiesAtom', () => {
170106 } )
171107 } )
172108
173- describe ( 'getCapabilities (viem )' , ( ) => {
109+ describe ( 'getCapabilities (wagmi )' , ( ) => {
174110 it ( 'returns capabilities when getCapabilities resolves for the current chain' , async ( ) => {
175111 const capabilities : WalletCapabilities = { atomic : { status : 'supported' } }
176112 mockGetCapabilities . mockResolvedValue ( capabilities )
@@ -181,8 +117,14 @@ describe('walletCapabilitiesAtom', () => {
181117 const result = await store . get ( walletCapabilitiesAtom )
182118
183119 expect ( result ) . toEqual ( capabilities )
184- expect ( mockGetCapabilities ) . toHaveBeenCalled ( )
185- expect ( mockWagmiConfigGetClient ) . toHaveBeenCalledWith ( { chainId : MOCK_CHAIN_ID } )
120+ expect ( mockGetCapabilities ) . toHaveBeenCalledWith (
121+ expect . anything ( ) ,
122+ expect . objectContaining ( {
123+ account : MOCK_ACCOUNT ,
124+ chainId : MOCK_CHAIN_ID ,
125+ connector : MOCK_CONNECTOR ,
126+ } ) ,
127+ )
186128 } )
187129
188130 it ( 'returns empty capabilities object when getCapabilities resolves with no fields' , async ( ) => {
@@ -196,111 +138,23 @@ describe('walletCapabilitiesAtom', () => {
196138 expect ( result ) . toEqual ( { } )
197139 } )
198140
199- it ( 'returns null when getCapabilities and wallet_getCapabilities both fail' , async ( ) => {
200- mockGetCapabilities . mockRejectedValue ( new Error ( 'viem error' ) )
201- const mockRequest = jest . fn ( ) . mockRejectedValue ( new Error ( 'legacy error' ) )
202-
203- const store = createStore ( )
204- setWalletInfo ( store , { provider : createMockEip1193Provider ( mockRequest ) } )
205-
206- const result = await store . get ( walletCapabilitiesAtom )
207-
208- expect ( result ) . toBeNull ( )
209- expect ( mockRequest ) . toHaveBeenCalledWith ( {
210- method : 'wallet_getCapabilities' ,
211- params : [ MOCK_ACCOUNT ] ,
212- } )
213- } )
214-
215- it ( 'returns null when getCapabilities fails and provider does not support EIP-1193 requests' , async ( ) => {
216- const error = new Error ( 'viem error' )
217- mockGetCapabilities . mockRejectedValue ( error )
141+ it ( 'returns null when getCapabilities fails' , async ( ) => {
142+ mockGetCapabilities . mockRejectedValue ( new Error ( 'wagmi error' ) )
218143
219144 const store = createStore ( )
220- setWalletInfo ( store , { provider : { } as PublicClient } )
221-
222- const result = await store . get ( walletCapabilitiesAtom )
223-
224- expect ( result ) . toBeNull ( )
225- } )
226-
227- it ( 'returns null when getCapabilities times out and provider does not support EIP-1193 requests' , async ( ) => {
228- jest . useFakeTimers ( )
229- mockGetCapabilities . mockImplementation ( ( ) => new Promise ( ( ) => undefined ) )
230-
231- const store = createStore ( )
232- setWalletInfo ( store , { provider : { } as PublicClient } )
233-
234- try {
235- const resultPromise = store . get ( walletCapabilitiesAtom )
236- await jest . advanceTimersByTimeAsync ( 5_000 )
237- const result = await resultPromise
238-
239- expect ( result ) . toBeNull ( )
240- } finally {
241- jest . useRealTimers ( )
242- }
243- } )
244- } )
245-
246- describe ( 'wallet_getCapabilities legacy fallback' , ( ) => {
247- it ( 'returns capabilities resolved from hex chain id key when viem throws' , async ( ) => {
248- const capabilities : WalletCapabilities = { atomic : { status : 'supported' } }
249- mockGetCapabilities . mockRejectedValue ( new Error ( 'viem error' ) )
250- const mockRequest = jest . fn ( ) . mockResolvedValue ( { '0x1' : capabilities } )
251-
252- const store = createStore ( )
253- setWalletInfo ( store , { provider : createMockEip1193Provider ( mockRequest ) } )
254-
255- const result = await store . get ( walletCapabilitiesAtom )
256-
257- expect ( result ) . toEqual ( capabilities )
258- } )
259-
260- it ( 'returns null when the current chain capability is missing' , async ( ) => {
261- const capabilities : WalletCapabilities = { atomic : { status : 'supported' } }
262- mockGetCapabilities . mockRejectedValue ( new Error ( 'viem error' ) )
263- const mockRequest = jest . fn ( ) . mockResolvedValue ( { '0x64' : capabilities } )
264-
265- const store = createStore ( )
266- setWalletInfo ( store , { provider : createMockEip1193Provider ( mockRequest ) } )
145+ setWalletInfo ( store )
267146
268147 const result = await store . get ( walletCapabilitiesAtom )
269148
270149 expect ( result ) . toBeNull ( )
271150 } )
272151
273- it ( 'uses legacy fallback when getCapabilities times out' , async ( ) => {
152+ it ( 'returns null when getCapabilities times out' , async ( ) => {
274153 jest . useFakeTimers ( )
275- const capabilities : WalletCapabilities = { atomic : { status : 'supported' } }
276154 mockGetCapabilities . mockImplementation ( ( ) => new Promise ( ( ) => undefined ) )
277- const mockRequest = jest . fn ( ) . mockResolvedValue ( { '0x1' : capabilities } )
278155
279156 const store = createStore ( )
280- setWalletInfo ( store , { provider : createMockEip1193Provider ( mockRequest ) } )
281-
282- try {
283- const resultPromise = store . get ( walletCapabilitiesAtom )
284- await jest . advanceTimersByTimeAsync ( 5_000 )
285- const result = await resultPromise
286-
287- expect ( result ) . toEqual ( capabilities )
288- expect ( mockRequest ) . toHaveBeenCalledWith ( {
289- method : 'wallet_getCapabilities' ,
290- params : [ MOCK_ACCOUNT ] ,
291- } )
292- } finally {
293- jest . useRealTimers ( )
294- }
295- } )
296-
297- it ( 'returns null when legacy fallback times out' , async ( ) => {
298- jest . useFakeTimers ( )
299- mockGetCapabilities . mockRejectedValue ( new Error ( 'viem error' ) )
300- const mockRequest = jest . fn ( ) . mockImplementation ( ( ) => new Promise ( ( ) => undefined ) )
301-
302- const store = createStore ( )
303- setWalletInfo ( store , { provider : createMockEip1193Provider ( mockRequest ) } )
157+ setWalletInfo ( store )
304158
305159 try {
306160 const resultPromise = store . get ( walletCapabilitiesAtom )
@@ -319,7 +173,6 @@ describe('isAtomicBatchSupportedAsyncAtom', () => {
319173 beforeEach ( ( ) => {
320174 jest . clearAllMocks ( )
321175 mockGetCapabilities . mockResolvedValue ( { } )
322- mockWagmiConfigGetClient . mockReturnValue ( { chainId : MOCK_CHAIN_ID } )
323176 } )
324177
325178 it ( 'returns false when walletInfoAtom yields no capabilities (disconnected)' , async ( ) => {
@@ -411,7 +264,6 @@ describe('isAtomicBatchSupportedLoadableAtom and isAtomicBatchSupportedAtom', ()
411264 beforeEach ( ( ) => {
412265 jest . clearAllMocks ( )
413266 mockGetCapabilities . mockResolvedValue ( { } )
414- mockWagmiConfigGetClient . mockReturnValue ( { chainId : MOCK_CHAIN_ID } )
415267 } )
416268
417269 it ( 'isAtomicBatchSupportedAtom returns null while loading' , async ( ) => {
@@ -438,9 +290,7 @@ describe('isAtomicBatchSupportedLoadableAtom and isAtomicBatchSupportedAtom', ()
438290 store . set ( writableIsSafeViaWcAtom , false )
439291 store . set ( writableIsSafeAppAtom , false )
440292 mockGetCapabilities . mockRejectedValue ( new Error ( 'network error' ) )
441- setWalletInfo ( store , {
442- provider : createMockEip1193Provider ( jest . fn ( ) . mockRejectedValue ( new Error ( 'legacy error' ) ) ) ,
443- } )
293+ setWalletInfo ( store )
444294
445295 store . get ( isAtomicBatchSupportedLoadableAtom )
446296 const asyncResult = await store . get ( isAtomicBatchSupportedAsyncAtom )
0 commit comments