1+ import { type CaipChainId , KnownCaipNamespace } from '@metamask/utils' ;
12import {
23 buildAdapterNamespaces ,
34 proposalReferencedAdapterNamespaces ,
45 seedAdapterPermissions ,
5- } from './namespaces ' ;
6+ } from './helpers ' ;
67import type { ChainAdapter , NamespaceConfig } from './types' ;
8+ import {
9+ getRedirectMethodsForChain ,
10+ mapRequestForSnap ,
11+ normalizeCaipChainIdInbound ,
12+ normalizeCaipChainIdOutbound ,
13+ normalizeSnapResponse ,
14+ } from './index' ;
15+ import { getAdapter , getAllAdapters } from './registry' ;
716
817jest . mock ( './registry' , ( ) => ( {
9- getAllAdapters : jest . fn ( ) ,
18+ getAdapter : jest . fn ( ) ,
19+ getAllAdapters : jest . fn ( ) . mockReturnValue ( [ ] ) ,
20+ getAllRegisteredNamespaces : jest . fn ( ) . mockReturnValue ( [ ] ) ,
1021} ) ) ;
1122
1223jest . mock ( '../../SDKConnect/utils/DevLogger' , ( ) => ( {
1324 log : jest . fn ( ) ,
1425} ) ) ;
1526
16- import { getAllAdapters } from './registry' ;
27+ const mockedGetAdapter = getAdapter as jest . Mock ;
1728
1829const mockedGetAllAdapters = getAllAdapters as jest . Mock ;
1930
2031const createFakeAdapter = (
2132 overrides : Partial < ChainAdapter > = { } ,
2233) : ChainAdapter => ( {
23- namespace : overrides . namespace ?? 'fake' ,
34+ namespace : ( overrides . namespace ?? 'fake' ) as KnownCaipNamespace ,
2435 redirectMethods : overrides . redirectMethods ?? [ ] ,
2536 proposalReferencesNamespace :
2637 overrides . proposalReferencesNamespace ?? jest . fn ( ) . mockReturnValue ( false ) ,
@@ -33,6 +44,15 @@ const createFakeAdapter = (
3344 normalizeSnapResponse :
3445 overrides . normalizeSnapResponse ??
3546 jest . fn ( ) . mockImplementation ( ( { result } ) => result ) ,
47+ buildScopedPermissionsNamespace :
48+ overrides . buildScopedPermissionsNamespace ??
49+ jest . fn ( ) . mockReturnValue ( undefined ) ,
50+ normalizeCaipChainIdInbound :
51+ overrides . normalizeCaipChainIdInbound ??
52+ jest . fn ( ) . mockImplementation ( ( caipChainId : CaipChainId ) => caipChainId ) ,
53+ normalizeCaipChainIdOutbound :
54+ overrides . normalizeCaipChainIdOutbound ??
55+ jest . fn ( ) . mockImplementation ( ( caipChainId : CaipChainId ) => caipChainId ) ,
3656} ) ;
3757
3858beforeEach ( ( ) => {
@@ -44,8 +64,14 @@ describe('seedAdapterPermissions', () => {
4464 const tronHook = jest . fn ( ) ;
4565 const solanaHook = jest . fn ( ) ;
4666 mockedGetAllAdapters . mockReturnValue ( [
47- createFakeAdapter ( { namespace : 'tron' , onBeforeApprove : tronHook } ) ,
48- createFakeAdapter ( { namespace : 'solana' , onBeforeApprove : solanaHook } ) ,
67+ createFakeAdapter ( {
68+ namespace : KnownCaipNamespace . Tron ,
69+ onBeforeApprove : tronHook ,
70+ } ) ,
71+ createFakeAdapter ( {
72+ namespace : KnownCaipNamespace . Solana ,
73+ onBeforeApprove : solanaHook ,
74+ } ) ,
4975 ] ) ;
5076
5177 await seedAdapterPermissions ( {
@@ -66,7 +92,9 @@ describe('seedAdapterPermissions', () => {
6692 } ) ;
6793
6894 it ( 'skips adapters that do not declare an onBeforeApprove hook' , async ( ) => {
69- const adapterWithoutHook = createFakeAdapter ( { namespace : 'btc' } ) ;
95+ const adapterWithoutHook = createFakeAdapter ( {
96+ namespace : KnownCaipNamespace . Bip122 ,
97+ } ) ;
7098 delete adapterWithoutHook . onBeforeApprove ;
7199 mockedGetAllAdapters . mockReturnValue ( [ adapterWithoutHook ] ) ;
72100
@@ -79,8 +107,14 @@ describe('seedAdapterPermissions', () => {
79107 const failingHook = jest . fn ( ) . mockRejectedValue ( new Error ( 'boom' ) ) ;
80108 const followingHook = jest . fn ( ) ;
81109 mockedGetAllAdapters . mockReturnValue ( [
82- createFakeAdapter ( { namespace : 'a' , onBeforeApprove : failingHook } ) ,
83- createFakeAdapter ( { namespace : 'b' , onBeforeApprove : followingHook } ) ,
110+ createFakeAdapter ( {
111+ namespace : 'a' as KnownCaipNamespace ,
112+ onBeforeApprove : failingHook ,
113+ } ) ,
114+ createFakeAdapter ( {
115+ namespace : 'b' as KnownCaipNamespace ,
116+ onBeforeApprove : followingHook ,
117+ } ) ,
84118 ] ) ;
85119
86120 await seedAdapterPermissions ( { proposal : { } , channelId : 'channel-2' } ) ;
@@ -117,11 +151,11 @@ describe('buildAdapterNamespaces', () => {
117151 } ;
118152 mockedGetAllAdapters . mockReturnValue ( [
119153 createFakeAdapter ( {
120- namespace : 'tron' ,
154+ namespace : KnownCaipNamespace . Tron ,
121155 buildNamespace : jest . fn ( ) . mockReturnValue ( tronSlice ) ,
122156 } ) ,
123157 createFakeAdapter ( {
124- namespace : 'solana' ,
158+ namespace : KnownCaipNamespace . Solana ,
125159 buildNamespace : jest . fn ( ) . mockReturnValue ( solanaSlice ) ,
126160 } ) ,
127161 ] ) ;
@@ -140,25 +174,25 @@ describe('buildAdapterNamespaces', () => {
140174 } ;
141175 mockedGetAllAdapters . mockReturnValue ( [
142176 createFakeAdapter ( {
143- namespace : 'tron' ,
177+ namespace : KnownCaipNamespace . Tron ,
144178 buildNamespace : jest . fn ( ) . mockReturnValue ( tronSlice ) ,
145179 } ) ,
146180 createFakeAdapter ( {
147- namespace : 'solana' ,
181+ namespace : KnownCaipNamespace . Solana ,
148182 buildNamespace : jest . fn ( ) . mockReturnValue ( undefined ) ,
149183 } ) ,
150184 ] ) ;
151185
152186 const result = buildAdapterNamespaces ( { proposal : { } } ) ;
153187
154188 expect ( result ) . toStrictEqual ( { tron : tronSlice } ) ;
155- expect ( result ) . not . toHaveProperty ( 'solana' ) ;
189+ expect ( result ) . not . toHaveProperty ( KnownCaipNamespace . Solana ) ;
156190 } ) ;
157191
158192 it ( 'forwards accounts/methods/events from existingNamespaces to each adapter' , ( ) => {
159193 const buildNamespace = jest . fn ( ) . mockReturnValue ( undefined ) ;
160194 mockedGetAllAdapters . mockReturnValue ( [
161- createFakeAdapter ( { namespace : 'tron' , buildNamespace } ) ,
195+ createFakeAdapter ( { namespace : KnownCaipNamespace . Tron , buildNamespace } ) ,
162196 ] ) ;
163197
164198 buildAdapterNamespaces ( {
@@ -183,7 +217,7 @@ describe('buildAdapterNamespaces', () => {
183217 it ( 'passes undefined existing fields when the namespace has no entry in existingNamespaces' , ( ) => {
184218 const buildNamespace = jest . fn ( ) . mockReturnValue ( undefined ) ;
185219 mockedGetAllAdapters . mockReturnValue ( [
186- createFakeAdapter ( { namespace : 'tron' , buildNamespace } ) ,
220+ createFakeAdapter ( { namespace : KnownCaipNamespace . Tron , buildNamespace } ) ,
187221 ] ) ;
188222
189223 buildAdapterNamespaces ( { proposal : { } , existingNamespaces : { } } ) ;
@@ -201,28 +235,31 @@ describe('proposalReferencedAdapterNamespaces', () => {
201235 it ( 'returns the namespaces of all adapters that recognize the proposal' , ( ) => {
202236 mockedGetAllAdapters . mockReturnValue ( [
203237 createFakeAdapter ( {
204- namespace : 'tron' ,
238+ namespace : KnownCaipNamespace . Tron ,
205239 proposalReferencesNamespace : jest . fn ( ) . mockReturnValue ( true ) ,
206240 } ) ,
207241 createFakeAdapter ( {
208- namespace : 'solana' ,
242+ namespace : KnownCaipNamespace . Solana ,
209243 proposalReferencesNamespace : jest . fn ( ) . mockReturnValue ( false ) ,
210244 } ) ,
211245 createFakeAdapter ( {
212- namespace : 'bitcoin' ,
246+ namespace : KnownCaipNamespace . Bip122 ,
213247 proposalReferencesNamespace : jest . fn ( ) . mockReturnValue ( true ) ,
214248 } ) ,
215249 ] ) ;
216250
217251 const result = proposalReferencedAdapterNamespaces ( { } ) ;
218252
219- expect ( result ) . toStrictEqual ( [ 'tron' , 'bitcoin' ] ) ;
253+ expect ( result ) . toStrictEqual ( [
254+ KnownCaipNamespace . Tron ,
255+ KnownCaipNamespace . Bip122 ,
256+ ] ) ;
220257 } ) ;
221258
222259 it ( 'returns an empty array when no adapter recognizes the proposal' , ( ) => {
223260 mockedGetAllAdapters . mockReturnValue ( [
224261 createFakeAdapter ( {
225- namespace : 'tron' ,
262+ namespace : KnownCaipNamespace . Tron ,
226263 proposalReferencesNamespace : jest . fn ( ) . mockReturnValue ( false ) ,
227264 } ) ,
228265 ] ) ;
@@ -238,3 +275,132 @@ describe('proposalReferencedAdapterNamespaces', () => {
238275 expect ( proposalReferencedAdapterNamespaces ( { } ) ) . toStrictEqual ( [ ] ) ;
239276 } ) ;
240277} ) ;
278+
279+ describe ( 'mapRequestForSnap' , ( ) => {
280+ it ( 'extracts the CAIP-2 namespace from the scope and looks up the adapter once' , ( ) => {
281+ mockedGetAdapter . mockReturnValue ( undefined ) ;
282+
283+ mapRequestForSnap ( {
284+ scope : 'tron:728126428' ,
285+ method : 'tron_signTransaction' ,
286+ params : [ ] ,
287+ } ) ;
288+
289+ expect ( mockedGetAdapter ) . toHaveBeenCalledTimes ( 1 ) ;
290+ expect ( mockedGetAdapter ) . toHaveBeenCalledWith ( 'tron' ) ;
291+ } ) ;
292+
293+ it ( 'delegates to the matched adapter and returns the mapped request' , ( ) => {
294+ const adapterMapped = { method : 'signTransaction' , params : { foo : 1 } } ;
295+ const fakeAdapter = createFakeAdapter ( {
296+ namespace : KnownCaipNamespace . Tron ,
297+ mapRequestForSnap : jest . fn ( ) . mockReturnValue ( adapterMapped ) ,
298+ } ) ;
299+ mockedGetAdapter . mockReturnValue ( fakeAdapter ) ;
300+
301+ const result = mapRequestForSnap ( {
302+ scope : 'tron:728126428' ,
303+ method : 'tron_signTransaction' ,
304+ params : [ { raw_data_hex : '0xabc' } ] ,
305+ } ) ;
306+
307+ expect ( result ) . toBe ( adapterMapped ) ;
308+ expect ( fakeAdapter . mapRequestForSnap ) . toHaveBeenCalledWith ( {
309+ method : 'tron_signTransaction' ,
310+ params : [ { raw_data_hex : '0xabc' } ] ,
311+ } ) ;
312+ } ) ;
313+
314+ it ( 'returns the original method/params when no adapter is registered for the scope' , ( ) => {
315+ mockedGetAdapter . mockReturnValue ( undefined ) ;
316+
317+ const result = mapRequestForSnap ( {
318+ scope : 'eip155:1' ,
319+ method : 'eth_sign' ,
320+ params : [ '0x1' , '0x2' ] ,
321+ } ) ;
322+
323+ expect ( result ) . toStrictEqual ( {
324+ method : 'eth_sign' ,
325+ params : [ '0x1' , '0x2' ] ,
326+ } ) ;
327+ } ) ;
328+ } ) ;
329+
330+ describe ( 'normalizeSnapResponse' , ( ) => {
331+ it ( 'delegates to the matched adapter and returns its normalized result' , ( ) => {
332+ const adapterResult = { txID : 'tx-1' , signature : [ '0xsig' ] } ;
333+ const fakeAdapter = createFakeAdapter ( {
334+ namespace : KnownCaipNamespace . Tron ,
335+ normalizeSnapResponse : jest . fn ( ) . mockReturnValue ( adapterResult ) ,
336+ } ) ;
337+ mockedGetAdapter . mockReturnValue ( fakeAdapter ) ;
338+
339+ const result = normalizeSnapResponse ( {
340+ scope : 'tron:728126428' ,
341+ method : 'tron_signTransaction' ,
342+ params : [ ] ,
343+ result : { signature : '0xsig' } ,
344+ } ) ;
345+
346+ expect ( result ) . toBe ( adapterResult ) ;
347+ expect ( fakeAdapter . normalizeSnapResponse ) . toHaveBeenCalledWith ( {
348+ method : 'tron_signTransaction' ,
349+ params : [ ] ,
350+ result : { signature : '0xsig' } ,
351+ } ) ;
352+ } ) ;
353+
354+ it ( 'returns the raw snap result when no adapter is registered for the scope' , ( ) => {
355+ mockedGetAdapter . mockReturnValue ( undefined ) ;
356+ const snapResult = { hello : 'world' } ;
357+
358+ const result = normalizeSnapResponse ( {
359+ scope : 'eip155:1' ,
360+ method : 'eth_sign' ,
361+ params : [ ] ,
362+ result : snapResult ,
363+ } ) ;
364+
365+ expect ( result ) . toBe ( snapResult ) ;
366+ } ) ;
367+ } ) ;
368+
369+ describe ( 'getRedirectMethodsForChain' , ( ) => {
370+ it ( 'returns the redirectMethods of the adapter for the scope namespace' , ( ) => {
371+ mockedGetAdapter . mockReturnValue (
372+ createFakeAdapter ( {
373+ namespace : KnownCaipNamespace . Tron ,
374+ redirectMethods : [ 'tron_signTransaction' , 'tron_signMessage' ] ,
375+ } ) ,
376+ ) ;
377+
378+ const result = getRedirectMethodsForChain ( 'tron:728126428' ) ;
379+
380+ expect ( result ) . toStrictEqual ( [ 'tron_signTransaction' , 'tron_signMessage' ] ) ;
381+ } ) ;
382+
383+ it ( 'returns an empty array when no adapter matches the scope' , ( ) => {
384+ mockedGetAdapter . mockReturnValue ( undefined ) ;
385+
386+ expect ( getRedirectMethodsForChain ( 'eip155:1' ) ) . toStrictEqual ( [ ] ) ;
387+ } ) ;
388+ } ) ;
389+
390+ describe ( 'CAIP chain id normalization helpers' , ( ) => {
391+ it ( 'normalizes tron hex chain ids inbound to decimal' , ( ) => {
392+ expect ( normalizeCaipChainIdInbound ( 'tron:0x2b6653dc' ) ) . toBe (
393+ 'tron:728126428' ,
394+ ) ;
395+ } ) ;
396+
397+ it ( 'normalizes tron decimal chain ids outbound to hex' , ( ) => {
398+ expect ( normalizeCaipChainIdOutbound ( 'tron:728126428' ) ) . toBe (
399+ 'tron:0x2b6653dc' ,
400+ ) ;
401+ } ) ;
402+
403+ it ( 'keeps non-numeric tron chain references unchanged outbound' , ( ) => {
404+ expect ( normalizeCaipChainIdOutbound ( 'tron:mainnet' ) ) . toBe ( 'tron:mainnet' ) ;
405+ } ) ;
406+ } ) ;
0 commit comments