@@ -192,6 +192,11 @@ export type KeyringControllerWithKeyringAction = {
192
192
handler : KeyringController [ 'withKeyring' ] ;
193
193
} ;
194
194
195
+ export type KeyringControllerWithReadonlyKeyringAction = {
196
+ type : `${typeof name } :withReadonlyKeyring`;
197
+ handler : KeyringController [ 'withReadonlyKeyring' ] ;
198
+ } ;
199
+
195
200
export type KeyringControllerStateChangeEvent = {
196
201
type : `${typeof name } :stateChange`;
197
202
payload : [ KeyringControllerState , Patch [ ] ] ;
@@ -233,7 +238,8 @@ export type KeyringControllerActions =
233
238
| KeyringControllerPatchUserOperationAction
234
239
| KeyringControllerSignUserOperationAction
235
240
| KeyringControllerAddNewAccountAction
236
- | KeyringControllerWithKeyringAction ;
241
+ | KeyringControllerWithKeyringAction
242
+ | KeyringControllerWithReadonlyKeyringAction ;
237
243
238
244
export type KeyringControllerEvents =
239
245
| KeyringControllerStateChangeEvent
@@ -1465,6 +1471,74 @@ export class KeyringController extends BaseController<
1465
1471
) ;
1466
1472
}
1467
1473
1474
+ /**
1475
+ * Execute an operation on the selected keyring.
1476
+ *
1477
+ * @param selector - Keyring selector object.
1478
+ * @param operation - Function to execute with the selected keyring.
1479
+ * @param options - Additional options.
1480
+ * @returns Promise resolving to the result of the function execution.
1481
+ * @template SelectedKeyring - The type of the selected keyring.
1482
+ * @template CallbackResult - The type of the value resolved by the callback function.
1483
+ */
1484
+ async #executeWithKeyring<
1485
+ SelectedKeyring extends EthKeyring = EthKeyring ,
1486
+ CallbackResult = void ,
1487
+ > (
1488
+ selector : KeyringSelector ,
1489
+ operation : ( {
1490
+ keyring,
1491
+ metadata,
1492
+ } : {
1493
+ keyring : SelectedKeyring ;
1494
+ metadata : KeyringMetadata ;
1495
+ } ) => Promise < CallbackResult > ,
1496
+
1497
+ options :
1498
+ | { createIfMissing ?: false }
1499
+ | { createIfMissing : true ; createWithData ?: unknown } ,
1500
+ ) : Promise < CallbackResult > {
1501
+ let keyring : SelectedKeyring | undefined ;
1502
+
1503
+ if ( 'address' in selector ) {
1504
+ keyring = ( await this . getKeyringForAccount ( selector . address ) ) as
1505
+ | SelectedKeyring
1506
+ | undefined ;
1507
+ } else if ( 'type' in selector ) {
1508
+ keyring = this . getKeyringsByType ( selector . type ) [ selector . index || 0 ] as
1509
+ | SelectedKeyring
1510
+ | undefined ;
1511
+
1512
+ if ( ! keyring && options . createIfMissing ) {
1513
+ keyring = ( await this . #newKeyring(
1514
+ selector . type ,
1515
+ options . createWithData ,
1516
+ ) ) as SelectedKeyring ;
1517
+ }
1518
+ } else if ( 'id' in selector ) {
1519
+ keyring = this . #getKeyringById( selector . id ) as SelectedKeyring ;
1520
+ }
1521
+
1522
+ if ( ! keyring ) {
1523
+ throw new Error ( KeyringControllerError . KeyringNotFound ) ;
1524
+ }
1525
+
1526
+ const result = await operation ( {
1527
+ keyring,
1528
+ metadata : this . #getKeyringMetadata( keyring ) ,
1529
+ } ) ;
1530
+
1531
+ if ( Object . is ( result , keyring ) ) {
1532
+ // Access to a keyring instance outside of controller safeguards
1533
+ // should be discouraged, as it can lead to unexpected behavior.
1534
+ // This error is thrown to prevent consumers using `withKeyring`
1535
+ // as a way to get a reference to a keyring instance.
1536
+ throw new Error ( KeyringControllerError . UnsafeDirectKeyringAccess ) ;
1537
+ }
1538
+
1539
+ return result ;
1540
+ }
1541
+
1468
1542
/**
1469
1543
* Select a keyring and execute the given operation with
1470
1544
* the selected keyring, as a mutually exclusive atomic
@@ -1552,45 +1626,60 @@ export class KeyringController extends BaseController<
1552
1626
this . #assertIsUnlocked( ) ;
1553
1627
1554
1628
return this . #persistOrRollback( async ( ) => {
1555
- let keyring : SelectedKeyring | undefined ;
1556
-
1557
- if ( 'address' in selector ) {
1558
- keyring = ( await this . getKeyringForAccount ( selector . address ) ) as
1559
- | SelectedKeyring
1560
- | undefined ;
1561
- } else if ( 'type' in selector ) {
1562
- keyring = this . getKeyringsByType ( selector . type ) [ selector . index || 0 ] as
1563
- | SelectedKeyring
1564
- | undefined ;
1565
-
1566
- if ( ! keyring && options . createIfMissing ) {
1567
- keyring = ( await this . #newKeyring(
1568
- selector . type ,
1569
- options . createWithData ,
1570
- ) ) as SelectedKeyring ;
1571
- }
1572
- } else if ( 'id' in selector ) {
1573
- keyring = this . #getKeyringById( selector . id ) as SelectedKeyring ;
1574
- }
1575
-
1576
- if ( ! keyring ) {
1577
- throw new Error ( KeyringControllerError . KeyringNotFound ) ;
1578
- }
1629
+ return await this . #executeWithKeyring( selector , operation , options ) ;
1630
+ } ) ;
1631
+ }
1579
1632
1580
- const result = await operation ( {
1581
- keyring,
1582
- metadata : this . #getKeyringMetadata( keyring ) ,
1583
- } ) ;
1633
+ /**
1634
+ * Select a keyring and execute the given operation with
1635
+ * the selected keyring, as a mutually exclusive atomic
1636
+ * operation.
1637
+ *
1638
+ * The method won't persists changes at the end of the
1639
+ * function execution.
1640
+ *
1641
+ * @param selector - Keyring selector object.
1642
+ * @param operation - Function to execute with the selected keyring.
1643
+ * @returns Promise resolving to the result of the function execution.
1644
+ * @template SelectedKeyring - The type of the selected keyring.
1645
+ * @template CallbackResult - The type of the value resolved by the callback function.
1646
+ */
1647
+ async withReadonlyKeyring <
1648
+ SelectedKeyring extends EthKeyring = EthKeyring ,
1649
+ CallbackResult = void ,
1650
+ > (
1651
+ selector : KeyringSelector ,
1652
+ operation : ( {
1653
+ keyring,
1654
+ metadata,
1655
+ } : {
1656
+ keyring : Readonly < SelectedKeyring > ;
1657
+ metadata : KeyringMetadata ;
1658
+ } ) => Promise < CallbackResult > ,
1659
+ ) : Promise < CallbackResult > ;
1584
1660
1585
- if ( Object . is ( result , keyring ) ) {
1586
- // Access to a keyring instance outside of controller safeguards
1587
- // should be discouraged, as it can lead to unexpected behavior.
1588
- // This error is thrown to prevent consumers using `withKeyring`
1589
- // as a way to get a reference to a keyring instance.
1590
- throw new Error ( KeyringControllerError . UnsafeDirectKeyringAccess ) ;
1591
- }
1661
+ async withReadonlyKeyring <
1662
+ SelectedKeyring extends EthKeyring = EthKeyring ,
1663
+ CallbackResult = void ,
1664
+ > (
1665
+ selector : KeyringSelector ,
1666
+ operation : ( {
1667
+ keyring,
1668
+ metadata,
1669
+ } : {
1670
+ keyring : Readonly < SelectedKeyring > ;
1671
+ metadata : KeyringMetadata ;
1672
+ } ) => Promise < CallbackResult > ,
1673
+ options :
1674
+ | { createIfMissing ?: false }
1675
+ | { createIfMissing : true ; createWithData ?: unknown } = {
1676
+ createIfMissing : false ,
1677
+ } ,
1678
+ ) : Promise < CallbackResult > {
1679
+ this . #assertIsUnlocked( ) ;
1592
1680
1593
- return result ;
1681
+ return this . #withControllerLock( async ( ) => {
1682
+ return await this . #executeWithKeyring( selector , operation , options ) ;
1594
1683
} ) ;
1595
1684
}
1596
1685
@@ -1913,6 +2002,11 @@ export class KeyringController extends BaseController<
1913
2002
`${ name } :withKeyring` ,
1914
2003
this . withKeyring . bind ( this ) ,
1915
2004
) ;
2005
+
2006
+ this . messagingSystem . registerActionHandler (
2007
+ `${ name } :withReadonlyKeyring` ,
2008
+ this . withReadonlyKeyring . bind ( this ) ,
2009
+ ) ;
1916
2010
}
1917
2011
1918
2012
/**
0 commit comments