4. FAQ

Q: Can you share the code walkthrough URL?

A: Here


Q: I am having issues building the project

A: Try our Dockerfile


Q: Can you explain what's RESERVED_ID? And how it's used?

A: As explained in the docs of ISize.sol and 3.3. Market Orders, some operations (buy credit, sell credit, compensate), expect a credit position ID as an input parameter so that you can buy an existing credit (from a lender), sell an existing credit (to a credit buyer), or compensate your debt from another loan you have. The special ID of RESERVED_ID = type(uint256).max can also be passed to represent "create a credit/debt pair" and execute the buying/selling/compensation operation at the same time. This is used for example to do a simple lend with buyCreditMarket(RESERVED_ID), do a simple borrow with sellCreditMarket(RESERVED_ID) or partially repay a loan with compensate(RESERVED_ID) + repay


Q: What does N/A mean in the context of input validation functions?

A: Not Applicable. The code has N/A comments for documentation purposes of fields that in reality do not need input validation. For example, in validateClaim, there's a N/A for msg.sender, since claim can be performed by anyone, as it is a permissionless method.


Q: Can you summarize credit limit and credit market in simple words ? A:

  • limit = limit order (maker)

  • market = market order (taker)

  • buy credit = lend

  • sell credit = borrow

  • sellCreditMarket (market borrow) fills a buyCreditLimit order

  • buyCreditMarket (market lend) fills a sellCreditLimit order, or buys an existing credit depending on the params passed


Q: Regarding the Claim functionality. The lender does not necessarily have to call claim function immediately and therefore can earn additional yield from Aave. However, anyone can claim on behalf of someone else, and the protocol has its own bots to claim for other lenders. What happens if I as a lender want to leave the debt repayment (not claim it) and earn yield from Aave but someone else / bot claims it for me? Is there any minimum or maximum time that I can leave my repayment unclaimed?

A: What happens is that nothing changes from the lender perspective. The reason is that claim will only do an internal balance change from address(this) to creditPosition.lender, but since all USDC is already deposited on Aave (through deposit / DepositTokenLibrary / NonTransferrableScaledToken), all user's borrow tokens/cash already automatically earn variable interest by default. Let's say there are two situations: Situation I

  1. repay

  2. claim

  3. pass time

  4. withdraw

Situation II

  1. repay

  2. pass time

  3. claim

  4. withdraw

Both Situation I and Situation II will have the same output for the lender, since in Situation I, the time passage will directly credit borrowAToken.balanceOf(lender), while in Situation II, it will credit borrowAToken.balanceOf(address(this)), but in the end, the credit amount will be proportionally sent to the lender's account during the claim step. You can refer to the following test case to see it in more detail: test_Claim_claim_at_different_times_may_have_different_interest

Q: Is there any minimum or maximum time that I can leave my repayment unclaimed

A: No


Q: When a borrower corresponding to a particular lender is susceptible to liquidation, is liquidate/liquidateWithReplacement being called by the protocol automatically to provide the lender with a healthy borrower or the lender needs to call the function themselves?

A:

The liquidate function is not permissioned and can be called by anyone to liquidate an unhealthy position.

The liquidateWithReplacement function is permissioned by the KEEPER_ROLE, and can only be called by Size. The protocol will employ bots to call liquidate/liquidateWithReplacement to stop bad debt from accruing within the protocol, but if these bots should fail for any reason, a lender or external liquidate can always call liquidate and make the lender whole, earning some incentive in the process.

Second, it is worth pointing out that "When a borrower corresponding to a particular lender" is not a completely accurate description of the most generic case of the protocol. The reason is that 1 credit seller (borrower) can have multiple credit buyers (lenders) in case the credit buyer resells part of their future cash flow (partial lender exit). For example, if a borrower picks a lender with sellCreditMarket(RESERVED_ID), then the lender can exit with sellCreditMarket(creditPositionId), and now the initial borrower may have 2 lenders. Check out the test case test_SellCreditMarket_sellCreditMarket_CreditPosition_credit_is_decreased_after_exit and documentation entry 2.2.2.2. Credit Position.

Third, the purpose of liquidateWithReplacement is for the protocol to earn some additional cash when an underwater borrower can be replaced by a healthy borrower on the orderbook who might be willing to pay a higher rate than the original borrower. For example, suppose the initial borrower was paying 1% yearly. Some time passes, and they are now liquidatable. Due to different market conditions, the new borrow rates are up, and there are borrowers willing to pay 2% yearly. Because of this spread, the protocol can replace the underwater borrower with a new healthy borrower and keep the change. The situation for the lenders does not change anything, except that now they are to be repaid by a healthier borrower.


Q: If mechanisms are already present to prevent the borrower from delaying the repayment of the loan, then how does the borrower become under-collateralized/ susceptible to liquidation?

A: There are no mechanisms to change the term of a loan. They all must be repaid by the due date. What happens if the borrower does not repay is that the loan becomes overdue and it can be liquidated. The borrower becomes under-collateralized by either withdrawing collateral or by a collateral price change. More information about that here. In a sense, liquidate/liquidateWithReplacement "prevent" the borrower from delaying the payment. More strictly, though, the liquidator will repay the loan on behalf of the borrower (so that the lenders can have their money back), and take the borrower's collateral (plus incentives) for not repaying by the due date.


Q: What is the purpose of multicall and isMulticall ?

A: Basically, multicall allows a user to submit multiple actions in a single transaction. This can be useful for liquidations, for example, where the liquidator calls deposit, then liquidate, then withdraw. Check out IMulticall.sol for more details, and also test case test_Multicall_liquidator_can_liquidate_and_withdraw The purpose of isMulticall is a flag to check if postcondition checks should be waived during a multicall operation. This is especially useful for deposit, so that a debt reduction operation can always succeed, no matter if the cap is reached or not. There are some docs about that on Deposit.sol


Q: Can you provide a simple example(no fees) with buyCreditMarket, splitted on different stages by showing Alice, Bob and Carl's funds, credits, dept etc. And how are they connected if Alice is the borrower and Bob & Carl are the lenders?

A: For the sake of simplicity, assume no fees:

  1. Alice deposits collateral and submits a sellCreditLimit specifying she wants to borrow at 20% yearly

  2. Bob deposits cash and picks Alice's offer with buyCreditMarket(alice, RESERVED_ID), lending $100 to Alice. Alice now owns a debt position debtPositionId1 of $120 future value due 365 days from now, and her debtToken is incremented by the same amount, reducing her collateral ratio. Bob owns a credit position creditPositionId1 of $120, with the debtPositionId parameter pointing to Alice's debt position debtPositionId1. This link is done in AccountingLibrary.createDebtAndCreditPositions

  3. Bob now wants to sell his credit and submits a sellCreditLimit specifying that he wants to earn 25% yearly.

  4. Carl wants to buy $30 worth of Bob's credit, and submits a buyCreditMarket(_, creditPositionId1). Bob's credit is brought to present value at a 25% rate, and Carl needs to pay $30/1.25 = $24 in cash for a part of Bob's credit. Bob's credit is fractionalized in AccountingLibrary.createCreditPosition, and now Bob's credit position is reduced to $90 = $120-$30, and Carl owns a credit creditPositionId2 worth $30.

  5. After all these operations, both credit positions creditPositionId1 and creditPositionId2 have the same debtPositionId parameter equal to debtPositionId1, ie, referring to Alice, the initial borrower.

  6. When Alice repays, her $120 future value is deposited to the protocol. Then, both Bob and Carl (or anyone, since it is a permissionless method) can call claim passing each of their respective credit position IDs. This will transfer cash from the protocol to the respective creditor. This way, Bob will get $90 in cash to his balance and Carl will get $30.

You can see a similar test case in test_SellCreditMarket_sellCreditMarket_exit_properties, but with different values and checks.

This example already shows some particularities of the Size protocol:

  • In (1), Alice submits a sellCreditLimit wanting to borrow money. In (3), Bob too submits a sellCreditLimit wanting to exit part of his loan. So the same operation can be either used for a "simple borrow" or for a "loan exit". As the docs explains quite well, in both cases users are selling credit (aka future cash flow). So this may be future cash flow in the sense of a borrow or future cash flow that they are receiving from someone else (lender exiting)

  • In (4), during the credit fractionalization, a critical invariant of the protocol must hold, which is that SUM(credit) = futureValue. As we have explained, there can be multiple creditors for the same loan, but if this sum is not exact, the protocol would be either printing money (ie, sum of amount to be received greater than amount to be repaid, if >) or not giving back to creditors at the due date (ie, sum of amount to be received lesser than the amount to be repaid, if <)

  • In (4), as you may see in the code, the borrower parameter is not used. This is a bit confusing, but the reason is that you are buying an existing credit, so the actual storage value you should be looking at is creditPosition.lender. This was an API choice so that both "buy an existing credit" and "create & buy and existing credit" actions would happen at the same function. You can see that in BuyCreditMarket.executeBuyCreditMarket


Q: What is the significance of the borrow token cap? It seems this cap is not really used anywhere except for a few functions that check if it hasn't been breached

A: The reason for the borrow token cap is to allow the protocol to have a "guarded launch", so that it can limit the supply as it grows. For example, in the beginning, while the TVL grows, the cap may be $1M in deposits, for example. However, as you may see, the protocol will still want to allow for debt reductions. So even if the cap is reached, all operations that increase the health of the system should be allowed (CapLibrary.sol). Projects may choose to do it for a number reasons. For example, to prevent growing at a faster pace than its security/safety/trust, perhaps to prevent growing before a bug bounty program is in place, etc.


Q: Can users deposit/withdraw ETH?

A: Users can deposit ETH/WETH for collateral and USDC for borrow tokens but only withdraw WETH and USDC


Q: What is the difference between credit and debt position?

A: Opposites.

Owner of a debt pays back the owner of the corresponding credit at maturity.

Credit = future cashflow

If you (market) borrow / sell credit, you have a debt position. If you (market) lend / buy credit, you have a credit.


Q: Why is the update of the swap fee and minimum/maximum tenor related?

A:

The reason is that it is multiplied by the loan duration to calculate the exact fee amount. The absolute swap fee amount is cash * swapFeeAPR * duration (decimals in percentage points)

So for example: If you borrow $100 due 73 days, you will pay $100 * 0.5 * 73 / 365 = $10 in fees The ending result is $100 - $10 = $90 So basically for the fee to not "eat up" the whole cash (since amountOut = amount * (1 - swapFeeAPR * duration), you need the fee times the duration to be at most 100% (otherwise, fees eat up the whole amount of cash), so swapFeeAPR * duration <= 1

Because of that, you have:

  • max swapFeeAPR = 1 / duration

    Same for max duration

  • max duration = 1 / swapFeeAPR

Take a look at the preconditions here.


Q: Why can't crLiquidation be increased by the admin?

A: The reason is that the admin shouldn't increase the CR liquidation or that could make borrowers instantly liquidatable. For example, suppose a borrower has $140 worth of collateral and $100 worth of debt. His CR is 140%, and he is healthy, since crLiquidation is initially set to 130%. But if the admin changes it to 150%, the borrower is now instantly liquidatable. So this check is a protection against a misconfiguration by the admin


Q: Are exiting lenders forced to have a borrow offer then?

A: Yes, they are if they want to passively have their credits bought (as you are doing with BuyCreditMarket). If lenders want to exit actively, with SellCreditMarket, then they don't need a borrow offer. The reason is that the borrow offer is a somewhat simplistic name because it both means "amount of interest I am willing to pay to borrow money" (from a borrower's point of view) and also "amount of interest I am willing to get to sell my credit" (from a credit seller point of view, which can be a lender). We thought about renaming BorrowOffer to CreditSellOffer, or something like that so that it would be more generic, but we've decided to keep it aligned with the borrower/lending perspective so that it could be easier to interpret on the default use case (simple borrow / simple lend).

But for credit sellers who may want to sell future cash flow from existing loans (ie, lenders), they can also use the borrow offer to sell credit. You can see some examples of "speculators" who are just buying credit and selling credit and will submit these two limit orders to speculate on market rates here: test_BuyCreditLimit_buyCreditLimit_experiment_strategy_speculator.

Another good way to see the borrow bid is that it’s simply an expression of how much the user values cash over credit, and thus is the price at which he would be happy to sell new credit for cash, or existing credit for cash.


Q: Why doesn't the protocol clear limit order after they are matched?

A: The reason is that users may still want to be matched, and it is simpler on the accounting to not keep track of current borrows/lends vs desired borrows/lends. For example, suppose I am a borrower and I deposited collateral, and I want to borrow $1000 in total. I have submitted a limit order with my borrow yield curve. Maybe lender A can only lend me $200, then lender B $300, so I'm still waiting to be fully matched and I don't want to change my borrow offer while I'm not fully matched. The same logic applies to lenders. The way borrowers & lenders, respectively, can "protect" themselves is by

  • submitting a openingBorrowLimitCR, if borrowers want a maximum CR when a lender matches with them differently than the protocol-defined 150%

  • submitting a maxDueDate, if lenders don't want to lend past a certain point


Q:

Last updated