pragma solidity ^0.8.20;
import {Test, console} from "forge-std/Test.sol";
import {Distribution} from "../contracts/Distribution.sol";
import {IDistribution} from "../contracts/interfaces/IDistribution.sol";
import {LinearDistributionIntervalDecrease} from "../contracts/libs/LinearDistributionIntervalDecrease.sol";
import {MOR} from "../contracts/MOR.sol";
import {StETHMock} from "../contracts/mock/tokens/StETHMock.sol";
import {WStETHMock} from "../contracts/mock/tokens/WStETHMock.sol";
import {LZEndpointMock} from "@layerzerolabs/solidity-examples/contracts/lzApp/mocks/LZEndpointMock.sol";
import {L1Sender} from "../contracts/L1Sender.sol";
import {IL1Sender} from "../contracts/interfaces/IL1Sender.sol";
import {L2MessageReceiver} from "../contracts/L2MessageReceiver.sol";
import {IL2MessageReceiver} from "../contracts/interfaces/IL2MessageReceiver.sol";
import {L2TokenReceiver} from "../contracts/L2TokenReceiver.sol";
import {IL2TokenReceiver} from "../contracts/interfaces/IL2TokenReceiver.sol";
import {GatewayRouterMock} from "../contracts/mock/GatewayRouterMock.sol";
import {SwapRouterMock} from "../contracts/mock/SwapRouterMock.sol";
import {NonfungiblePositionManagerMock} from "../contracts/mock/NonfungiblePositionManagerMock.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract DistributionTest is Test {
Distribution distribution;
MOR mor;
StETHMock stETH;
WStETHMock wstETH;
LZEndpointMock lZEndpointMockSender;
LZEndpointMock lZEndpointMockReceiver;
L1Sender l1Sender;
L2MessageReceiver l2MessageReceiver;
L2TokenReceiver l2TokenReceiver;
NonfungiblePositionManagerMock nonfungiblePositionManager;
SwapRouterMock swapRouterMock;
GatewayRouterMock gatewayRouter;
ERC1967Proxy l2MessageReceiverProxy;
ERC1967Proxy l2TokenReceiverProxy;
ERC1967Proxy l1SenderProxy;
ERC1967Proxy distributionProxy;
uint16 public senderChainId = 101;
uint16 public receiverChainId = 110;
address public owner = makeAddr("owner");
address public user1 = makeAddr("user1");
address public user2 = makeAddr("user2");
address public rewardToken;
address public depositToken;
address public l2TokenReceiverImplementation;
address public l2MessageReceiverImplementation;
address public l1SenderImplementation;
function setUp() public {
vm.warp(1 hours);
distribution = new Distribution();
mor = new MOR(1000000000 ether);
rewardToken = address(mor);
stETH = new StETHMock();
depositToken = address(stETH);
wstETH = new WStETHMock(depositToken);
lZEndpointMockSender = new LZEndpointMock(senderChainId);
lZEndpointMockReceiver = new LZEndpointMock(receiverChainId);
l1Sender = new L1Sender();
l2MessageReceiver = new L2MessageReceiver();
l2TokenReceiver = new L2TokenReceiver();
l2TokenReceiverImplementation = address(l2TokenReceiver);
l2MessageReceiverImplementation = address(l2MessageReceiver);
l1SenderImplementation = address(l1Sender);
swapRouterMock = new SwapRouterMock();
nonfungiblePositionManager = new NonfungiblePositionManagerMock();
gatewayRouter = new GatewayRouterMock();
Distribution.Pool[] memory pools;
L2TokenReceiver.SwapParams memory swapParams = IL2TokenReceiver.SwapParams({
tokenIn: depositToken,
tokenOut: depositToken,
fee: 3000,
sqrtPriceLimitX96: 0
});
l2MessageReceiverProxy =
new ERC1967Proxy(l2MessageReceiverImplementation, abi.encodeWithSignature("L2MessageReceiver__init()"));
l2TokenReceiverProxy = new ERC1967Proxy(
l2TokenReceiverImplementation,
abi.encodeWithSignature(
"L2TokenReceiver__init(address,address,(address,address,uint24,uint160))",
address(swapRouterMock),
address(nonfungiblePositionManager),
swapParams
)
);
IL1Sender.RewardTokenConfig memory rewardTokenConfig = IL1Sender.RewardTokenConfig({
gateway: address(lZEndpointMockSender),
receiver: address(l2MessageReceiverProxy),
receiverChainId: receiverChainId
});
IL1Sender.DepositTokenConfig memory depositTokenConfig = IL1Sender.DepositTokenConfig({
token: address(wstETH),
gateway: address(gatewayRouter),
receiver: address(l2TokenReceiverProxy)
});
distributionProxy = new ERC1967Proxy(address(distribution), bytes(""));
l1SenderProxy = new ERC1967Proxy(
l1SenderImplementation,
abi.encodeWithSignature(
"L1Sender__init(address,(address,address,uint16),(address,address,address))",
address(distributionProxy),
rewardTokenConfig,
depositTokenConfig
)
);
(bool success,) = address(distributionProxy).call(
abi.encodeWithSelector(Distribution.Distribution_init.selector, depositToken, address(l1SenderProxy), pools)
);
mor.transferOwnership(address(l2MessageReceiverProxy));
L2MessageReceiver.Config memory config = IL2MessageReceiver.Config({
gateway: address(lZEndpointMockReceiver),
sender: address(l1SenderProxy),
senderChainId: senderChainId
});
(success,) = address(l2MessageReceiverProxy).call(
abi.encodeWithSelector(IL2MessageReceiver.setParams.selector, rewardToken, config)
);
lZEndpointMockSender.setDestLzEndpoint(address(l2MessageReceiverProxy), address(lZEndpointMockReceiver));
stETH.mint(owner, 1000e18);
stETH.mint(user1, 1000e18);
stETH.mint(user2, 1000e18);
stETH.approve(address(distributionProxy), 1000e18);
vm.prank(user1);
stETH.approve(address(distributionProxy), 1000e18);
vm.prank(user2);
stETH.approve(address(distributionProxy), 1000e18);
(success,) = address(l1SenderProxy).call(
abi.encodeWithSelector(l1Sender.transferOwnership.selector, address(distributionProxy))
);
}
modifier createPool() {
uint256 poolId = 0;
Distribution.Pool memory pool = IDistribution.Pool({
payoutStart: 1 days,
decreaseInterval: 1 days,
withdrawLockPeriod: 12 hours,
claimLockPeriod: 12 hours,
withdrawLockPeriodAfterStake: 1 days,
initialReward: 100e18,
rewardDecrease: 2e18,
minimalStake: 0.1e18,
isPublic: true
});
(bool success,) =
address(distributionProxy).call(abi.encodeWithSelector(Distribution.createPool.selector, pool));
_;
}
function testClaimOnBehalf() public createPool {
uint256 poolId = 0;
vm.warp(2 hours);
vm.roll(2 hours);
vm.deal(user1, 1000 ether);
vm.deal(user2, 1000 ether);
vm.prank(user2);
(bool success,) =
address(distributionProxy).call(abi.encodeWithSelector(Distribution.stake.selector, poolId, 1e18));
vm.startPrank(user1);
(success,) = address(distributionProxy).call(abi.encodeWithSelector(Distribution.stake.selector, poolId, 1e18));
vm.warp(2 days);
vm.roll(2 days);
(success,) = address(distributionProxy).call{value: 0.5 ether}(
abi.encodeWithSelector(Distribution.claim.selector, poolId, user1)
);
console.log("balance of user1, first claim %s", mor.balanceOf(user1));
vm.stopPrank();
vm.startPrank(user2);
vm.warp(2 days + 1 days);
vm.roll(2 days + 1 days);
(success,) = address(distributionProxy).call{value: 0.5 ether}(
abi.encodeWithSelector(Distribution.claim.selector, poolId, user1)
);
console.log("balance of user1, second claim %s", mor.balanceOf(user1));
}
function testClaimNormal() public createPool {
uint256 poolId = 0;
vm.warp(2 hours);
vm.roll(2 hours);
vm.deal(user1, 1000 ether);
vm.deal(user2, 1000 ether);
vm.prank(user2);
(bool success,) =
address(distributionProxy).call(abi.encodeWithSelector(Distribution.stake.selector, poolId, 1e18));
vm.startPrank(user1);
(success,) = address(distributionProxy).call(abi.encodeWithSelector(Distribution.stake.selector, poolId, 1e18));
vm.warp(2 days);
vm.roll(2 days);
(success,) = address(distributionProxy).call{value: 0.5 ether}(
abi.encodeWithSelector(Distribution.claim.selector, poolId, user1)
);
console.log("balance of user1, first claim %s", mor.balanceOf(user1));
vm.stopPrank();
vm.prank(user2);
(success,) =
address(distributionProxy).call(abi.encodeWithSelector(Distribution.withdraw.selector, poolId, 1e18));
vm.warp(2 days + 1 days);
vm.roll(2 days + 1 days);
vm.startPrank(user1);
(success,) = address(distributionProxy).call{value: 0.5 ether}(
abi.encodeWithSelector(Distribution.claim.selector, poolId, user1)
);
console.log("balance of user1, second claim %s", mor.balanceOf(user1));
}
}