Testing and Simulating UniLend V2 || Reporting a Price-Oracle Bug

BUILDBEAR // TUTORIALS

Testing and Simulating UniLend V2 || Reporting a Price-Oracle Bug

BuildBear Logo

Bug Found 🐛: We discovered a bug where the prices of assets fetched from the UnilendV2oracle contract using the getChainLinkAssetPrice function are returned as Zero.

What is UniLend?

UniLend Protocol is a permissionless decentralized money market protocol with lending and borrowing service through smart contracts. UniLend enables users to utilize their cryptocurrencies by supplying collateral to the network that may be borrowed by pledging over-collateralized cryptocurrencies. This creates a secure lending environment where the lender receives a compounded interest rate annually (APY) paid by the borrowers.

In this article, we are going to be using the latest version UniLend V2. UniLend is currently live on the Sepolia and zkEVM Testnet.

Playing around with UniLendV2

We played, we liked, we wrote —> that is what this article is; it is our review of the UniLendV2.

Why UniLend? UniLend is completely market-driven. This means there are multiple scenarios under which you need to test/simulate how UniLend Protocol functions. This makes UniLend ideal for BuildBear to test it.

Please note, for a thorough review of UniLend, it is imperative that we do a test that pretty much covers all the types of transactions that you will execute on UniLend. That is the (a) Deposit and (b) Borrow transactions, (c) the payback, etc etc. However, the MOST important of all is to be able to see the performance of the dApp with the manipulation of the time. This is WHAT THIS TEST IS ABOUT! It is way larger than you think. We will NOT just lend and borrow; we will manipulate the system to be able to do an edge to edge testing.

We could simply test on the Sepolia Testnet, but then:

  1. What about time? Do I wait 1 year to test results? and if I do, will this review be useful at all?
  2. Do I have the ability to change the Price of the Token? (No).

You get the drill. It's basically a lock-out. Cannot use Sepolia Testnet.

Could we do the local hardhat fork or anvil fork? Without a doubt. But then, we would need to take some pain in impersonating some accounts to get some ERC20 Tokens; do some more hacks to be able to “see” our transactions and the outcome of each of those and whatnot. Hence, {shameless self-sell} we utilized BuildBear.io to create a Private Testnet of the Sepolia Testnet. This allowed us to effectively utilize the UniLendV2 front end.

NOTE: Since we are (obviously) a premium user of BuildBear, the chainId that we get from BuildBear is the same chainID that we are forking. Eg, the private Testnet that we created on BuildBear (Name: batman)[ We got a custom name] also has the chainId 11155111, since this Testnet is a fork of the Sepolia.

Set-up before the testing: Using our Private Testnet Faucet to mint 20K DAI

Untitled design (7) (1).gif

Transaction 1: Lending 20K DAI into the UniLendV2 Protocol

Untitled (41).png

Quick Side Note:

  1. Lend APY is the combined effect of all supply and borrow positions on net worth, including incentives. It is possible to have a negative net APY if debt APY is higher than supply APY.
  2. Health factor Safety of your deposited collateral against the borrowed assets and their underlying value. If the health factor goes below 1, the liquidation of your collateral might be triggered.

Transaction 2: Borrowing 5K Matic

We borrowed 5K Matic using the 20K DAI deposited as collateral. At borrow APY of 2.100% With this borrowing and the math used by UniLendV2 Protocol, our Health Factor was 3.15 after the borrowing.

Untitled (42).png

Transaction 3: Lending 5k UFT Tokens

Transaction 4: Lending 5k USDC Tokens

The REAL Test

Advancing the time for a year (Using the BuildBear utility tools we were able to Advance time by a year as shown below).

Untitled design (9) (1).gif

Executing different scenarios

In order to facilitate this, it is crucial to have the following abilities:

  1. The ability to modify the price of assets.
  2. The ability to save the state of the Testnet at different points of testing. This eliminates the need to create multiple Testnets for executing different scenarios. To accomplish this, we have utilized the BuildBear utility tool, as demonstrated below, to create a snapshot of the Testnet at the required points.
Untitled design (8) (1).gif

Case 1: The price of Assets Remains the same || The simplest of the test

Repaying the Matic we have Borrowed with interest.

Screenshot (982).png

Redeeming USDC, UFT, and DAI Tokens we received from our lending positions.

Screenshot (984).png
  1. Case 2: The Price of Assets Increase
    To change the price of Assets we Impersonated the owner account of UnilendV2oracle and Altered the USDC oracle source address to [0xf46ba822fFb594a2fFcaeCa9994075FD40529059] to change the price of the USDC Token [0x6f14C02Fc1F78322cFd7d707aB90f18baD3B54f5].
Untitled (47).png

We encountered a Bug 🐛

Then while fetching the price of the USDC [0x6f14C02Fc1F78322cFd7d707aB90f18baD3B54f5] Using getChainLinkAssestPrice the function we encountered the Call_exception error. As shown below.

Untitled (51).png

After further inspection, we also found the price of the other tokens that are listed on UniLendV2 are fetching as Zero. (Weth Token Address-0xeD43f81C17976372Fcb5786Dd214572e7dbB92c7)

Untitled (48).png

When we passed the Source Address of the USDC [ 0xf46ba822fFb594a2fFcaeCa9994075FD40529059] into the getChainLinkAssestPrice we got the price As Follows.

Untitled (53).png

Testing the Issue in-depth

For the purpose of testing the issue, we have used two contracts (both instances of AggregatorV3Interface) as assets and sources and vice versa.

The issue seems to be fixed in GitHub, and this is the code

function getChainLinkAssetPrice(address asset) public view returns (int256 price) {
    AggregatorV3Interface source = assetsOracles[asset];
    if(address(source) != address(0)){
        price = getLatestPrice(source);
    }
}

But while testing we got behaviour similar to

function getChainLinkAssetPrice(address asset) public view returns (int256 price) {
    AggregatorV3Interface source = assetsOracles[asset];
    if(address(source) != address(0)){
        price = getLatestPrice(asset); // This is an issue
    }
}

We tested this using a script and storing the value of the asset and the source as

// address[] assets, address[] sources

["0xf46ba822fFb594a2fFcaeCa9994075FD40529059", "0xf0cd67e684fb8bb227c7d9dc6ff96184c9a72e7c"], ["0xf0cd67e684fb8bb227c7d9dc6ff96184c9a72e7c", "0xf46ba822fFb594a2fFcaeCa9994075FD40529059"]

Both of the contracts implement AggregatorV3Interface  and return different values upon invocation by the getLatestPrice function.

The contract 0xf0cd67e684fb8bb227c7d9dc6ff96184c9a72e7c returns 1000000000000000

The contract 0xf46ba822fFb594a2fFcaeCa9994075FD40529059 returns 22

Upon calling getChainLinkAssetPrice function with 0xf0cd67e684fb8bb227c7d9dc6ff96184c9a72e7c  it is supposed to return 22 (because its source is 0xf46ba822fFb594a2fFcaeCa9994075FD40529059) but it returned 1000000000000000

Similarly, upon calling getChainLinkAssetPrice function with 0xf46ba822fFb594a2fFcaeCa9994075FD40529059  it is supposed to return 1000000000000000 (because its source is 0xf0cd67e684fb8bb227c7d9dc6ff96184c9a72e7c) but it returned 22

The issue seems to be fixed in GitHub, but the deployed code is behaving in this manner

We have reported the bug to the UniLend team, and they have also confirmed that the changes on GitHub are not live on Sepolia Yet.

Due to this bug, we encountered difficulties in executing Test Case 2 and Case 3.

Conclusion: The BuildBear impersonate feature, which enabled us to act as the owner of UnilendV2oracle and change the Price oracle Address, played a crucial role in identifying the issue.

Using BuildBear for your QA

If you are DeFi Web3 App and you are looking to either

  1. Create an internal QA Environment for your team, or
  2. Creating a private testing environment for your community before releasing to the Mainnet, or

Come talk to us at team@buildbear.io We can help you create your own, 100% customized testing environment, so that you can use it without any hassle.

NOTE: Information in this article shouldn’t be taken as Finance Advice.

Author: chandan

Let’s get started then, Shall we?