Aave — Part III
Supply, Withdraw, Borrow, Repay, and Liquidate
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.
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/activateInstall 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
- etherscanCreate 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 == 1Execute 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_balanceNow 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.



