As BytePitch we have a considerable amount of work resource allocated for blockchain development ranging from a variety of frameworks. In this article we will go over a lending protocol we built on Agoric.
Introduction: What is Agoric?
Agoric is one of the platforms that BytePitch is building on. Agoric lives on the Cosmos network and has the capability to communicate with other Cosmos networks via IBC(Inter-Blockchain Communication) protocol.
How does Agoric work and what it brings to the table?
Eventual send with E()
Far(), Remotable and Marshaling
Notifiers and Subscriptions
Lending and borrowing have always been one of the most crucial parts of world economy. Until now, banks have been the main institutions who took on this job. With the development of Web3 and blockchain technologies gaining more and more power, we now do not have to rely on banks to evaluate us as "low-risk" and provide us with the liquidity we need.
Enter Compound Finance
Compound Finance is one of the first protocols to successfully implement a pool based lending protocol. It lives on Ethereum network and currently has almost $4 billion locked into it and lent almost $1 billion.
The numbers above are correct at the time of writing this article and probably will change later.
How does Compound Finance work?
In Compound Finance users can either supply liquidity to one of the pools or they can borrow some asset from one of the pools.
Users supply an asset to one of the existing pool and receive a cToken in return. For instance, if you supply ETH to the ETH pool you receive cETH in return. You can use this cETH to redeem your underlying, meaning ETH, assets later. There's an exchange rate between the underlying asset and the protocol asset in each pool, we'll cover that in detail later in this article.
There are two main incentives for supplying liquidity to the protocol;
Earning Interest: Users earn interest per block. The longer the liquidity stays the more money users will make.
COMP Governance Token: COMP is the official governance token for Compound protocol. Users receive these tokens as they interact with the protocol. Thus, when a user supplies an asset to one of the pools they earn COMP tokens as well. COMP token is listed on many of the markets so users can turn them into some other asset. Also they can choose to hold their COMP tokens and vote on governance proposals. Participating in another liquidity mining protocol and earning rewards might be an option too.
Borrowing an asset from one of the pools is another essential part of Compound Finance. The main difference between borrowing from a decentralized protocol instead of a traditional bank is that decentralized protocols do not ask questions like below before they lend you the money;
Who are you?
How old are you?
Where do you live
Are you married?
Do you have children?
What do you do for living?
How much do you make a year?
Have you been working in your last job longer than one year?
Did you pay your prior debts?
We can extend the list but you get the idea. The banks ask these questions in order to evaluate you'll pay your debt or not. So it's a matter of risk calculation, if people do not pay their debts institutions who gave them the money will go bankrupt. There's quote that explains this situation very well, I believe:
"At some point a debt is a problem of the ones who lend the money not the ones who borrow it."
So how does decentralized lending protocols like Compound not go bankrupt? How do they get away with lending money to everyone whoever is willing to pay the interest?
The answer is: Over-collateralization. There are two types of collateralization strategies when it comes to lending money;
Under-collateralization: The word 'under' means that value of the collateral requested from the borrower is less than the value of the actual debt. Say if you are to borrow $100 and the strategy is under-collateralization with %30 collateralization factor, it means that you should give away $30 of collateral before you borrow money. This strategy is like the more traditional one compared to the other one.
Over-collateralization: The word 'over' means that the value of collateral should be higher than the value of debt. For instance, if you have $100 to use as collateral and the collateralization factor is %80 then you can borrow $80 at most. The main idea is to secure the lender in a way that even if the borrower doesn't pay their debt, the lender does not lose money.
Relationship Between Over-Collateralized Debt and Liquidation
There's one important concept that we should mention if we're talking about over-collateralization and that is liquidation. In this context liquidation refers to the action of turning the collateral at hand into the debt has been given away. Liquidation is the point where the lender is sure that the borrower won't pay their debt and takes action to secure their money given away as debt.
Think of it like this, in the earlier example we said that there's $100 as collateral and max value of debt is $80. Say the borrower borrowed $78 worth of some asset as debt. This is a dangerous situation because the liquidation will occur as soon as the value of debt exceeds $80. Let's imagine we have a very irresponsible borrower and they refuse to pay their debt. As interest accrues on the principal debt, it is for sure that at some point the value of debt will reach the liquidation limit which is $80(max debt). The borrower insists on not paying the debt and the value of debt reaches to $80. At this point the lending protocol initiates liquidation and sells the collateral in order to cover the value of debt. Once the collateral is sold, the borrower has no debt left and their loan is closed. There are other possibilities that might trigger liquidation but here we just wanted to talk about the process.
Several Strategies To Sell The Collateral
Sell the collateral in an Automated Market Maker(AMM)
Sell the collateral yourself with a slightly lower price than the market price and expect people to take advantage of this opportunity
Compound uses the later strategy in order to pursue full decentralization but we'll use the first one in our protocol since Agoric has its own native AMM.
One other cool feature of Compound Finance is that its rates, whether it is the exchange rate between the underlying asset and the cToken or the interest rate charged on every debt, are dynamic. Meaning they change in response to current market situation. This is good because it enables the market to balance itself and not run out of liquidity.
For example, let's say there's $1000 of some underlying asset in a pool and also $100 of total debt lended out there. At this point of the market let's also say the annual interest rate applied to loans is %x. If someone borrows $50 more now the current market state is changed to, $950 of underlying asset liquidity and $150 total debt out there. So, for the sake of dynamic interest rate new annual interest rate is now %y and we are sure that y > x because the underlying liquidity is now less than what it was so the price of money, interest rate, is now increased.
There are three important rates for Compound protocol;
Below are the formulas for those rates;
You can take a look at the markets listed in the Compound protocol if you wish to see how these variables are used in real life.
You can check the Compound Whitepaper for more information.
For the implementation you can see Compound Protocol Repository on GitHub.
Pool Based Lending Protocol On Agoric
After some context we are now ready to talk about our version of a pool based lending protocol. Before we dive in you'll need some prerequisites in place;
ERTP: Agoric's interpretation of digital assets, or tokens.
Zoe: Agoric's Smart contract development framework. Protects users' assets from bugs and malicious developers.
Agoric Offers: Basic logic used when interacting with contracts.
Agoric Native AMM: An Automated Market Maker where you can trade various tokens whether they're native Agoric tokens or tokens belong to some other network and imported via IBC.
PriceAuthority: An interface between Zoe and price oracles.
You might also want to check out this documentation guide to have roadmap for more all around information.
The complete code for this protocol can be found in this repo. Essentially, a user is able to do five main operations in our protocol;
Redeem Deposited Money
Functionality for these operations is distributed among multiple components. An high level overview of protocol components can be found below.
Entry point to the protocol. Points users to the correct pools after doing necessary checks. See the code.
PoolManagers are identified by their underlying assets. If there's a token called VAN and we decide add a pool for that token to our protocol, we refer to that pool as 'VAN Pool'. A PoolManager is where the actual underlying assets are stored and protocol tokens are minted. Our VAN Pool mints a protocol token called 'AgVAN' on every deposit transaction and hands this protocol token to the user in return to their underlying asset. PoolManagers charge interest on the underlying asset they lended. This is where the actual deposit, borrow and redeem operations happen. See the code.
A hub containing the price authorities this protocol uses. Since we depend heavily on price authorities and the number of price authorities we need is a lot, it makes sense to manage all price authorities in a wrapper. See the code.
The module we implement the formulas for exchangeRate, utilizationRatio and borrowRate. See the code.
Our pools accept multiple types of protocol tokens as collateral. In order to group loans with the same collateral type and same debt type we use this module since they'll behave similarly in response to changes in the market like when to trigger liquidation. See the code.
The main module for giving out loans. Enables users to execute adjustBalance, closeLoan transactions. Also notifies dapps about its state. See the code.
Every debtsPerCollateral module has this module observing price changes in order to trigger a liquidationOperation in case some loans get underwater. See the code.
A Sample User Scenario
A simple but powerful example of an interaction with our protocol can be the scenario where a user deposits money into a pool. Diagram below can help you understand what an end-to-end interaction would look like.
Setup Your Environment
You need to have the below dependencies in order to build and use the agoric-sdk.
Platform: Linux shell or equivalent
Node.js: An LTS version >= 14.x.x
We first need to clone the source code and build it in our local environment. A more detailed explanation about installation can be found in the docs, I'm just going over this briefly here.
Clone this repo any location you want in your machine
git clone https://github.com/Agoric/agoric-sdk.git
git checkout beta
Build the sdk
Add agoric binary to the path
yarn link-cli ~/bin/agoric
Check the version
Our protocol is built on the agoric-sdk beta branch so the version should be 0.15.0
Startup The Dapp
Get the code and checkout the blog/bytepitch branch.
git clone https://github.com/anilhelvaci/dapp-pool-lending-protocol.git
git checkout blog/bytepitch
Start a simulated chain
In Agoric there are two types of local test chains; a sim-chain and a local-chain. A sim-chain, simulated chain, is where we can test our dapps and contracts but there's no transaction cost. So it's simulated, not real. On the other hand there's a local-chain which is based on the cosmos-sdk, therefore it's based on GoLang. Starting a local-chain requires building some go packages. If you plan to test with multiple clients interacting with the same chain, local-chain is the way to go. Check this page for further information.
Go to dapp folder and start the sim-chain;
agoric start --verbose --reset
For Agoric CLI reference see this page.
Leave that terminal running and open a new one;
agoric deploy contract/deploy/deploy.js api/deploy.js
A Word On Deployment
agoric deploy can accept multiple scripts as arguments and runs them in the order of passing. Here we pass two scripts;
contract/deploy/deploy.js: Sets up the required elements for the environment such as AMM, price authorities, faucets for the test tokens and then starts the actual LendingPool contract.
api/deploy.js: This scripts calls several other scripts in order to get tokens from faucets, create and add some liquidity to the pools. Basically we bootstrap the protocol with this script.
Once the deployment is done, start your wallet ui;
agoric open --repl
This command should open a browser automatically. Then open a new terminal and start the client ui;
Interact With The Dapp
To interact with the dapp, you mast approve it in your wallet. Go to your wallet and approve the dapp. Then go to localhost:3000, you should be able to see something like below;
We have two main tabs in our app;
Markets: Displays general market data and enables you to Supply or Borrow money from a selected pool.
Activity: Shows the user's activity such as deposited assets and borrows. Also enables the user Redeem, Close or Adjust Loan.
Above screenshot belongs to the 'Markets' tab. Most of the data displayed there is self-explanatory here is some explanation for the ones that are not;
APY: Annual Percentage Yield is basically how much interest one is going to pay for a given debt and how much interest one is going to earn for their supplied assets. In compound these numbers are different from each other because of something called 'ReserveFactor'. ReserveFactor is subtracted from the Borrow APY to calculate the Supply APY. In our protocol we do not use a ReserveFactor so Supply and Borrow APYs are the same, that's why we have only one APY field on our dashboard.
MMR: Minimum Margin Requirement is the ratio between the value of collateral and the value of debt. This means that if you put $150 of collateral you can borrow $100 at most.
If you switch to the 'Activity' tab;
Here we list the supplied assets and borrowed assets per pool.
As I mentioned in the section about deploy scripts, we bootstrap the protocol with some liquidity already pushed into it. That's why deposits side not empty.
I'll walk you through a full scenario where full functionality of the protocol where a user(you);
Deposit money to a pool
Borrow money from another pool using the protocol tokens you received when you deposit money to the first pool
Adjust your loan (pay some debt)
Close your loan (pay all your debt and get collateral in return)
Redeem your deposited money
You might notice that the numbers you see in supply screenshots is inconsistent with rest of the screenshots, that's because I took them out of sync from the rest of the sequence.
Click on VAN market and below dialog should open
Here, enter the amount you wish to supply and the protocol token you'll receive automatically be calculated, you can also specify slippage rate to protect your transaction from the sudden price changes of the market.
Then go to your wallet and approve the below offer;
Click 'Approve' then wait for the numbers in the purses change. Here we should care about VAN and AgVAN purses. Below is before transfer;
And this after the transaction;
Let's borrow some VAN. Click on VAN market and switch to the 'Borrow' tab in the dialog. Then enter how much you want to borrow;
Go to your wallet and approve the transaction once you hit 'Borrow';
Purses will change from this;
You can see that 'TotalBorrow' for the VAN market is now increased, 'TotalSupply' is decreased and 'APY' is increased as there's less liquidity in the pool so the value of money is increased. Also you can see that 'TotalBorrow' for the user in profile is now $110.
You can now view your loans in the 'Activity' tab;
Notice that the exchange rate did not change because there's no interest accrued yet. So let's accrue some interest.
From the below screenshot you should notice that total borrows for both market and user profile is increased and also the exchange rate for VAN market is Increased also.
You can see that current debt for the loan is increased in 'Activity' tab also;
Let's pay some of our debt now. Go to 'Activity' tab and click on your loan. From the dialog opened, switch to 'Adjust' tab. From this dialog you can also request some of your collateral but here we'll just pay some of our debt.
Again, go to your wallet and approve the below transaction;
Purses before transaction passes;
In the 'Activity' tab you can see that the 'Borrow Balance' for your loan is decreased and '% Of Limit' is also updated;
Now let's close our loan by paying all the remaining debt receiving the collateral we first put. Go to 'Close' tab in the loan dialog;
Go to your wallet approve the below transaction;
Purses before close operation;
In the 'Activity' tab you can see that your loan is gone and your 'Total Borrow' is now $0;
Redeem Your Money
Now click on your VAN deposit on the left side of 'Activity' dialog. Below dialog should pop;
Now go to your wallet and approve the transaction;
Purses before redeem;
The following questions were answered in this article:
What Agoric is, how Agoric works, how Agoric development works
How to develop a smart contract with Agoric
How a pool based lending protocol works and it's most famous implementation (Compound Finance)
What our version of a pool based lending protocol looks like
An end-to-end user scenario
Real field implementations and practical examples of blockchain development, De-Fi development, DApp Development, Web3 development, Ethereum development, Smart Contract development, blockchain frameworks, blockchain protocols
I hope this article was helpful. Feel free to share your comments and questions in this issue. Thanks for reading. Until next time, take care!