Supply Flow¶
End-to-end execution flow for depositing assets into Aave V3.
Quick Reference¶
Aspect |
Details |
|---|---|
Entry Point |
|
Key Transformations |
|
State Changes |
|
Events Emitted |
|
Flow Diagram¶
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.supply
asset, amount,
onBehalfOf,
referralCode"] --> Branch{Bridge Path?}
%% Bridge path
Branch -->|Yes| BridgePath["BridgeLogic
executeMintUnbacked"]
class BridgePath bridge
BridgePath --> BridgeEvent["EMIT
MintUnbacked"]
class BridgeEvent event
BridgeEvent --> BridgeCollat["EMIT
ReserveUsedAsCollateralEnabled"]
class BridgeCollat event
%% Main flow
Branch -->|No| 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: All validations must pass or transaction reverts
%% CRITICAL: Supply cap check prevents overflow attacks
%% Link styles for critical paths
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
function supply(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external virtual override {
SupplyLogic.executeSupply(
_reserves,
_reservesList,
_usersConfig[onBehalfOf],
DataTypes.ExecuteSupplyParams({
asset: asset,
amount: amount,
onBehalfOf: onBehalfOf,
referralCode: referralCode
})
);
}
2. Execute Supply¶
File: contracts/protocol/libraries/logic/SupplyLogic.sol
function executeSupply(
mapping(address => DataTypes.ReserveData) storage reserves,
mapping(uint256 => address) storage reservesList,
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(
msg.sender,
reserveCache.aTokenAddress,
params.amount
);
// Mint aTokens
bool isFirstSupply = IAToken(reserveCache.aTokenAddress).mint(
msg.sender,
params.onBehalfOf,
params.amount,
reserveCache.nextLiquidityIndex
);
// Handle collateral configuration
if (isFirstSupply) {
bool canUseAsCollateral = ValidationLogic.validateUseAsCollateral(
reserves,
reservesList,
reserveCache
);
if (canUseAsCollateral) {
userConfig.setUsingAsCollateral(reserve.id, true);
emit ReserveUsedAsCollateralEnabled(
params.asset,
params.onBehalfOf
);
}
}
emit Supply(
params.asset,
msg.sender,
params.onBehalfOf,
params.amount,
params.referralCode
);
}
3. AToken Mint¶
File: contracts/protocol/tokenization/AToken.sol
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 for details on amount.rayDiv(index)
4. Validation Checks¶
File: contracts/protocol/libraries/logic/ValidationLogic.sol
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¶
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.
event ReserveUsedAsCollateralEnabled(
address indexed reserve,
address indexed user
);
Error Conditions¶
Error |
Condition |
File |
|---|---|---|
|
|
ValidationLogic.sol |
|
Reserve is not active |
ValidationLogic.sol |
|
Reserve is frozen |
ValidationLogic.sol |
|
|
ValidationLogic.sol |
Source File Locations¶
contracts/protocol/pool/Pool.sol
contracts/protocol/libraries/logic/SupplyLogic.sol
contracts/protocol/libraries/logic/ValidationLogic.sol
contracts/protocol/tokenization/AToken.sol
contracts/protocol/libraries/logic/ReserveLogic.sol