@@ -3,6 +3,16 @@ import { Messenger, deriveStateFromMetadata } from '@metamask/base-controller';
3
3
import type { HandleSnapRequest , HasSnap } from '@metamask/snaps-controllers' ;
4
4
import type { SnapId } from '@metamask/snaps-sdk' ;
5
5
import type { Hex } from '@metamask/utils' ;
6
+ import {
7
+ CHAIN_ID ,
8
+ DELEGATOR_CONTRACTS ,
9
+ } from '@metamask/delegation-deployments' ;
10
+ import {
11
+ createTimestampTerms ,
12
+ createNativeTokenStreamingTerms ,
13
+ encodeDelegations ,
14
+ } from '@metamask/delegation-core' ;
15
+ import { toHex } from '@metamask/controller-utils' ;
6
16
7
17
import type { GatorPermissionsControllerMessenger } from './GatorPermissionsController' ;
8
18
import GatorPermissionsController from './GatorPermissionsController' ;
@@ -23,6 +33,7 @@ import type {
23
33
ExtractAvailableAction ,
24
34
ExtractAvailableEvent ,
25
35
} from '../../base-controller/tests/helpers' ;
36
+ import { DELEGATION_FRAMEWORK_VERSION } from './decodePermission' ;
26
37
27
38
const MOCK_CHAIN_ID_1 : Hex = '0xaa36a7' ;
28
39
const MOCK_CHAIN_ID_2 : Hex = '0x1' ;
@@ -453,6 +464,179 @@ describe('GatorPermissionsController', () => {
453
464
` ) ;
454
465
} ) ;
455
466
} ) ;
467
+
468
+ describe ( 'decodePermissionFromPermissionContextForOrigin' , ( ) => {
469
+ const chainId = CHAIN_ID . sepolia ;
470
+ const contracts =
471
+ DELEGATOR_CONTRACTS [ DELEGATION_FRAMEWORK_VERSION ] [ chainId ] ;
472
+
473
+ let controller : GatorPermissionsController ;
474
+
475
+ beforeEach ( ( ) => {
476
+ controller = new GatorPermissionsController ( {
477
+ messenger : getMessenger ( ) ,
478
+ } ) ;
479
+ } ) ;
480
+
481
+ it ( 'decodes a native-token-stream permission context successfully' , async ( ) => {
482
+ const {
483
+ TimestampEnforcer,
484
+ NativeTokenStreamingEnforcer,
485
+ ExactCalldataEnforcer,
486
+ } = contracts ;
487
+
488
+ const delegator = '0x1111111111111111111111111111111111111111' ;
489
+ const delegate = '0x2222222222222222222222222222222222222222' ;
490
+
491
+ const timestampBeforeThreshold = 1720000 ;
492
+ const expiryTerms = createTimestampTerms (
493
+ { timestampAfterThreshold : 0 , timestampBeforeThreshold } ,
494
+ { out : 'hex' } ,
495
+ ) ;
496
+
497
+ const initialAmount = 123456n ;
498
+ const maxAmount = 999999n ;
499
+ const amountPerSecond = 1n ;
500
+ const startTime = 1715664 ;
501
+ const streamTerms = createNativeTokenStreamingTerms (
502
+ { initialAmount, maxAmount, amountPerSecond, startTime } ,
503
+ { out : 'hex' } ,
504
+ ) ;
505
+
506
+ const caveats = [
507
+ {
508
+ enforcer : TimestampEnforcer ,
509
+ terms : expiryTerms ,
510
+ args : '0x' ,
511
+ } as const ,
512
+ {
513
+ enforcer : NativeTokenStreamingEnforcer ,
514
+ terms : streamTerms ,
515
+ args : '0x' ,
516
+ } as const ,
517
+ { enforcer : ExactCalldataEnforcer , terms : '0x' , args : '0x' } as const ,
518
+ ] ;
519
+
520
+ const permissionContext = encodeDelegations (
521
+ [
522
+ {
523
+ delegate,
524
+ delegator,
525
+ authority : '0x' ,
526
+ caveats,
527
+ salt : 0n ,
528
+ signature : '0x' ,
529
+ } ,
530
+ ] ,
531
+ { out : 'hex' } ,
532
+ ) ;
533
+
534
+ const result =
535
+ await controller . decodePermissionFromPermissionContextForOrigin ( {
536
+ origin : controller . permissionsProviderSnapId ,
537
+ chainId,
538
+ permissionContext,
539
+ } ) ;
540
+
541
+ expect ( result . chainId ) . toBe ( toHex ( chainId ) ) ;
542
+ expect ( result . address ) . toBe ( delegator ) ;
543
+ expect ( result . signer ) . toStrictEqual ( {
544
+ type : 'account' ,
545
+ data : { address : delegate } ,
546
+ } ) ;
547
+ expect ( result . permission . type ) . toBe ( 'native-token-stream' ) ;
548
+ expect ( result . expiry ) . toBe ( timestampBeforeThreshold ) ;
549
+ expect ( result . permission . data ) . toStrictEqual ( {
550
+ initialAmount,
551
+ maxAmount,
552
+ amountPerSecond,
553
+ startTime,
554
+ } ) ;
555
+ } ) ;
556
+
557
+ it ( 'throws when origin does not match permissions provider' , async ( ) => {
558
+ await expect (
559
+ controller . decodePermissionFromPermissionContextForOrigin ( {
560
+ origin : 'not-the-provider' ,
561
+ chainId : 1 ,
562
+ permissionContext : '0x' ,
563
+ } ) ,
564
+ ) . rejects . toThrow ( 'Origin not-the-provider not allowed' ) ;
565
+ } ) ;
566
+
567
+ it ( 'throws when the permission context contains multiple delegations' , async ( ) => {
568
+ const permissionContext = encodeDelegations (
569
+ [
570
+ {
571
+ delegate : '0x1' ,
572
+ delegator : '0x2' ,
573
+ authority : '0x' ,
574
+ caveats : [ ] ,
575
+ salt : 0n ,
576
+ signature : '0x' ,
577
+ } ,
578
+ {
579
+ delegate : '0x3' ,
580
+ delegator : '0x4' ,
581
+ authority : '0x' ,
582
+ caveats : [ ] ,
583
+ salt : 0n ,
584
+ signature : '0x' ,
585
+ } ,
586
+ ] ,
587
+ { out : 'hex' } ,
588
+ ) ;
589
+
590
+ await expect (
591
+ controller . decodePermissionFromPermissionContextForOrigin ( {
592
+ origin : controller . permissionsProviderSnapId ,
593
+ chainId : 1 ,
594
+ permissionContext,
595
+ } ) ,
596
+ ) . rejects . toThrow ( 'Failed to decode permission' ) ;
597
+ } ) ;
598
+
599
+ it ( 'throws when enforcers do not identify a supported permission' , async ( ) => {
600
+ const { TimestampEnforcer, ValueLteEnforcer } = contracts ;
601
+
602
+ const expiryTerms = createTimestampTerms (
603
+ { timestampAfterThreshold : 0 , timestampBeforeThreshold : 100 } ,
604
+ { out : 'hex' } ,
605
+ ) ;
606
+
607
+ const caveats = [
608
+ {
609
+ enforcer : TimestampEnforcer ,
610
+ terms : expiryTerms ,
611
+ args : '0x' ,
612
+ } as const ,
613
+ // Include a forbidden/irrelevant enforcer without required counterparts
614
+ { enforcer : ValueLteEnforcer , terms : '0x' , args : '0x' } as const ,
615
+ ] ;
616
+
617
+ const permissionContext = encodeDelegations (
618
+ [
619
+ {
620
+ delegate : '0x1111111111111111111111111111111111111111' ,
621
+ delegator : '0x2222222222222222222222222222222222222222' ,
622
+ authority : '0x' ,
623
+ caveats,
624
+ salt : 0n ,
625
+ signature : '0x' ,
626
+ } ,
627
+ ] ,
628
+ { out : 'hex' } ,
629
+ ) ;
630
+
631
+ await expect (
632
+ controller . decodePermissionFromPermissionContextForOrigin ( {
633
+ origin : controller . permissionsProviderSnapId ,
634
+ chainId,
635
+ permissionContext,
636
+ } ) ,
637
+ ) . rejects . toThrow ( 'Unable to identify permission type' ) ;
638
+ } ) ;
639
+ } ) ;
456
640
} ) ;
457
641
458
642
/**
0 commit comments