A liquidity provider in the Lending Pool can deposit their rToken into the Stability Pool. The rToken balance of a user increases when the liquidity rate rises. When a user makes a deposit, their deposit mapping is updated, as shown in L-176 in the Stability Pool.
In order to run this coded POC you will have to add foundry to the project to do so you will have to follow 4 steps :
pragma solidity ^0.8.0;
import {Test, console,stdError} from "forge-std/Test.sol";
import {crvUSDToken} from "contracts/mocks/core/tokens/crvUSDToken.sol";
import {RAACToken} from "contracts/core/tokens/RAACToken.sol";
import {veRAACToken} from "contracts/core/tokens/veRAACToken.sol";
import {RAACReleaseOrchestrator} from "contracts/core/minters/RAACReleaseOrchestrator/RAACReleaseOrchestrator.sol";
import {RAACHousePrices} from "contracts/core/primitives/RAACHousePrices.sol";
import {RAACNFT} from "contracts/core/tokens/RAACNFT.sol";
import {RToken} from "contracts/core/tokens/RToken.sol";
import {DebtToken} from "contracts/core/tokens/DebtToken.sol";
import {LendingPool, ILendingPool} from "contracts/core/pools/LendingPool/LendingPool.sol";
import {Treasury} from "contracts/core/collectors/Treasury.sol";
import {FeeCollector} from "contracts/core/collectors/FeeCollector.sol";
import {DEToken} from "contracts/core/tokens/DEToken.sol";
import {StabilityPool} from "contracts/core/pools/StabilityPool/StabilityPool.sol";
import {RAACMinter} from "contracts/core/minters/RAACMinter/RAACMinter.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {ERC4626} from "@openzeppelin/contracts/token/ERC20/extensions/ERC4626.sol";
import {Auction} from "contracts/zeno/Auction.sol";
import {ZENO} from "contracts/zeno/ZENO.sol";
import {ERC20Mock} from "contracts/mocks/core/tokens/ERC20Mock.sol";
import {TimelockController} from "contracts/core/governance/proposals//TimelockController.sol";
import {Governance} from "contracts/core/governance/proposals//Governance.sol";
import {RAACGauge} from "contracts/core/governance/gauges/RAACGauge.sol";
import {RWAGauge} from "contracts/core/governance/gauges/RWAGauge.sol";
import {BoostController} from "contracts/core/governance/boost/BoostController.sol";
import {GaugeController} from "contracts/core/governance/gauges/GaugeController.sol";
import {IGaugeController} from "contracts/interfaces/core/governance/gauges/IGaugeController.sol";
import {IGovernance} from "contracts/interfaces/core/governance/proposals/IGovernance.sol";
import {ReserveLibrary} from "contracts/libraries/pools/ReserveLibrary.sol";
import {LockManager} from "contracts/libraries/governance/LockManager.sol";
import {VotingPowerLib} from "contracts/libraries/governance/VotingPowerLib.sol";
import {TimeWeightedAverage} from "contracts/libraries/math/TimeWeightedAverage.sol";
import "contracts/libraries/math/WadRayMath.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract CodedPOC is Test{
using WadRayMath for uint256;
address constant BOB = address(0x10000);
address constant ALICE = address(0x20000);
address constant CHARLIE = address(0x30000);
address[] internal users;
address owner = makeAddr("Owner");
address[] public proposers = [makeAddr("Proposers")];
address[] public executors = [makeAddr("Executors")];
crvUSDToken crvUSD;
RAACToken raac;
veRAACToken veRAAC;
RAACReleaseOrchestrator raacReleaseOrchestrator;
RAACHousePrices raacHousePrices;
RAACNFT raacNFT;
RToken rToken;
DebtToken debtToken;
LendingPool lendingPool;
Treasury treasury;
FeeCollector feeCollector;
DEToken deToken;
StabilityPool stabilityPool;
RAACMinter raacMinter;
Treasury repairFund;
CurveMock curveMock;
ERC20Mock usdc;
ERC20Mock rewardToken;
ZENO currentZeno;
ZENO[] zenos;
Auction currentAuction;
Auction[] auctions;
TimelockController timelock;
Governance governance;
GaugeController gaugeController;
RAACGauge raacGauge;
RWAGauge rwaGauge;
BoostController boostController;
uint256[] proposalIds;
address[] gauges;
uint256 timestampBefore;
function setUp() public {
vm.warp(1524785992);
vm.roll(4370000);
users = [BOB, ALICE, CHARLIE];
vm.label(BOB, "Bob");
vm.label(ALICE, "Alice");
vm.label(CHARLIE, "Charlie");
vm.label(owner, "Owner");
vm.label(address(this), "Test");
crvUSD = new crvUSDToken(owner);
curveMock = new CurveMock(address(crvUSD));
vm.label(address(curveMock), "CurveMock");
vm.prank(owner);
crvUSD.setMinter(address(this));
raac = new RAACToken(owner, 200, 50);
vm.label(address(raac), "RAAC");
veRAAC = new veRAACToken(address(raac));
vm.label(address(veRAAC), "veRAAC");
raacReleaseOrchestrator = new RAACReleaseOrchestrator(owner);
vm.label(address(raacReleaseOrchestrator), "RAACReleaseOrchestrator");
raacHousePrices = new RAACHousePrices(owner);
raacNFT = new RAACNFT(address(crvUSD), address(raacHousePrices), owner);
rToken = new RToken("RToken", "RT", owner, address(crvUSD));
debtToken = new DebtToken("DebtToken", "DT", owner);
lendingPool = new LendingPool(
address(crvUSD), address(rToken), address(debtToken), address(raacNFT), address(raacHousePrices), 0.1e27
);
lendingPool.setPrimeRateOracle(address(owner));
treasury = new Treasury(address(owner));
repairFund = new Treasury(address(owner));
feeCollector =
new FeeCollector(address(raac), address(veRAAC), address(treasury), address(repairFund), address(owner));
deToken = new DEToken("DEToken", "DET", owner, address(rToken));
stabilityPool = new StabilityPool(address(owner));
raacMinter = new RAACMinter(address(raac), address(stabilityPool), address(lendingPool), address(treasury));
vm.prank(owner);
raac.setFeeCollector(address(feeCollector));
vm.prank(owner);
raac.manageWhitelist(address(feeCollector), true);
vm.prank(owner);
raac.manageWhitelist(address(veRAAC), true);
vm.prank(owner);
raac.manageWhitelist(address(owner), true);
vm.prank(owner);
raac.setMinter(owner);
usdc = new ERC20Mock("USDC", "USDC");
vm.label(address(usdc), "USDC");
vm.prank(owner);
raac.setMinter(address(raacMinter));
bytes32 role = feeCollector.FEE_MANAGER_ROLE();
vm.prank(owner);
feeCollector.grantRole(role, owner);
role = feeCollector.EMERGENCY_ROLE();
vm.prank(owner);
feeCollector.grantRole(role, owner);
role = feeCollector.DISTRIBUTOR_ROLE();
vm.prank(owner);
feeCollector.grantRole(role, owner);
vm.prank(owner);
rToken.setReservePool(address(lendingPool));
vm.prank(owner);
debtToken.setReservePool(address(lendingPool));
vm.prank(owner);
deToken.setStabilityPool(address(stabilityPool));
vm.prank(owner);
raac.transferOwnership(address(raacMinter));
vm.prank(owner);
rToken.transferOwnership(address(lendingPool));
vm.prank(owner);
debtToken.transferOwnership(address(lendingPool));
vm.prank(owner);
stabilityPool.initialize(
address(rToken), address(deToken), address(raac), address(raacMinter), address(crvUSD), address(lendingPool)
);
lendingPool.setStabilityPool(address(stabilityPool));
vm.prank(owner);
raacHousePrices.setOracle(owner);
vm.prank(owner);
stabilityPool.setRAACMinter(address(raacMinter));
_createAuction();
rewardToken = new ERC20Mock("RewardToken", "RT");
timelock = new TimelockController(2 days, proposers, executors, owner);
governance = new Governance(address(veRAAC), address(timelock));
role = timelock.PROPOSER_ROLE();
vm.prank(owner);
timelock.grantRole(role, address(governance));
role = timelock.EXECUTOR_ROLE();
vm.prank(owner);
timelock.grantRole(role, address(governance));
role = timelock.CANCELLER_ROLE();
vm.prank(owner);
timelock.grantRole(role, address(governance));
gaugeController = new GaugeController(address(veRAAC));
raacGauge = new RAACGauge(address(rewardToken), address(raac), address(gaugeController));
vm.label(address(raacGauge), "RAACGauge");
gaugeController.addGauge(address(raacGauge), IGaugeController.GaugeType.RAAC, 10000);
raacGauge.grantRole(raacGauge.CONTROLLER_ROLE(), owner);
raacGauge.grantRole(raacGauge.FEE_ADMIN(), address(owner));
raacGauge.grantRole(raacGauge.EMERGENCY_ADMIN(), address(owner));
vm.prank(owner);
raacGauge.setBoostParameters(25000, 10000, 1 weeks);
vm.prank(owner);
raacGauge.setDistributionCap(10000000e18);
vm.prank(owner);
raacGauge.setInitialWeight(5000);
rwaGauge = new RWAGauge(address(rewardToken), address(raac), address(gaugeController));
vm.label(address(rwaGauge), "RWAGauge");
gaugeController.addGauge(address(rwaGauge), IGaugeController.GaugeType.RWA, 10000);
rwaGauge.grantRole(rwaGauge.CONTROLLER_ROLE(), address(owner));
rwaGauge.grantRole(rwaGauge.FEE_ADMIN(), address(owner));
rwaGauge.grantRole(rwaGauge.EMERGENCY_ADMIN(), address(owner));
vm.prank(owner);
rwaGauge.setBoostParameters(
25000,
10000,
7 * 24 * 3600
);
vm.prank(owner);
rwaGauge.setDistributionCap(10000000e18);
vm.prank(owner);
rwaGauge.setMonthlyEmission(100000e18);
boostController = new BoostController(address(veRAAC));
boostController.modifySupportedPool(address(lendingPool), true);
gauges = [address(raacGauge), address(rwaGauge)];
for (uint256 i; i < users.length; i++) {
crvUSD.mint(users[i], 1_000_000_000e18);
vm.prank(users[i]);
crvUSD.approve(address(raacNFT), type(uint256).max);
vm.prank(users[i]);
crvUSD.approve(address(lendingPool), type(uint256).max);
vm.prank(users[i]);
debtToken.approve(address(lendingPool), type(uint256).max);
vm.prank(users[i]);
rToken.approve(address(stabilityPool), type(uint256).max);
vm.prank(users[i]);
raac.approve(address(veRAAC), type(uint256).max);
usdc.mint(users[i], 1_000_000_000e18);
vm.prank(users[i]);
raac.approve(address(raacGauge), type(uint256).max);
vm.prank(users[i]);
raac.approve(address(rwaGauge), type(uint256).max);
}
timestampBefore = block.timestamp;
}
function _createAuction() internal {
currentZeno = new ZENO(address(usdc), 365 days, "ZENO", "ZN", owner);
currentAuction = new Auction(
address(currentZeno),
address(usdc),
address(currentZeno),
block.timestamp + 1 days,
block.timestamp + 11 days,
500,
10,
1000e18,
owner
);
vm.prank(owner);
currentZeno.transferOwnership(address(currentAuction));
for (uint256 i; i < users.length; i++) {
vm.prank(users[i]);
usdc.approve(address(currentAuction), type(uint256).max);
}
auctions.push(currentAuction);
zenos.push(currentZeno);
}
function test_lockTwoTimePOC() public {
vm.startPrank(CHARLIE);
lendingPool.deposit(10_000e18);
stabilityPool.deposit(rToken.balanceOf(CHARLIE));
vm.warp(block.timestamp+ 6003);
vm.roll(block.number + uint256(6003)/15);
stabilityPool.withdraw(deToken.balanceOf(CHARLIE));
veRAAC.lock(raac.balanceOf(CHARLIE)/2,31554267);
uint256 balanceOfCharlie = veRAAC.balanceOf(CHARLIE);
uint256 charlieVotingPower = veRAAC.getVotingPower(CHARLIE);
assertEq(balanceOfCharlie, charlieVotingPower);
veRAAC.lock(raac.balanceOf(CHARLIE)/2,31537156);
vm.stopPrank();
balanceOfCharlie = veRAAC.balanceOf(CHARLIE);
charlieVotingPower = veRAAC.getVotingPower(CHARLIE);
assertEq(balanceOfCharlie, charlieVotingPower);
}
function test_UserHaveDebtAfterLiquidation() public {
vm.warp(block.timestamp+30299853);
vm.roll(block.number + 2271488);
vm.prank(owner);
raacHousePrices.setHousePrice(1, 653482530205154129803885);
vm.startPrank(BOB);
raacNFT.mint(1, 653482530205154129803885);
raacNFT.approve(address(lendingPool),1);
vm.warp(block.timestamp+6027005);
vm.roll(block.number + 2058436);
lendingPool.deposit(425197589667951795675698);
vm.warp(block.timestamp+24960365);
vm.roll(block.number + 111874);
lendingPool.depositNFT(1);
vm.warp(block.timestamp+1148672);
vm.roll(block.number + 653211);
lendingPool.borrow(345941753031933799366395);
vm.stopPrank();
vm.warp(block.timestamp+14784);
vm.roll(block.number + 1474);
lendingPool.setParameter(ILendingPool.OwnerParameter.LiquidationThreshold,1044);
vm.warp(block.timestamp+5437407);
vm.roll(block.number + 1619098);
vm.prank(ALICE);
lendingPool.initiateLiquidation(BOB);
vm.warp(block.timestamp+140 days);
vm.roll(block.number + uint(2961535)/140);
uint256 userDebt ;
uint256 scaledUserDebt;
uint256 beforeState = vm.snapshot();
lendingPool.updateState();
userDebt = lendingPool.getUserDebt(BOB);
console.log("User debt after the state update: %d",userDebt);
vm.revertTo(beforeState);
userDebt = lendingPool.getUserDebt(BOB);
console.log("User debt without updating the state: %d",userDebt);
scaledUserDebt = WadRayMath.rayMul(userDebt, lendingPool.getNormalizedDebt());
crvUSD.mint(address(stabilityPool), scaledUserDebt);
vm.expectRevert();
vm.prank(owner);
stabilityPool.liquidateBorrower(BOB);
}
function test_GaugeWeigthPOC() public {
vm.startPrank(BOB);
lendingPool.deposit(697106689045178731767167);
vm.warp(block.timestamp + 8801775);
vm.roll(block.number + 1931936);
stabilityPool.deposit(414308523880797109194139);
stabilityPool.withdraw(1702830198);
veRAAC.lock(15290, 31544587) ;
gaugeController.vote(gauges[0], 7174);
veRAAC.increase(43310635545591792929782);
vm.expectRevert(stdError.arithmeticError);
gaugeController.vote(gauges[0], 5362);
vm.stopPrank();
}
function test_increaseUnderflow() public {
vm.startPrank(ALICE);
lendingPool.deposit(14069761205393756697566);
stabilityPool.deposit(13624960859879652433378);
vm.warp(block.timestamp+ 7046960);
vm.roll(block.number + uint(7046960)/15);
stabilityPool.withdraw(5621714943215731129846);
veRAAC.lock(18407952740223189400, 115231135);
vm.warp(block.timestamp+ 2688);
vm.roll(block.number + uint(2688)/15);
vm.expectRevert(stdError.arithmeticError);
veRAAC.increase(6524);
vm.stopPrank();
}
function test_stuckFund() public {
vm.warp(block.timestamp + 20445256);
vm.roll(block.number + 134873);
vm.prank(owner);
raacHousePrices.setHousePrice(1, 104340533608757567757358);
vm.prank(ALICE);
raacNFT.mint(1, 104340533608757567757358);
vm.prank(ALICE);
raacNFT.approve(address(lendingPool), 1);
vm.warp(block.timestamp + 19516953);
vm.roll(block.number + 323395);
vm.startPrank(ALICE);
vm.warp(block.timestamp + 28940007);
vm.roll(block.number + 2464632);
lendingPool.deposit(99400641569938798721074);
vm.warp(block.timestamp + 2114289);
vm.roll(block.number + 1016112);
lendingPool.depositNFT(1);
vm.warp(block.timestamp + 13230446);
vm.roll(block.number + 1947131);
lendingPool.borrow(89049154235015082950118);
vm.warp(block.timestamp + 29353503);
vm.roll(block.number + 21);
lendingPool.repay(35083772923593810086738);
vm.warp(block.timestamp + 66);
vm.roll(block.number + 15);
stabilityPool.deposit(1e18);
vm.warp(block.timestamp + 28826820);
vm.roll(block.number + 856831);
stabilityPool.withdraw(1e18);
vm.stopPrank();
uint256 totalDeposit = stabilityPool.getTotalDeposits();
uint256 deTokenBalance = deToken.balanceOf(address(ALICE));
console.log("Total deposit: %d \n deToken balance of Alice : %d", totalDeposit, deTokenBalance);
assertGt(totalDeposit, 0, "deposit stuck");
assertEq(deTokenBalance, 0, "no deToken balance");
}
}
contract CurveMock is ERC4626 {
IERC20 curveToken;
constructor(address token) ERC4626(IERC20(token)) ERC20("curve vault", "CV") {}
function withdraw(uint256 assets, address receiver, address owner, uint256 maxLoss, address[] calldata strategies)
external
returns (uint256 shares) {
withdraw(assets, receiver, owner);
}
}
The stability pool should use the liquidity index to compute the amount to transfer to the user.