Hyper capital efficient lending protocol, built on top of Uniswap hooks!
Something that hasn't really been changed much in defi is lending/borrowing protocols, they are all mostly some flavour of the aave model with offchain liquidation keepers tracking the state of each loan and liquidating them depending on oracle prices. This is very inefficient as on sudden price drops (liquidation events are generally these) bots can't really keep up and there is no gurantee of execution. This additional risk is what forces borrowing protocols to limit their LTVs (Loan to Value ratios) to 80%.
Uniswap changed the way we do defi using their price ticks and i thought that lending could be an interesting implementation of ticks. Here instead of price ticks i have come up with something called ratio ticks which is basically
ratio = debt/ collateral ratio tick = 1.0015 ^ ratio
This model is much better than the existing ones because:
The last point is very interesting because as a result you incentivise activity on your pool of the two assets:
I have also thought of the potential threats to this system where bad actors might try to manipulate the price of the pool and get certain borrowers liquidated, in order to deal with this issue i am backstopping every liquidation with a call to an oracle (chainlink) and depending on a deviation factor it's decided whether to liquidated positions or not. Once the oracle and the pool prices have reconcilled liquidations will resume (which means in the other cases there were bad actors involved and liquidating then would be an exploit).
The biggest one is using uniswap hooks specifically the afterSwap hook, which gets me the price of the pool post a swap. On doing this if you look in the LendingHook.sol you will see that's where the liquidation logic is.
Ratio Tick Math: As for how does the whole tick logic works:
a refresher from the intro:
ratio (loan) = debt of position/ collateral of position ratio tick = 1.0015 ^ ratio
for example the liquidation threshold of 0.95 will have a tick of -34.22
the next question is this ratio keeps changing as price of the collateral wrt the debt token keeps changing, so to account for this i calculate a tick adjustment factor which is basically:
using the laws of logs,
new debt/ collateral = old debt/ (collateral * old price / new price)
take log base 1.0015 on both sides:
log(new debt/ collateral) = log(old debt/ (collateral * old price / new price))
after a little re-arrangement we come to:
tick adjustment factor = log 1.0015 1.001 ^ (new tick - old tick)
tick adjustment factor = log 1.0015 1.001 ^ (new tick - old tick)
tick adjustment factor = (new tick - old tick) * log 1.0015 1.001
which makes moving the ticks around super efficient and simple as i can just precalculate the multiplying factor,
so this is just one part.
Every time price changes this tickAdjustmentFactor is calculated and on every liquidation check the initial liquidation threshold tick is moved further left (assuming value of collateral has reduced) making more positions liquidateable.
One very salient feature about using ticks is you rely on a lot of bitmasking in order to keep track of arrays of positions, but it all happens like a simple bitwise operation that costs lesser gas than an addition. This is exactly what i do to keep track of all debt in all the ticks.
Liquidation is just looping over chunks of 256 such ticks (one bitwise operation) and checking if there are ticks that have debt or not beyond the liquidation threshold.
Liquidations can be triggered by bad actors trying to manipulate the price of the pool alone, in order to fend this off i am backstopping every liquidation call with that of oracle price and a priorly set deviation factor. I am using chainlink's datastreams as the oracle to get the averaged out price of the pool. If the deviation factor is exceeded then the liquidation is not executed.
Once the prices reconcile (the bad actor is forced to sell back the collateral) liquidations resume.
I have deployed this app to work entirely on worldcoin mini apps, i have deployed contrats to world chain as well. The reason for mini apps is the ease of access to active users via the worldcoin app. The hardest part in this was a last minute realisation that the worldcoin miniapps don't support ERC20 approvals, which meant i had to change my contracts last minute to work with permit2 (a standard for approvals so you don't do 2 transactions for approvals and transfers).
This was definitely one of the bigger risks as i had all my tests working for a version that didn't have permit2.
Within worldcoin mini apps i have used privy to enable sigining in with ethereum for a smooth user experience. It caches the user's wallet address and doesn't require them to sign in every time. I have implemented this using next auth.
The ui parts were written in shadcn/ui and nextjs. The contracts were written in solidity and tested using foundry. pretty much all of it was on foundry.