1-click looping is effectively a "flash loan" mechanism to borrowing all the $BERA needed to establish the position up-front. Additionally it benefits from discounted baking fees. If the fees are the exact same (i.e. bake fee = 2.69% AND looping fee = 2.69%), then the outcome is the same (within a small expected tolerance related to order of operations).
To clarify: calling 1-click loop with 10K BERA is effectively the same thing as having 10K $BERA in your wallet, baking $BREAD with it, and borrowing at 99% LTV against it. Except 1-click loop lets you do it without having any $BERA in your wallet, and for a discounted bake fee!
This is a mathematical property and invariant that is enforced in the code. =lease see the Solidity test written below for an example:
Test comparing looping vs normal borrow:
bakeFee and leverageFee are both set to 2.69%
10K $BERA for 365 days
function test_LoopVsBorrow() public {
vm.startPrank(bread.owner());
bread.setLeverageFee(269);
bread.setBakeFee(269);
vm.stopPrank();
uint256 amountToLoop = 10_000 ether;
uint256 daysToLeverage = 365;
vm.deal(user4, amountToLoop + 1 ether);
vm.startPrank(user4);
{
uint256 breadLastPriceBefore = bread.lastPrice();
uint256 beraBalanceBefore = user4.balance;
console.log("Bread price before: ", breadLastPriceBefore);
console.log("Bera balance before: ", beraBalanceBefore);
console.log("Bera balance before: ", bread.balanceOf(user4));
console.log("Bread totalSupply before:", bread.totalSupply());
uint256 snapshot = vm.snapshotState();
uint256 balanceAfterLoop;
uint256 breadPriceAfterLoop;
uint256 collateralAfterLoop;
uint256 borrowedAfterLoop;
uint256 treasuryBalanceAfterLoop;
{
console.log("---testing loop---");
bread.loop{value: amountToLoop}(amountToLoop, daysToLeverage);
(uint256 collateral, uint256 borrowed, , ,) = bread.activeLoans(user4);
balanceAfterLoop = user4.balance;
breadPriceAfterLoop = bread.prevPrice();
collateralAfterLoop = collateral;
borrowedAfterLoop = borrowed;
treasuryBalanceAfterLoop = address(breadTreasury).balance;
console.log("Balance after: ", balanceAfterLoop);
console.log("Collateral after: ", collateralAfterLoop);
console.log("Borrowed after: ", borrowedAfterLoop);
console.log("Bread balance after: ", address(bread).balance);
console.log("Treasury balance after: ", treasuryBalanceAfterLoop);
console.log("Bread price after: ", breadPriceAfterLoop);
console.log("Bread totalSupply after:", bread.totalSupply());
}
vm.revertToState(snapshot);
assertEq(breadLastPriceBefore, bread.lastPrice(), "Bread price should be the same after revert");
assertEq(beraBalanceBefore, user4.balance, "Bera balance should be the same after revert");
{
console.log("---testing max borrow after minting BREAD---");
bread.bake{value: amountToLoop}(user4);
console.log("Bread balance after mint: ", bread.balanceOf(user4));
uint256 borrowBasedOnLoopInput = amountToLoop - amountToLoop * bread.leverageFeeBps() / bread.BPS_DENOMINATOR();
uint256 borrowBasedOnCurrentBread = bread.BREADtoBERAFloor(bread.balanceOf(user4));
uint256 collateralSaved = (borrokwBasedOnCurrentBread - borrowBasedOnLoopInput) * bread.BPS_DENOMINATOR() / bread.COLLATERAL_RATIO();
console.log("Borrow to input based on function loop: ", borrowBasedOnLoopInput);
console.log("Borrow to input based on calc: ", bread.BREADtoBERAFloor(bread.balanceOf(user4)));
console.log("Collateral saved from calculation difference: ", collateralSaved);
bread.borrow(borrowBasedOnLoopInput, daysToLeverage); //use the same input as in loop calcs (slightly less due to old bread price)
(uint256 collateral, uint256 borrowed, , ,) = bread.activeLoans(user4);
console.log("Balance after: ", user4.balance);
console.log("Collateral after: ", collateral);
console.log("Borrowed after: ", borrowed);
console.log("Bread balance after: ", address(bread).balance);
console.log("Treasury balance after: ", address(breadTreasury).balance);
console.log("Bread price after: ", bread.lastPrice());
console.log("Bread totalSupply after:", bread.totalSupply());
assertApproxEqAbs(user4.balance, balanceAfterLoop, 1, "Bera balance after borrow should be the same as after loop");
assertApproxEqAbs(borrowed, borrowedAfterLoop, 1, "Borrowed after borrow should be the same as after loop");
assertApproxEqAbs(bread.prevPrice(), breadPriceAfterLoop, 1, "Bread price after borrow should be the same as after loop");
assertApproxEqAbs(collateral, collateralAfterLoop, borrowBasedOnCurrentBread-borrowBasedOnLoopInput, "Collateral after borrow should be same as after loop (ex collateral saved)");
}
}
}