# Wharf-hardhat ### AssetPool.sol * `accumulateRewards()` is missing `onlyLending` modifier, maybe because of tests. But we can add this modifier and keep Alice as a lending address for `AssetPool.test.js` * No reenterency guard added on deposit/withdraw (maybe we can use on other function calls too depending on their nature) * `uint256 public totalBorrow;` should be added * `mapping(address => uint256) public userBorrowed;` can be added for future calculations * `availableReward()` ❌ calculation should be revised on time based reward index, each address shares will be signed difference from globalIndex to avoid double spending of rewards. * `transferAsset()` <span style="color:orange">⚠️</span> should keep track of borrows and borrow by address. * Missing `returnAsset()` for paybacks from `Lending.sol` * `accumulateReward()` should keep system GlobalIndex for rewards with timestamp, which will help distribute fair reward to everyone ### RateProvider.sol * `rpow()` from `Compounder.sol` can be used inside `getBorrowRate()` instead of doing `.mul(1e5)`. Unless we are fine with having only 5 decimal places precision * `rpow()` can be moved to different contract as a acted as a library * $$ U_{w} $$ is calculated multiple times `mul(borrows).div(available)` and at other place with precision of `mul(1e5)`, I think we should have consistency for precision and think of a way to avoid calculating it again to reduce gas cost * `uint256 public secondsPerYear` can be a constant `SECONDS_PER_YEAR` and instead of calling it like a function, we can directly call wherever contract have extended it. ### Lending.sol * `borrow()` ✅ * `payback()` <span style="color:orange">⚠️</span> instead of `IERC20(assetPool.underlyingToken()).safeTransferFrom()`, there should be `returnAsset()` inside `AssetPool.sol` which will allow keeping borrow/supply state of specific address. * `supplyCollateral()` ✅ * `withdrawCollateral()` ✅ * `liquidate()` <span style="color:orange">⚠️</span> should `preAccrue(debitor)` too and debitor health should be checked after liquidation (*`Good to have, can skip this`*) > <span style="color:orange">*have to validate burning and reward on liquidation yet*</span> ```sol function liquidate( address loanAsset, address collateralAsset, address debitor ) external override preAccrue(msg.sender) returns (uint256) ``` * `factor()` ✅ (complexity can be reduced if user `_collateral` and `_loans` are stored in mapping too and will be done in single loop) * `setCollateralFactor()` ✅ (onlyOwner or governance to be added) (does not support factor in decimal places, only discrete 0-100% and 80.5% is not possible) * `setInterest()` ✅ (onlyOwner or governance to be added) * `getCollateral()` ✅ * `getAllCollateral()` ✅ * `getLoan()` ✅ (complexity can be reduced if user `_loans` is stored in mapping too) * `getAllLoans()` ✅ * `getPrice()` ✅ * `getUnaccruedDebt()` ✅ (complexity will get reduced when `factor()` is getting optimized) * `accrueInterest()` ✅ * `getCurrentRate()` <span style="color:orange">⚠️</span>❌ Denial of service can be fixed when `getBorrows()` is implemented in `AssetPool.sol` * No reenterency guard added on borrow and payback, also we should think of others functions that might be victim of reenterency. * <span style="color:#998523">`_loans` could be converted into mapping (considering the fact we have to iterate through all of loans) to avoid multiple loops</span> * <span style="color:#998523">Overall there are multiple nested function calls and loops inside calls, we should try to minimize these considering fact that each operation gets converted into approx ~2-3 assembly instructions which might make us run out of gas in some of calculations. (Have to look more in-depth)</span> --- ## Next Steps * Add Governance/Ownable * Convert contracts to upgradeable * Build off-chain liquidation bot * Treasury Contract * Wharf-Reserve --- #### Dependency Graph ```graphviz digraph G { graph [ ratio = "auto", page = "100", compound =true, bgcolor = "#2e3e56" ]; node [ style = "filled", fillcolor = "#edad56", color = "#edad56", penwidth =3 ]; edge [ color = "#fcfcfc", penwidth =2, fontname = "helvetica Neue Ultra Light" ]; subgraph "clusterLending" { graph [ label = "Lending", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ]; "Lending.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ]; "Lending.preAccrue" [ label = "preAccrue", color = "#1bc6a6", shape =doubleoctagon ]; "Lending.borrow" [ label = "borrow", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.payback" [ label = "payback", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.supplyCollateral" [ label = "supplyCollateral", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.withdrawCollateral" [ label = "withdrawCollateral", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.liquidate" [ label = "liquidate", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.factor" [ label = "factor" ]; "Lending.setCollateralFactor" [ label = "setCollateralFactor", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.setInterest" [ label = "setInterest", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.getCollateral" [ label = "getCollateral", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.getAllCollateral" [ label = "getAllCollateral", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.getLoan" [ label = "getLoan" ]; "Lending.getAllLoans" [ label = "getAllLoans", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "Lending.getPrice" [ label = "getPrice" ]; "Lending.getUnaccruedDebt" [ label = "getUnaccruedDebt" ]; "Lending.accrueInterest" [ label = "accrueInterest" ]; "Lending.getCurrentRate" [ label = "getCurrentRate" ]; "Lending.type" [ label = "type" ]; "Lending.compound" [ label = "compound" ]; "Lending.getBorrowRate" [ label = "getBorrowRate" ]; "Lending.secondsPerYear" [ label = "secondsPerYear" ]; } subgraph "clusterContext" { graph [ label = "Context", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ]; "Context._msgSender" [ label = "_msgSender", color = "#f2c383", fillcolor = "#f2c383" ]; "Context._msgData" [ label = "_msgData", color = "#f2c383", fillcolor = "#f2c383" ]; } subgraph "clusterIOracle" { graph [ label = "IOracle (iface)", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ]; "IOracle.getPrice" [ label = "getPrice", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; } subgraph "clusterAssetPool" { graph [ label = "AssetPool", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ]; "AssetPool.underlyingToken" [ label = "underlyingToken" ]; "AssetPool.burn" [ label = "burn", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "AssetPool.transferAsset" [ label = "transferAsset", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "AssetPool.totalSupply" [ label = "totalSupply" ]; "AssetPool.<Constructor>" [ label = "<Constructor>", color = "#FF9797", fillcolor = "#FF9797" ]; "AssetPool.onlyLending" [ label = "onlyLending", color = "#1bc6a6", shape =doubleoctagon ]; "AssetPool.decimals" [ label = "decimals", color = "#FF9797", fillcolor = "#FF9797" ]; "AssetPool.deposit" [ label = "deposit", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "AssetPool.withdraw" [ label = "withdraw", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "AssetPool.availableReward" [ label = "availableReward" ]; "AssetPool.claimReward" [ label = "claimReward", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "AssetPool._claim" [ label = "_claim" ]; "AssetPool.accumulateReward" [ label = "accumulateReward", color = "#ffbdb9", fillcolor = "#ffbdb9" ]; "AssetPool._mint" [ label = "_mint" ]; "AssetPool.balanceOf" [ label = "balanceOf" ]; "AssetPool._burn" [ label = "_burn" ]; } subgraph "clusterCompounder" { graph [ label = "Compounder", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ]; "Compounder.compound" [ label = "compound", color = "#FF9797", fillcolor = "#FF9797" ]; "Compounder.rpow" [ label = "rpow" ]; } subgraph "clusterRateProvider" { graph [ label = "RateProvider", color = "#445773", fontcolor = "#f0f0f0", style = "rounded", bgcolor = "#445773" ]; "RateProvider.getBorrowRate" [ label = "getBorrowRate", color = "#FF9797", fillcolor = "#FF9797" ]; "RateProvider.getSupplyRate" [ label = "getSupplyRate", color = "#FF9797", fillcolor = "#FF9797" ]; } subgraph "clusterrate" { graph [ label = "rate", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "rate.div" [ label = "div" ]; } subgraph "clusteramount" { graph [ label = "amount", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "amount.mul" [ label = "mul" ]; } subgraph "clustershares" { graph [ label = "shares", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "shares.mul" [ label = "mul" ]; } subgraph "clusterp" { graph [ label = "p", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "p.mul" [ label = "mul" ]; } subgraph "clusterr" { graph [ label = "r", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "r.add" [ label = "add" ]; } subgraph "clusterB1" { graph [ label = "B1", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "B1.mul" [ label = "mul" ]; } subgraph "clusterB2" { graph [ label = "B2", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "B2.mul" [ label = "mul" ]; } subgraph "clusterborrows" { graph [ label = "borrows", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "borrows.mul" [ label = "mul" ]; } subgraph "clusterbw" { graph [ label = "bw", color = "#e8726d", fontcolor = "#f0f0f0", style = "rounded,dashed", bgcolor = "#3b4b63" ]; "bw.mul" [ label = "mul" ]; } "Lending.preAccrue"; "Lending.accrueInterest"; "Lending.borrow"; "Lending.factor"; "Lending.payback"; "AssetPool.underlyingToken"; "Lending.withdrawCollateral"; "Lending.liquidate"; "Lending.type"; "Lending.getPrice"; "AssetPool.burn"; "AssetPool.transferAsset"; "Lending.getUnaccruedDebt"; "IOracle.getPrice"; "Lending.getLoan"; "Lending.getCurrentRate"; "Lending.compound"; "AssetPool.totalSupply"; "Lending.getBorrowRate"; "rate.div"; "Lending.secondsPerYear"; "AssetPool.deposit"; "AssetPool._mint"; "amount.mul"; "AssetPool.withdraw"; "AssetPool.availableReward"; "AssetPool._claim"; "AssetPool.balanceOf"; "AssetPool._burn"; "shares.mul"; "AssetPool.claimReward"; "AssetPool.accumulateReward"; "Compounder.compound"; "p.mul"; "Compounder.rpow"; "r.add"; "RateProvider.getBorrowRate"; "B1.mul"; "B2.mul"; "borrows.mul"; "RateProvider.getSupplyRate"; "bw.mul"; "Lending.preAccrue" -> "Lending.accrueInterest" [ color = "#80e097" ]; "Lending.borrow" -> "Lending.factor" [ color = "#80e097" ]; "Lending.payback" -> "AssetPool.underlyingToken" [ color = "white" ]; "Lending.withdrawCollateral" -> "Lending.factor" [ color = "#80e097" ]; "Lending.liquidate" -> "Lending.factor" [ color = "#80e097" ]; "Lending.liquidate" -> "Lending.type" [ color = "#1bc6a6" ]; "Lending.liquidate" -> "Lending.getPrice" [ color = "#80e097" ]; "Lending.liquidate" -> "Lending.getPrice" [ color = "#80e097" ]; "Lending.liquidate" -> "AssetPool.underlyingToken" [ color = "white" ]; "Lending.liquidate" -> "AssetPool.burn" [ color = "white" ]; "Lending.liquidate" -> "AssetPool.transferAsset" [ color = "white" ]; "Lending.factor" -> "Lending.getPrice" [ color = "#80e097" ]; "Lending.factor" -> "Lending.getUnaccruedDebt" [ color = "#80e097" ]; "Lending.factor" -> "Lending.getPrice" [ color = "#80e097" ]; "Lending.getPrice" -> "IOracle.getPrice" [ color = "white" ]; "Lending.getUnaccruedDebt" -> "Lending.getLoan" [ color = "#80e097" ]; "Lending.getUnaccruedDebt" -> "Lending.getCurrentRate" [ color = "#80e097" ]; "Lending.getUnaccruedDebt" -> "Lending.compound" [ color = "#80e097" ]; "Lending.accrueInterest" -> "Lending.getCurrentRate" [ color = "#80e097" ]; "Lending.accrueInterest" -> "Lending.compound" [ color = "#80e097" ]; "Lending.getCurrentRate" -> "AssetPool.totalSupply" [ color = "white" ]; "Lending.getCurrentRate" -> "AssetPool.underlyingToken" [ color = "white" ]; "Lending.getCurrentRate" -> "Lending.getBorrowRate" [ color = "#80e097" ]; "Lending.getCurrentRate" -> "rate.div" [ color = "white" ]; "Lending.getCurrentRate" -> "Lending.secondsPerYear" [ color = "#80e097" ]; "AssetPool.deposit" -> "AssetPool.totalSupply" [ color = "#1bc6a6" ]; "AssetPool.deposit" -> "AssetPool._mint" [ color = "#1bc6a6" ]; "AssetPool.deposit" -> "amount.mul" [ color = "white" ]; "AssetPool.withdraw" -> "AssetPool.availableReward" [ color = "#1bc6a6" ]; "AssetPool.withdraw" -> "AssetPool._claim" [ color = "#1bc6a6" ]; "AssetPool.withdraw" -> "AssetPool.balanceOf" [ color = "#1bc6a6" ]; "AssetPool.withdraw" -> "AssetPool._burn" [ color = "#1bc6a6" ]; "AssetPool.withdraw" -> "amount.mul" [ color = "white" ]; "AssetPool.availableReward" -> "AssetPool.balanceOf" [ color = "#1bc6a6" ]; "AssetPool.availableReward" -> "AssetPool.totalSupply" [ color = "#1bc6a6" ]; "AssetPool.availableReward" -> "shares.mul" [ color = "white" ]; "AssetPool.claimReward" -> "AssetPool.availableReward" [ color = "#1bc6a6" ]; "AssetPool.claimReward" -> "AssetPool._claim" [ color = "#1bc6a6" ]; "AssetPool.burn" -> "AssetPool._burn" [ color = "#1bc6a6" ]; "AssetPool.accumulateReward" -> "AssetPool.totalSupply" [ color = "#1bc6a6" ]; "Compounder.compound" -> "p.mul" [ color = "white" ]; "Compounder.compound" -> "Compounder.rpow" [ color = "#1bc6a6" ]; "Compounder.compound" -> "r.add" [ color = "white" ]; "RateProvider.getBorrowRate" -> "B1.mul" [ color = "white" ]; "RateProvider.getBorrowRate" -> "B2.mul" [ color = "white" ]; "RateProvider.getBorrowRate" -> "borrows.mul" [ color = "white" ]; "RateProvider.getSupplyRate" -> "RateProvider.getBorrowRate" [ color = "#1bc6a6" ]; "RateProvider.getSupplyRate" -> "bw.mul" [ color = "white" ]; rankdir=LR node [shape=plaintext] subgraph cluster_01 { label = "Legend"; key [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0"> <tr><td align="right" port="i1">Internal Call</td></tr> <tr><td align="right" port="i2">External Call</td></tr> <tr><td align="right" port="i3">Defined Contract</td></tr> <tr><td align="right" port="i4">Undefined Contract</td></tr> </table>>] key2 [label=<<table border="0" cellpadding="2" cellspacing="0" cellborder="0"> <tr><td port="i1">&nbsp;&nbsp;&nbsp;</td></tr> <tr><td port="i2">&nbsp;&nbsp;&nbsp;</td></tr> <tr><td port="i3" bgcolor="#445773">&nbsp;&nbsp;&nbsp;</td></tr> <tr><td port="i4"> <table border="1" cellborder="0" cellspacing="0" cellpadding="7" color="#e8726d"> <tr> <td></td> </tr> </table> </td></tr> </table>>] key:i1:e -> key2:i1:w [color="#1bc6a6"] key:i2:e -> key2:i2:w [color="white"] } } ```