AAVE Source Code Analysis: Deployment and Initialization

Summary
Because AAVE contains many contracts, its deployment process is also fairly complex and is split into multiple Hardhat tasks.

Because AAVE contains many contracts, its deployment process is correspondingly complex. AAVE splits deployment into seven tasks, each implemented as a hardhat task, and then uses a full task to orchestrate deployment and initialization end to end.

Full task

Inside package.json, the project defines many deployment commands. The following one deploys to mainnet and verifies contracts:

1
    "aave:main:full:migration": "npm run compile && npm run hardhat:main -- aave:mainnet --verify",

Here, aave:mainnet is the task name, and it is defined in tasks/migrations/aave.mainnet.ts. The main body looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    // 1 Deploy address-provider-related contracts, POOL_NAME is Aave
    await DRE.run('full:deploy-address-provider', { pool: POOL_NAME, skipRegistry });

    // 2 Deploy lending-pool-related contracts
    await DRE.run('full:deploy-lending-pool', { pool: POOL_NAME });

    // 3 Deploy oracle-related contracts
    await DRE.run('full:deploy-oracles', { pool: POOL_NAME });

    // 4 Deploy data-provider-related contracts
    await DRE.run('full:data-provider', { pool: POOL_NAME });

    // 5 Deploy WETH gateway
    await DRE.run('full-deploy-weth-gateway', { pool: POOL_NAME });

    // 6 Initialize related contracts
    await DRE.run('full:initialize-lending-pool', { pool: POOL_NAME });

    if (verify) {
      printContracts();
      // 7 Verify contracts
      await DRE.run('verify:general', { all: true, pool: POOL_NAME });

      // 8 Verify aToken and debt token contracts
      await DRE.run('verify:tokens', { pool: POOL_NAME });
    }

    if (usingTenderly()) {
    }
    // Print deployed contract info
    printContracts();

Now let’s look at what each task does.

1. full:deploy-address-provider

Task name: full:deploy-address-provider

Task file: tasks/full/1_address_provider.ts

Purpose: deploy the address registry and address provider

Execution flow:

  • deploy LendingPoolAddressesProvider with marketId such as Aave or AMM
  • run the add-market-to-registry task
    • if the registry does not exist, deploy LendingPoolAddressesProviderRegistry
    • call registerAddressesProvider
  • call setPoolAdmin
  • call setEmergencyAdmin

2. full:deploy-lending-pool

Task name: full:deploy-lending-pool

Task file: tasks/full/2_lending_pool.ts

Purpose: deploy the lending pool and related contracts

Execution flow:

  • if LendingPool is not yet deployed, deploy the related libraries and the LendingPool implementation, then initialize it
  • use the address provider created in step 1 to deploy a proxy for LendingPool, and point that proxy to the LendingPool implementation
  • deploy LendingPoolConfigurator
  • call addressesProvider.setLendingPoolConfiguratorImpl, which deploys a proxy for LendingPoolConfigurator and stores that address in the provider
  • deploy StableAndVariableTokensHelper
  • deploy ATokensAndRatesHelper

3. full:deploy-oracles

Task name: full:deploy-oracles

Task file: tasks/full/3_oracles.ts

Purpose: deploy oracle contracts

Execution flow:

  • if AaveOracle does not exist, deploy it and call setAssetSources
  • if LendingRateOracle does not exist, deploy it and initialize it through setInitialMarketRatesInRatesOracleByHelper
  • set addressesProvider.setPriceOracle to the deployed AaveOracle
  • set addressesProvider.setLendingRateOracle to the deployed LendingRateOracle

4. full:data-provider

Task name: full:data-provider

Task file: tasks/full/4_data-provider.ts

Purpose: deploy data-provider contracts

Execution flow:

  • deploy AaveProtocolDataProvider

5. WETH gateway contract

Task name: full-deploy-weth-gateway

Task file: tasks/full/5-deploy-wethGateWay.ts

Purpose: deploy WETHGateway

Execution flow:

  • deploy WETHGateway

6. Initialize contracts

Task name: full:initialize-lending-pool

Task file: tasks/full/6-initialize.ts

Purpose: deploy contracts, set proxies, and initialize the system

Execution flow:

  • initReservesByHelper: for each token in the market, do the following:
    • deploy AToken
    • deploy StableDebtToken
    • deploy VariableDebtToken
    • deploy DefaultReserveInterestRateStrategy
    • call LendingPoolConfigurator to deploy proxies for aToken and debt tokens, then initialize them
  • configureReservesByHelper
  • if LendingPoolCollateralManager does not exist, deploy it
  • set the LendingPoolCollateralManager address in addressesProvider
  • deploy WalletBalanceProvider, a read-only helper that exposes aggregated data
  • configure the WETHGateway

7. Contract verification

Verification is mostly done through hardhat-etherscan.

AToken / debtToken

AToken, VariableDebtToken, and StableDebtToken all run behind proxies. Their proxy pattern is somewhat similar to OpenZeppelin’s transparent proxy model.

LendingPoolConfigurator is the creator and manager of those aToken and debt-token proxies, and is also responsible for initializing and upgrading them.

At the same time, LendingPoolConfigurator itself sits behind a proxy created by AddressProvider, and upgrades are managed through AddressProvider.