Skip to content

Commit 9ed0c23

Browse files
authored
chore: Migrate spokepool contract test to foundry (#1289)
* chore: Migrate spokepool contract test to foundry Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * more tests Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * svm + evm mixed leaf tests Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * added rest of the tests Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * delete old test Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> --------- Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
1 parent 6632890 commit 9ed0c23

16 files changed

+4265
-2411
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Test } from "forge-std/Test.sol";
5+
import { ERC1967Proxy } from "@openzeppelin/contracts-v4/proxy/ERC1967/ERC1967Proxy.sol";
6+
import { MockSpokePool } from "../../../../../contracts/test/MockSpokePool.sol";
7+
import { WETH9 } from "../../../../../contracts/external/WETH9.sol";
8+
9+
contract SpokePoolAdminTest is Test {
10+
MockSpokePool public spokePool;
11+
WETH9 public weth;
12+
address public owner;
13+
address public crossDomainAdmin;
14+
address public hubPool;
15+
16+
bytes32 public mockRelayerRefundRoot;
17+
bytes32 public mockSlowRelayRoot;
18+
19+
event PausedDeposits(bool isPaused);
20+
event PausedFills(bool isPaused);
21+
event EmergencyDeletedRootBundle(uint256 indexed rootBundleId);
22+
23+
function setUp() public {
24+
owner = makeAddr("owner");
25+
crossDomainAdmin = makeAddr("crossDomainAdmin");
26+
hubPool = makeAddr("hubPool");
27+
28+
mockRelayerRefundRoot = keccak256("mockRelayerRefundRoot");
29+
mockSlowRelayRoot = keccak256("mockSlowRelayRoot");
30+
31+
weth = new WETH9();
32+
33+
vm.startPrank(owner);
34+
MockSpokePool implementation = new MockSpokePool(address(weth));
35+
address proxy = address(
36+
new ERC1967Proxy(
37+
address(implementation),
38+
abi.encodeCall(MockSpokePool.initialize, (0, crossDomainAdmin, hubPool))
39+
)
40+
);
41+
spokePool = MockSpokePool(payable(proxy));
42+
vm.stopPrank();
43+
}
44+
45+
function testCanSetInitialDepositId() public {
46+
vm.startPrank(owner);
47+
MockSpokePool implementation = new MockSpokePool(address(weth));
48+
address proxy = address(
49+
new ERC1967Proxy(
50+
address(implementation),
51+
abi.encodeCall(MockSpokePool.initialize, (1, crossDomainAdmin, hubPool))
52+
)
53+
);
54+
MockSpokePool newSpokePool = MockSpokePool(payable(proxy));
55+
vm.stopPrank();
56+
57+
assertEq(newSpokePool.numberOfDeposits(), 1);
58+
}
59+
60+
function testPauseDeposits() public {
61+
assertEq(spokePool.pausedDeposits(), false);
62+
63+
vm.startPrank(owner);
64+
65+
vm.expectEmit(true, true, true, true);
66+
emit PausedDeposits(true);
67+
spokePool.pauseDeposits(true);
68+
assertEq(spokePool.pausedDeposits(), true);
69+
70+
vm.expectEmit(true, true, true, true);
71+
emit PausedDeposits(false);
72+
spokePool.pauseDeposits(false);
73+
assertEq(spokePool.pausedDeposits(), false);
74+
75+
vm.stopPrank();
76+
}
77+
78+
function testPauseFills() public {
79+
assertEq(spokePool.pausedFills(), false);
80+
81+
vm.startPrank(owner);
82+
83+
vm.expectEmit(true, true, true, true);
84+
emit PausedFills(true);
85+
spokePool.pauseFills(true);
86+
assertEq(spokePool.pausedFills(), true);
87+
88+
vm.expectEmit(true, true, true, true);
89+
emit PausedFills(false);
90+
spokePool.pauseFills(false);
91+
assertEq(spokePool.pausedFills(), false);
92+
93+
vm.stopPrank();
94+
}
95+
96+
function testDeleteRootBundle() public {
97+
vm.startPrank(owner);
98+
spokePool.relayRootBundle(mockRelayerRefundRoot, mockSlowRelayRoot);
99+
100+
(bytes32 slowRelayRoot, bytes32 relayerRefundRoot) = spokePool.rootBundles(0);
101+
assertEq(slowRelayRoot, mockSlowRelayRoot);
102+
assertEq(relayerRefundRoot, mockRelayerRefundRoot);
103+
104+
vm.expectEmit(true, true, true, true);
105+
emit EmergencyDeletedRootBundle(0);
106+
spokePool.emergencyDeleteRootBundle(0);
107+
108+
(slowRelayRoot, relayerRefundRoot) = spokePool.rootBundles(0);
109+
assertEq(slowRelayRoot, bytes32(0));
110+
assertEq(relayerRefundRoot, bytes32(0));
111+
112+
vm.stopPrank();
113+
}
114+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Test } from "forge-std/Test.sol";
5+
import { ERC1967Proxy } from "@openzeppelin/contracts-v4/proxy/ERC1967/ERC1967Proxy.sol";
6+
import { MockSpokePool } from "../../../../../contracts/test/MockSpokePool.sol";
7+
import { ExpandedERC20WithBlacklist } from "../../../../../contracts/test/ExpandedERC20WithBlacklist.sol";
8+
import { WETH9 } from "../../../../../contracts/external/WETH9.sol";
9+
import { AddressToBytes32 } from "../../../../../contracts/libraries/AddressConverters.sol";
10+
11+
contract SpokePoolClaimRelayerRefundTest is Test {
12+
using AddressToBytes32 for address;
13+
14+
MockSpokePool public spokePool;
15+
ExpandedERC20WithBlacklist public destErc20;
16+
WETH9 public weth;
17+
18+
address public owner;
19+
address public relayer;
20+
address public rando;
21+
22+
uint256 public constant AMOUNT_TO_RELAY = 25e18;
23+
uint256 public constant AMOUNT_HELD_BY_POOL = AMOUNT_TO_RELAY * 4;
24+
uint256 public constant AMOUNT_TO_RETURN = 1e18;
25+
uint256 public constant DESTINATION_CHAIN_ID = 1342;
26+
27+
function setUp() public {
28+
owner = makeAddr("owner");
29+
relayer = makeAddr("relayer");
30+
rando = makeAddr("rando");
31+
32+
weth = new WETH9();
33+
34+
// Deploy destErc20 with blacklist functionality
35+
destErc20 = new ExpandedERC20WithBlacklist("L2 USD Coin", "L2 USDC", 18);
36+
// Add this test contract as minter (Minter role = 1)
37+
destErc20.addMember(1, address(this));
38+
39+
// Deploy SpokePool via proxy
40+
vm.startPrank(owner);
41+
MockSpokePool implementation = new MockSpokePool(address(weth));
42+
address proxy = address(
43+
new ERC1967Proxy(address(implementation), abi.encodeCall(MockSpokePool.initialize, (0, owner, owner)))
44+
);
45+
spokePool = MockSpokePool(payable(proxy));
46+
spokePool.setChainId(DESTINATION_CHAIN_ID);
47+
vm.stopPrank();
48+
49+
// Seed the SpokePool with tokens
50+
destErc20.mint(address(spokePool), AMOUNT_HELD_BY_POOL);
51+
}
52+
53+
function testBlacklistOperatesAsExpected() public {
54+
// Transfer tokens to relayer before blacklisting works as expected
55+
destErc20.mint(owner, AMOUNT_TO_RELAY);
56+
57+
vm.prank(owner);
58+
destErc20.transfer(relayer, AMOUNT_TO_RELAY);
59+
assertEq(destErc20.balanceOf(relayer), AMOUNT_TO_RELAY);
60+
61+
// Blacklist the relayer
62+
destErc20.setBlacklistStatus(relayer, true);
63+
64+
// Attempt to transfer tokens to the blacklisted relayer should revert
65+
destErc20.mint(owner, AMOUNT_TO_RELAY);
66+
vm.prank(owner);
67+
vm.expectRevert("Recipient is blacklisted");
68+
destErc20.transfer(relayer, AMOUNT_TO_RELAY);
69+
}
70+
71+
function testDistributeRelayerRefundsHandlesBlacklistedAddresses() public {
72+
// No starting relayer liability
73+
assertEq(spokePool.getRelayerRefund(address(destErc20), relayer), 0);
74+
assertEq(destErc20.balanceOf(rando), 0);
75+
assertEq(destErc20.balanceOf(relayer), 0);
76+
77+
// Blacklist the relayer
78+
destErc20.setBlacklistStatus(relayer, true);
79+
80+
// Distribute relayer refunds - some refunds go to blacklisted address, some to non-blacklisted
81+
uint256[] memory refundAmounts = new uint256[](2);
82+
refundAmounts[0] = AMOUNT_TO_RELAY;
83+
refundAmounts[1] = AMOUNT_TO_RELAY;
84+
85+
address[] memory refundAddresses = new address[](2);
86+
refundAddresses[0] = relayer;
87+
refundAddresses[1] = rando;
88+
89+
vm.prank(owner);
90+
spokePool.distributeRelayerRefunds(
91+
DESTINATION_CHAIN_ID,
92+
AMOUNT_TO_RETURN,
93+
refundAmounts,
94+
0,
95+
address(destErc20),
96+
refundAddresses
97+
);
98+
99+
// Blacklisted relayer should have their refund tracked in relayerRefund mapping
100+
assertEq(spokePool.getRelayerRefund(address(destErc20), relayer), AMOUNT_TO_RELAY);
101+
// Non-blacklisted address should receive tokens directly
102+
assertEq(destErc20.balanceOf(rando), AMOUNT_TO_RELAY);
103+
// Blacklisted relayer should not have received tokens
104+
assertEq(destErc20.balanceOf(relayer), 0);
105+
}
106+
107+
function testBlacklistedRelayerCanClaimRefundToNewAddress() public {
108+
// Blacklist the relayer
109+
destErc20.setBlacklistStatus(relayer, true);
110+
111+
// Distribute relayer refunds to blacklisted address
112+
uint256[] memory refundAmounts = new uint256[](1);
113+
refundAmounts[0] = AMOUNT_TO_RELAY;
114+
115+
address[] memory refundAddresses = new address[](1);
116+
refundAddresses[0] = relayer;
117+
118+
vm.prank(owner);
119+
spokePool.distributeRelayerRefunds(
120+
DESTINATION_CHAIN_ID,
121+
AMOUNT_TO_RETURN,
122+
refundAmounts,
123+
0,
124+
address(destErc20),
125+
refundAddresses
126+
);
127+
128+
// Attempting to claim to the blacklisted address should fail
129+
vm.prank(relayer);
130+
vm.expectRevert("Recipient is blacklisted");
131+
spokePool.claimRelayerRefund(address(destErc20).toBytes32(), relayer.toBytes32());
132+
133+
// Claiming to a different (non-blacklisted) address should succeed
134+
assertEq(destErc20.balanceOf(rando), 0);
135+
136+
vm.prank(relayer);
137+
spokePool.claimRelayerRefund(address(destErc20).toBytes32(), rando.toBytes32());
138+
139+
assertEq(destErc20.balanceOf(rando), AMOUNT_TO_RELAY);
140+
}
141+
}

0 commit comments

Comments
 (0)