# Position Manager Flow End-to-end execution flow for managing position manager approvals and performing actions on behalf of users in Aave V3. ## Quick Reference | Aspect | Details | |--------|---------| | **Entry Points** | `Pool.approvePositionManager(positionManager, approve)`
`Pool.renouncePositionManagerRole(user)`
`Pool.setUserUseReserveAsCollateralOnBehalfOf(asset, useAsCollateral, onBehalfOf)`
`Pool.setUserEModeOnBehalfOf(categoryId, onBehalfOf)` | | **Key Transformations** | None - direct storage updates | | **State Changes** | `_positionManager[user][positionManager] = approve` | | **Events Emitted** | `PositionManagerApproved`, `PositionManagerRevoked` | --- ## Flow Diagram ```mermaid flowchart TD %% Styling definitions classDef validation fill:#ffcccc,stroke:#ff0000,stroke-width:2px classDef transformation fill:#ccffcc,stroke:#00aa00,stroke-width:2px classDef storage fill:#ccccff,stroke:#0000ff,stroke-width:2px classDef event fill:#ffffcc,stroke:#aaaa00,stroke-width:2px classDef error fill:#ff0000,stroke:#000000,color:#fff subgraph ApprovalFlow ["1. Approve Position Manager Flow"] direction TB Entry1["Pool.approvePositionManager
positionManager, approve"] --> CheckState1["VALIDATION
Check current approval state"] class CheckState1 validation CheckState1 -->|Already same state| Skip1[Return early] CheckState1 -->|State change needed| Store1["STORAGE UPDATE
_positionManager
[msg.sender][positionManager]
= approve"] class Store1 storage Store1 --> Branch1{approve?} Branch1 -->|Yes| EventApproved["EMIT
PositionManagerApproved"] class EventApproved event Branch1 -->|No| EventRevoked["EMIT
PositionManagerRevoked"] class EventRevoked event end subgraph RenounceFlow ["2. Renounce Position Manager Role Flow"] direction TB Entry2["Pool.renouncePositionManagerRole
user"] --> CheckState2["VALIDATION
Check if caller is approved
as position manager"] class CheckState2 validation CheckState2 -->|Not approved| Skip2[Return early] CheckState2 -->|Is approved| Store2["STORAGE UPDATE
_positionManager
[user][msg.sender]
= false"] class Store2 storage Store2 --> EventRevoked2["EMIT
PositionManagerRevoked"] class EventRevoked2 event end subgraph CollateralFlow ["3. Set Collateral On Behalf Flow"] direction TB Entry3["Pool.setUserUseReserveAsCollateral
OnBehalfOf
asset, useAsCollateral,
onBehalfOf"] --> ValidateManager["VALIDATION
onlyPositionManager
modifier check"] class ValidateManager validation ValidateManager -->|Revert| Error1["ERROR
CallerNotPositionManager"] class Error1 error ValidateManager -->|Pass| ExecuteCollateral["SupplyLogic
executeUseReserveAsCollateral"] ExecuteCollateral --> ValidateCollateral["ValidationLogic
validateUseAsCollateral"] class ValidateCollateral validation ValidateCollateral --> UpdateConfig["UserConfig
setUsingAsCollateral"] class UpdateConfig storage UpdateConfig --> CollateralEvent["EMIT
ReserveUsedAsCollateral
Enabled/Disabled"] class CollateralEvent event end subgraph EModeFlow ["4. Set E-Mode On Behalf Flow"] direction TB Entry4["Pool.setUserEModeOnBehalfOf
categoryId, onBehalfOf"] --> ValidateManager2["VALIDATION
onlyPositionManager
modifier check"] class ValidateManager2 validation ValidateManager2 -->|Revert| Error2["ERROR
CallerNotPositionManager"] class Error2 error ValidateManager2 -->|Pass| ExecuteEMode["SupplyLogic
executeSetUserEMode"] ExecuteEMode --> ValidateEMode["ValidationLogic
validateEModeCategory"] class ValidateEMode validation ValidateEMode --> UpdateEMode["STORAGE UPDATE
_usersEModeCategory
[onBehalfOf] = categoryId"] class UpdateEMode storage UpdateEMode --> EModeEvent["EMIT
UserEModeSet"] class EModeEvent event end ``` --- ## Step-by-Step Execution ### 1. Approve Position Manager **File:** `contracts/protocol/pool/Pool.sol` ```solidity function approvePositionManager(address positionManager, bool approve) external override { // Skip if already in desired state (gas optimization) if (_positionManager[_msgSender()][positionManager] == approve) return; // Update storage _positionManager[_msgSender()][positionManager] = approve; // Emit appropriate event if (approve) { emit PositionManagerApproved({user: _msgSender(), positionManager: positionManager}); } else { emit PositionManagerRevoked({user: _msgSender(), positionManager: positionManager}); } } ``` **Key Points:** - User (msg.sender) approves a position manager to act on their behalf - State check prevents redundant storage writes - Emits different events based on approval/revocation --- ### 2. Renounce Position Manager Role **File:** `contracts/protocol/pool/Pool.sol` ```solidity function renouncePositionManagerRole(address user) external override { // Only proceed if caller is actually an approved position manager if (_positionManager[user][_msgSender()] == false) return; // Revoke approval _positionManager[user][_msgSender()] = false; emit PositionManagerRevoked({user: user, positionManager: _msgSender()}); } ``` **Key Points:** - Position manager can voluntarily renounce their role - Returns early if caller is not actually approved - Emits PositionManagerRevoked event --- ### 3. Access Control Modifier **File:** `contracts/protocol/pool/Pool.sol` ```solidity /** * @dev Only an approved position manager can call functions marked by this modifier. */ modifier onlyPositionManager(address onBehalfOf) { _onlyPositionManager(onBehalfOf); _; } function _onlyPositionManager(address onBehalfOf) internal view virtual { require(_positionManager[onBehalfOf][_msgSender()], Errors.CallerNotPositionManager()); } ``` **Key Points:** - Checks if msg.sender is approved as position manager for `onBehalfOf` user - Reverts with `CallerNotPositionManager` if not authorized - Used by both `setUserUseReserveAsCollateralOnBehalfOf` and `setUserEModeOnBehalfOf` --- ### 4. Set Collateral On Behalf Of User **File:** `contracts/protocol/pool/Pool.sol` ```solidity function setUserUseReserveAsCollateralOnBehalfOf( address asset, bool useAsCollateral, address onBehalfOf ) external override onlyPositionManager(onBehalfOf) { SupplyLogic.executeUseReserveAsCollateral( _reserves, _reservesList, _eModeCategories, _usersConfig[onBehalfOf], onBehalfOf, asset, useAsCollateral, ADDRESSES_PROVIDER.getPriceOracle(), _usersEModeCategory[onBehalfOf] ); } ``` **File:** `contracts/protocol/libraries/logic/SupplyLogic.sol` ```solidity function executeUseReserveAsCollateral( mapping(address => DataTypes.ReserveData) storage reserves, mapping(uint256 => address) storage reservesList, mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, DataTypes.UserConfigurationMap storage userConfig, address user, address asset, bool useAsCollateral, address priceOracle, uint8 userEModeCategory ) external { DataTypes.ReserveData storage reserve = reserves[asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); // Update state reserve.updateState(reserveCache); // Validate ValidationLogic.validateUseAsCollateral( reserves, reservesList, reserveCache, userConfig, asset, user, useAsCollateral, priceOracle, userEModeCategory ); // Update user configuration userConfig.setUsingAsCollateral(reserve.id, useAsCollateral); // Emit appropriate event if (useAsCollateral) { emit ReserveUsedAsCollateralEnabled(asset, user); } else { emit ReserveUsedAsCollateralDisabled(asset, user); } } ``` **Key Points:** - Position manager can enable/disable collateral usage for user - Requires `onlyPositionManager` modifier - validates access control - Performs same validations as direct user call - Emits collateral events --- ### 5. Set E-Mode On Behalf Of User **File:** `contracts/protocol/pool/Pool.sol` ```solidity function setUserEModeOnBehalfOf( uint8 categoryId, address onBehalfOf ) external override onlyPositionManager(onBehalfOf) { SupplyLogic.executeSetUserEMode( _reserves, _reservesList, _eModeCategories, _usersEModeCategory, _usersConfig[onBehalfOf], onBehalfOf, ADDRESSES_PROVIDER.getPriceOracle(), categoryId ); } ``` **File:** `contracts/protocol/libraries/logic/SupplyLogic.sol` ```solidity function executeSetUserEMode( mapping(address => DataTypes.ReserveData) storage reserves, mapping(uint256 => address) storage reservesList, mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, mapping(address => uint8) storage usersEModeCategory, DataTypes.UserConfigurationMap storage userConfig, address user, address priceOracle, uint8 categoryId ) external { // Validation ValidationLogic.validateEModeCategory( reserves, reservesList, userConfig, eModeCategories, categoryId, user, priceOracle ); // Update user's eMode category usersEModeCategory[user] = categoryId; emit UserEModeSet(user, categoryId); } ``` **Key Points:** - Position manager can set user's eMode category - Validates that user can enter specified eMode category - Updates storage and emits UserEModeSet event --- ### 6. Check Approval Status **File:** `contracts/protocol/pool/Pool.sol` ```solidity function isApprovedPositionManager( address user, address positionManager ) external view override returns (bool) { return _positionManager[user][positionManager]; } ``` --- ## Storage Layout ```solidity // PoolStorage.sol // Allowlisted permissionManagers can enable collaterals & switch eModes on behalf of a user mapping(address user => mapping(address permittedPositionManager => bool)) internal _positionManager; ``` **Key Points:** - Double mapping: user → position manager → bool - Position manager must be explicitly approved by user - Approval is required before any "onBehalfOf" actions --- ## Event Details ### PositionManagerApproved Event Emitted when a user approves a position manager. ```solidity event PositionManagerApproved( address indexed user, // User granting approval address indexed positionManager // Address being approved ); ``` ### PositionManagerRevoked Event Emitted when position manager approval is revoked (either by user or by renouncement). ```solidity event PositionManagerRevoked( address indexed user, // User whose approval is revoked address indexed positionManager // Address being revoked ); ``` ### ReserveUsedAsCollateralEnabled Event Emitted when position manager enables collateral for user. ```solidity event ReserveUsedAsCollateralEnabled( address indexed reserve, address indexed user ); ``` ### ReserveUsedAsCollateralDisabled Event Emitted when position manager disables collateral for user. ```solidity event ReserveUsedAsCollateralDisabled( address indexed reserve, address indexed user ); ``` ### UserEModeSet Event Emitted when position manager sets eMode category for user. ```solidity event UserEModeSet( address indexed user, uint8 categoryId ); ``` --- ## Error Conditions | Error | Condition | File | |-------|-----------|------| | `CallerNotPositionManager` | `msg.sender` is not approved as position manager for `onBehalfOf` | Pool.sol | | `HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD` | Disabling collateral would put user's health factor below liquidation threshold | ValidationLogic.sol | | `EModeCategoryReserved` | Attempting to use category 0 (reserved) | Pool.sol | | `InconsistentEModeCategory` | User has collateral that cannot be used in the requested eMode category | ValidationLogic.sol | --- ## Related Flows - [Supply Flow](./supply.md) - Depositing assets - [Borrow Flow](./borrow.md) - Borrowing against collateral - [Collateral Management](./collateral_management.md) - Direct collateral configuration --- ## Source File Locations ``` contracts/protocol/pool/Pool.sol contracts/protocol/pool/PoolStorage.sol contracts/protocol/libraries/logic/SupplyLogic.sol contracts/protocol/libraries/logic/ValidationLogic.sol contracts/protocol/libraries/helpers/Errors.sol ``` --- ## Security Considerations 1. **Approval Required**: Position managers must be explicitly approved before they can act on behalf of a user 2. **Revocation**: Users can revoke approval at any time via `approvePositionManager(manager, false)` 3. **Self-Renouncement**: Position managers can voluntarily renounce their role via `renouncePositionManagerRole(user)` 4. **Limited Actions**: Position managers can ONLY: - Enable/disable user's collateral (`setUserUseReserveAsCollateralOnBehalfOf`) - Set user's eMode category (`setUserEModeOnBehalfOf`) They CANNOT: - Supply on behalf of user - Withdraw on behalf of user - Borrow on behalf of user - Repay on behalf of user - Liquidate positions 5. **Validation Still Applied**: All actions performed by position managers go through the same validation logic as direct user actions 6. **Gas Optimization**: `approvePositionManager` returns early if state is already correct