# Supply With Permit Flow End-to-end execution flow for depositing assets into Aave V3 using ERC-2612 permit for gasless approvals. ## Quick Reference | Aspect | Details | |--------|---------| | **Entry Point** | `Pool.supplyWithPermit(asset, amount, onBehalfOf, referralCode, deadline, permitV, permitR, permitS)` | | **Key Transformations** | [Amount → Scaled Balance](../transformations/index.md#collateral-token-transformations) | | **State Changes** | `_scaledBalance[onBehalfOf] += scaledAmount` | | **Events Emitted** | `Supply`, `ReserveUsedAsCollateralEnabled` (conditional) | --- ## 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 classDef bridge fill:#ffccff,stroke:#aa00aa,stroke-width:2px %% Entry point Entry["Pool.supplyWithPermit
asset, amount,
onBehalfOf, referralCode,
deadline, permitV,
permitR, permitS"] --> Permit["IERC20WithPermit
permit
Approves Pool to
spend tokens"] class Permit validation Permit --> MainPath["SupplyLogic
executeSupply"] subgraph StateUpdate ["1. State Updates"] direction TB UpdateState["ReserveLogic
updateState
Updates indexes,
accrues treasury"] --> Validate["ValidationLogic
validateSupply"] class Validate validation Validate --> UpdateRates["ReserveLogic
updateInterestRates"] end MainPath --> StateUpdate subgraph TokenFlow ["2. Token Flow"] direction TB Transfer["IERC20
safeTransferFrom
msg.sender -> aToken"] --> Mint["AToken
mint"] Mint --> Transform["TRANSFORMATION
scaledAmount =
amount.rayDiv
(liquidityIndex)"] class Transform transformation Transform --> Store["STORAGE UPDATE
_scaledBalance
[onBehalfOf] +=
scaledAmount"] class Store storage end UpdateRates --> TokenFlow subgraph CollateralCheck ["3. Collateral Configuration"] direction TB FirstCheck{"First Supply?"} -->|Yes| ValidateCollat["ValidationLogic
validateUseAsCollateral"] class ValidateCollat validation ValidateCollat --> CanCollat{"Can Use as
Collateral?"} CanCollat -->|Yes| SetCollat["UserConfig
setUsingAsCollateral
(reserve.id, true)"] class SetCollat storage SetCollat --> CollatEvent["EMIT
ReserveUsedAsCollateralEnabled"] class CollatEvent event CanCollat -->|No| Skip1[Continue] FirstCheck -->|No| Skip1 end Store --> FirstCheck Skip1 --> FinalEvent["EMIT
Supply"] class FinalEvent event %% Error annotations %% CRITICAL: Permit signature must be valid or transaction reverts %% CRITICAL: All validations must pass or transaction reverts %% CRITICAL: Supply cap check prevents overflow attacks %% Link styles for critical paths linkStyle 0 stroke:#ff0000,stroke-width:3px linkStyle 8 stroke:#ff0000,stroke-width:3px linkStyle 16 stroke:#ff0000,stroke-width:3px ``` --- ## Step-by-Step Execution ### 1. Entry Point **File:** `contracts/protocol/pool/Pool.sol` ```solidity function supplyWithPermit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode, uint256 deadline, uint8 permitV, bytes32 permitR, bytes32 permitS ) public virtual override { try IERC20WithPermit(asset).permit( _msgSender(), address(this), amount, deadline, permitV, permitR, permitS ) {} catch {} SupplyLogic.executeSupply( _reserves, _reservesList, _eModeCategories, _usersConfig[onBehalfOf], DataTypes.ExecuteSupplyParams({ user: _msgSender(), asset: asset, interestRateStrategyAddress: RESERVE_INTEREST_RATE_STRATEGY, amount: amount, onBehalfOf: onBehalfOf, referralCode: referralCode, supplierEModeCategory: _usersEModeCategory[onBehalfOf] }) ); } ``` **Key Differences from `supply()`:** - Additional permit parameters: `deadline`, `permitV`, `permitR`, `permitS` - Calls `IERC20WithPermit.permit()` before executing supply - Permit is wrapped in try/catch to handle cases where permit was already consumed - Same `executeSupply` call as regular supply after permit approval ### 2. Permit Approval **File:** `contracts/interfaces/IERC20WithPermit.sol` ```solidity interface IERC20WithPermit is IERC20 { function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; } ``` **Purpose:** - Implements EIP-2612 gasless approval - User signs a message off-chain authorizing the Pool to spend tokens - Eliminates need for separate `approve()` transaction - Signature parameters (`v`, `r`, `s`) are verified against the deadline ### 3. Execute Supply **File:** `contracts/protocol/libraries/logic/SupplyLogic.sol` ```solidity function executeSupply( mapping(address => DataTypes.ReserveData) storage reserves, mapping(uint256 => address) storage reservesList, mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories, DataTypes.UserConfigurationMap storage userConfig, DataTypes.ExecuteSupplyParams memory params ) external { DataTypes.ReserveData storage reserve = reserves[params.asset]; DataTypes.ReserveCache memory reserveCache = reserve.cache(); // Update state (indexes, timestamp) reserve.updateState(reserveCache); // Validate supply ValidationLogic.validateSupply( reserves, reserveCache, params.amount, params.onBehalfOf ); // Update interest rates reserve.updateInterestRates( reserveCache, params.asset, 0, // liquidityAdded 0 // liquidityTaken ); // Transfer from user IERC20(params.asset).safeTransferFrom( params.user, reserveCache.aTokenAddress, params.amount ); // Mint aTokens bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint( params.user, params.onBehalfOf, params.amount, reserveCache.nextLiquidityIndex ); // Handle collateral configuration if (isFirstSupply) { bool canUseAsCollateral = ValidationLogic.validateUseAsCollateral( reserves, reservesList, eModeCategories, reserveCache, userConfig, params.supplierEModeCategory ); if (canUseAsCollateral) { userConfig.setUsingAsCollateral(reserve.id, true); emit ReserveUsedAsCollateralEnabled( params.asset, params.onBehalfOf ); } } emit Supply( params.asset, params.user, params.onBehalfOf, params.amount, params.referralCode ); } ``` ### 4. AToken Mint **File:** `contracts/protocol/tokenization/AToken.sol` ```solidity function mint( address caller, address onBehalfOf, uint256 amount, uint256 index ) external override onlyPool returns (bool) { return _mintScaled(caller, onBehalfOf, amount, index); } function _mintScaled( address caller, address onBehalfOf, uint256 amount, uint256 index ) internal returns (bool) { uint256 scaledAmount = amount.rayDiv(index); // [TRANSFORMATION] _scaledBalance[onBehalfOf] += scaledAmount; // Return true if first supply return (scaledAmount != 0 && _scaledBalance[onBehalfOf] == scaledAmount); } ``` **[TRANSFORMATION]:** See [Collateral Token Transformations](../transformations/index.md#collateral-token-transformations) for details on `amount.rayDiv(index)` ### 5. Validation Checks **File:** `contracts/protocol/libraries/logic/ValidationLogic.sol` ```solidity function validateSupply( mapping(address => DataTypes.ReserveData) storage reserves, DataTypes.ReserveCache memory reserveCache, uint256 amount, address onBehalfOf ) internal view { require(amount != 0, Errors.INVALID_AMOUNT); // Check reserve is active and not frozen require( reserveCache.reserveConfiguration.getActive(), Errors.RESERVE_INACTIVE ); require( !reserveCache.reserveConfiguration.getFrozen(), Errors.RESERVE_FROZEN ); // Check supply cap uint256 supplyCap = reserveCache.reserveConfiguration.getSupplyCap(); if (supplyCap != 0) { uint256 totalSupply = IERC20(reserveCache.aTokenAddress) .scaledTotalSupply() .rayMul(reserveCache.nextLiquidityIndex); uint256 scaledCap = supplyCap * 10**reserveCache.reserveConfiguration.getDecimals(); require(totalSupply + amount <= scaledCap, Errors.SUPPLY_CAP_EXCEEDED); } // Validate onBehalfOf can receive aTokens _validateERC20Getter(onBehalfOf); } ``` --- ## Amount Transformations ### Input → Storage ``` User Input (WAD decimals) ↓ amount = 1000 * 10^18 // 1000 tokens ↓ liquidityIndex = 1.0001 * 10^27 // Current index ↓ scaledAmount = amount.rayDiv(liquidityIndex) = (1000 * 10^18 * 10^27) / (1.0001 * 10^27) = 999.9 * 10^18 (approximate) ↓ _scaledBalance[onBehalfOf] += scaledAmount ``` **Key Points:** - User provides WAD-decimal amount (18 decimals) - Scaled balance uses RAY precision (27 decimals) - Index accrues interest over time - Later withdrawal: `scaledAmount.rayMul(currentIndex)` gives amount + interest --- ## Event Details ### Supply Event ```solidity event Supply( address indexed reserve, // Asset address address indexed user, // msg.sender address indexed onBehalfOf, // Recipient of aTokens uint256 amount, // Amount supplied uint16 referralCode // Referral code (0 if none) ); ``` ### ReserveUsedAsCollateralEnabled Event Emitted only on first supply if Asset can be used as collateral. ```solidity event ReserveUsedAsCollateralEnabled( address indexed reserve, address indexed user ); ``` --- ## Error Conditions | Error | Condition | File | |-------|-----------|------| | `INVALID_AMOUNT` | `amount == 0` | ValidationLogic.sol | | `RESERVE_INACTIVE` | Reserve is not active | ValidationLogic.sol | | `RESERVE_FROZEN` | Reserve is frozen | ValidationLogic.sol | | `SUPPLY_CAP_EXCEEDED` | `totalSupply + amount > supplyCap` | ValidationLogic.sol | **Note:** Permit signature failures are silently caught via try/catch. The actual token transfer will revert if approval is insufficient. --- ## Related Flows - [Supply Flow](./supply.md) - Standard supply without permit - [Withdraw Flow](./withdraw.md) - Reverse operation - [Collateral Management](./collateral_management.md) - Enabling/disabling collateral - [Liquidation Flow](./liquidation.md) - When collateral is seized --- ## Source File Locations ``` contracts/protocol/pool/Pool.sol contracts/interfaces/IERC20WithPermit.sol contracts/protocol/libraries/logic/SupplyLogic.sol contracts/protocol/libraries/logic/ValidationLogic.sol contracts/protocol/tokenization/AToken.sol contracts/protocol/libraries/logic/ReserveLogic.sol ``` --- ## EIP-2612 Permit Standard The `supplyWithPermit` function implements the [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) standard for gasless approvals. ### Benefits 1. **Gasless Approvals**: Users sign a message off-chain instead of submitting an approval transaction 2. **Better UX**: Single transaction for approval + supply 3. **Meta-Transactions**: Can be integrated with relayers for gasless transactions ### Permit Parameters | Parameter | Description | |-----------|-------------| | `deadline` | Timestamp after which the permit is invalid | | `permitV` | ECDSA signature component (recovery id) | | `permitR` | ECDSA signature component (first 32 bytes) | | `permitS` | ECDSA signature component (second 32 bytes) | ### Security Considerations - The permit is wrapped in try/catch to handle race conditions where the permit has already been used - Users must still have sufficient token balance for the transfer to succeed - The deadline protects against replay attacks with expired signatures