Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions foundry.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
"name": "v5.5.0",
"rev": "fcbae5394ae8ad52d8e580a3477db99814b9d565"
}
},
"lib/openzeppelin-contracts-upgradeable": {
"rev": "7e1007d923edfddbb7d30ddf317f2fb52cde17be"
}
}
10 changes: 9 additions & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@ optimizer_runs = 10000
via_ir = false
evm_version = "cancun"
remappings = [
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/"
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/"
]
fs_permissions = [{ access = "read-write", path = "./deployments" }]
gas_reports = ["SplitBaseV1", "RegistryV1", "ExecutorV1"]

[fuzz]
runs = 256

[invariant]
runs = 256

[rpc_endpoints]
base = "https://mainnet.base.org"
Expand Down
207 changes: 207 additions & 0 deletions test/ExecutorV1.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {Test} from "forge-std/Test.sol";
import {ExecutorV1} from "../src/ExecutorV1.sol";
import {SplitBaseV1} from "../src/SplitBaseV1.sol";
import {MockUSDC} from "./mocks/MockUSDC.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract ExecutorV1Test is Test {
ExecutorV1 public executor;
SplitBaseV1 public splitBase;
MockUSDC public usdc;

address public owner = address(this);
address public user1 = address(0x1);
address public user2 = address(0x2);
address public executor1 = address(0x10);
address public executor2 = address(0x20);
address public recipient1 = address(0x3);
address public recipient2 = address(0x4);

event ExecutionScheduled(uint256 indexed poolId, address indexed executor, uint256 amount, uint256 scheduledAt);
event ExecutionCompleted(uint256 indexed poolId, uint256 amount, uint256 executedAt);
event ExecutorAdded(uint256 indexed poolId, address indexed executor);
event ExecutorRemoved(uint256 indexed poolId, address indexed executor);

function setUp() public {
usdc = new MockUSDC();

SplitBaseV1 splitBaseImpl = new SplitBaseV1();
ERC1967Proxy splitBaseProxy = new ERC1967Proxy(
address(splitBaseImpl),
abi.encodeCall(SplitBaseV1.initialize, (address(usdc)))
);
splitBase = SplitBaseV1(address(splitBaseProxy));

ExecutorV1 executorImpl = new ExecutorV1();
ERC1967Proxy executorProxy = new ERC1967Proxy(
address(executorImpl),
abi.encodeCall(ExecutorV1.initialize, (address(splitBase), address(usdc)))
);
executor = ExecutorV1(address(executorProxy));
}

function testInitialize() public view {
assertEq(address(executor.splitBase()), address(splitBase));
assertEq(address(executor.usdc()), address(usdc));
assertEq(executor.owner(), owner);
}

function testRegisterPool() public {
uint256 poolId = splitBase.createPool();

executor.registerPool(poolId);

assertEq(executor.poolOwners(poolId), owner);
}

function testRegisterPoolUnauthorized() public {
uint256 poolId = splitBase.createPool();

vm.prank(user1);
vm.expectRevert(ExecutorV1.Unauthorized.selector);
executor.registerPool(poolId);
}

function testAddExecutor() public {
uint256 poolId = splitBase.createPool();
executor.registerPool(poolId);

vm.expectEmit(true, true, false, false);
emit ExecutorAdded(poolId, executor1);

executor.addExecutor(poolId, executor1);

assertTrue(executor.executors(poolId, executor1));
}

function testAddExecutorUnauthorized() public {
uint256 poolId = splitBase.createPool();
executor.registerPool(poolId);

vm.prank(user1);
vm.expectRevert(ExecutorV1.Unauthorized.selector);
executor.addExecutor(poolId, executor1);
}

function testRemoveExecutor() public {
uint256 poolId = splitBase.createPool();
executor.registerPool(poolId);
executor.addExecutor(poolId, executor1);

assertTrue(executor.executors(poolId, executor1));

vm.expectEmit(true, true, false, false);
emit ExecutorRemoved(poolId, executor1);

executor.removeExecutor(poolId, executor1);

assertFalse(executor.executors(poolId, executor1));
}

function testExecuteFullFlow() public {
vm.prank(address(executor));
uint256 poolId = splitBase.createPool();

vm.startPrank(address(executor));
splitBase.addRecipient(poolId, recipient1, 100);
splitBase.addRecipient(poolId, recipient2, 200);
executor.registerPool(poolId);
executor.addExecutor(poolId, executor1);
vm.stopPrank();

uint256 amount = 300_000000;
usdc.mint(executor1, amount);

vm.startPrank(executor1);
usdc.approve(address(executor), amount);

vm.expectEmit(true, false, false, false);
emit ExecutionCompleted(poolId, amount, 0);

executor.execute(poolId, amount);
vm.stopPrank();

assertEq(usdc.balanceOf(recipient1), 100_000000);
assertEq(usdc.balanceOf(recipient2), 200_000000);
assertEq(usdc.balanceOf(executor1), 0);
}

function testExecuteUnauthorized() public {
vm.prank(address(executor));
uint256 poolId = splitBase.createPool();

vm.prank(address(executor));
executor.registerPool(poolId);

uint256 amount = 100_000000;
usdc.mint(user1, amount);

vm.startPrank(user1);
usdc.approve(address(executor), amount);

vm.expectRevert(ExecutorV1.Unauthorized.selector);
executor.execute(poolId, amount);
vm.stopPrank();
}

function testExecutePoolOwnerCanExecute() public {
vm.prank(address(executor));
uint256 poolId = splitBase.createPool();

vm.startPrank(address(executor));
splitBase.addRecipient(poolId, recipient1, 100);
splitBase.addRecipient(poolId, recipient2, 200);
executor.registerPool(poolId);
vm.stopPrank();

uint256 amount = 300_000000;
usdc.mint(address(executor), amount);

vm.startPrank(address(executor));
usdc.approve(address(executor), amount);

executor.execute(poolId, amount);
vm.stopPrank();

assertEq(usdc.balanceOf(recipient1), 100_000000);
assertEq(usdc.balanceOf(recipient2), 200_000000);
}

function testScheduleExecution() public {
uint256 poolId = splitBase.createPool();
executor.registerPool(poolId);
executor.addExecutor(poolId, executor1);

uint256 amount = 100_000000;

vm.prank(executor1);
vm.expectEmit(true, true, false, true);
emit ExecutionScheduled(poolId, executor1, amount, block.timestamp);

executor.scheduleExecution(poolId, amount);
}

function testScheduleExecutionUnauthorized() public {
uint256 poolId = splitBase.createPool();
executor.registerPool(poolId);

uint256 amount = 100_000000;

vm.prank(user1);
vm.expectRevert(ExecutorV1.Unauthorized.selector);
executor.scheduleExecution(poolId, amount);
}

function testUpgradeAuthorization() public {
ExecutorV1 newImplementation = new ExecutorV1();

executor.upgradeToAndCall(address(newImplementation), "");

vm.prank(user1);
vm.expectRevert();
executor.upgradeToAndCall(address(newImplementation), "");
}
}
Loading