Files
fluxswap/test/FluxPair.t.sol
T

136 lines
4.7 KiB
Solidity

// SPDX-License-Identifier: MIT
pragma solidity 0.8.36;
import {Test} from "forge-std/Test.sol";
import {FluxFactory} from "../src/FluxFactory.sol";
import {FluxPair} from "../src/FluxPair.sol";
import {TestErc20} from "../src/TestErc20.sol";
import {IFluxCallee} from "../src/interfaces/IFluxCallee.sol";
contract ReentrantSwapper is IFluxCallee {
FluxPair private pair;
constructor(FluxPair pairAddress) {
pair = pairAddress;
}
function fluxCall(address, uint256, uint256, bytes calldata) external {
pair.swap(0, 1, address(this), "");
}
}
contract FluxPairTest is Test {
address private constant receiver = address(0xBEEF);
FluxFactory private factory;
FluxPair private pair;
TestErc20 private token0;
TestErc20 private token1;
uint256 private initialLp;
function setUp() public {
factory = new FluxFactory(address(this));
TestErc20 tokenA = new TestErc20("Alpha", "ALPHA", 1_000_000e18);
TestErc20 tokenB = new TestErc20("Beta", "BETA", 1_000_000e18);
address pairAddress = factory.createPair(address(tokenA), address(tokenB));
pair = FluxPair(pairAddress);
token0 = TestErc20(pair.token0());
token1 = TestErc20(pair.token1());
token0.transfer(pairAddress, 100e18);
token1.transfer(pairAddress, 400e18);
initialLp = pair.mint(address(this));
}
function testInitialMint() public view {
assertEq(initialLp, 199999999999999999000);
assertEq(pair.balanceOf(address(this)), 199999999999999999000);
assertEq(pair.totalSupply(), 200e18);
assertEq(pair.balanceOf(address(0)), 1000);
(uint112 reserve0, uint112 reserve1,) = pair.getReserves();
assertEq(reserve0, 100e18);
assertEq(reserve1, 400e18);
}
function testProportionalMint() public {
token0.transfer(address(pair), 50e18);
token1.transfer(address(pair), 200e18);
uint256 minted = pair.mint(receiver);
assertEq(minted, 100e18);
assertEq(pair.balanceOf(receiver), 100e18);
assertEq(pair.totalSupply(), 300e18);
(uint112 reserve0, uint112 reserve1,) = pair.getReserves();
assertEq(reserve0, 150e18);
assertEq(reserve1, 600e18);
}
function testSwapMovesReservesAndKeepsInvariant() public {
uint256 expectedOut = 36264435755205965263;
uint256 kBefore = uint256(100e18) * 400e18;
token0.transfer(address(pair), 10e18);
pair.swap(0, expectedOut, receiver, "");
assertEq(token1.balanceOf(receiver), expectedOut);
(uint112 reserve0, uint112 reserve1,) = pair.getReserves();
assertEq(reserve0, 110e18);
assertEq(reserve1, 363735564244794034737);
assertGt(uint256(reserve0) * uint256(reserve1), kBefore);
}
function testSwapRevertsWhenInvariantBroken() public {
token0.transfer(address(pair), 10e18);
vm.expectRevert(bytes("Flux: K"));
pair.swap(0, 36264435755205965263 + 1e15, receiver, "");
}
function testSwapRevertsWithoutInput() public {
vm.expectRevert(bytes("Flux: INSUFFICIENT_INPUT_AMOUNT"));
pair.swap(0, 1e18, receiver, "");
}
function testBurnReturnsProportional() public {
uint256 lp = pair.balanceOf(address(this));
pair.transfer(address(pair), lp);
(uint256 amount0, uint256 amount1) = pair.burn(receiver);
assertEq(amount0, 99999999999999999500);
assertEq(amount1, 399999999999999998000);
assertEq(token0.balanceOf(receiver), 99999999999999999500);
assertEq(token1.balanceOf(receiver), 399999999999999998000);
assertEq(pair.totalSupply(), 1000);
}
function testSkimSendsExcessAndKeepsReserves() public {
token0.transfer(address(pair), 5e18);
pair.skim(receiver);
assertEq(token0.balanceOf(receiver), 5e18);
(uint112 reserve0,,) = pair.getReserves();
assertEq(reserve0, 100e18);
}
function testSyncAdoptsBalances() public {
token0.transfer(address(pair), 5e18);
pair.sync();
(uint112 reserve0, uint112 reserve1,) = pair.getReserves();
assertEq(reserve0, 105e18);
assertEq(reserve1, 400e18);
}
function testPriceAccumulatorGrowsOverTime() public {
assertEq(pair.price0CumulativeLast(), 0);
vm.warp(block.timestamp + 3600);
pair.sync();
assertGt(pair.price0CumulativeLast(), 0);
assertGt(pair.price1CumulativeLast(), 0);
}
function testReentrancyLockBlocksSwap() public {
ReentrantSwapper attacker = new ReentrantSwapper(pair);
token0.transfer(address(pair), 10e18);
vm.expectRevert(bytes("Flux: LOCKED"));
pair.swap(0, 1e18, address(attacker), "0x01");
}
}