Smart Contract Development
  • Introduction
    • What is a Transaction
    • Accounts and Signing
    • What is a smart contract
  • Learning Solidity
    • Introduction
    • Module 1
      • Variable Types
      • Variable Scope: State & Local variables
      • Global variables
      • Functions
        • View and Pure
        • Shadowing in Fuctions
      • Mapping
      • Require
      • Events
    • Project #1: Simple Registry
    • Module 2
      • Constructor
      • Data Location: Value & Reference
      • Interface
      • Import
        • Importing in Foundry
      • Inheritance
      • ERC-20
      • Checks-effect-interaction pattern
    • Project #2: Basic Vault
    • Module 3
      • Payable
      • Receive
      • Fallback
      • Returns
    • Project #3: ERC20+ETH Wrapper
    • Module 4
      • Immutable and Constant
      • Fixed-point Math
      • Abstract contracts
      • ERC-4626
      • Modifier + Inheritance +Ownable
      • Type
    • Project #4: Fractional Wrapper
    • Module 5
      • If-else
      • Libraries
        • TransferHelper
      • Chainlink Oracle
    • Project #5: Collateralized Vault
  • Compendium
    • Solidity Basics
      • Variable Types
      • Value Types
        • address
        • enum
      • Reference Types
        • strings
        • mappings
        • struct
        • Arrays
        • Multi-Dimensional arrays
      • Global Objects
      • Functions
        • Function types
        • Constructor Function
        • Transaction vs Call
        • Require, Revert, Assert
      • Function signature + selectors
      • Payable
        • Payable + withdraw
        • msg.value & payable functions
      • Receive
      • Fallback function (sol v 0.8)
        • Fallback function (sol v 0.6)
      • call, staticcall, delegatecall
    • Return & Events
    • Control Variable Visibility
    • Local Variables (Storage v Memory)
    • Data Location and Assignment Behaviors
    • Modifiers & Inheritance & Import
      • import styles
    • Interface & Abstract Contracts
    • ABI & Debugging
    • Libraries
    • Conditional(ternary) operators
    • Smart Contract Life-cycle
      • Pausing Smart Contracts
      • Destroying Smart Contracts
    • Merkle Trie and MPT
    • Merkle Tree Airdrop
  • Try & catch
  • Ethereum Signatures
  • EVM, Storage, Opcodes
    • EVM
    • Wei, Ether, Gas
    • Storage
    • ByteCode and Opcodes
    • Transaction costs & Execution costs
  • Reading txn input data
  • Data Representation
  • Yul
    • Yul
      • Intro
      • Basic operations
      • Storage Slots
      • Storage of Arrays and Mappings
      • Memory Operations
      • Memory: how solidity uses memory
      • Memory: Return, Require, Tuples and Keccak256
      • Memory: Logs and Events
      • Inter-contract calls
      • calldata
      • free memory pointer
    • Yul Exercises
      • read state variable
      • read mapping
      • iterate Array, Return Sum
    • memory-safe
  • Upgradable Contracts
    • Upgradability & Proxies
    • UUPS Example
    • Minimal Proxy Example
    • TPP Example
    • 🚧Diamond
      • On Storage
  • Gas Opt
    • Block Limit
    • gasLimit & min cost
    • Solidity Optimiser
    • Memory v calldata
    • Memory caching vs direct storage vs pointers
    • < vs <=
    • reverting early
    • X && Y, ||
    • constant and immutable
    • caching sload into mload
    • Syntactic Sugar
    • using unchecked w/o require
    • Compact Strings
    • Calling a view function
    • Custom errors over require
    • usage of this.
      • multiple address(this)
  • ERCs & EIPs
    • ERC-20.sol
      • Core functions
      • transfer()
      • transferFrom()
      • TLDR transfer vs transferFrom
    • Landing
      • ERC721.sol
      • EIP-721
        • LooksRare
        • Page 1
      • ERC-1271
      • EIP-2981
      • ERC-165
      • EIP-1167: Minimal Proxy Contract
    • VRFConsumerBase
    • UniswapV2Library
  • Yield Mentorship 2022
    • Projects
      • #1 Simple Registry
      • #2 Basic Vault
      • #3 ERC20+ETH Wrapper
        • setFailTransferTrue
      • #4 Fractional Wrapper
      • #5 Collateralized Vault
        • Process
        • Vault.sol
        • Testing
        • Chainlink Oracles
        • Pricing + Decimal scaling
        • Refactor for Simplicity
      • #9 Flash Loan Vault
        • Implementing ERC3156
        • Full code for lender
        • Ex-rate calculation
    • State Inheritance Testing
    • Testing w/ Mocks
    • Yield Style Guide
    • Github Actions
    • TransferHelper.sol
    • math logic + internal fn
    • Interfaces: IERC20
  • Foundry
    • Overview
    • Importing Contracts
    • Testing
      • stdError.arithmeticError
      • assume vs bound
      • Traces
      • label & console2
      • std-storage
  • Smart Contract Security
    • Damn Vulnerable Defi
      • 1. Unstoppable
      • 2. Naive receiver
      • 3. Truster
      • 4. Side Entrance
      • 5. The Rewarder
      • 6. Selfie
      • 7. Compromised
      • 8. Puppet
      • 9. Puppet V2
      • 10 - Free Rider
    • Merkle Tree: shortened proof attack
  • Fixed-Point Math
    • AMM Math
  • Solidity Patterns
    • checks-effects-interactions pattern
    • Router // batch
    • claimDelegate: stack unique owners
    • claimDelegate: cache previous user
  • Array: dup/ascending check
  • Deployment
    • Behind the Scenes
    • Interacting with External Contracts
    • Logging, Events, Solidity, Bloom Filter
  • Misc
    • Mnemonic Phrases
    • Bidul Ideas
  • Archive
    • Brownie Framework
      • Brownie basics
        • storing wallets in .env
        • Deployment to ganache
        • Interacting with contract
        • Unit Testing
        • Testnet deployment
        • Interacting w/ deployed contract
        • Brownie console
      • Brownie Advanced
        • Dependencies: import contracts
        • helpful_scripts.py
        • verify and publish
        • Forking and Mocking
        • Mocking
        • Forking
      • Testing
      • Scripts Framework
        • deploy.py
        • get_accounts
        • deploy_mocks()
        • fund_with_<token>()
      • Brownie Networks
    • Brownie Projects
      • SharedWallet
        • Multiple Beneficiaries
        • Common Code Contract
        • Adding Events
        • Renounce Ownership
        • Separate Files
      • Supply Chain
        • ItemManager()
        • Adding Events
        • Adding unique address to each item
      • Lottery
      • Aave - Lending and Borrowing
        • Approve & Deposit
        • Borrow
      • NFT
      • Advanced Collectible
        • adv_deploy() + Testing
        • Create Metadata
        • Setting the TokenURI
    • node npm
    • Ganache
    • Truffle
    • Remix
    • Installing Env
Powered by GitBook
On this page
  • ILendingPoolAddressesProvider
  • Lending Pool
  • Code thus far
  • Approve
  • approve_erc20()
  • Deposit
  1. Archive
  2. Brownie Projects
  3. Aave - Lending and Borrowing

Approve & Deposit

PreviousAave - Lending and BorrowingNextBorrow

Last updated 3 years ago

Aave LendingPool contract is the main contract of the protocol. It exposes all the user-oriented actions that can be invoked using either Solidity or web3 libraries.

  • LendingPool contracts are upgradable. This means that their addresses may change in the future.

  • To prevent third party DApps from falling behind, Aave provides a LendingPoolAddressesProvider contract that will never be upgraded.

  • This is used to retrieve the latest LendingPool. As soon as we have the latest LendingPool, we can start depositing.

To interact with ILendingPoolAddressesProvider contract, we will need its address and ABI.

ILendingPoolAddressesProvider

ABI

To obtain the ABI, we will use an interface.

Since we only require a select few functions: deposit, withdraw and so forth, we can create our own interface.

  • create ILendingPoolAddressesProvider.sol in interfaces folder.

  • either create your own interface by defining the functions (refer to etherscan or github)

  • or, just copy aave's interfaces

Address

Addresses Provider -> Deployed Contracts Section

Add these to brownie-config.yaml

def get_lendingpool():
    # create lending_pool_addressess_provider contract object
    lending_pool_addressess_provider = interface.ILendingPoolAddressesProvider(config["networks"][network.show_active()]["lending_pool_addressess_provider"])
    lending_pool = lending_pool_addressess_provider.getLendingPool()
    return lending_pool

Lending Pool

ABI -> interface

  • Change import path from local directories to github

Original:

import {ILendingPoolAddressesProvider} from './ILendingPoolAddressesProvider.sol';
import {DataTypes} from './DataTypes.sol';

Modified:

import {ILendingPoolAddressesProvider} from '@aave/contract/interfaces/ILendingPoolAddressesProvider.sol';
import {DataTypes} from '@aave/contract/protocol/libraries/types/DataTypes.sol';

compile after, to check if interfaces are correct.

dependencies:
  - aave/protocol-v2@1.0.1

compiler:
  solc:
    remappings:
      - '@aave=aave/protocol-v2@1.0.1'

Address

We will get the address by calling getLendingPool() from the ILendingPoolAddressesProvider

def get_lendingpool():
    # create lending_pool_addressess_provider contract object
    lending_pool_addressess_provider = interface.ILendingPoolAddressesProvider(config["networks"][network.show_active()]["lending_pool_addressess_provider"])
    #get lending pool address
    lending_pool = lending_pool_addressess_provider.getLendingPool()
    return lending_pool

Code thus far

from code import interact
from distutils.command.config import config
from scripts.helpful_scripts import get_account
from brownie import network, config, interface
from scripts.get_weth import get_weth

def main():
    account = get_account()
    deposit_token = config["networks"][network.show_active()]["weth_token"]
    # if no WETH, get_weth()
    # local mainnet fork can use dummy acocunts to get WETH
    # since local mainnet fork, can use dummy accounts. if actual mainnet/testnet then use private key account.
    if network.show_active() in ["mainnet-fork"]:
        get_weth()

    # Get lending pool contract
    lending_pool_address = get_lendingpool()
    lending_pool = interface.ILendingPool(lending_pool_address)
    print(f"....Lending pool contract: {lending_pool_address}....")

    # Approve sending our ERC20(WETH) tokens
    approve_erc20(deposit_amount, lending_pool.address, deposit_token, account)

      
def get_lendingpool():
    # create lending_pool_addressess_provider contract object
    lending_pool_addressess_provider = interface.ILendingPoolAddressesProvider(config["networks"][network.show_active()]["lending_pool_addressess_provider"])
    #get lending pool address
    lending_pool = lending_pool_addressess_provider.getLendingPool()
    return lending_pool
from brownie import network,accounts,config

LOCAL_BLOCKCHAIN_ENV = ["development", "ganache-local"]
FORKED_LOCAL_ENV = ["mainnet-fork", "mainnet-fork-dev"]

def get_account(index=None,id=None):
    # accounts[0]  -- ganache accounts
    # accounts.add("env")  -- private key from env file ->  accounts.add(config["wallets"]["wallet1"])
    # accounts.load("id") -- load from brownie accounts list 
    if index:
        # if index was passed return ganache account
        return accounts[index]
    
    if id:
        return accounts.load(id)

    if network.show_active() in LOCAL_BLOCKCHAIN_ENV or network.show_active() in FORKED_LOCAL_ENV:
        return accounts[0]                                  #use ganache generated account.  
      
    else: #look in config.yaml
        return accounts.add(config["wallets"]["wallet1"])  
dependencies:
  - aave/protocol-v2@1.0.1

compiler:
  solc:
    remappings:
      - '@aave=aave/protocol-v2@1.0.1'

dotenv: .env

networks:
  default: development

  rinkeby:
    weth_token: '0xc778417E063141139Fce010982780140Aa0cD5Ab'  #WEthGateway contract
    lending_pool_addressess_provider: '0x88757f2f99175387ab4c6a4b3067c77a695b0349'
    verify: True

  kovan:
    weth_token: '0xd0A1E359811322d97991E03f863a0C30C2cF029C'  #WEthGateway contract
    lending_pool_addressess_provider: '0x88757f2f99175387ab4c6a4b3067c77a695b0349'
    verify: True
  
  mainnet-fork: #development env, fork uses live mainnet addresses 
    weth_token: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'  #WEthGateway contract
    lending_pool_addressess_provider: '0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5'

  mainnet: # for production
    weth_token: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'  #WEthGateway contract

wallets:
  wallet1: ${PRIVATE_KEY

Approve

Before we can deposit we must approve Aave's contract to use our WETH tokens. This is done with the approve function.

Because DApps (decentralized applications) use smart contracts to execute transactions, you must give permission for the smart contract to transfer up to a certain amount of your token (allowance).

To reiterate, transacting with ERC-20 tokens is a 2-step process:

  1. Approval of token allowance

  2. Submission of transaction

approve_erc20()

def approve_erc20(amount,spender, erc20_address, account):
    print("....Approving ERC20 token...")
    # get ERC20 interface: https://github.com/PatrickAlphaC/aave_brownie_py_freecode/tree/main/interfaces
    erc20 = interface.IERC20(erc20_address)

    # approve(address spender, uint256 value)
    tx = erc20.approve(spender,amount,{"from": account})
    tx.wait(1)
    print("....Approved....")
    return tx

In a generic implementation, we would create IERC20.sol in our interfaces folder, and pass the ERC20 token contract into the interface as above. Subsequently, we call on the token contract to approve setting our allowance for another 3rd party contract (via erc20.approve).

from scripts.helpful_scripts import get_account
from brownie import network, config, interface
from scripts.get_weth import get_weth
from web3 import Web3

#0.1 ETH - 0.1*(10**18)
deposit_amount = Web3.toWei(0.1, "ether")  

def get_lendingpool():
    # create lending_pool_addressess_provider contract object
    lending_pool_addressess_provider = interface.ILendingPoolAddressesProvider(config["networks"][network.show_active()]["lending_pool_addressess_provider"])
    #get lending pool address
    lending_pool = lending_pool_addressess_provider.getLendingPool()
    return lending_pool

def approve_erc20(amount,spender, erc20_address, account):
    print("....Approving ERC20 token...")
    # get ERC20 interface: https://github.com/PatrickAlphaC/aave_brownie_py_freecode/tree/main/interfaces
    # approve(address spender, uint256 value)
    erc20 = interface.IERC20(erc20_address)
    tx = erc20.approve(spender,amount,{"from": account})
    tx.wait(1)
    print("....Approved....")
    return tx



def main():
    account = get_account()
    deposit_token = config["networks"][network.show_active()]["weth_token"]
    # if no WETH, get_weth()
    # local mainnet fork can use dummy acocunts to get WETH
    # since local mainnet fork, can use dummy accounts. if actual mainnet/testnet then use private key account.
    if network.show_active() in ["mainnet-fork"]:
        get_weth()

    # Get lending pool contract
    lending_pool_address = get_lendingpool()
    lending_pool = interface.ILendingPool(lending_pool_address)
    print(f"....Lending pool contract: {lending_pool_address}....")

    # Approve sending our ERC20(WETH) tokens
    approve_erc20(deposit_amount, lending_pool.address, deposit_token, account)

Added to main():

  • approve_erc20(deposit_amount, lending_pool.address, deposit_token, account)

  • deposit_amount as global variable

since we are always runnning on mainnet-fork, set default = mainnet-fork in brownie-config.yaml, under the networks section.

Deposit

When depositing, the LendingPool contract must haveallowance()to spend funds on behalf ofmsg.sender for at-leastamount for the asset being deposited. This can be done via the standard ERC20 approve() method.

The referral program is currently inactive and you can pass0 as thereferralCode.

In future for referral code to be active again, a governance proposal, with the list of unique referral codes for various integration must be passed via governance.

We will the below to main():

## Deposit: deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)
# referralCode is deprecated - pass a 0 as parameter
print("....Depositing....")
tx = lending_pool.deposit(deposit_token_address, deposit_amount, account.address,0, {"from": account})
tx.wait(1) # wait one block
print("....Deposited!....")
from scripts.helpful_scripts import get_account
from brownie import network, config, interface
from scripts.get_weth import get_weth
from web3 import Web3

#0.1 ETH - 0.1*(10**18)
deposit_amount = Web3.toWei(0.1, "ether")  

def get_lendingpool():
    # create lending_pool_addressess_provider contract object
    lending_pool_addressess_provider = interface.ILendingPoolAddressesProvider(config["networks"][network.show_active()]["lending_pool_addressess_provider"])
    #get lending pool address
    lending_pool = lending_pool_addressess_provider.getLendingPool()
    return lending_pool

def approve_erc20(amount,spender, erc20_address, account):
    print("....Approving ERC20 token...")
    # get ERC20 interface: https://github.com/PatrickAlphaC/aave_brownie_py_freecode/tree/main/interfaces
    # approve(address spender, uint256 value)
    erc20 = interface.IERC20(erc20_address)
    tx = erc20.approve(spender,amount,{"from": account})
    tx.wait(1)
    print("....Approved....")
    return tx


def main():
    account = get_account()
    deposit_token_address = config["networks"][network.show_active()]["weth_token"]
    # if no WETH, get_weth()
    # local mainnet fork can use dummy acocunts to get WETH
    # since local mainnet fork, can use dummy accounts. if actual mainnet/testnet then use private key account.
    if network.show_active() in ["mainnet-fork"]:
        get_weth()

    # Get lending pool contract
    lending_pool_address = get_lendingpool()
    lending_pool = interface.ILendingPool(lending_pool_address)
    print(f"....Lending pool contract: {lending_pool_address}....")

    # Approve sending our ERC20(WETH) tokens
    approve_erc20(deposit_amount, lending_pool.address, deposit_token_address, account)

    ## Deposit: deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)
    # referralCode is deprecated - just pass a 0 as parameter
    print("....Depositing....")
    tx = lending_pool.deposit(deposit_token_address, deposit_amount, account.address,0, {"from": account})
    tx.wait(1) # wait one block
    print("....Deposited!....")

https://docs.aave.com/developers/v/2.0/deployed-contracts/deployed-contracts
https://docs.aave.com/developers/v/2.0/the-core-protocol/lendingpool/ilendingpool
https://docs.aave.com/developers/v/2.0/the-core-protocol/lendingpool
ERC20.sol