Source code for verbs_examples.agents.borrow_agent

"""
Agent that supplies and borrows tokens from an Aave pool
"""

from typing import List, Tuple

import numpy as np
import verbs


[docs] class BorrowAgent: """ Borrower agent who supplies and borrows tokens from an Aave pool """
[docs] def __init__( self, env, i: int, pool_implementation_abi: type, oracle_abi: type, mintable_erc20_abi: type, pool_address: bytes, oracle_address: bytes, token_a_address: bytes, token_b_address: bytes, activation_rate: float, ): """ Initialise the Borrower agent and create the corresponding account in the EVM. The agent stores the ABIs of the Aave contracts and the token contracts that they will be interacting with. ABIs are loaded using the function :py:func:`verbs.abi.load_abi`. Parameters ---------- env: verbs.types.Env Simulation environment i: int Agent index in the simulation pool_implementation_abi: type abi of the Aave v3 pool contract oracle_abi: type abi of the Aave oracle contract for collateral and debt tokens mintable_erc20_abi: type abi of ERC20 contract pool_address: bytes Addres of Aave v3 pool contract oracle_address: bytes Address of Aave oracle contract for collateral and debt tokens token_a_address: bytes Address of collateral token (usually the risky token) token_b_address: bytes Address of debt token (usually the less risky token) activation_rate: float Probability of taking an action (either provide collateral or borrow) at each step """ self.address = verbs.utils.int_to_address(i) env.create_account(self.address, int(1e30)) self.pool_implementation_abi = pool_implementation_abi self.pool_address = pool_address self.oracle_address = oracle_address self.oracle_abi = oracle_abi self.mintable_erc20_abi = mintable_erc20_abi # collateral token - risky asset self.token_a_address = token_a_address # debt token - stablecoin self.token_b_address = token_b_address self.decimals_token_b = mintable_erc20_abi.decimals.call( env, self.address, self.token_b_address, [] )[0][0] self.has_borrowed = False self.has_supplied = False assert ( 0 < activation_rate and activation_rate < 1 ), "activation_rate has to be between 0 and 1" self.activation_rate = activation_rate self.step = 0
[docs] def update(self, rng: np.random.Generator, env) -> List[verbs.types.Transaction]: """ Update the state of the agent and returns list of transactions according to their policy. Borrower agent can either supply collateral to the Aave pool or borrow debt assets. Parameters ---------- rng: numpy.random.Generator Numpy random generator, used for any random sampling to ensure determinism of the simulation. env: verbs.types.Env Network/EVM that the simulation interacts with. Returns ------- list List of transactions to be processed in the next block of the simulation. This can be an empty list if the agent is not submitting any transactions. """ self.step += 1 if rng.random() < self.activation_rate: if not self.has_supplied: supply_tx = self.pool_implementation_abi.supply.transaction( self.address, self.pool_address, [self.token_a_address, 10**18, self.address, 0], ) self.has_supplied = True return [supply_tx] elif not self.has_borrowed: user_data = self.pool_implementation_abi.getUserAccountData.call( env, self.address, self.pool_address, [self.address] )[0] # available to borrow in base currency (in Aave, base currency is USD) available_borrow_base = user_data[2] # we convert the available to borrow to borrow asset units borrow_asset_price = self.oracle_abi.getAssetPrice.call( env, self.address, self.oracle_address, [self.token_b_address] )[0][0] coef = 10 ** (self.decimals_token_b - 4) u = rng.integers(low=7000, high=9300) available_borrow = int( coef * available_borrow_base * u / borrow_asset_price ) if available_borrow > 0: borrow_tx = self.pool_implementation_abi.borrow.transaction( self.address, self.pool_address, [self.token_b_address, available_borrow, 2, 0, self.address], ) self.has_borrowed = True return [borrow_tx] else: return [] else: return [] else: return []
[docs] def record(self, env) -> Tuple[int, float, float, float]: """ Record the state of the agent This method is called at the end of each step for all agents. It should return any data to be recorded over the course of the simulation. Parameters ---------- env: verbs.types.Env Network/EVM that the simulation interacts with. Returns ------- tuple[int, float, float, float] Tuple containing: - Step of the simulation. - Health factor of the borrower's position at the current step. - Collateral value of the borrower's position in the base currency In Aave the base currency is USD and it has 8 decimal places - Debt asset value of the borrower's position in the base currency """ user_data = self.pool_implementation_abi.getUserAccountData.call( env, self.address, self.pool_address, [self.address] )[0] health_factor = user_data[5] / 10**18 health_factor = min(health_factor, 100) collateral_base = user_data[0] / 10**8 debt_base = user_data[1] / 10**8 return self.step, health_factor, collateral_base, debt_base