# GHO Borrowing Flow
End-to-end execution flow for borrowing GHO (Aave's native stablecoin) from Aave V3.
> **Note**: GHO is Aave's native decentralized stablecoin. Unlike other assets, GHO is minted by the protocol rather than supplied by lenders. GHO borrowing uses a specialized VariableDebtToken with unique interest accrual mechanics.
## Quick Reference
| Aspect | Details |
|--------|---------|
| **Entry Point** | `Pool.borrow(GHO_UNDERLYING, amount, 2, referralCode, onBehalfOf)` |
| **Token Operations** | `VariableDebtTokenMainnetGHO.mint(user, onBehalfOf, amount, scaledAmount, index)` |
| **Key Transformations** | [Amount → Scaled Debt](../transformations/index.md#debt-token-transformations) |
| **State Changes** | `_userState[onBehalfOf].balance += scaledAmount` |
| **Events Emitted** | `Borrow`, `Mint`, `Transfer` |
---
## GHO Key Differences
GHO borrowing differs from regular variable debt borrowing:
1. **Native Minting**: GHO is minted by the protocol, not transferred from lenders
2. **Fixed Interest Rate**: Unlike variable debt which uses an interest rate strategy, GHO interest rate is set by Aave governance
3. **Virtual Accounting**: GHO uses virtual accounting where the underlying is "minted" rather than transferred
4. **Scaled Balances**: Uses the standard variable debt scaled balance approach (`amount.rayDiv(index)`)
---
## 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 critical stroke:#ff0000,stroke-width:3px
%% Entry point
Entry["Pool.borrow
GHO_UNDERLYING, amount,
interestRateMode=2,
onBehalfOf"] --> Execute["BorrowLogic
executeBorrow"]
subgraph StateUpdate ["1. State Updates"]
direction TB
UpdateState["ReserveLogic
updateState
Updates indexes"] --> Validate["ValidationLogic
validateBorrow
CRITICAL: Borrow cap"]
class Validate validation
end
subgraph HealthCheck ["2. Health Factor Checks"]
direction TB
CalcAccount["GenericLogic
calculateUserAccountData
Calculates HF, totalDebt"] --> IsolationMode{"Isolation
Mode Active?"}
IsolationMode -->|Yes| IsoDebt["STORAGE UPDATE
isolationModeTotalDebt += amount"]
class IsoDebt storage
IsolationMode -->|No| SkipIso[Continue]
IsoDebt --> IsoEvent["EMIT
IsolationModeTotalDebtUpdated"]
class IsoEvent event
end
subgraph TokenOps ["3. GHO Debt Token Operations"]
direction TB
IsoEvent --> GHOMint["VariableDebtTokenMainnetGHO
mint"]
SkipIso --> GHOMint
GHOMint --> Transform["TRANSFORMATION
scaledAmount =
amount.rayDiv(index)"]
class Transform transformation
Transform --> UpdateUser["TRANSFORMATION
Accrue interest
balanceIncrease =
scaledBalance.rayMul(index) -
scaledBalance.rayMul(previousIndex)"]
class UpdateUser transformation
UpdateUser --> StoreBalance["STORAGE UPDATE
_userState[onBehalfOf]
.balance +=
scaledAmount"]
class StoreBalance storage
StoreBalance --> UpdateIndex["STORAGE UPDATE
_userState[onBehalfOf]
.additionalData = index"]
class UpdateIndex storage
UpdateIndex --> FirstBorrow{"First
Borrow?"}
FirstBorrow -->|Yes| SetBorrow["UserConfig
setBorrowing"]
class SetBorrow storage
FirstBorrow -->|No| SkipFirst[Continue]
SetBorrow --> UpdateRates["ReserveLogic
updateInterestRates"]
SkipFirst --> UpdateRates
end
Execute --> StateUpdate
StateUpdate --> HealthCheck
HealthCheck --> TokenOps
%% Critical path: Mint GHO tokens
UpdateRates --> MintGHO["GHO Facilitator
mintUnderlying
CRITICAL: GHO is
minted, not transferred"]
class MintGHO transformation
MintGHO --> FinalEvent["EMIT
Borrow"]
class FinalEvent event
MintGHO --> MintEvent["EMIT
Mint"]
class MintEvent event
%% Error annotations
%% CRITICAL: Isolation mode debt ceiling enforced
%% CRITICAL: Health factor validated via calculateUserAccountData
%% CRITICAL: Borrow cap prevents protocol overflow
%% CRITICAL: GHO minting requires facilitator authorization
%% Link styles for critical paths
linkStyle 10 stroke:#ff0000,stroke-width:3px
linkStyle 18 stroke:#ff0000,stroke-width:3px
```
---
## Step-by-Step Execution
### 1. Entry Point
**File:** `contracts/protocol/pool/Pool.sol`
```solidity
function borrow(
address asset,
uint256 amount,
uint256 interestRateMode,
uint16 referralCode,
address onBehalfOf
) external virtual override {
BorrowLogic.executeBorrow(
_reserves,
_reservesList,
_eModeCategories,
_usersConfig[onBehalfOf],
DataTypes.ExecuteBorrowParams({
asset: asset, // GHO_UNDERLYING
user: msg.sender,
onBehalfOf: onBehalfOf,
amount: amount,
interestRateMode: DataTypes.InterestRateMode(interestRateMode), // VARIABLE (2)
referralCode: referralCode,
releaseUnderlying: true,
maxStableRateBorrowSizePercent: _maxStableRateBorrowSizePercent,
reservesCount: _reservesCount,
oracle: ADDRESSES_PROVIDER.getPriceOracle(),
userEModeCategory: _usersEModeCategory[onBehalfOf],
priceOracleSentinel: ADDRESSES_PROVIDER.getPriceOracleSentinel()
})
);
}
```
### 2. Execute Borrow
**File:** `contracts/protocol/libraries/logic/BorrowLogic.sol`
```solidity
function executeBorrow(
mapping(address => DataTypes.ReserveData) storage reserves,
mapping(uint256 => address) storage reservesList,
mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
DataTypes.UserConfigurationMap storage userConfig,
DataTypes.ExecuteBorrowParams memory params
) external {
DataTypes.ReserveData storage reserve = reserves[params.asset];
DataTypes.ReserveCache memory reserveCache = reserve.cache();
// Update state
reserve.updateState(reserveCache);
// Validate borrow
ValidationLogic.validateBorrow(
reserves,
reservesList,
eModeCategories,
DataTypes.ValidateBorrowParams({
reserveCache: reserveCache,
userConfig: userConfig,
asset: params.asset,
user: params.user,
amount: params.amount,
interestRateMode: params.interestRateMode,
maxStableLoanPercent: params.maxStableRateBorrowSizePercent,
reservesCount: params.reservesCount,
oracle: params.oracle,
userEModeCategory: params.userEModeCategory
})
);
// Calculate user account data
(
uint256 totalCollateralInBaseCurrency,
uint256 totalDebtInBaseCurrency,
uint256 avgLtv,
uint256 avgLiquidationThreshold,
uint256 healthFactor,
bool hasZeroLtvCollateral
) = GenericLogic.calculateUserAccountData(/* ... */);
// Handle isolation mode
if (hasZeroLtvCollateral) {
reserve.isolationModeTotalDebt += uint128(params.amount);
emit IsolationModeTotalDebtUpdated(
params.asset,
reserve.isolationModeTotalDebt
);
}
// Mint GHO debt tokens (Variable rate only - GHO doesn't support stable)
bool isFirstBorrowing = false;
// For GHO, interestRateMode is VARIABLE (2)
isFirstBorrowing = IVariableDebtToken(reserveCache.variableDebtTokenAddress)
.mint(
params.user,
params.onBehalfOf,
params.amount,
params.amount.rayDiv(reserveCache.nextVariableBorrowIndex), // scaledAmount
reserveCache.nextVariableBorrowIndex
);
// Update user configuration
if (isFirstBorrowing) {
userConfig.setBorrowing(reserve.id, true);
}
// Update interest rates
reserve.updateInterestRates(
reserveCache,
params.asset,
0, // liquidityAdded
params.amount // liquidityTaken (for GHO this triggers mint)
);
// For GHO: underlying is minted via facilitator, not transferred from Pool contract
// The AToken calls the GHO facilitator to mint GHO to the borrower
IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(
params.user,
params.amount
);
emit Borrow(
params.asset,
params.user,
params.onBehalfOf,
params.amount,
params.interestRateMode,
reserveCache.currVariableBorrowRate,
params.referralCode
);
}
```
### 3. GHO Variable Debt Token Mint
**File:** `contracts/gho/tokenization/VariableDebtTokenMainnetGHO.sol` (rev_6)
```solidity
function mint(
address user,
address onBehalfOf,
uint256 amount,
uint256 scaledAmount,
uint256 index
) external virtual override onlyPool returns (uint256) {
if (user != onBehalfOf) {
uint256 scaledBalanceOfOnBehalfOf = super.balanceOf(onBehalfOf);
// Handle credit delegation allowance
_decreaseBorrowAllowance(
onBehalfOf,
user,
amount,
(scaledBalanceOfOnBehalfOf + scaledAmount).getVTokenBalance(index) -
scaledBalanceOfOnBehalfOf.getVTokenBalance(index)
);
}
_mintScaled({
caller: user,
onBehalfOf: onBehalfOf,
amountScaled: scaledAmount,
index: index,
getTokenBalance: TokenMath.getVTokenBalance
});
return scaledTotalSupply();
}
```
### 4. Scaled Balance Mint Operation
**File:** `contracts/protocol/tokenization/base/ScaledBalanceTokenBase.sol`
```solidity
function _mintScaled(
address caller,
address onBehalfOf,
uint256 amountScaled,
uint256 index,
function(uint256, uint256) internal pure returns (uint256) getTokenBalance
) internal returns (bool) {
require(amountScaled != 0, Errors.INVALID_MINT_AMOUNT);
uint256 scaledBalance = super.balanceOf(onBehalfOf);
// Calculate interest accrued since last action
uint256 balanceIncrease = scaledBalance.rayMul(index) -
scaledBalance.rayMul(_userState[onBehalfOf].additionalData);
// Update user's last index
_userState[onBehalfOf].additionalData = index.toUint128();
// Mint the scaled tokens
_mint(onBehalfOf, amountScaled.toUint128());
uint256 amountToMint = amount + balanceIncrease;
emit Transfer(address(0), onBehalfOf, amountToMint);
emit Mint(caller, onBehalfOf, amountToMint, balanceIncrease, index);
return (scaledBalance == 0); // Returns true if this is first borrow
}
```
### 5. Balance Calculation (with Discount - Deprecated in v3.4)
**File:** `contracts/gho/tokenization/GhoVariableDebtToken.sol` (rev_1 - deprecated)
```solidity
function balanceOf(address user) public view virtual override returns (uint256) {
uint256 scaledBalance = super.balanceOf(user);
if (scaledBalance == 0) {
return 0;
}
uint256 index = POOL.getReserveNormalizedVariableDebt(_underlyingAsset);
uint256 previousIndex = _userState[user].additionalData;
uint256 balance = scaledBalance.rayMul(index);
if (index == previousIndex) {
return balance;
}
// DEPRECATED in v3.4: Discount mechanism
uint256 discountPercent = _ghoUserState[user].discountPercent;
if (discountPercent != 0) {
uint256 balanceIncrease = balance - scaledBalance.rayMul(previousIndex);
balance -= balanceIncrease.percentMul(discountPercent);
}
return balance;
}
```
### 6. Validation Checks
**File:** `contracts/protocol/libraries/logic/ValidationLogic.sol`
```solidity
function validateBorrow(
mapping(address => DataTypes.ReserveData) storage reserves,
mapping(uint256 => address) storage reservesList,
mapping(uint8 => DataTypes.EModeCategory) storage eModeCategories,
DataTypes.ValidateBorrowParams memory params
) internal view {
require(params.amount != 0, Errors.INVALID_AMOUNT);
// Check reserve is active and borrowing enabled
require(
params.reserveCache.reserveConfiguration.getActive(),
Errors.RESERVE_INACTIVE
);
require(
params.reserveCache.reserveConfiguration.getBorrowingEnabled(),
Errors.BORROWING_NOT_ENABLED
);
require(
!params.reserveCache.reserveConfiguration.getFrozen(),
Errors.RESERVE_FROZEN
);
// Validate oracle
require(
params.oracle != address(0),
Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED
);
// Get asset price
uint256 assetPrice = IPriceOracleGetter(params.oracle).getAssetPrice(
params.asset
);
require(assetPrice != 0, Errors.PRICE_ORACLE_SENTINEL_CHECK_FAILED);
// Calculate user account data
(
uint256 totalCollateralInBaseCurrency,
uint256 totalDebtInBaseCurrency,
uint256 avgLtv,
,
uint256 healthFactor,
bool hasZeroLtvCollateral
) = GenericLogic.calculateUserAccountData(/* ... */);
// Check borrow cap (applies to GHO too)
uint256 borrowCap = params.reserveCache.reserveConfiguration.getBorrowCap();
if (borrowCap != 0) {
uint256 totalDebt = IERC20(params.reserveCache.variableDebtTokenAddress)
.scaledTotalSupply()
.rayMul(params.reserveCache.nextVariableBorrowIndex);
uint256 scaledCap = borrowCap * 10**params.reserveCache.reserveConfiguration.getDecimals();
require(totalDebt + params.amount <= scaledCap, Errors.BORROW_CAP_EXCEEDED);
}
// Check isolation mode debt ceiling
if (hasZeroLtvCollateral) {
uint256 isolationModeDebtCeiling = params.reserveCache
.reserveConfiguration
.getDebtCeiling();
require(
params.reserveCache.isolationModeTotalDebt + params.amount <=
isolationModeDebtCeiling,
Errors.DEBT_CEILING_EXCEEDED
);
}
// Note: For GHO, there's no available liquidity check since GHO is minted
// The borrow cap check above is the primary constraint
}
```
---
## Amount Transformations
### GHO Borrow
```
User requests borrow (WAD decimals)
↓
amount = 1000 * 10^18 // 1000 GHO
↓
nextVariableBorrowIndex = 1.0003 * 10^27 // Current index
↓
scaledAmount = amount.rayDiv(nextVariableBorrowIndex)
= (1000 * 10^18 * 10^27) / (1.0003 * 10^27)
= 999.7 * 10^18 (approximate)
↓
_userState[onBehalfOf].balance += scaledAmount
↓
GHO is minted to user via facilitator
```
### Interest Accrual (View Function)
```
Current scaled balance: 1000 * 10^27 (scaled GHO)
Previous index: 1.05 * 10^27
Current index: 1.10 * 10^27
Calculation:
previousBalance = scaledBalance.rayMul(previousIndex)
= 1000e27 * 1.05e27 / 1e27
= 1050e18 (1050 GHO)
currentBalance = scaledBalance.rayMul(currentIndex)
= 1000e27 * 1.10e27 / 1e27
= 1100e18 (1100 GHO)
balanceIncrease = currentBalance - previousBalance
= 50e18 (50 GHO interest accrued)
Result: User's debt balance is 1100 GHO
```
### Key Differences from Regular Variable Debt
1. **No Actual Underlying Transfer**: GHO is minted by the facilitator, not transferred from the **Pool contract**
2. **Fixed Rate via Governance**: GHO interest rate is set by Aave governance, not by an algorithmic strategy
3. **Facilitator Model**: The AToken for GHO has special logic to mint GHO via the GHO facilitator contract
4. **Virtual Accounting**: GHO operates with virtual accounting enabled
---
## Event Details
### Borrow Event
```solidity
event Borrow(
address indexed reserve, // GHO_UNDERLYING address
address indexed user, // msg.sender
address indexed onBehalfOf, // Debt recipient
uint256 amount, // Amount borrowed (GHO)
DataTypes.InterestRateMode interestRateMode, // VARIABLE (2)
uint256 borrowRate, // Current GHO borrow rate (from governance)
uint16 referralCode // Referral code
);
```
### Mint Event (from VariableDebtToken)
```solidity
event Mint(
address indexed caller, // Address triggering mint (user)
address indexed onBehalfOf, // User receiving debt
uint256 value, // Amount minted (including accrued interest)
uint256 balanceIncrease, // Interest accrued since last action
uint256 index // Current variable debt index
);
```
### Transfer Event
```solidity
event Transfer(
address indexed from, // address(0) for mints
address indexed to, // User receiving debt tokens
uint256 value // Amount transferred (amount + interest)
);
```
### IsolationModeTotalDebtUpdated Event
Emitted when borrowing against isolated collateral.
```solidity
event IsolationModeTotalDebtUpdated(
address indexed asset, // GHO address
uint256 totalDebt // New total isolation mode debt
);
```
---
## Error Conditions
| Error | Condition | File |
|-------|-----------|------|
| `INVALID_AMOUNT` | `amount == 0` | ValidationLogic.sol |
| `RESERVE_INACTIVE` | GHO reserve is not active | ValidationLogic.sol |
| `BORROWING_NOT_ENABLED` | Borrowing is disabled for GHO | ValidationLogic.sol |
| `RESERVE_FROZEN` | GHO reserve is frozen | ValidationLogic.sol |
| `BORROW_CAP_EXCEEDED` | `totalDebt + amount > borrowCap` | ValidationLogic.sol |
| `DEBT_CEILING_EXCEEDED` | `isolationModeTotalDebt + amount > debtCeiling` | ValidationLogic.sol |
| `PRICE_ORACLE_SENTINEL_CHECK_FAILED` | Oracle price is 0 or sentinel check fails | ValidationLogic.sol |
| `INVALID_MINT_AMOUNT` | `scaledAmount == 0` | ScaledBalanceTokenBase.sol |
---
## Related Flows
- [Borrow Flow](./borrow.md) - General borrow flow for all assets
- [GHO Discount Flow](./gho_discount.md) - Deprecated discount mechanism for GHO (v3.4+)
- [Repay Flow](./repay.md) - Debt repayment including GHO
- [Liquidation Flow](./liquidation.md) - GHO liquidation handling
---
## Source File Locations
```
contracts/protocol/pool/Pool.sol
contracts/protocol/libraries/logic/BorrowLogic.sol
contracts/protocol/libraries/logic/ValidationLogic.sol
contracts/protocol/libraries/logic/GenericLogic.sol
contracts/gho/tokenization/VariableDebtTokenMainnetGHO.sol
contracts/protocol/tokenization/base/ScaledBalanceTokenBase.sol
contracts/protocol/tokenization/base/DebtTokenBase.sol
contracts/protocol/libraries/logic/ReserveLogic.sol
contracts/protocol/tokenization/AToken.sol
```
---
## GHO Contract Addresses (Ethereum Mainnet)
| Contract | Address |
|----------|---------|
| GHO_UNDERLYING | `0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f` |
| GHO_A_TOKEN | `0x00907f9921424583e7ffBfEdf84F92B7B2Be4977` |
| GHO_V_TOKEN | `0x786dBff3f1292ae8F92ea68Cf93c30b34B1ed04B` |
| GHO_ORACLE | `0xD110cac5d8682A3b045D5524a9903E031d70FCCd` |
| GHO_INTEREST_RATE_STRATEGY | `0x9ec6F08190DeA04A54f8Afc53Db96134e5E3FdFB` |
---
## Version History
| Version | Changes |
|---------|---------|
| rev_1-5 | Original GhoVariableDebtToken with discount mechanism via stkAAVE |
| rev_6+ | Discount mechanism deprecated (v3.4.0+), now extends VariableDebtToken directly |
> **DEPRECATED Feature**: The GHO discount mechanism (stkAAVE holders receiving interest discounts) was deprecated in Aave V3.4. The `updateDiscountDistribution` function is now a no-op in current implementations.