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
2 changes: 1 addition & 1 deletion cache/solidity-files-cache.json

Large diffs are not rendered by default.

345 changes: 189 additions & 156 deletions frontend/src/config/contracts.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion out/PiggyBank.sol/PiggyBank.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion out/PiggyBank.t.sol/PiggyBankTest.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion out/PiggyBank.t.sol/ReentrancyAttacker.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions out/PiggyBankSecurity.t.sol/PiggyBankSecurityTest.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"id":"aab777803c1ade9c","source_id_to_path":{"0":"lib/forge-std/src/Base.sol","1":"lib/forge-std/src/StdAssertions.sol","2":"lib/forge-std/src/StdChains.sol","3":"lib/forge-std/src/StdCheats.sol","4":"lib/forge-std/src/StdConstants.sol","5":"lib/forge-std/src/StdError.sol","6":"lib/forge-std/src/StdInvariant.sol","7":"lib/forge-std/src/StdJson.sol","8":"lib/forge-std/src/StdMath.sol","9":"lib/forge-std/src/StdStorage.sol","10":"lib/forge-std/src/StdStyle.sol","11":"lib/forge-std/src/StdToml.sol","12":"lib/forge-std/src/StdUtils.sol","13":"lib/forge-std/src/Test.sol","14":"lib/forge-std/src/Vm.sol","15":"lib/forge-std/src/console.sol","16":"lib/forge-std/src/console2.sol","17":"lib/forge-std/src/interfaces/IMulticall3.sol","18":"lib/forge-std/src/safeconsole.sol","19":"src/PiggyBank.sol","20":"test/PiggyBank.t.sol"},"language":"Solidity"}
{"id":"1e2f2db3cba34709","source_id_to_path":{"0":"lib/forge-std/src/Base.sol","1":"lib/forge-std/src/StdAssertions.sol","2":"lib/forge-std/src/StdChains.sol","3":"lib/forge-std/src/StdCheats.sol","4":"lib/forge-std/src/StdConstants.sol","5":"lib/forge-std/src/StdError.sol","6":"lib/forge-std/src/StdInvariant.sol","7":"lib/forge-std/src/StdJson.sol","8":"lib/forge-std/src/StdMath.sol","9":"lib/forge-std/src/StdStorage.sol","10":"lib/forge-std/src/StdStyle.sol","11":"lib/forge-std/src/StdToml.sol","12":"lib/forge-std/src/StdUtils.sol","13":"lib/forge-std/src/Test.sol","14":"lib/forge-std/src/Vm.sol","15":"lib/forge-std/src/console.sol","16":"lib/forge-std/src/console2.sol","17":"lib/forge-std/src/interfaces/IMulticall3.sol","18":"lib/forge-std/src/safeconsole.sol","19":"src/PiggyBank.sol","20":"test/PiggyBank.t.sol","21":"test/PiggyBankSecurity.t.sol"},"language":"Solidity"}
152 changes: 77 additions & 75 deletions test/PiggyBankSecurity.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,267 +14,269 @@ contract PiggyBankSecurityTest is Test {
address public guardian = address(2);
address public user1 = address(3);
address public user2 = address(4);

uint256 public constant INITIAL_DEPOSIT = 1 ether;
uint256 public constant LOCK_TIME = 365 days;

event TestResult(string testName, bool passed);

event Deposited(address indexed depositor, uint256 amount);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);

function setUp() public {
vm.prank(owner);
piggyBank = new PiggyBank(block.timestamp + LOCK_TIME);

// Set emergency guardian
vm.prank(owner);
piggyBank.setEmergencyGuardian(guardian);

// Fund users for testing
vm.deal(user1, 10 ether);
vm.deal(user2, 10 ether);
}

// ============ SECURITY TESTS ============

function testDepositLimits() public {
vm.startPrank(user1);

// Test minimum deposit
vm.expectRevert(PiggyBank.PiggyBank__DepositTooLow.selector);
piggyBank.deposit{value: 0.0001 ether}();

// Test maximum deposit
vm.expectRevert(PiggyBank.PiggyBank__DepositTooHigh.selector);
piggyBank.deposit{value: 2000 ether}();

// Test valid deposit
piggyBank.deposit{value: INITIAL_DEPOSIT}();
assertEq(piggyBank.deposits(user1), INITIAL_DEPOSIT);

emit TestResult("Deposit Limits", true);
vm.stopPrank();
}

function testReentrancyProtection() public {
vm.startPrank(user1);
piggyBank.deposit{value: INITIAL_DEPOSIT}();

// Fast-forward time to unlock
vm.warp(block.timestamp + LOCK_TIME + 1);

// Multiple simultaneous withdraw attempts should be prevented
vm.expectRevert(PiggyBank.PiggyBank__Paused.selector);
piggyBank.withdraw();

emit TestResult("Reentrancy Protection", true);
vm.stopPrank();
}

function testAccessControls() public {
// Test owner-only functions
vm.prank(user1);
vm.expectRevert(PiggyBank.PiggyBank__Unauthorized.selector);
piggyBank.pause();

vm.prank(user1);
vm.expectRevert(PiggyBank.PiggyBank__Unauthorized.selector);
piggyBank.setEmergencyGuardian(address(5));

vm.prank(guardian);
vm.expectRevert(PiggyBank.PiggyBank__Unauthorized.selector);
piggyBank.activateEmergencyMode(block.timestamp + LOCK_TIME);

emit TestResult("Access Controls", true);
}

function testEmergencyMode() public {
vm.prank(guardian);
piggyBank.activateEmergencyMode(block.timestamp + 30 days);

assertTrue(piggyBank.paused());
assertTrue(piggyBank.emergencyMode());

// Users should be able to withdraw in emergency mode
vm.startPrank(user1);
piggyBank.deposit{value: INITIAL_DEPOSIT}();
piggyBank.withdraw();
assertEq(piggyBank.deposits(user1), 0);

emit TestResult("Emergency Mode", true);
vm.stopPrank();
}

function testDirectETHTransfers() public {
// Test receive function
vm.startPrank(user1);
(bool success, ) = address(piggyBank).call{value: INITIAL_DEPOSIT}("");
assertTrue(success);
assertEq(piggyBank.deposits(user1), INITIAL_DEPOSIT);

// Test fallback function reverts
vm.expectRevert("PiggyBank: Direct calls not allowed");
(success, ) = address(piggyBank).call("unexpected function");

emit TestResult("Direct ETH Transfers", true);
vm.stopPrank();
}

function testCustomErrors() public {
// Test various custom error scenarios
vm.startPrank(user1);

// Zero value deposit
vm.expectRevert(PiggyBank.PiggyBank__ZeroValue.selector);
piggyBank.deposit{value: 0}();

// No deposits to withdraw
vm.warp(block.timestamp + LOCK_TIME + 1);
vm.expectRevert(PiggyBank.PiggyBank__NoDeposits.selector);
vm.expectRevert(PiggyBank.PiggyBank__NoDeposit.selector);
piggyBank.withdraw();

emit TestResult("Custom Errors", true);
vm.stopPrank();
}

function testGasOptimizations() public {
vm.startPrank(user1);

uint256 gasBefore = gasleft();
piggyBank.deposit{value: INITIAL_DEPOSIT}();
uint256 gasUsed = gasBefore - gasleft();

// Verify deposit was successful with minimal gas
assertEq(piggyBank.deposits(user1), INITIAL_DEPOSIT);

emit TestResult("Gas Optimizations", true);
vm.stopPrank();
}

function testStatisticsTracking() public {
assertEq(piggyBank.numberOfDepositors(), 0);
assertEq(piggyBank.totalDeposits(), 0);

vm.startPrank(user1);
piggyBank.deposit{value: INITIAL_DEPOSIT}();
vm.stopPrank();

assertEq(piggyBank.numberOfDepositors(), 1);
assertEq(piggyBank.totalDeposits(), INITIAL_DEPOSIT);

vm.startPrank(user2);
piggyBank.deposit{value: INITIAL_DEPOSIT}();
vm.stopPrank();

assertEq(piggyBank.numberOfDepositors(), 2);
assertEq(piggyBank.totalDeposits(), INITIAL_DEPOSIT * 2);

emit TestResult("Statistics Tracking", true);
}

function testViewFunctions() public {
// Test canDeposit
assertTrue(piggyBank.canDeposit(user1));

// Test canWithdraw (should be false before unlock)
assertFalse(piggyBank.canWithdraw(user1));

// Test getMaxAdditionalDeposit
assertEq(piggyBank.getMaxAdditionalDeposit(user1), 1000 ether);

// Test getTimeRemaining
uint256 timeRemaining = piggyBank.getTimeRemaining();
assertTrue(timeRemaining > 0);
assertTrue(timeRemaining <= LOCK_TIME);

emit TestResult("View Functions", true);
}

function testEventEmissions() public {
vm.startPrank(user1);

// Test Deposited event
vm.expectEmit(true, true, false, true);
emit Deposited(user1, INITIAL_DEPOSIT, block.timestamp);
emit Deposited(user1, INITIAL_DEPOSIT);
piggyBank.deposit{value: INITIAL_DEPOSIT}();

// Test OwnershipTransferred event
address newOwner = address(5);
vm.expectEmit(true, true, false, true);
emit OwnershipTransferred(owner, newOwner);
vm.prank(owner);
piggyBank.transferOwnership(newOwner);

emit TestResult("Event Emissions", true);
vm.stopPrank();
}

// ============ INTEGRATION TESTS ============

function testFullDepositWithdrawCycle() public {
vm.startPrank(user1);

// Deposit
piggyBank.deposit{value: INITIAL_DEPOSIT}();
assertEq(piggyBank.deposits(user1), INITIAL_DEPOSIT);
assertEq(piggyBank.getBalance(), INITIAL_DEPOSIT);

// Fast-forward to unlock time
vm.warp(block.timestamp + LOCK_TIME + 1);

// Withdraw
piggyBank.withdraw();
assertEq(piggyBank.deposits(user1), 0);
assertEq(user1.balance, 10 ether - INITIAL_DEPOSIT); // Account for gas costs

emit TestResult("Full Deposit Withdraw Cycle", true);
vm.stopPrank();
}

function testMultipleDepositsSameUser() public {
vm.startPrank(user1);

// Multiple deposits from same user
piggyBank.deposit{value: 0.5 ether}();
piggyBank.deposit{value: 0.3 ether}();
piggyBank.deposit{value: 0.2 ether}();

assertEq(piggyBank.deposits(user1), 1 ether);
assertEq(piggyBank.userDepositCount(user1), 3);
assertEq(piggyBank.numberOfDepositors(), 1); // Should still be 1

emit TestResult("Multiple Deposits Same User", true);
vm.stopPrank();
}

function testContractPausing() public {
vm.startPrank(user1);
piggyBank.deposit{value: INITIAL_DEPOSIT}();
vm.stopPrank();

// Pause contract
vm.prank(owner);
piggyBank.pause();

// Should not be able to deposit while paused
vm.startPrank(user2);
vm.expectRevert(PiggyBank.PiggyBank__Paused.selector);
piggyBank.deposit{value: INITIAL_DEPOSIT}();

// Should not be able to withdraw while paused (unless emergency mode)
vm.startPrank(user1);
vm.expectRevert(PiggyBank.PiggyBank__Paused.selector);
piggyBank.withdraw();

// Unpause
vm.prank(owner);
piggyBank.unpause();

// Should work again
piggyBank.deposit{value: INITIAL_DEPOSIT}();

emit TestResult("Contract Pausing", true);
vm.stopPrank();
}

function testAllSecurityFeatures() public {
// Run comprehensive security test
testDepositLimits();
Expand All @@ -287,16 +289,16 @@ contract PiggyBankSecurityTest is Test {
testStatisticsTracking();
testViewFunctions();
testEventEmissions();

emit TestResult("All Security Features", true);
}

function testAllIntegrationFeatures() public {
// Run comprehensive integration test
testFullDepositWithdrawCycle();
testMultipleDepositsSameUser();
testContractPausing();

emit TestResult("All Integration Features", true);
}
}
}
Loading