- Deployment upgradable smart contracts
- Interacting with dapp - AAVE and test
Main feature / functionalities:
- Deposit / supply to Aave protocol from eoa or on behalf of someone
- Check the current value of users' collateral asset locked in Aave (in ETH)
- Withdraw / redeem from Aave protocol with interest (if any) from eoa or on behalf of someone
Requirements:
- Using Python version3.9+ + Brownie package & cli
- Using Ganache with Infura for forked Ethereum mainnet for local testing
- Aave protocol
- On macOS environment
- Install Brownie and other python packages (Please refer to the requirements.txt)
One way is simply as below:
pip install eth-brownie
pip install -r requirements.txt
Please install Ganache and sign up Infura to get a free API key (with 100k rate limit).
if you have npm, can simply install as below.
npm install -g ganache-cli
- Create a virtual environment for testing and then install brownie or anything you need in this environment
Can simply use below or virtualenv or other ways.
$ python3 -m venv .venv
$ source .venv/bin/activate
or
$ pip install virtualenv
$ virtualenv venv
We will run our local forked network to test out the smart contract instead of testnet here (as sometimes testnet code/ functions may not be 100% replicating how mainnet is functioning).
Be aware on the port. If you are using ganache UI, the port is 7545, while 8545 for using Ganache cli.
$ ganache --fork.url wss://mainnet.infura.io/ws/v3/<API-KEY>2. Using brownie console to create specific env using local forked network (so u can separate the environment)
$ brownie networks add Docker ganache-eth-fork host=http://127.0.0.1:8545 chainid=1337 explorer=https://api.etherscan.io/apiAnd you can jump into using brownie console to start playing around like getting accounts.
$ brownie console --network ganache-eth-forkIt is similar to using web3 library with JS (as below)
$ brownie initYou can use,
- Ganache's forked mainnet mode as the default network (ganache-eth-fork)
- add the dependencies you need
- unlock some address that you wanna try.
Below as example,
Can use remix (with local forked network) or remix (with metamask connected to local network) also. But in this exercise, more using brownie for all testing.
Main functions contracts/V2/Strategy.sol (as mentioned above as well):
- Deposit / supply to Aave protocol
- Check the current value of users' collateral asset locked in Aave (in ETH)
- Withdraw / redeem from Aave protocol with interest (if any)
Notes: Upgradable functionality is done in python proxy contract for now. Not Yet complete trying to use the external openzeppelin upgrade library (with Hardhat / Truffle only)
Just keep the original Strategy.sol contract which completed all above functions. Then, create another 2 separated smart contract to try out the upgradable contract (with the same functions but little adjustments to cater for upgradabilities.
contracts/V2/UpgradableStrategyV1.sol
contracts/V2/UpgradableStrategyV2.sol$ brownie run scripts/deploy_upgradable_strategy.py
$ brownie run scripts/update_upgradable_contract.pyThe first one (deploy_upgradable_strategy.py) will deploy UpgradableStrategyV1 as implementation contract.
Deploy a ProxyAdmin contract to be the admin of the proxy.
Deploy a TransparentUpgradeableProxy to be the proxy for the implementations.
Example as below:
Then, the upgrade script (update_upgradable_contract.py) will:
Deploy a new implementation contract UpgradableStrategyV2.
Upgrade the proxy to point to the new implementation contract, essentially upgrading your infrastructure.
Then it will call a function returnIsNewContract in UpgradableStrategyV2 to test out if it is the new contract.
Example as below:
Reference: https://github.com/brownie-mix/upgrades-mix
Next steps: using hardhat upgradable. It is more easy or manageable on upgrading the smart contracts.
6. Can check out the script named 'run_test.py' (under scripts directory) which is the linear test script to the smart contract
The script will try 3 things as below using the smart contract.
i) Supply 100 USDT(ERC20) tokens to Aave to earn interest
Pls find below two ways to get some "free" USDT for testing in local forked network.
- Using unlock account in brownie
- Using Uniswap to swap some
You can get unlock account using the function "get_unlocked_account" from scripts/utils.py. And then transfer USDT or other asset in this unlocked acc to the account you are using for testing:
There is function in scripts/utils.py for using uniswap router to do any swap. Here, we will swap some USDT to the account:
Swap Result:
Check the allowance before deposit:
Do the deposit:
ii) Read value of users collateral locked in Aave (in Eth)
Wait for a while for incurred interest. (Here example is 10 block for local forked network):
Then Can see the balance of USDT aToken (with incurred interest):
Get the price of underlying asset and balance of aToken on Aave using PriceOracle:
Get the balance of users collateral locked in Aave (in Eth):
iii) Redeem 100 USDT with interest from Aave after some time(blocks) have passed. Show USDT balance with accrued interest.
Passed some blocks above and withdraw all back to address.
$ brownie compileor
$ brownie compile <contract-file-name.sol>$ brownie run scripts/deploy_<contract_name>.pyOr, this with specifying network
$ brownie run scripts/deploy_<contract_name>.py --network ganache-eth-forkExample:
$ brownie run scripts/deploy_strategy.py --network ganache-eth-forkOr u can run detailed unit test in later steps.
$ brownie run scripts/run_test.pyResult Example:
Also, after compile you can also use brownie console to interact with deployment and testing transaction / functions.
Notes: I know for smart contract. Unit testing is very important. Need a bit more to cover / test out different things.
- Technical part:
- will mainly use brownie to do unit test on the smart contract which is basically using pytest framework.
- All test scripts will put under the tests directory with 'test_.py' or '_test.py'.
- Like using brownie fixture to re initialise the state for each test function with assertions, focus test on txn revert cases, parameterizing test for different input params, coverage with call trace when error and evaluate gas fee. Can add codes of require on smart contract to check also.
- Things to be considered (just summary):
- Try every path of the functions (including good or fail case) to make sure it always comes to an end. Basically check all path to the end outputs in the function
- Functions input params validation
- Step-by-step check in smart contract such as approve. Call trace.
- Try valid, or invalid inputs, normal cases, extreme cases.
- Beware of limits of infura using when testing local forked network.
- Avoid using for loop
- Think more about gas consumption
- Test security of smart contract.
To run the tests,
$ brownie test
or
$ brownie test --network ganache-eth-forkAlso, can run test on specific scripts,
$ brownie test tests/test_<name>.pyOr open console to debug in each fail test as below or simply open brownie console
$ brownie test tests/test_<name>.py --interactiveHere is some examples:
Also, u can test the gas consumption:
Within the console, you can do a more detailed debugging (like getting txn info, see the revert message or see the trace) as below.
Txn info:
Trace:
Try hardhat (instead of raw python way) to upgrade smart contract
- Easier and manageable (with UI also)
Package / Repo structure
- for different protocols and different chains
- For using external web3 tools (maybe separated repo)
https://docs.aave.com/developers/v/2.0/deployed-contracts/deployed-contracts
https://eth-brownie.readthedocs.io/en/stable/core-transactions.html
























