Skip to content

Commit ccfcbc0

Browse files
committed
strategy now works with ERC20 collateral instead of native tokens
1 parent ecdaa83 commit ccfcbc0

16 files changed

Lines changed: 1219 additions & 1808 deletions

script/WMEGAAddresses.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ pragma solidity ^0.8.20;
66
library WMEGAAddresses {
77
// MegaETH WMEGA addresses
88
address public constant WMEGA_MAINNET =
9-
0x4eB2Bd7beE16F38B1F4a0A5796Fffd028b6040e9;
9+
0x4200000000000000000000000000000000000006;
1010
address public constant WMEGA_TESTNET =
11-
0x4eB2Bd7beE16F38B1F4a0A5796Fffd028b6040e9;
11+
0x4200000000000000000000000000000000000006;
1212

1313
/// @dev Returns the appropriate WMEGA address for the current chain
1414
/// @return The WMEGA address, or address(0) for unsupported chains (local testing)

src/Strategy.sol

Lines changed: 94 additions & 91 deletions
Large diffs are not rendered by default.

test/StrategyAtomicity.t.sol

Lines changed: 45 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,17 @@
22
pragma solidity ^0.8.20;
33

44
import {Test, console} from "forge-std/Test.sol";
5-
import {Strategy, IWMEGA} from "../src/Strategy.sol";
5+
import {Strategy} from "../src/Strategy.sol";
66

7-
// Mock WMEGA for this standalone test
8-
contract MockWMEGALocal {
7+
// Mock MEGA ERC20 for this standalone test
8+
contract MockMEGALocal {
99
mapping(address => uint256) public balanceOf;
1010
mapping(address => mapping(address => uint256)) public allowance;
11+
uint256 public totalSupply;
1112

12-
function deposit() external payable {
13-
balanceOf[msg.sender] += msg.value;
14-
}
15-
16-
function withdraw(uint256 amount) external {
17-
require(balanceOf[msg.sender] >= amount, "Insufficient balance");
18-
balanceOf[msg.sender] -= amount;
19-
(bool success, ) = msg.sender.call{value: amount}("");
20-
require(success, "MEGA transfer failed");
13+
function mint(address to, uint256 amount) external {
14+
balanceOf[to] += amount;
15+
totalSupply += amount;
2116
}
2217

2318
function approve(address spender, uint256 amount) external returns (bool) {
@@ -49,15 +44,11 @@ contract MockWMEGALocal {
4944

5045
return true;
5146
}
52-
53-
receive() external payable {
54-
balanceOf[msg.sender] += msg.value;
55-
}
5647
}
5748

5849
contract StrategyAtomicityTest is Test {
5950
Strategy public giga;
60-
MockWMEGALocal public wmega;
51+
MockMEGALocal public mega;
6152

6253
address public alice = address(0x1);
6354
address public bob = address(0x2);
@@ -78,40 +69,40 @@ contract StrategyAtomicityTest is Test {
7869
);
7970

8071
function setUp() public {
81-
wmega = new MockWMEGALocal();
82-
giga = new Strategy(address(wmega));
72+
mega = new MockMEGALocal();
73+
giga = new Strategy(address(mega));
8374

84-
vm.deal(alice, 100 ether);
85-
vm.deal(bob, 100 ether);
86-
vm.deal(charlie, 100 ether);
75+
// Fund accounts with MEGA tokens
76+
mega.mint(alice, 100 ether);
77+
mega.mint(bob, 100 ether);
78+
mega.mint(charlie, 100 ether);
79+
}
80+
81+
// Helper to approve and mint
82+
function mintGiga(address user, uint256 amount) internal {
83+
vm.startPrank(user);
84+
mega.approve(address(giga), amount);
85+
giga.mint(amount);
86+
vm.stopPrank();
8787
}
8888

8989
function testFenwickTreeAtomicityDuringTransfers() public {
9090
// Setup: Create holders with exact amounts
91-
vm.expectEmit(true, true, true, true);
92-
emit Minted(alice, 10 ether, 9.9 ether, 0.1 ether);
93-
vm.prank(alice);
94-
giga.mint{value: 10 ether}();
91+
mintGiga(alice, 10 ether);
9592
assertEq(
9693
giga.balanceOf(alice),
9794
9.9 ether,
9895
"Alice should have 9,900 tokens"
9996
);
10097

101-
vm.expectEmit(true, true, true, true);
102-
emit Minted(bob, 5 ether, 4.95 ether, 0.05 ether);
103-
vm.prank(bob);
104-
giga.mint{value: 5 ether}();
98+
mintGiga(bob, 5 ether);
10599
assertEq(
106100
giga.balanceOf(bob),
107101
4.95 ether,
108102
"Bob should have 4,950 tokens"
109103
);
110104

111-
vm.expectEmit(true, true, true, true);
112-
emit Minted(charlie, 3 ether, 2.97 ether, 0.03 ether);
113-
vm.prank(charlie);
114-
giga.mint{value: 3 ether}();
105+
mintGiga(charlie, 3 ether);
115106
assertEq(
116107
giga.balanceOf(charlie),
117108
2.97 ether,
@@ -170,10 +161,7 @@ contract StrategyAtomicityTest is Test {
170161

171162
function testFenwickTreeAtomicityDuringMintAndBurn() public {
172163
// Initial mint
173-
vm.expectEmit(true, true, true, true);
174-
emit Minted(alice, 10 ether, 9.9 ether, 0.1 ether);
175-
vm.prank(alice);
176-
giga.mint{value: 10 ether}();
164+
mintGiga(alice, 10 ether);
177165
assertEq(
178166
giga.balanceOf(alice),
179167
9.9 ether,
@@ -216,22 +204,13 @@ contract StrategyAtomicityTest is Test {
216204
);
217205

218206
// Add another holder - mint slightly more to meet minimum requirement
219-
// After minting period, there's a minimum mint of 100 wei
220-
// 0.0001 MEGA will mint ~0.1 tokens (proportional to backing ratio)
221-
// But actually after redemption backing changed, need to calculate properly
222-
// Contract has ~9.901 MEGA, total supply is 9900 tokens
223-
// To mint 100 wei: need (100 * 9.901) / 9900e18 = ~1e-16 MEGA
224-
// But that's too small, let's mint 0.0001 MEGA to get a reasonable amount
225-
uint256 mintNative = 0.0001 ether;
226-
uint256 expectedTokens = (mintNative * giga.totalSupply()) /
227-
address(giga).balance;
207+
uint256 mintAmount = 0.0001 ether;
208+
uint256 expectedTokens = (mintAmount * giga.totalSupply()) /
209+
giga.getMegaReserve();
228210
uint256 expectedFee = expectedTokens / 100;
229211
uint256 expectedNet = expectedTokens - expectedFee;
230212

231-
vm.expectEmit(true, true, true, true);
232-
emit Minted(bob, mintNative, expectedNet, expectedFee);
233-
vm.prank(bob);
234-
giga.mint{value: mintNative}(); // Within capacity after redemption
213+
mintGiga(bob, mintAmount);
235214
assertEq(
236215
giga.balanceOf(bob),
237216
expectedNet,
@@ -253,9 +232,11 @@ contract StrategyAtomicityTest is Test {
253232
address[10] memory users;
254233
for (uint256 i = 0; i < 10; i++) {
255234
users[i] = address(uint160(0x100 + i));
256-
vm.deal(users[i], 10 ether);
257-
vm.prank(users[i]);
258-
giga.mint{value: 1 ether}();
235+
mega.mint(users[i], 10 ether);
236+
vm.startPrank(users[i]);
237+
mega.approve(address(giga), 1 ether);
238+
giga.mint(1 ether);
239+
vm.stopPrank();
259240
}
260241

261242
// Verify initial state
@@ -300,12 +281,11 @@ contract StrategyAtomicityTest is Test {
300281
// the Fenwick tree remains consistent due to atomic updates
301282

302283
// Create a malicious contract that tries to reenter
303-
MaliciousReentrant malicious = new MaliciousReentrant(giga);
304-
vm.deal(address(malicious), 10 ether);
284+
MaliciousReentrant malicious = new MaliciousReentrant(giga, mega);
285+
mega.mint(address(malicious), 10 ether);
305286

306287
// Initial state
307-
vm.prank(alice);
308-
giga.mint{value: 10 ether}();
288+
mintGiga(alice, 10 ether);
309289

310290
uint256 initialFenwick = giga.getSuffixSum(1);
311291
assertEq(
@@ -360,8 +340,7 @@ contract StrategyAtomicityTest is Test {
360340
// Test that Fenwick tree correctly handles accounts going to/from zero balance
361341

362342
// Alice mints
363-
vm.prank(alice);
364-
giga.mint{value: 1 ether}();
343+
mintGiga(alice, 1 ether);
365344

366345
uint256 aliceBalance = giga.balanceOf(alice);
367346
uint256 fenwick1 = giga.getSuffixSum(1);
@@ -380,8 +359,7 @@ contract StrategyAtomicityTest is Test {
380359
);
381360

382361
// Alice mints again (goes from 0 to positive)
383-
vm.prank(alice);
384-
giga.mint{value: 2 ether}();
362+
mintGiga(alice, 2 ether);
385363

386364
// Both should be tracked now
387365
uint256 fenwick3 = giga.getSuffixSum(1);
@@ -393,18 +371,21 @@ contract StrategyAtomicityTest is Test {
393371
// Helper contract for reentrancy test
394372
contract MaliciousReentrant {
395373
Strategy public giga;
374+
MockMEGALocal public mega;
396375
bool public attacked = false;
397376

398-
constructor(Strategy _giga) {
377+
constructor(Strategy _giga, MockMEGALocal _mega) {
399378
giga = _giga;
379+
mega = _mega;
400380
}
401381

402382
// Try to reenter when receiving tokens
403383
function onERC20Received(address, uint256) external returns (bytes4) {
404384
if (!attacked) {
405385
attacked = true;
406386
// Try to mint during a transfer (should fail due to reentrancy guard)
407-
try giga.mint{value: 1 ether}() {
387+
mega.approve(address(giga), 1 ether);
388+
try giga.mint(1 ether) {
408389
// Should not reach here
409390
} catch {
410391
// Expected to fail

0 commit comments

Comments
 (0)