Degen Code

Degen Code

Aave — Part III

Supply, Withdraw, Borrow, Repay, and Liquidate

Nov 29, 2025
∙ Paid

The market is volatile and someone has to stand guard against bad debt. Will you answer the call, dear reader?

Continuing the Aave Series, let’s build to the ultimate goal of liquidating unhealthy debt positions.

We’ll cover the five primary functions of the Pool contract, ordered to build layered understanding until we reach our target: supply, withdraw, borrow, repay, and liquidationCall.

The lesson is structured as a series of tests using Ape Framework that interacts with each of the five contract functions, plus some accessory calls to other contracts that will support our efforts.

This entry assumes you are familiar with the Aave concepts and Pool contract covered in Part II, how to set up Ape Framework and how to use it for executing tests against a forked chain.

Aave

Aave

BowTiedDevil
·
Nov 25
Read full story
Introduction to Ape Framework

Introduction to Ape Framework

BowTiedDevil
·
October 8, 2023
Read full story
Testing With Ape Framework

Testing With Ape Framework

BowTiedDevil
·
February 28, 2024
Read full story

Project Setup

I will be working from a new Ape project in ~/code/aave.

Create a virtual environment:

btd@dev:~/code/aave$ uv venv --python 3.13
Using CPython 3.13.8
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate

Install Ape with the set of recommended plugins:

btd@dev:~/code/aave$ uv pip install eth-ape[recommended-plugins]
...
Installed 133 packages in 420ms
...

Create the Ape project using uv run ape init and edit the configuration so that the default network used by tests is a local fork from Ethereum mainnet using Foundry:

ape-config.yaml

name: Aave Pool

default_ecosystem: ethereum

ethereum:
  default_network: mainnet-fork
  mainnet:
    default_provider: node
  mainnet_fork:
    default_provider: foundry

node:  
  ethereum:
    mainnet:
      uri: http://localhost:8545 # EDIT THIS

plugins:
  - foundry
  - etherscan

Create a connection test to confirm that ape test launches the fork from the correct chain:

tests/test_aave_pool.py

from ape.managers.networks import NetworkManager
from ape_foundry.provider import FoundryForkProvider

def test_fork_is_launched(networks: NetworkManager):
    assert isinstance(networks.provider, FoundryForkProvider)
    assert networks.provider.chain_id == 1

Execute using uv run ape test and confirm that test passes. Now we can be confident that tests will run against the fork.

supply

The supply function from Pool.sol:

function supply(
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
) public virtual override {
    SupplyLogic.executeSupply(
        _reserves,
        _reservesList,
        _usersConfig[onBehalfOf],
        DataTypes.ExecuteSupplyParams({
        user: _msgSender(),
        asset: asset,
        interestRateStrategyAddress: RESERVE_INTEREST_RATE_STRATEGY,
        amount: amount,
        onBehalfOf: onBehalfOf,
        referralCode: referralCode
        })
    );
}

We need to provide four inputs:

  • asset — the supplied asset address

  • amount — how much we will supply

  • onBehalfOf — the user address to credit the supply

  • referralCode — a unique referral code (set to zero, the referral program is currently inactive)

We will be using WETH in various tests, so let’s make a fixture that creates a contract instance for use in other tests:

WETH_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"

@pytest.fixture
def weth():
    return Contract(WETH_ADDRESS)

And we’ll need a tester account with a balance:

@pytest.fixture
def tester_account(accounts):
    return accounts[0]

Create a test that asserts that tester_account has a balance, and that wrapping at the WETH contract works as expected:

def test_wrap_ether(weth: Contract, tester_account):
    starting_ether_balance = tester_account.balance
    starting_weth_balance = weth.balanceOf(tester_account)

    deposit_amount = 100 * 10**18
    assert starting_ether_balance > deposit_amount

    weth.deposit(value=deposit_amount, sender=tester_account)
    assert weth.balanceOf(tester_account) == (
        deposit_amount + starting_weth_balance
    )
    assert tester_account.balance < starting_ether_balance

Now we can write a test that wraps 100 Ether, approves it for use by the Pool contract, and supplies it:

AAVE_V3_POOL = “0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2”

@pytest.fixture
def aave_pool():
    return Contract(AAVE_V3_POOL)


def test_supply(aave_pool: Contract, weth: Contract, tester_account):
    deposit_amount = 100 * 10**18
    weth.deposit(value=deposit_amount, sender=tester_account)
    weth.approve(
        aave_pool.address, 
        deposit_amount, 
        sender=tester_account
    )

    starting_weth_balance = weth.balanceOf(tester_account)
    tx = aave_pool.supply(
        weth.address, 
        deposit_amount, 
        tester_account.address, 
        0, 
        sender=tester_account
    )
    assert weth.balanceOf(tester_account) == (
        starting_weth_balance - deposit_amount
    )

The test works as expected, but doesn’t tell us much. Add a simple tx.show_trace() at the end and run the test with -s to see the output:

Keep reading with a 7-day free trial

Subscribe to Degen Code to keep reading this post and get 7 days of free access to the full post archives.

Already a paid subscriber? Sign in
© 2025 BowTiedDevil · Privacy ∙ Terms ∙ Collection notice
Start your SubstackGet the app
Substack is the home for great culture