# RPCs for Session Keys and the Wallet Server Model Many teams across the industry are working on various pieces of the session key flow. Notably, we have: - Session-key compatible account contracts from various AA stack providers. - ERC-7715 "Grant Permissions from Wallets," which defines RPC methods that enable app developers to create and revoke session keys. - ERC-7679 "UserOperation Builder," which defines a contract interface for account providers to implement to put how they encode user operations onchain. At a high level, app developers who wish to use session keys are currently expected to: 1. Generate a key pair to use as a session key. 2. Use the ERC-7715 `wallet_grantPermissions` RPC to turn the generated key pair into a session key for a user's account. 3. Use the account's provided ERC-7679 "UserOperation Builder" contract (provided in the response to the `wallet_grantPermissions` request), along with the app developer's own bundler and paymaster setup to construct and submit a user operation. One of our guiding principles as we've built out new functionality for the Coinbase Smart Wallet is "smarter wallets, dumber apps." With this principle in mind, we've taken a slightly different approach to building session keys that replaces the above third step with RPC methods that, similar to EIP-5792 methods, abstract away the details of account implementations. ## Overview ```mermaid sequenceDiagram autonumber participant App participant Provider participant Wallet participant Bundler App ->> App: Generate session key pair App ->> Provider: wallet_grantPermissions({..., sessionKeyPublicKey}) Provider ->> Wallet: wallet_grantPermissions({..., sessionKeyPublicKey}) Wallet ->> Wallet: User signs Wallet ->> Provider: permissionsContext Provider ->> App: permissionsContext App ->> Provider: wallet_prepareCalls({calls, capabilities: {permissionsContext}}) Provider ->> Wallet: wallet_prepareCalls({calls, capabilities: {permissionsContext}}) Wallet --> Bundler: Prepare calls (construct userOp) Wallet ->> Provider: {preparedCalls, signatureRequest, context} Provider ->> App: {preparedCalls, signatureRequest, context} App ->> App: Signs signatureRequest with session key App ->> Provider: wallet_sendPreparedCalls({preparedCalls, signature, context}) Provider ->> Wallet: wallet_sendPreparedCalls({preparedCalls, signature, context}) Wallet ->> Bundler: eth_sendUserOperation Bundler ->> Wallet: userOp hash Wallet ->> Provider: callsId Provider ->> App: callsId ``` Starting at step 7, the above diagram details our proposed alternative to app developers constructing and submitting user operations themselves with ERC-7679. Specifically, we propose two new RPC methods: - `wallet_prepareCalls` - Accepts an EIP-5792 `wallet_sendCalls` request. - Responds with the prepared calls (in the case of Coinbase Smart Wallet, a v0.6 user operation), some context, and a signature request. - `wallet_sendPreparedCalls` - Accepts prepared calls, a signature, and the context returned from `wallet_prepareCalls` if present. - Returns an EIP-5792 calls ID. ## Specifications ### `wallet_prepareCalls` Accepts an EIP-5792 `wallet_sendCalls` request, and returns the prepared calls according to the account's implementation. #### Parameters ```typescript! type PrepareCallsParams = [{ from: `0x${string}` chainId: `0x${string}` calls: { to: `0x${string}` data: `0x${string}` value: `0x${string}` }[]; capabilities: Record<string, any> }] ``` ##### Example Parameters ```typescript! wallet_prepareCalls([{ from: '0x...', chainId: '0x...', calls: [{ to: '0x...' data: '0x...' value: '0x...' }], capabilities: { permissions: { context: '0x...' // Importantly for session keys, wallets will likely need the ERC-7715 permissions context for userOp construction } } }]) ``` #### Return Value ```typescript! type PrepareCallsReturnValue = [{ preparedCalls: { type: string data: any chainId: `0x${string}` } signatureRequest: { hash: `0x${string}` } context: `0x${string}` }] ``` ##### Example Return Value ```typescript! [{ preparedCalls: { type: 'user-operation-v06', type data: { // ...userOp sender: '0x...', ... }, chainId: '0x01' }, signatureRequest: { hash: '0x...' // user op hash in our case }, context: '0x...' // params.capabilities.permissions.context in our case }] ``` After calling `wallet_prepareCalls`, app developers are expected to sign the hash provided in the `signatureRequest` field and submit the prepared and signed calls back to the wallet using the second RPC method, `wallet_sendPreparedCalls`. ### `wallet_sendPreparedCalls` Accepts prepared calls from the response to a `wallet_prepareCalls` request along with a signature, and returns an EIP-5792 call bundle ID. #### Parameters ```typescript! type SendPreparedCallsParams = [{ preparedCalls: { type: string data: any chainId: `0x${string}` } signature: Hex; context: Hex; }] ``` ##### Example Parameters ```typescript! wallet_sendPreparedCalls([{ preparedCalls: { // Same as response to wallet_prepareCalls type: 'user-operation-v06', data: { // ...userOp sender: '0x...', ... }, chainId: '0x01' } signature: '0x...', // Session key signature context: '0x...', // Same as response to wallet_prepareCalls }]) ``` #### Return Value ```typescript! type SendPreparedCallsReturnValue = string ``` ##### Example Return Value ```typescript! '0x...' ``` The `wallet_sendPreparedCalls` RPC responds with an EIP-5792 call bundle identifier, so apps that already use `wallet_sendCalls` to submit calls to the wallet popup can continue to use `wallet_getCallsStatus` to get the status of submitted calls. ## The Wallet Server Model While reading this you might have wondered why or how an app developer would use `wallet_` namespaced RPCs when the point of session keys is to bypass the wallet popup / extension / app. This is where the wallet server comes in. ![walletserver](https://hackmd.io/_uploads/HkWgvBh3C.svg) The wallet server is a complementary backend that an EIP-1193 provider routes specific requests (such as `wallet_prepareCalls` and `wallet_sendPreparedCalls`) to. With the wallet server model in mind, a more accurate representation of the flow proposed looks likes the following: ```mermaid sequenceDiagram autonumber participant App participant Provider box Wallet participant Wallet Frontend participant Wallet Server end participant Bundler App ->> App: Generate session key pair App ->> Provider: wallet_grantPermissions({..., sessionKeyPublicKey}) Provider ->> Wallet Frontend: wallet_grantPermissions({..., sessionKeyPublicKey}) Wallet Frontend ->> Wallet Frontend: User signs Wallet Frontend ->> Provider: permissionsContext Provider ->> App: permissionsContext App ->> Provider: wallet_prepareCalls({calls, capabilities: {permissionsContext}}) Provider ->> Wallet Server: wallet_prepareCalls({calls, capabilities: {permissionsContext}}) Wallet Server --> Bundler: Prepare calls (construct userOp) Wallet Server ->> Provider: {preparedCalls, signatureRequest, context} Provider ->> App: {preparedCalls, signatureRequest, context} App ->> App: Signs signatureRequest with session key App ->> Provider: wallet_sendPreparedCalls({preparedCalls, signature, context}) Provider ->> Wallet Server: wallet_sendPreparedCalls({preparedCalls, signature, context}) Wallet Server ->> Bundler: eth_sendUserOperation Bundler ->> Wallet Server: userOp hash Wallet Server ->> Provider: callsId Provider ->> App: callsId ``` ## What's Next This is all still a work in progress, and we are eager to hear feedback. That said, we have a working testnet prototype of this proposal (see docs [here](https://www.smartwallet.dev/guides/session-keys)) and are actively working on getting this ready for mainnet.