--- eip: 7715 title: Request Permissions from Wallets description: Adds JSON-RPC method for requesting permissions from a wallet author: Luka Isailovic (@lukaisailovic), Derek Rein (@arein), Dan Finlay (@danfinlay), Derek Chiang (@derekchiang), Fil Makarov (@filmakarov), Pedro Gomes (@pedrouid), Conner Swenberg (@ilikesymmetry), Lukas Rosario (@lukasrosario), Idris Bowman (@V00D00-child), Jeff Smale (@jeffsmale90) discussions-to: https://ethereum-magicians.org/t/erc-7715-grant-permissions-from-wallets/20100 status: Draft type: Standards Track category: ERC created: 2024-05-24 requires: 4337, 7710 --- ## Abstract We define a new JSON-RPC method `wallet_requestExecutionPermissions` for DApp to request a Wallet to grant permissions in order to execute transactions on the user’s behalf. This enables two use cases: - Executing transactions for users without a wallet connection. - Executing transactions for users with a wallet connection that is scoped with permissions. ## Motivation Currently most DApps implement a flow similar to the following: ![Wallet Approve Flow](../assets/eip-7715/approve-flow.svg) Each interaction requires the user to sign a transaction with their wallet. The problems are: - It can get tedious for the user to manually approve every transaction, especially in highly-interactive applications such as games. - It’s impossible to send transactions for users without an active wallet connection. This invalidates use cases such as subscriptions, passive investments, limit orders, and more. ## Specification The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119. ### Permission Types, Rule Types This ERC does not specify an exhaustive list of rule or permission types, since we expect more rule and permission types to be developed as wallets get more advanced. A permission type, or rule type is valid as long as both the DApp and the wallet are willing to support it. However, if two permissions or two rules share the same type name, a DApp could request with one type of permission, or rule while the wallet grants another. Therefore, it’s important that no two permissions, or two rules share the same type. Furthermore, new permission types or rule types should be specified in addition ERCs. In all cases, these new types MUST inherit from the `BasePermission` or `BaseRule` scheme. #### Permissions `isAdjustmentAllowed` defines a boolean value that allows DApp to define whether the Wallet MAY attenuate(reduce or increase) the authority of a "permission" to meet the user’s terms for approval. _For example, a DApp may require an allowance for a specific asset to complete a payment and does not want the user to adjust the requested allowance._ ```tsx type BasePermission = { type: string; // enum defined by ERCs isAdjustmentAllowed: boolean; // whether the wallet MAY attenuate the permission data: Record; // specific to the type, structure defined by ERCs }; ``` #### Rules ```tsx type BaseRule = { type: string; // enum defined by ERCs data: Record; // specific to the type, structure defined by ERCs }; // Constrains a permission so that it is only valid until a specified timestamp. type ExpiryRule = BaseRule & { type: "expiry"; data: { timestamp: number; // unix timestamp at which the permission becomes invalid }; }; ``` ### `wallet_requestExecutionPermissions` We introduce a `wallet_requestExecutionPermissions` method for the DApp to request the Wallet to grant permissions. #### Request Specification ```tsx type PermissionRequest = { chainId: Hex; // hex-encoding of uint256 from?: Address; to: Address; permission: { type: string; // enum defined by ERCs isAdjustmentAllowed: boolean; // whether the permission can be adjusted data: Record; //specific to the type, structure defined by ERCs }; rules?: { type: string; // enum defined by ERCs data: Record; // specific to the type, structure defined by ERCs }[]; }[]; ``` `chainId` defines the chain with [EIP-155](./eip-155.md) which applies to this permission request and all addresses can be found defined by other parameters. `from` identifies the account being targeted for this permission request which is useful when a connection has been established and multiple accounts have been exposed. It is optional to let the user choose which account to grant permission for. `to` is a field that identifies the DApp session account associated with the permission `permission` defines the allowed behavior the `to` account can do on behalf of the `from` account. See the “Permission” section for details. `rules` define the restrictions or conditions that a `to` account MUST abide by when using a permission to act on behalf of an account. See the “Rule” section for details. **Request example**: An array of `PermissionRequest` objects is the final `params` field expected by the `wallet_requestExecutionPermissions` RPC. ```tsx [ { chainId: "0x01", from: "0x...", to: "0x016562aA41A8697720ce0943F003141f5dEAe006", permission: { type: "native-token-allowance", isAdjustmentAllowed: false, data: { allowance: "0x1DCD6500", }, }, rules: [ { type: "expiry", data: { timestamp: 1577840461, }, }, ], }, ]; ``` #### Response Specification ```tsx type PermissionResponse = PermissionRequest & { context: Hex; dependencies: { factory: `0x${string}`; factoryData: `0x${string}`; }[]; delegationManager: `0x${string}`; }; ``` First note that the response contains all of the parameters of the original request and it is not guaranteed that the values received are equivalent to those requested. `context` is a catch-all to identify a permission for revoking permissions or redeeming permissions, and can contain non-identifying data as well. The `context` is required as defined in [ERC-7710](./eip-7710.md). See “Rationale” for details. `dependencies` is an array of objects, each containing fields for `factory` and `factoryData` as defined in [ERC-4337](./eip-4337.md). Either both `factory` and `factoryData` must be specified in an entry, or neither. This array is used describe accounts that are not yet deployed but MUST be deployed in order for a permission to be successfully redeemed. If any of the involved accounts have not yet been deployed, the wallet MUST return the corresponding `dependencies`. If all accounts have already been deployed, the wallet MUST return an empty `dependencies` array. The DApp MUST deploy each account by calling the `factory` contract with `factoryData` as the calldata. `delegationManager` is required as defined in [ERC-7710](./eip-7710.md). If the request is malformed or the wallet is unable/unwilling to grant permissions, wallet MUST return an error with a code as defined in [ERC-1193](./eip-1193.md). `wallet_requestExecutionPermissions` response example: An array of `PermissionResponse` objects is the final `result` field expected by the `wallet_requestExecutionPermissions` RPC. ```tsx [ { // original request with modifications chainId: "0x01", from: "0x...", to: "0x016562aA41A8697720ce0943F003141f5dEAe006", permission: { type: "native-token-allowance", isAdjustmentAllowed: true, data: { allowance: "0x1DCD65000000", }, }, // response-specific fields context: "0x0x016562aA41A8697720ce0943F003141f5dEAe0060000771577157715", dependencies: [ { factory: "0x...", factoryData: "0x...", }, ], delegationManager: "0x...", }, ]; ``` ### `wallet_revokeExecutionPermission` Permissions can be revoked by calling this method and the wallet will respond with an empty response when successful. #### Request Specification ```tsx type RevokeExecutionPermissionRequestParams = { permissionContext: "0x{string}"; }; ``` #### Response Specification ```tsx type GetPermissionsInfoResultParams = { chainIds: `0x${string}`[]; }; ``` ### `wallet_getSupportedExecutionPermissions` We introduce a `wallet_getSupportedExecutionPermissions` method for the Wallet to specify the permission types and rules types it supports. #### Request Specification **Request example**: ```tsx window.ethereum.request({ "method": "wallet_getSupportedExecutionPermissions", "params": [] }): Promise ``` #### Response Specification The wallet SHOULD include an object keyed on supported permission types including `ruleTypes` (`string[]`) that can be applied to the permission. ```tsx type GetSupportedExecutionPermissionsResult = Record< "permission-type", { chainIds: `0x${string}`[]; ruleTypes: string[]; } >; // Hex chain id ``` An object keyed on all permission types supported by the Wallet expected by the `wallet_getSupportedExecutionPermissions` RPC. ```json { "native-token-allowance": { "chainIds": ["0x123", "0x345"], "rulesTypes": ["expiry"] }, "erc20-token-allowance": { "chainIds": ["0x123"], "rulesTypes": [] }, "erc721-token-allowance": { "chainIds": ["0x123"], "rulesTypes": ["expiry"] } } ``` ### `wallet_getGrantedExecutionPermissions` We introduce a `wallet_getGrantedExecutionPermissions` method for the DApp to retrieve previously granted permissions. #### Request Specification **Request example**: ```tsx window.ethereum.request({ "method": "wallet_getGrantedExecutionPermissions", "params": [] }): Promise ``` #### Response Specification The wallet MUST include all granted permissions that are not yet revoked. ```tsx type PermissionResponses; ``` Example: ```tsx [ { chainId: "0x01", from: "0x...", to: "0x016562aA41A8697720ce0943F003141f5dEAe006", permission: { type: "native-token-allowance", isAdjustmentAllowed: true, data: { allowance: "0x1DCD65000000", }, }, context: "0x0x016562aA41A8697720ce0943F003141f5dEAe0060000771577157715", dependencies: [ { factory: "0x...", factoryData: "0x...", }, ], delegationManager: "0x...", }, ]; ``` ### Sending transaction to redeem permissions The permission response data will be redeemable by the `account` defined in the `to` field, using the interfaces specified in ERC-7710. This allows the recipient of the permissions to use any account type (EOA or contract) to form a transaction or UserOp using whatever payment or relay infrastructure they prefer, by sending an internal message to the returned `permissions.delegationManager` and calling its `function redeemDelegation(bytes[] calldata _permissionContexts, bytes32[] calldata _modes, bytes[] calldata _executionCallData) external;` function with the `_permissionContexts` parameter set to the returned `permissions.context`, and the `_executionCallData` data forming the message that the permissions recipient desires the user's account to emit, as defined by this struct: ``` struct Execution { address target; uint256 value; bytes callData; } ``` A simple pseudocode example of using a permission in this way, where DApp wants to request a permission from `bob` might be like this: ```typescript // Alice requests a permission from Bob const permissionsResponse = await window.ethereum.request({ method: 'wallet_requestExecutionPermissions', params: [{ from: bob.address, chainId: "0x01", to: '0x_dapp_session_account', permission: { type: 'native-token-allowance', isAdjustmentAllowed: true, data: { allowance: '0x0DE0B6B3A7640000' }, }, rules: [ { type: 'expiry'; data: { timestamp: Math.floor(Date.now() / 1000) + 3600 // 1 hour from now }, }, ], }] }); // Extract the permissionsContext and delegationManager const permissionsContext = permissionsResponse.context; const delegationManager = permissionsResponse.delegationManager; // DApp forms the execution they want Bob's account to take const execution = { target: bob.address, value: '0x06F05B59D3B20000', callData: '0x' }; const encodedExecutionCalldata = encodePacked( ['address', 'uint256', 'bytes'], [execution.target, execution.value, execution.callData], ); // Chose execution mode (SingleDefault) const executionMode = '0x0000000000000000000000000000000000000000000000000000000000000000'; // DApp sends the transaction by calling redeemDelegation on with encode execution on Bob's account const tx = await dapp.sendTransaction({ to: delegationManager, data: encodeFunctionData({ abi: DelegationManager.abi, functionName: 'redeemDelegations', args: [ [permissionsContext], [executionMode], [encodedExecutionCalldata], ], }) }); ``` **Example of the entire flow:** ```mermaid sequenceDiagram participant DApp participant Provider as window.ethereum participant Wallet participant User participant Chain as Relay infrastructure Note over DApp: DApp discovers supported permission and rules types DApp->>Provider: request({method: "wallet_getSupportedExecutionPermissions", params: []}) Provider->>Wallet: wallet_getSupportedExecutionPermissions Wallet->>DApp: Returns supported permission and rules types Note over DApp: DApp triggers permissions request DApp->>Provider: request({method: "wallet_requestExecutionPermissions", params: [ PermissionRequest[] ]}) Provider->>Wallet: wallet_requestExecutionPermissions Wallet->>User: Display permission request
(permissions, rules, to = account) User-->>Wallet: Approve or reject Wallet-->>Provider: PermissionResponse[]
includes context,
delegationManager,
dependencies Provider-->>DApp: PermissionResponse[] alt Undeployed user account(s) DApp->>Chain: Deploy via factory using dependencies Chain-->>DApp: Deployment success end Note over DApp: DApp forms Action calldata
to be executed by user's account DApp->>Chain: sendTransaction({
to: delegationManager,
data: redeemDelegations([context], [executionMode], [encodedAction])
}) Chain-->>DApp: tx receipt ``` ## Rationale The typical transaction flow of `suggesting transactions => approving transactions => sending transactions` is deeply limiting in several ways: - Users must be online to send transactions. DApps cannot send transactions for users when they are offline, which makes use cases such as subscriptions or automated trading impossible. - Users must manually approve every transaction, interrupting what could otherwise be a smooth user experience. With this ERC, DApps can request Wallets to grant permissions and execute transactions on the user's behalf, therefore circumventing the issues above. ### `permissionsContext` Since this ERC only specifies the interaction between the wallet and the DApp but not how the wallet enforces permissions, we need a flexible way for the wallet to pass along information to the DApp so that it can construct transactions that imbue the permissions. The `permissionsContext` field is meant to be an opaque string that's maximally flexible and can encode arbitrary information for different permissions schemes. DApps must submit transactions with the `account` specified in the `to` field, using the `permissionsContext` as the `_data` when interacting with the delegation manager. ### Non-exhaustive list of permissions and rules With the advancement in wallet technologies, we expect new types of permissions and rules to be developed. We considered mandating that each permission and rule must have a UUID in order to avoid collisions, but ultimately decided to stick with the simpler approach for now of simply mandating that these types be defined in ERCs. ## **Backwards Compatibility** Wallets that don’t support `wallet_requestExecutionPermissions` SHOULD return an error message if the JSON-RPC method is called. ## **Reference Implementation** For a minimal reference implementation focusing on permission granting from a [EIP-1193](./eip-1193.md) Ethereum provider, please see [Example7715PermissionsRequestHandler](../assets/eip-7715/Example7715PermissionsRequestHandler.html). For a complete reference implementation of a Permissions handler, see the MetaMask Permissions Snap, which includes features such as: - Support for commonly used permission and rule types with ability to attenuate(reduce or increase) the requested capability to meet the user’s terms for approval. - User encrypted storage for all permissions granted through the Wallet handler to enable revocation mechanisms. ## **Security Considerations** ### **Limited Permission Scope** DApps should only request the permissions they need, with a reasonable expiration time. Wallets MUST correctly enforce permissions. Ultimately, users must trust that their wallet software is implemented correctly, and permissions should be considered a part of the wallet implementation. ### **Phishing Attacks** Malicious DApps could pose as legitimate applications and trick users into granting broad permissions. Wallets MUST clearly display the permissions to users and warn them against granting dangerous permissions. ## Copyright Copyright and related rights waived via [CC0](../LICENSE.md).