From cf4d8aa483c91a7d908a82eac8a9ec95f69e76af Mon Sep 17 00:00:00 2001 From: aboudjem Date: Fri, 8 Dec 2023 21:28:02 +0400 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=93=9D=20improve=20doc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorials/sessionkeys/introduction.md | 53 ++++- .../sessionkeys/sessionvalidationmodule.md | 210 ++++++++---------- 2 files changed, 135 insertions(+), 128 deletions(-) diff --git a/docs/tutorials/sessionkeys/introduction.md b/docs/tutorials/sessionkeys/introduction.md index 13614ad2..0e0fd23a 100644 --- a/docs/tutorials/sessionkeys/introduction.md +++ b/docs/tutorials/sessionkeys/introduction.md @@ -1,14 +1,55 @@ --- -sidebar_label: 'Introduction' +sidebar_label: "Introduction" sidebar_position: 1 --- -# Introduction +# Introduction to Session Keys in dApps 🌐 -This tutorial will cover how to use session keys in a dApp. In this tutorial we will: +This guide focuses on **session keys** in decentralized applications (dApps), highlighting their role in enhancing user experience and security. -- Go over a smart contract that allows for sessions to be validated for ERC20 token transfers. -- Go over initilization and creation of a session key on a Next JS frontend. (note that the steps for this will be the same as any React frontend) -- Execute a basic ERC20 token transfer without the need to sign +:::note +**Session Keys**: Session keys are **temporary cryptographic keys** used in dApps for validating transactions or operations without the need for constant user interaction, maintaining both security and ease of use. They're like one-time passwords but for blockchain transactions. +::: +## Why Session Keys? +Traditionally, blockchain operations require **explicit user approval** for each transaction, typically through a **digital signature**. While secure, this can be cumbersome, particularly for frequent transactions. Session keys offer a **seamless and user-friendly alternative**. + +:::tip +**User-Friendly Transactions**: Utilizing session keys allows dApps to process multiple transactions with **single user approval**, greatly enhancing the user experience. +::: + +## How Do Session Keys Work? + +Session keys are **temporary** and have **defined permissions**. Once a user approves a session, the dApp can autonomously execute transactions within the session's limits, eliminating the need for further approvals. + +:::info +**Scope and Permissions**: The scope of a session key, like the **types, duration and volume of transactions** it can authorize, is predefined. This ensures a balance between **control and convenience**. +::: + +## Use Cases of Session Keys + +- **Token Transfers**: Automating small, recurrent token transfers without requiring the user to confirm each one. +- **Voting in DAOs**: Facilitating users to participate in multiple votes in a decentralized autonomous organization (DAO) without repeated confirmations. + +:::warning +**Security Reminder**: Despite their convenience, session keys must be handled with care. It's crucial to **strictly define their scope** to mitigate potential security risks. +::: + +## Next Steps + +Throughout this tutorial series, we'll explore: + +- **Smart Contract Analysis:** Understanding the Session Validation Module. +- **Frontend Initialization:** Setting up the frontend using Next JS. +- **SDK Integration:** Integrating Biconomy SDK for smart account management. +- **Session Key Management:** Creating and managing session keys. +- **ERC20 Transfer Execution:** Using session keys for ERC20 token transfers. + +Ready to get started? Let's head over to the **Session Validation Module** in the next section! 🌟 + +:::danger +**Advanced Topic**: This guide is tailored for those with a grasp of blockchain concepts and basic programming skills. New to blockchain? Consider reviewing foundational concepts first. +::: + +--- diff --git a/docs/tutorials/sessionkeys/sessionvalidationmodule.md b/docs/tutorials/sessionkeys/sessionvalidationmodule.md index 129fe2b9..5e45ce8f 100644 --- a/docs/tutorials/sessionkeys/sessionvalidationmodule.md +++ b/docs/tutorials/sessionkeys/sessionvalidationmodule.md @@ -1,20 +1,34 @@ --- -sidebar_label: 'Session Validation Module' +sidebar_label: "Session Validation Module" sidebar_position: 2 --- # Session Validation Module -Before we continue it is important to understand Session Validation Modules and the Session Key Manager Module which interacts with them via the SDK. +Diving into the **Session Validation Module**, we explore its significance and interaction with the **Session Key Manager Module** via SDK. -To utilize session keys in a blockchain context, we require a smart contract that verifies whether a given user operation adheres to the permissions defined within the session key and confirms that the operation has been appropriately signed by the said session key. In this section we will cover a deployed contract that validates specific permissions to execute ERC20 token transfers. Using this ERC20 Validation module you will be able to create a small dApp that allows user to send a limited amount of funds to a specific address without needing to sign a transaction every single time. +:::note +Understanding the **Session Validation Modules** is crucial for leveraging **session keys** effectively in blockchain applications, particularly for tasks like managing ERC20 token transfers. +::: -The address of our deployed contract is: [0x000000D50C68705bd6897B2d17c7de32FB519fDA](https://mumbai.polygonscan.com/address/0x000000D50C68705bd6897B2d17c7de32FB519fDA#code) +## The Purpose of Session Validation Modules -The Smart contract we will be breaking down is the one shown below: +At the core, a Session Validation Module is a smart contract designed to authenticate whether a user's operation complies with the permissions set within a session key. It functions to validate user operations based on pre-defined session key permissions. +:::info +**Key Functionality**: We'll dissect a deployed contract that validates permissions for ERC20 token transfers, enabling dApps to execute transactions without user signatures every time. +Check the contract [here](https://mumbai.polygonscan.com/address/0x000000D50C68705bd6897B2d17c7de32FB519fDA#code). +::: -```javascript +## Breaking Down the Contract + +The smart contract we focus on is structured to validate user operations (userOps) for ERC20 transfers using session key signatures. It's tailored for standard ERC20 tokens and can interact with any contract implementing the method `(address, uint256)` interface. + +:::warning +**Technical Deep Dive**: The following contract breakdown is technical in nature, aimed at developers with a solid understanding of smart contract functionalities. +::: + +```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./ISessionValidationModule.sol"; @@ -23,58 +37,34 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /** * @title ERC20 Session Validation Module for Biconomy Smart Accounts. * @dev Validates userOps for ERC20 transfers and approvals using a session key signature. - * - Recommended to use with standard ERC20 tokens only - * - Can be used with any method of any contract which implement - * method(address, uint256) interface - * - * @author Fil Makarov - */ - contract ERC20SessionValidationModule is ISessionValidationModule { - /** - * @dev validates that the call (destinationContract, callValue, funcCallData) - * complies with the Session Key permissions represented by sessionKeyData - * @param destinationContract address of the contract to be called - * @param callValue value to be sent with the call - * @param _funcCallData the data for the call. is parsed inside the SVM - * @param _sessionKeyData SessionKey data, that describes sessionKey permissions - */ + function validateSessionParams( address destinationContract, uint256 callValue, bytes calldata _funcCallData, bytes calldata _sessionKeyData, - bytes calldata /*_callSpecificData*/ ) external virtual override returns (address) { - ( - address sessionKey, - address token, - address recipient, - uint256 maxAmount - ) = abi.decode(_sessionKeyData, (address, address, address, uint256)); - require(destinationContract == token, "ERC20SV Invalid Token"); - require(callValue == 0, "ERC20SV Non Zero Value"); + // Decode the session key data + (address sessionKey, address token, address recipient, uint256 maxAmount) = + abi.decode(_sessionKeyData, (address, address, address, uint256)); - (address recipientCalled, uint256 amount) = abi.decode( - _funcCallData[4:], - (address, uint256) - ); + // Validate the contract and call value + require(destinationContract == token, "Invalid Token"); + require(callValue == 0, "Non Zero Value"); - require(recipient == recipientCalled, "ERC20SV Wrong Recipient"); - require(amount <= maxAmount, "ERC20SV Max Amount Exceeded"); + // Check recipient and amount + (address recipientCalled, uint256 amount) = + abi.decode(_funcCallData[4:], (address, uint256)); + require(recipient == recipientCalled, "Wrong Recipient"); + require(amount <= maxAmount, "Max Amount Exceeded"); return sessionKey; } /** - * @dev validates if the _op (UserOperation) matches the SessionKey permissions - * and that _op has been signed by this SessionKey - * Please mind the decimals of your exact token when setting maxAmount - * @param _op User Operation to be validated. - * @param _userOpHash Hash of the User Operation to be validated. - * @param _sessionKeyData SessionKey data, that describes sessionKey permissions - * @param _sessionKeySignature Signature over the the _userOpHash. - * @return true if the _op is valid, false otherwise. + * @dev Validates if the UserOperation matches the SessionKey permissions. */ function validateSessionUserOp( UserOperation calldata _op, @@ -82,65 +72,51 @@ contract ERC20SessionValidationModule is ISessionValidationModule { bytes calldata _sessionKeyData, bytes calldata _sessionKeySignature ) external pure override returns (bool) { + + // Ensure correct operation and signature require( bytes4(_op.callData[0:4]) == EXECUTE_OPTIMIZED_SELECTOR || - bytes4(_op.callData[0:4]) == EXECUTE_SELECTOR, - "ERC20SV Invalid Selector" + bytes4(_op.callData[0:4]) == EXECUTE_SELECTOR, + "Invalid Selector" ); - ( - address sessionKey, - address token, - address recipient, - uint256 maxAmount - ) = abi.decode(_sessionKeyData, (address, address, address, uint256)); + // Decode session key data + (address sessionKey, address token, address recipient, uint256 maxAmount) = + abi.decode(_sessionKeyData, (address, address, address, uint256)); - { - // we expect _op.callData to be `SmartAccount.execute(to, value, calldata)` calldata - (address tokenAddr, uint256 callValue, ) = abi.decode( - _op.callData[4:], // skip selector - (address, uint256, bytes) - ); - if (tokenAddr != token) { - revert("ERC20SV Wrong Token"); - } - if (callValue != 0) { - revert("ERC20SV Non Zero Value"); - } - } - // working with userOp.callData - // check if the call is to the allowed recepient and amount is not more than allowed + // Validate token and call value + (address tokenAddr, uint256 callValue, ) = + abi.decode(_op.callData[4:], (address, uint256, bytes)); + require(tokenAddr == token, "Wrong Token"); + require(callValue == 0, "Non Zero Value"); + + // Validate recipient and amount bytes calldata data; - { - uint256 offset = uint256(bytes32(_op.callData[4 + 64:4 + 96])); - uint256 length = uint256( - bytes32(_op.callData[4 + offset:4 + offset + 32]) - ); - //we expect data to be the `IERC20.transfer(address, uint256)` calldata - data = _op.callData[4 + offset + 32:4 + offset + 32 + length]; - } - if (address(bytes20(data[16:36])) != recipient) { - revert("ERC20SV Wrong Recipient"); - } - if (uint256(bytes32(data[36:68])) > maxAmount) { - revert("ERC20SV Max Amount Exceeded"); - } - return - ECDSA.recover( - ECDSA.toEthSignedMessageHash(_userOpHash), - _sessionKeySignature - ) == sessionKey; + uint256 offset = uint256(bytes32(_op.callData[4 + 64:4 + 96])); + uint256 length = uint256(bytes32(_op.callData[4 + offset:4 + offset + 32])); + data = _op.callData[4 + offset + 32:4 + offset + 32 + length]; + require(address(bytes20(data[16:36])) == recipient, "Wrong Recipient"); + require(uint256(bytes32(data[36:68])) <= maxAmount, "Max Amount Exceeded"); + + // Verify signature + return ECDSA.recover(ECDSA.toEthSignedMessageHash(_userOpHash), _sessionKeySignature) == sessionKey; } } ``` -This contract extends the ISessionValidationModule interface which gives us the needed parameters for `validateSessionUserOp` and `validateSessionParams`. We will need to extend the implementation of these two functions in order to create a session validation module. You can view this interface [here](https://github.com/bcnmy/scw-contracts/blob/master/contracts/smart-account/modules/SessionValidationModules/ISessionValidationModule.sol). +The contract, extending the `ISessionValidationModule` interface, contains essential functions like `validateSessionUserOp` and `validateSessionParams`, each serving distinct roles in operation validation. + +## Solidity Contract Breakdown -Coming back to the validation module lets break down the two functions: +Here's the Solidity contract in question: -## validateSessionUserOp +### Function Analysis: `validateSessionUserOp` -```javascript +:::note +This function is essential for **validating user operations** against **session key permissions** and ensuring they are correctly signed. +::: + +```solidity /** * @dev validates if the _op (UserOperation) matches the SessionKey permissions * and that _op has been signed by this SessionKey @@ -208,29 +184,20 @@ Coming back to the validation module lets break down the two functions: } ``` +**Execution Steps:** -When validating a single user operation this function will be called by the Session Key Manager Module from the Biconomy SDK. Let's break down the code: - -This function takes four arguments: - -- Useroperation -- _userOpHash -- _sessionKeyData -- _sessionKeySignature +1. **Match Function Selectors:** Verifies the user operation aligns with specific function selectors. +2. **Decode Session Key Data:** Extracts essential details like session key, token, recipient, and maximum transaction amount. +3. **Verify Operation Details:** Checks the operation's token address and call value, and confirms recipient and amount limits. +4. **Signature Validation:** Utilizes ECDSA to confirm the operation's signature matches the session key. +## Function Analysis: `validateSessionParams` -- The first check verifies that the useroperation supplied matches the function selectors that are defined in the interface. -- Next we decode the session key data - these arguments are what we need in order to validate if the userOp should be allowed to execute. Depending on your use case you may want different arguments here. In this case we get addresses for: sessionKey(the eoa that will sign on our behalf), token, recipent, and the maximum amount of tokens that can be transferred. -- Next we decode the token address and call value fields the userop -- If the token address in the userop does not match the token address of the session key we revert the transaction as this is not the token we have permission to send -- If the call value does not equal zero we will also revert. If your logic requires some value to be sent by the end user you would update this condition accordingly. -- Next we look at the call data and verify the recipient is who we want it to be as defined in the session and that the maximum amount of tokens to be sent has not been exceeded. -- Finally the ECDSA functions are used to confirm the operations signature matches the session key. - -## validateSessionParams - -```javascript +:::note +This function plays a vital role in **batch session validation**, ensuring each operation aligns with the set session key permissions. It's key for processing multiple operations efficiently. +::: +```solidity /** * @dev validates that the call (destinationContract, callValue, funcCallData) * complies with the Session Key permissions represented by sessionKeyData @@ -265,24 +232,23 @@ This function takes four arguments: require(amount <= maxAmount, "ERC20SV Max Amount Exceeded"); return sessionKey; } - ``` -When validating a batch of sessions this function will be called by the Session Key Manager Module from the Biconomy SDK. Let's break down the code: +**Operational Flow:** -The `validateSessionParams` takes five arguments supplied by the Session Key Manager Module: +1. **Decode Session Key Data:** Extracts session key, token address, recipient address, and maximum token amount. +2. **Validation Checks:** Ensures the destination contract and call value are as required. +3. **Recipient and Amount Verification:** Compares recipient and transaction amount against session key data. +4. **Return Session Key:** If all checks pass, returns the session key address. -- destinationContract -- callValue -- _funcCallData -- _sessionKeyData -- _callSpecificData +Both `validateSessionUserOp` and `validateSessionParams` are integral to our dApp's security framework, ensuring strict adherence to permissions and enhancing transaction integrity. -This is very similar to the `validateSessionUserOp` +## Next Steps -- First we decode the session key address, token address, recipient address, and maximum amount of tokens. Again this can be altered based on your needs if you are building your own session key module. -- Two checks are then conducted to make sure that the destination contract is the token address and that the call value is zero. You can edit this if you need a value transferred for the transaction such as paying to mint an nft. -- The recipientCalled and amount are then returned from decoding the _funcCallData and compared to make sure they match with session key data -- the session key address is returned +With a foundational understanding of the Session Validation Module, we're set to move forward. Up next, we'll embark on initializing the frontend and integrating the Biconomy SDK, crucial steps in bringing our dApp to life. -This is a breakdown of the contract we will be using to power our session key demo. In the next section we will initialize our frontend and start integrating the Biconomy SDK. \ No newline at end of file +:::tip +**Explore the Interface**: Familiarize yourself with the `ISessionValidationModule` interface [here](https://github.com/bcnmy/scw-contracts/blob/master/contracts/smart-account/modules/SessionValidationModules/ISessionValidationModule.sol) for a comprehensive understanding. +::: + +--- From c899a7b455a56172538a306dd36da2dd5365f077 Mon Sep 17 00:00:00 2001 From: aboudjem Date: Fri, 8 Dec 2023 21:46:43 +0400 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8E=A8=20=20change=20snippet=20from?= =?UTF-8?q?=20solidity=20->=20javascript=20+=20prettiered?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorials/sessionkeys/sessionvalidationmodule.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/tutorials/sessionkeys/sessionvalidationmodule.md b/docs/tutorials/sessionkeys/sessionvalidationmodule.md index 5e45ce8f..7f63c13b 100644 --- a/docs/tutorials/sessionkeys/sessionvalidationmodule.md +++ b/docs/tutorials/sessionkeys/sessionvalidationmodule.md @@ -28,7 +28,7 @@ The smart contract we focus on is structured to validate user operations (userOp **Technical Deep Dive**: The following contract breakdown is technical in nature, aimed at developers with a solid understanding of smart contract functionalities. ::: -```solidity +```javascript // SPDX-License-Identifier: MIT pragma solidity 0.8.17; import "./ISessionValidationModule.sol"; @@ -116,8 +116,8 @@ Here's the Solidity contract in question: This function is essential for **validating user operations** against **session key permissions** and ensuring they are correctly signed. ::: -```solidity -/** +```javascript + /** * @dev validates if the _op (UserOperation) matches the SessionKey permissions * and that _op has been signed by this SessionKey * Please mind the decimals of your exact token when setting maxAmount @@ -197,8 +197,8 @@ This function is essential for **validating user operations** against **session This function plays a vital role in **batch session validation**, ensuring each operation aligns with the set session key permissions. It's key for processing multiple operations efficiently. ::: -```solidity -/** +```javascript + /** * @dev validates that the call (destinationContract, callValue, funcCallData) * complies with the Session Key permissions represented by sessionKeyData * @param destinationContract address of the contract to be called