### Example Session Configuration Source: https://context7.com/erc7579/smartsessions/llms.txt Demonstrates how to build a Session struct with specific action scoping for an ERC-20 transfer, including usage limit policies. This example shows practical application of the data structures. ```solidity // Example: build a full session with a time-scoped ERC-20 transfer permission Session memory session = Session({ sessionValidator: ISessionValidator(address(simpleSessionValidator)), sessionValidatorInitData: abi.encodePacked(sessionSigner.addr), salt: bytes32(0), userOpPolicies: new PolicyData[](0), erc7739Policies: ERC7739Data({ allowedERC7739Content: new ERC7739Context[](0), erc1271Policies: new PolicyData[](0) }), actions: new ActionData[](1), permitERC4337Paymaster: false }); // Scope to ERC-20 transfer(address,uint256) on a specific token session.actions[0] = ActionData({ actionTargetSelector: IERC20.transfer.selector, actionTarget: address(usdcToken), actionPolicies: new PolicyData[](1) }); session.actions[0].actionPolicies[0] = PolicyData({ policy: address(usageLimitPolicy), initData: abi.encodePacked(uint128(10)) // max 10 transfers }); ``` -------------------------------- ### Install SmartSessionCompatibilityFallback as Fallback Handler Source: https://context7.com/erc7579/smartsessions/llms.txt Demonstrates how to install the SmartSessionCompatibilityFallback contract as a module on a smart account. This is typically done by encoding the selector of `supportsNestedTypedDataSign` and specifying the CALLTYPE_STATIC. After installation, the smart account will correctly signal ERC-7739 support. ```solidity // Install as a fallback handler on the smart account: bytes4 selector = SmartSessionCompatibilityFallback.supportsNestedTypedDataSign.selector; instance.installModule({ moduleTypeId: MODULE_TYPE_FALLBACK, module: address(compatibilityFallback), data: abi.encodePacked(selector, CALLTYPE_STATIC, "") }); // After installation, the account correctly signals ERC-7739 support: bytes32 result = ISmartAccountWithFallback(account).supportsNestedTypedDataSign(); // result == bytes32(bytes4(0xd620c85a)) ``` -------------------------------- ### onInstall Source: https://context7.com/erc7579/smartsessions/llms.txt Installs SmartSession onto a smart account, optionally bootstrapping one or more sessions in the same transaction. Called by the account during ERC-7579 module installation. ```APIDOC ## `onInstall` — Install SmartSession as an ERC-7579 Module ### Description Installs SmartSession onto a smart account, optionally bootstrapping one or more sessions in the same transaction. Called by the account during ERC-7579 module installation. Accepts either empty data (no sessions) or `abi.encodePacked(SmartSessionMode, abi.encode(Session[]))` where the mode must be an ENABLE mode. ### Function Signature ```solidity function onInstall(bytes calldata data) external; ``` ### Usage Examples #### Install with no initial sessions: ```javascript instance.installModule({ moduleTypeId: MODULE_TYPE_VALIDATOR, module: address(smartSession), data: "" }); ``` #### Install and atomically enable sessions: ```javascript Session[] memory sessions = new Session[](1); sessions[0] = Session({ sessionValidator: ISessionValidator(address(simpleSessionValidator)), sessionValidatorInitData: abi.encodePacked(sessionSigner.addr), salt: bytes32(0), userOpPolicies: new PolicyData[](0), erc7739Policies: ERC7739Data({ allowedERC7739Content: new ERC7739Context[](0), erc1271Policies: new PolicyData[](0) }), actions: actions, permitERC4337Paymaster: false }); bytes memory installData = abi.encodePacked( SmartSessionMode.UNSAFE_ENABLE, abi.encode(sessions) ); instance.installModule({ moduleTypeId: MODULE_TYPE_VALIDATOR, module: address(smartSession), data: installData }); ``` ### Verification ```javascript // Verify installation bool initialized = smartSession.isInitialized(instance.account); // initialized == true ``` ``` -------------------------------- ### Install SmartSession with Initial Sessions Source: https://context7.com/erc7579/smartsessions/llms.txt Installs the SmartSession module onto a smart account and atomically enables one or more sessions. This is called by the account during ERC-7579 module installation. Ensure the `data` field is correctly encoded with the desired `SmartSessionMode` and session configurations. ```solidity function onInstall(bytes calldata data) external; ``` ```javascript Session[] memory sessions = new Session[](1); sessions[0] = Session({ sessionValidator: ISessionValidator(address(simpleSessionValidator)), sessionValidatorInitData: abi.encodePacked(sessionSigner.addr), salt: bytes32(0), userOpPolicies: new PolicyData[](0), erc7739Policies: ERC7739Data({ allowedERC7739Content: new ERC7739Context[](0), erc1271Policies: new PolicyData[](0) }), actions: actions, permitERC4337Paymaster: false }); bytes memory installData = abi.encodePacked( SmartSessionMode.UNSAFE_ENABLE, abi.encode(sessions) ); instance.installModule({ moduleTypeId: MODULE_TYPE_VALIDATOR, module: address(smartSession), data: installData }); // Verify installation bool initialized = smartSession.isInitialized(instance.account); // initialized == true ``` -------------------------------- ### Install SmartSession without Initial Sessions Source: https://context7.com/erc7579/smartsessions/llms.txt Installs the SmartSession module onto a smart account without enabling any initial sessions. This is useful when sessions will be enabled later. Called by the account during ERC-7579 module installation. ```javascript instance.installModule({ moduleTypeId: MODULE_TYPE_VALIDATOR, module: address(smartSession), data: "" }); ``` -------------------------------- ### Configure Sudo Session Source: https://context7.com/erc7579/smartsessions/llms.txt Configures a session with a sudo fallback action, allowing the session key to perform any action. This setup uses the SudoPolicy as the sole policy for the fallback action, ensuring all actions are approved. This is suitable for implementing ownership-equivalent access patterns but requires careful consideration due to its broad permissions. ```solidity // Configure a sudo session (session key can do anything): ActionData memory fallbackAction = ActionData({ actionTarget: FALLBACK_TARGET_FLAG, // address(1) actionTargetSelector: FALLBACK_TARGET_SELECTOR_FLAG, // 0x00000001 actionPolicies: new PolicyData[](1) }); fallbackAction.actionPolicies[0] = PolicyData({ policy: address(sudoPolicy), initData: "" }); Session memory sudoSession = Session({ sessionValidator: ISessionValidator(address(simpleSessionValidator)), sessionValidatorInitData: abi.encodePacked(sessionSigner.addr), salt: bytes32(0), userOpPolicies: new PolicyData[](0), // no userOp restrictions erc7739Policies: ERC7739Data({ allowedERC7739Content: new ERC7739Context[](0), erc1271Policies: new PolicyData[](0) }), actions: new ActionData[](1), // only the fallback entry permitERC4337Paymaster: false }); sudoSession.actions[0] = fallbackAction; // With this config, any action falls back to SudoPolicy => always approved ``` -------------------------------- ### Query SmartSession State Source: https://context7.com/erc7579/smartsessions/llms.txt Use these view functions to check if SmartSession is initialized, list active permissions, check specific permissions, retrieve nonces for replay protection, and get session validator configurations. ```solidity // SmartSessionBase.sol — all view functions // Check if SmartSession is installed and has active sessions on an account: bool initialized = smartSession.isInitialized(address account); // List all PermissionIds active on an account: PermissionId[] memory ids = smartSession.getPermissionIDs(address account); // Check a specific permission: bool active = smartSession.isPermissionEnabled(PermissionId permissionId, address account); // Replay-protection nonce for ENABLE flow (incremented on revokeEnableSignature): uint256 nonce = smartSession.getNonce(PermissionId permissionId, address account); // Retrieve the session validator address and its stored config: (address validator, bytes memory config) = smartSession.getSessionValidatorAndConfig(account, permissionId); // List all UserOp policies for a permission: address[] memory uoPolicies = smartSession.getUserOpPolicies(account, permissionId); // List all ERC-1271 policies: address[] memory erc1271Policies = smartSession.getERC1271Policies(account, permissionId); // List all action policies for a given ActionId: address[] memory actionPolicies = smartSession.getActionPolicies(account, permissionId, actionId); // List all enabled ActionIds: bytes32[] memory actionIds = smartSession.getEnabledActions(account, permissionId); // Batch enabled-check using PolicyData arrays (easier for off-chain tooling): bool allUoEnabled = smartSession.areUserOpPoliciesEnabled(account, permissionId, userOpPolicies); bool allErcEnabled = smartSession.areERC1271PoliciesEnabled(account, permissionId, erc1271Policies); bool allActEnabled = smartSession.areActionsEnabled(account, permissionId, actions); // Check individual policy addresses: bool isPolicyOn = smartSession.isUserOpPolicyEnabled(account, permissionId, policyAddress); bool isActionPolicyOn = smartSession.isActionPolicyEnabled(account, permissionId, actionId, policyAddress); bool isERC7739On = smartSession.isERC7739ContentEnabled(account, permissionId, appDomainSeparator, contentName); // Module type check (SmartSession is type 1 = VALIDATOR): bool isValidator = smartSession.isModuleType(1); // true ``` -------------------------------- ### Registering CallDataSizePolicy Source: https://context7.com/erc7579/smartsessions/llms.txt Example of how to register the CallDataSizePolicy for a session, setting a maximum callData length of 256 bytes. ```solidity // Register in a session: PolicyData memory policyData = PolicyData({ policy: address(callDataSizePolicy), initData: abi.encode(uint256(256)) // max 256 bytes callData }); ``` -------------------------------- ### Registering NoValuePolicy for an Action Source: https://context7.com/erc7579/smartsessions/llms.txt Example of registering the NoValuePolicy for a specific ERC-20 transfer action, ensuring no ETH value is sent with the transfer. ```solidity // Register as an action policy: ActionData memory action = ActionData({ actionTargetSelector: IERC20.transfer.selector, actionTarget: address(tokenContract), actionPolicies: new PolicyData[](1) }); action.actionPolicies[0] = PolicyData({ policy: address(noValuePolicy), initData: "" }); ``` -------------------------------- ### Initialize SimpleGasPolicy with Gas and Cost Limits Source: https://context7.com/erc7579/smartsessions/llms.txt Initializes the SimpleGasPolicy with specific gas and cost limits. The initData is encoded as bytes32, with the first 16 bytes for gasLimit and the next 16 bytes for costLimit (in wei). ```Solidity // external/policies/SimpleGasPolicy.sol contract SimpleGasPolicy is IUserOpPolicy { function initializeWithMultiplexer(address account, ConfigId configId, bytes calldata initData) external; // initData = bytes32: first 16 bytes = uint128 gasLimit, next 16 bytes = uint128 costLimit (wei) function checkUserOpPolicy(ConfigId id, PackedUserOperation calldata userOp) external returns (uint256); function getGasConfig(ConfigId configId, address multiplexer, address userOpSender) external view returns (uint128 gasLimit, uint128 gasUsed, uint128 costLimit, uint128 costUsed); } // Allow up to 1M gas units, costing at most 0.05 ETH: uint128 gasLimit = 1_000_000; uint128 costLimit = 0.05 ether; PolicyData memory gasPolicy = PolicyData({ policy: address(simpleGasPolicy), initData: abi.encodePacked(gasLimit, costLimit) }); session.userOpPolicies = new PolicyData[](1); session.userOpPolicies[0] = gasPolicy; // Query current consumption: (uint128 gLimit, uint128 gUsed, uint128 cLimit, uint128 cUsed) = simpleGasPolicy.getGasConfig(configId, address(smartSession), smartAccountAddress); ``` -------------------------------- ### Initialize UniActionPolicy with Parameter Rules Source: https://context7.com/erc7579/smartsessions/llms.txt Configure fine-grained calldata inspection for actions. This policy allows setting up to 16 rules per action, each with conditions, offsets, and optional cumulative usage limits. ```solidity // external/policies/UniActionPolicy.sol contract UniActionPolicy is IActionPolicy { function initializeWithMultiplexer(address account, ConfigId configId, bytes calldata initData) external; // initData = abi.encode(ActionConfig) function checkAction(ConfigId id, address account, address, uint256 value, bytes calldata data) external returns (uint256); } ``` ```solidity struct ActionConfig { uint256 valueLimitPerUse; // max ETH value per single call (0 = no ETH allowed) ParamRules paramRules; } struct ParamRules { uint256 length; // number of active rules (≤ 16) ParamRule[16] rules; } struct ParamRule { ParamCondition condition; // EQUAL | GREATER_THAN | LESS_THAN | GREATER_THAN_OR_EQUAL | LESS_THAN_OR_EQUAL | NOT_EQUAL | IN_RANGE uint64 offset; // byte offset into calldata AFTER the 4-byte selector bool isLimited; // true = enforce cumulative usage limit on this param bytes32 ref; // reference value for comparison (or packed min+max for IN_RANGE) LimitUsage usage; // { limit, used } — only relevant when isLimited = true } ``` ```solidity // Allow ERC-20 transfer only to a fixed recipient with at most 100 USDC per call, // and a 500 USDC total lifetime cap: ParamRule[16] memory rules; // Rule 0: recipient must equal a fixed address (offset 0 in transfer(address,uint256) args) rules[0] = ParamRule({ condition: ParamCondition.EQUAL, offset: 0, isLimited: false, ref: bytes32(uint256(uint160(recipientAddress))), usage: LimitUsage({ limit: 0, used: 0 }) }); // Rule 1: amount <= 100e6 per call AND <= 500e6 total (offset 32) rules[1] = ParamRule({ condition: ParamCondition.LESS_THAN_OR_EQUAL, offset: 32, isLimited: true, ref: bytes32(uint256(100e6)), usage: LimitUsage({ limit: 500e6, used: 0 }) }); ActionConfig memory config = ActionConfig({ valueLimitPerUse: 0, paramRules: ParamRules({ length: 2, rules: rules }) }); PolicyData memory uniPolicy = PolicyData({ policy: address(uniActionPolicy), initData: abi.encode(config) }); ``` -------------------------------- ### Initialize ValueLimitPolicy with ETH Value Limit Source: https://context7.com/erc7579/smartsessions/llms.txt Initializes the ValueLimitPolicy with a maximum ETH value limit. The initData is a bytes32 containing the uint256 valueLimit. ```Solidity // external/policies/ValueLimitPolicy.sol contract ValueLimitPolicy is IActionPolicy, IUserOpPolicy { function initializeWithMultiplexer(address account, ConfigId configId, bytes calldata initData) external; // initData = bytes32 containing uint256 valueLimit function checkAction(ConfigId id, address account, address, uint256 value, bytes calldata) external returns (uint256); function checkUserOpPolicy(ConfigId id, PackedUserOperation calldata op) external returns (uint256); function getUsed(ConfigId configId, address msgSender, address userOpSender) external view returns (uint256); function getValueLimit(ConfigId configId, address msgSender, address userOpSender) external view returns (uint256); } // Limit total ETH sent by session key to 0.1 ETH: uint256 valueLimit = 0.1 ether; PolicyData memory valuePolicy = PolicyData({ policy: address(valueLimitPolicy), initData: abi.encode(valueLimit) }); // Attach as action policy on a specific target: actions[0].actionPolicies[0] = valuePolicy; // Or as UserOp policy (sums all ETH values in all executions of each UserOp): session.userOpPolicies[0] = valuePolicy; // Query usage: uint256 used = valueLimitPolicy.getUsed(configId, address(smartSession), smartAccountAddress); ``` -------------------------------- ### Initialize ERC20SpendingLimitPolicy with Token Limits Source: https://context7.com/erc7579/smartsessions/llms.txt Configure spending limits for specific ERC-20 tokens within a session. The initData encodes an array of token addresses and their corresponding spending limits. ```solidity // external/policies/ERC20SpendingLimitPolicy.sol contract ERC20SpendingLimitPolicy is IActionPolicy { function initializeWithMultiplexer(address account, ConfigId configId, bytes calldata initData) external; // initData = abi.encode(address[] tokens, uint256[] limits) function checkAction(ConfigId id, address account, address target, uint256 value, bytes calldata callData) external returns (uint256); function getPolicyData(ConfigId id, address multiplexer, address token, address userOpSender) external view returns (uint256 spendingLimit, uint256 alreadySpent, uint256 approvedAmount); } ``` ```solidity // Allow transferring up to 500 USDC and 1000 DAI within the session: address[] memory tokens = new address[](2); uint256[] memory limits = new uint256[](2); tokens[0] = address(usdcToken); limits[0] = 500e6; // 500 USDC (6 decimals) tokens[1] = address(daiToken); limits[1] = 1000e18; // 1000 DAI PolicyData memory spendPolicy = PolicyData({ policy: address(erc20SpendingLimitPolicy), initData: abi.encode(tokens, limits) }); ``` ```solidity // Attach to the USDC transfer action: ActionData memory usdcAction = ActionData({ actionTargetSelector: IERC20.transfer.selector, actionTarget: address(usdcToken), actionPolicies: new PolicyData[](1) }); usdcAction.actionPolicies[0] = spendPolicy; ``` ```solidity // Query remaining budget: (uint256 limit, uint256 spent, uint256 approved) = erc20SpendingLimitPolicy.getPolicyData( configId, address(smartSession), address(usdcToken), smartAccountAddress ); uint256 remaining = limit - spent - approved; ``` -------------------------------- ### Initialize TimeFramePolicy with Validity Window Source: https://context7.com/erc7579/smartsessions/llms.txt Packs the validity window into ERC-4337 ValidationData. The EntryPoint enforces expiry natively. initData is packed as uint48(validUntil) ++ uint48(validAfter). ```solidity // external/policies/TimeFramePolicy.sol contract TimeFramePolicy is IPolicy, IUserOpPolicy, IActionPolicy, I1271Policy { function initializeWithMultiplexer(address account, ConfigId configId, bytes calldata initData) external; // initData = bytes12 packed as: uint48(validUntil) ++ uint48(validAfter) (big-endian, upper bytes first) function checkUserOpPolicy(ConfigId id, PackedUserOperation calldata op) external view returns (uint256); function checkAction(ConfigId id, address account, address, uint256, bytes calldata) external view returns (uint256); function check1271SignedAction(ConfigId id, address, address smartAccount, bytes32, bytes calldata) external view returns (bool); function getTimeFrameConfig(ConfigId id, address multiplexer, address smartAccount) external view returns (TimeFrameConfig); } ``` ```solidity // Allow actions only between now and 24 hours from now: uint48 validAfter = uint48(block.timestamp); uint48 validUntil = uint48(block.timestamp + 1 days); // Pack: high 6 bytes = validUntil, low 6 bytes = validAfter bytes memory timeFrameInitData = abi.encodePacked( bytes6(uint48(validUntil)), bytes6(uint48(validAfter)) ); PolicyData memory timePolicy = PolicyData({ policy: address(timeFramePolicy), initData: timeFrameInitData }); // Use as userOp policy (applies to all UserOps under this permission): sessions[0].userOpPolicies = new PolicyData[](1); sessions[0].userOpPolicies[0] = timePolicy; // Or scope to a specific action: actions[0].actionPolicies[0] = timePolicy; ``` -------------------------------- ### Enable Multiple Sessions for Smart Account Source: https://context7.com/erc7579/smartsessions/llms.txt Enables an array of `Session` configurations for the calling smart account during the ERC-4337 execution phase. Each enabled session returns a deterministic `PermissionId`. Ensure `sessions` array is correctly populated with session details. ```solidity function enableSessions(Session[] calldata sessions) external returns (PermissionId[] memory permissionIds); ``` ```javascript Session[] memory sessions = new Session[](1); sessions[0] = Session({ sessionValidator: ISessionValidator(address(simpleSessionValidator)), sessionValidatorInitData: abi.encodePacked(sessionSigner.addr), salt: keccak256("my-dapp-session-v1"), userOpPolicies: new PolicyData[](0), erc7739Policies: ERC7739Data({ allowedERC7739Content: new ERC7739Context[](0), erc1271Policies: new PolicyData[](0) }), actions: actions, permitERC4337Paymaster: false }); // enableSessions is called from the account's execution phase (e.g., via UserOp) UserOpData memory userOpData = instance.getExecOps({ target: address(smartSession), value: 0, callData: abi.encodeCall(ISmartSession.enableSessions, (sessions)), txValidator: address(mockK1Validator) }); userOpData.userOp.signature = sign(userOpData.userOpHash, owner.key); instance.submitUserOp(userOpData); // Retrieve the PermissionId off-chain: PermissionId permissionId = smartSession.getPermissionId(sessions[0]); bool enabled = smartSession.isPermissionEnabled(permissionId, instance.account); // enabled == true ``` -------------------------------- ### Initialize UsageLimitPolicy with Execution Count Cap Source: https://context7.com/erc7579/smartsessions/llms.txt Limits the total number of UserOps or action executions. Increments an on-chain counter and returns VALIDATION_FAILED when the limit is reached. initData contains a uint128 limit. ```solidity // external/policies/UsageLimitPolicy.sol contract UsageLimitPolicy is IUserOpPolicy, IActionPolicy { function initializeWithMultiplexer(address account, ConfigId configId, bytes calldata initData) external; // initData = bytes16 containing uint128 limit (big-endian) function checkUserOpPolicy(ConfigId id, PackedUserOperation calldata op) external returns (uint256); function checkAction(ConfigId id, address account, address, uint256, bytes calldata) external returns (uint256); function getUsageLimit(ConfigId id, address mxer, address smartAccount) external view returns (uint128 limit); function getUsed(ConfigId id, address mxer, address smartAccount) external view returns (uint128 used); } ``` ```solidity // Limit a session to at most 5 UserOps: uint128 maxOps = 5; PolicyData memory limitPolicy = PolicyData({ policy: address(usageLimitPolicy), initData: abi.encodePacked(maxOps) // first 16 bytes = uint128 }); session.userOpPolicies = new PolicyData[](1); session.userOpPolicies[0] = limitPolicy; // After enabling, query remaining uses: ConfigId configId = ...; // derived from permissionId uint128 used = usageLimitPolicy.getUsed(configId, address(smartSession), smartAccountAddress); uint128 limit = usageLimitPolicy.getUsageLimit(configId, address(smartSession), smartAccountAddress); // remaining = limit - used ``` -------------------------------- ### Manage UserOp Policies Source: https://context7.com/erc7579/smartsessions/llms.txt Use `enableUserOpPolicies` and `disableUserOpPolicies` to add or remove policies that apply to every UserOp validated under a specific `PermissionId`. These functions can only be called when the permission is enabled. Query current policies using `getUserOpPolicies` and check individual policy status with `isUserOpPolicyEnabled`. ```solidity // SmartSessionBase.sol function enableUserOpPolicies(PermissionId permissionId, PolicyData[] memory userOpPolicies) public; function disableUserOpPolicies(PermissionId permissionId, address[] calldata policies) public; ``` ```solidity // Add a gas limit policy to an existing session: PolicyData[] memory newPolicies = new PolicyData[](1); newPolicies[0] = PolicyData({ policy: address(simpleGasPolicy), // gasLimit = 500_000 units, costLimit = 0.01 ETH initData: abi.encodePacked(uint128(500_000), uint128(0.01 ether)) }); // Called from a UserOp execution targeting the smart session: bytes memory callData = abi.encodeCall( ISmartSession.enableUserOpPolicies, (permissionId, newPolicies) ); // Submit callData as a UserOp execution on the account // Remove the gas policy later: address[] memory toRemove = new address[](1); toRemove[0] = address(simpleGasPolicy); bytes memory disableCallData = abi.encodeCall( ISmartSession.disableUserOpPolicies, (permissionId, toRemove) ); // Query current policies: address[] memory policies = smartSession.getUserOpPolicies(account, permissionId); bool isEnabled = smartSession.isUserOpPolicyEnabled(account, permissionId, address(simpleGasPolicy)); ``` -------------------------------- ### SmartSession View Functions Source: https://context7.com/erc7579/smartsessions/llms.txt These functions allow querying the state and configuration of SmartSessions for off-chain tooling and dApp UIs. ```APIDOC ## View / Query Functions **Query session state and configuration for off-chain tooling and dApp UIs.** ```solidity // SmartSessionBase.sol — all view functions // Check if SmartSession is installed and has active sessions on an account: bool initialized = smartSession.isInitialized(address account); // List all PermissionIds active on an account: PermissionId[] memory ids = smartSession.getPermissionIDs(address account); // Check a specific permission: bool active = smartSession.isPermissionEnabled(PermissionId permissionId, address account); // Replay-protection nonce for ENABLE flow (incremented on revokeEnableSignature): uint256 nonce = smartSession.getNonce(PermissionId permissionId, address account); // Retrieve the session validator address and its stored config: (address validator, bytes memory config) = smartSession.getSessionValidatorAndConfig(account, permissionId); // List all UserOp policies for a permission: address[] memory uoPolicies = smartSession.getUserOpPolicies(account, permissionId); // List all ERC-1271 policies: address[] memory erc1271Policies = smartSession.getERC1271Policies(account, permissionId); // List all action policies for a given ActionId: address[] memory actionPolicies = smartSession.getActionPolicies(account, permissionId, actionId); // List all enabled ActionIds: bytes32[] memory actionIds = smartSession.getEnabledActions(account, permissionId); // Batch enabled-check using PolicyData arrays (easier for off-chain tooling): bool allUoEnabled = smartSession.areUserOpPoliciesEnabled(account, permissionId, userOpPolicies); bool allErcEnabled = smartSession.areERC1271PoliciesEnabled(account, permissionId, erc1271Policies); bool allActEnabled = smartSession.areActionsEnabled(account, permissionId, actions); // Check individual policy addresses: bool isPolicyOn = smartSession.isUserOpPolicyEnabled(account, permissionId, policyAddress); bool isActionPolicyOn = smartSession.isActionPolicyEnabled(account, permissionId, actionId, policyAddress); bool isERC7739On = smartSession.isERC7739ContentEnabled(account, permissionId, appDomainSeparator, contentName); // Module type check (SmartSession is type 1 = VALIDATOR): bool isValidator = smartSession.isModuleType(1); // true ``` ``` -------------------------------- ### SmartSession Core Data Structures Source: https://context7.com/erc7579/smartsessions/llms.txt Defines the structs used to configure sessions, policies, actions, and ERC-7739 data within SmartSession. These are essential for building session configurations. ```solidity struct Session { ISessionValidator sessionValidator; bytes sessionValidatorInitData; bytes32 salt; PolicyData[] userOpPolicies; ERC7739Data erc7739Policies; ActionData[] actions; bool permitERC4337Paymaster; } struct PolicyData { address policy; bytes initData; } struct ActionData { bytes4 actionTargetSelector; address actionTarget; PolicyData[] actionPolicies; } struct ERC7739Data { ERC7739Context[] allowedERC7739Content; PolicyData[] erc1271Policies; } enum SmartSessionMode { USE, ENABLE, UNSAFE_ENABLE } address constant FALLBACK_TARGET_FLAG = address(1); bytes4 constant FALLBACK_TARGET_SELECTOR_FLAG = 0x00000001; ``` -------------------------------- ### SimpleGasPolicy Source: https://context7.com/erc7579/smartsessions/llms.txt An `IUserOpPolicy` that enforces cumulative limits on total gas units and total ETH cost for UserOps within a session. Both limits are tracked and enforced cumulatively. ```APIDOC ## `SimpleGasPolicy` — Aggregate Gas & Cost Cap Policy Built-in `IUserOpPolicy` that caps the total gas units and total ETH cost (gas × gas price) consumed across all UserOps in a session. Both limits are tracked cumulatively; once either is exceeded, validation fails. ```solidity // external/policies/SimpleGasPolicy.sol contract SimpleGasPolicy is IUserOpPolicy { function initializeWithMultiplexer(address account, ConfigId configId, bytes calldata initData) external; // initData = bytes32: first 16 bytes = uint128 gasLimit, next 16 bytes = uint128 costLimit (wei) function checkUserOpPolicy(ConfigId id, PackedUserOperation calldata userOp) external returns (uint256); function getGasConfig(ConfigId configId, address multiplexer, address userOpSender) external view returns (uint128 gasLimit, uint128 gasUsed, uint128 costLimit, uint128 costUsed); } // Allow up to 1M gas units, costing at most 0.05 ETH: uint128 gasLimit = 1_000_000; uint128 costLimit = 0.05 ether; PolicyData memory gasPolicy = PolicyData({ policy: address(simpleGasPolicy), initData: abi.encodePacked(gasLimit, costLimit) }); session.userOpPolicies = new PolicyData[](1); session.userOpPolicies[0] = gasPolicy; // Query current consumption: (uint128 gLimit, uint128 gUsed, uint128 cLimit, uint128 cUsed) = simpleGasPolicy.getGasConfig(configId, address(smartSession), smartAccountAddress); ``` ``` -------------------------------- ### enableSessions Source: https://context7.com/erc7579/smartsessions/llms.txt Enables an array of `Session` configurations for the calling smart account during the ERC-4337 execution phase. Returns the deterministic `PermissionId` for each session. ```APIDOC ## `enableSessions` — Enable One or More Sessions ### Description Enables an array of `Session` configurations for the calling smart account during the ERC-4337 execution phase. Returns the deterministic `PermissionId` for each session. Each `PermissionId` is `keccak256` of the session's validator address, salt, and validator init data. ### Function Signature ```solidity function enableSessions(Session[] calldata sessions) external returns (PermissionId[] memory permissionIds); ``` ### Usage Example ```javascript // Build sessions Session[] memory sessions = new Session[](1); sessions[0] = Session({ sessionValidator: ISessionValidator(address(simpleSessionValidator)), sessionValidatorInitData: abi.encodePacked(sessionSigner.addr), salt: keccak256("my-dapp-session-v1"), userOpPolicies: new PolicyData[](0), erc7739Policies: ERC7739Data({ allowedERC7739Content: new ERC7739Context[](0), erc1271Policies: new PolicyData[](0) }), actions: actions, permitERC4337Paymaster: false }); // enableSessions is called from the account's execution phase (e.g., via UserOp) UserOpData memory userOpData = instance.getExecOps({ target: address(smartSession), value: 0, callData: abi.encodeCall(ISmartSession.enableSessions, (sessions)), txValidator: address(mockK1Validator) }); userOpData.userOp.signature = sign(userOpData.userOpHash, owner.key); instance.submitUserOp(userOpData); ``` ### Verification ```javascript // Retrieve the PermissionId off-chain: PermissionId permissionId = smartSession.getPermissionId(sessions[0]); bool enabled = smartSession.isPermissionEnabled(permissionId, instance.account); // enabled == true ``` ``` -------------------------------- ### Enable, Disable, and Clear Action Policies Source: https://context7.com/erc7579/smartsessions/llms.txt Use these functions to manage policies for specific actions within a session. Policies are enforced only when the UserOp executes the matching call. ActionId is computed using `keccak256(abi.encodePacked(actionTarget, actionTargetSelector))`. ```Sol // SmartSessionBase.sol function enableActionPolicies(PermissionId permissionId, ActionData[] memory actionPolicies) public; function disableActionPolicies(PermissionId permissionId, ActionId actionId, address[] calldata policies) public; function disableActionId(PermissionId permissionId, ActionId actionId) public; ``` ```Sol // Add an ERC-20 spending limit policy for USDC transfers: ActionData[] memory newActions = new ActionData[](1); newActions[0] = ActionData({ actionTargetSelector: IERC20.transfer.selector, actionTarget: address(usdcToken), actionPolicies: new PolicyData[](1) }); newActions[0].actionPolicies[0] = PolicyData({ policy: address(erc20SpendingLimitPolicy), initData: abi.encode( Solarray.addresses(address(usdcToken)), Solarray.uint256s(1000e6) // 1000 USDC lifetime cap ) }); bytes memory callData = abi.encodeCall( ISmartSession.enableActionPolicies, (permissionId, newActions) ); ``` ```Sol // Compute the ActionId to later disable it: // ActionId = keccak256(abi.encodePacked(actionTarget, actionTargetSelector)) ActionId actionId = ActionId.wrap(keccak256(abi.encodePacked(address(usdcToken), IERC20.transfer.selector))); // Disable just the spending limit (leave other action policies intact): address[] memory toRemove = new address[](1); toRemove[0] = address(erc20SpendingLimitPolicy); bytes memory disableCallData = abi.encodeCall( ISmartSession.disableActionPolicies, (permissionId, actionId, toRemove) ); ``` ```Sol // Or remove the entire action entry (all policies for this actionId): bytes memory disableIdCallData = abi.encodeCall( ISmartSession.disableActionId, (permissionId, actionId) ); ``` ```Sol // Query: address[] memory actionPolicies = smartSession.getActionPolicies(account, permissionId, actionId); bool idEnabled = smartSession.isActionIdEnabled(account, permissionId, actionId); ``` -------------------------------- ### enableUserOpPolicies / disableUserOpPolicies Source: https://context7.com/erc7579/smartsessions/llms.txt Manages UserOp-level policies by adding or removing policies that run on every UserOp validated under a given `PermissionId`. This can only be called while the permission is enabled. Useful for dynamically tightening or extending restrictions post-session creation. ```APIDOC ## `enableUserOpPolicies` / `disableUserOpPolicies` — Manage UserOp-Level Policies Adds or removes policies that run on every UserOp validated under a given `PermissionId`. Can only be called while the permission is enabled (guarded by `enableWithPermissionId` / `disableWithPermissionId` modifiers). Useful for dynamically tightening or extending restrictions post-session creation. ```solidity // SmartSessionBase.sol function enableUserOpPolicies(PermissionId permissionId, PolicyData[] memory userOpPolicies) public; function disableUserOpPolicies(PermissionId permissionId, address[] calldata policies) public; // Add a gas limit policy to an existing session: PolicyData[] memory newPolicies = new PolicyData[](1); newPolicies[0] = PolicyData({ policy: address(simpleGasPolicy), // gasLimit = 500_000 units, costLimit = 0.01 ETH initData: abi.encodePacked(uint128(500_000), uint128(0.01 ether)) }); // Called from a UserOp execution targeting the smart session: bytes memory callData = abi.encodeCall( ISmartSession.enableUserOpPolicies, (permissionId, newPolicies) ); // Submit callData as a UserOp execution on the account // Remove the gas policy later: address[] memory toRemove = new address[](1); toRemove[0] = address(simpleGasPolicy); bytes memory disableCallData = abi.encodeCall( ISmartSession.disableUserOpPolicies, (permissionId, toRemove) ); // Query current policies: address[] memory policies = smartSession.getUserOpPolicies(account, permissionId); bool isEnabled = smartSession.isUserOpPolicyEnabled(account, permissionId, address(simpleGasPolicy)); ``` ``` -------------------------------- ### Enable and Disable ERC-1271 Policies Source: https://context7.com/erc7579/smartsessions/llms.txt Configure which `(appDomainSeparator, contentName)` pairs a session key can sign off-chain messages for. Uses ERC-7739 nested EIP-712 for phishing resistance. The session key can only sign messages whose type string matches a whitelisted `contentName` under an allowed `appDomainSeparator`. ```Sol // SmartSessionBase.sol function enableERC1271Policies(PermissionId permissionId, ERC7739Data calldata erc1271Policies) public; function disableERC1271Policies( PermissionId permissionId, address[] calldata policies, ERC7739Context[] calldata contexts ) public; ``` ```Sol // Allow the session key to sign Permit2 messages for the Uniswap domain: bytes32 permit2DomainSeparator = keccak256(abi.encode( keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"), keccak256("Permit2"), block.chainid, address(permit2Contract) )); ERC7739Context[] memory contexts = new ERC7739Context[](1); contexts[0] = ERC7739Context({ appDomainSeparator: permit2DomainSeparator, contentNames: Solarray.strings("PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)") }); ERC7739Data memory erc7739Data = ERC7739Data({ allowedERC7739Content: contexts, erc1271Policies: new PolicyData[](0) // no additional ERC-1271 policies }); bytes memory callData = abi.encodeCall( ISmartSession.enableERC1271Policies, (permissionId, erc7739Data) ); ``` ```Sol // Query enabled content: bool contentEnabled = smartSession.isERC7739ContentEnabled( account, permissionId, permit2DomainSeparator, "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)" ); ``` -------------------------------- ### Enable Paymaster Use for a Permission Source: https://context7.com/erc7579/smartsessions/llms.txt Toggles whether UserOps signed under a given `PermissionId` can use an ERC-4337 paymaster. This flag is required when `paymasterAndData` is non-empty in a UserOp and at least one UserOp policy is present. Emits `PermissionIdPermit4337Paymaster`. ```solidity // SmartSessionBase.sol function setPermit4337Paymaster(PermissionId permissionId, bool enabled) external; // Enable paymaster usage (called from a UserOp execution): bytes memory callData = abi.encodeCall( SmartSessionBase.setPermit4337Paymaster, (permissionId, true) ); // Can also be set at session-creation time via Session.permitERC4337Paymaster = true Session memory session = Session({ ... permitERC4337Paymaster: true, ... }); ``` -------------------------------- ### getSessionDigest Source: https://context7.com/erc7579/smartsessions/llms.txt Computes the EIP-712 structured-data digest for a session. This digest must be signed by the account owner to authorize enabling the session, incorporating nonce, account address, contract address, and mode to prevent replay attacks. ```APIDOC ## `getSessionDigest` — Compute the EIP-712 Session Digest Returns the EIP-712 structured-data digest for a session that must be signed by the account owner to authorize enabling the session. Incorporates the current nonce, account address, SmartSession contract address, and the mode to prevent replay attacks. ```solidity // SmartSessionBase.sol function getSessionDigest( PermissionId permissionId, address account, Session memory data, SmartSessionMode mode ) public view returns (bytes32); // Derive the PermissionId first: PermissionId permissionId = smartSession.getPermissionId(session); // Get the digest for the ENABLE flow: bytes32 digest = smartSession.getSessionDigest({ permissionId: permissionId, account: 0xYourSmartAccount, data: session, mode: SmartSessionMode.ENABLE }); // The account owner signs this digest: (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerKey, digest); bytes memory enableSig = abi.encodePacked(r, s, v); // For multichain use, wrap multiple per-chain digests in a MultiChainSession: // HashLib.multichainDigest(chainDigests) produces the single digest to sign once for all chains. bytes32 multichainDigest = HashLib.multichainDigest(chainDigests); ``` ``` -------------------------------- ### ERC-4337 UserOp Validation (ENABLE Mode) Source: https://context7.com/erc7579/smartsessions/llms.txt Enables a session and validates a UserOp atomically in a single round-trip. The userOp.signature contains an EnableSession blob and the session key's signature for the UserOp. Requires the account owner's EIP-712 signature over the enable digest. ```Solidity struct EnableSession { uint8 chainDigestIndex; // Index into hashesAndChainIds for the current chain ChainDigest[] hashesAndChainIds; // Multichain digest list signed by the account owner Session sessionToEnable; // The session to enable bytes permissionEnableSig; // Account owner's EIP-712 signature over the enable digest } ``` ```Solidity // Build the enable digest (must be signed by the account owner, e.g., via mockK1Validator): PermissionId permissionId = smartSession.getPermissionId(session); bytes32 sessionDigest = smartSession.getSessionDigest({ permissionId: permissionId, account: instance.account, data: session, mode: SmartSessionMode.ENABLE }); // Wrap into multichain format (even for single chain, array length = 1): ChainDigest[] memory chainDigests = new ChainDigest[](1); chainDigests[0] = ChainDigest({ chainId: uint64(block.chainid), sessionDigest: sessionDigest }); EnableSession memory enableData = EnableSession({ chainDigestIndex: 0, hashesAndChainIds: chainDigests, sessionToEnable: session, permissionEnableSig: "" // filled below }); // Compute the multichain digest and sign it with the account owner key: bytes32 multichainDigest = HashLib.multichainDigest(chainDigests); enableData.permissionEnableSig = sign(multichainDigest, owner.key); // Encode final userOp signature (session key signs the userOpHash): bytes memory sessionKeySig = sign(userOpHash, sessionSigner.key); bytes memory packedSig = EncodeLib.encodeUnsafeEnable(sessionKeySig, enableData); // or for registry-checked ENABLE mode: // packedSig = abi.encodePacked(SmartSessionMode.ENABLE, abi.encode(enableData, sessionKeySig).flzCompress()); userOp.signature = packedSig; ``` -------------------------------- ### Manage ERC1271 Policies Source: https://context7.com/erc7579/smartsessions/llms.txt These functions configure which (appDomainSeparator, contentName) pairs a session key may sign off-chain, and the policies enforced during ERC-1271 validation. The session key can only sign messages whose type string matches a whitelisted contentName under an allowed appDomainSeparator. ```APIDOC ## `enableERC1271Policies` / `disableERC1271Policies` — Manage ERC-1271 Policies Configures which `(appDomainSeparator, contentName)` pairs a session key may sign off-chain, and the policies enforced during ERC-1271 validation. Uses ERC-7739 nested EIP-712 for phishing resistance. The session key can only sign messages whose type string matches a whitelisted `contentName` under an allowed `appDomainSeparator`. ```solidity // SmartSessionBase.sol function enableERC1271Policies(PermissionId permissionId, ERC7739Data calldata erc1271Policies) public; function disableERC1271Policies( PermissionId permissionId, address[] calldata policies, ERC7739Context[] calldata contexts ) public; // Allow the session key to sign Permit2 messages for the Uniswap domain: bytes32 permit2DomainSeparator = keccak256(abi.encode( keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)"), keccak256("Permit2"), block.chainid, address(permit2Contract) )); ERC7739Context[] memory contexts = new ERC7739Context[](1); contexts[0] = ERC7739Context({ appDomainSeparator: permit2DomainSeparator, contentNames: Solarray.strings("PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)") }); ERC7739Data memory erc7739Data = ERC7739Data({ allowedERC7739Content: contexts, erc1271Policies: new PolicyData[](0) // no additional ERC-1271 policies }); bytes memory callData = abi.encodeCall( ISmartSession.enableERC1271Policies, (permissionId, erc7739Data) ); // Query enabled content: bool contentEnabled = smartSession.isERC7739ContentEnabled( account, permissionId, permit2DomainSeparator, "PermitTransferFrom(TokenPermissions permitted,address spender,uint256 nonce,uint256 deadline)" ); ``` ```