pragma solidity ^0.8.24;
import {Test} from "forge-std/Test.sol";
import {ERC20, IERC20Errors} from "../src/ERC20.sol";
contract ERC20BaseDeployTest is Test {
ERC20 internal base;
function setUp() public {
base = new ERC20("BaseToken", "BASE");
}
function test_deployBase_hasZeroSupply_andNoMintFunction() public {
assertEq(base.totalSupply(), 0, "totalSupply should be 0 on base ERC20 deployment");
(bool success, bytes memory ret) = address(base).call(
abi.encodeWithSignature("mint(address,uint256)", address(this), 1)
);
assertFalse(success, "mint should not exist on the base ERC20");
assertEq(ret.length, 0, "no revert data expected for non-existent function");
}
function test_transferFromZeroBalance_revertsWithInsufficientBalance() public {
address sender = address(0xBEEF);
address receiver = address(0xCAFE);
vm.startPrank(sender);
vm.expectRevert(
abi.encodeWithSelector(
IERC20Errors.ERC20InsufficientBalance.selector,
sender,
uint256(0),
uint256(1)
)
);
base.transfer(receiver, 1);
vm.stopPrank();
}
function test_transferFrom_withAllowance_stillRevertsDueToZeroBalance() public {
address owner = address(0xA11CE);
address spender = address(0x5ED);
address to = address(0xF00D);
vm.startPrank(owner);
bool ok = base.approve(spender, 5);
assertTrue(ok, "approve should succeed");
assertEq(base.allowance(owner, spender), 5, "allowance not recorded");
vm.stopPrank();
vm.startPrank(spender);
vm.expectRevert(
abi.encodeWithSelector(
IERC20Errors.ERC20InsufficientBalance.selector,
owner,
uint256(0),
uint256(5)
)
);
base.transferFrom(owner, to, 5);
vm.stopPrank();
assertEq(base.allowance(owner, spender), 5, "allowance should remain unchanged after revert");
}
}
[⠊] Compiling...
No files changed, compilation skipped
Ran 3 tests for test/poc.t.sol:ERC20BaseDeployTest
[PASS] test_deployBase_hasZeroSupply_andNoMintFunction() (gas: 14645)
Traces:
[14645] ERC20BaseDeployTest::test_deployBase_hasZeroSupply_andNoMintFunction()
├─ [2317] ERC20::totalSupply() [staticcall]
│ └─ ← [Return] 0
├─ [0] VM::assertEq(0, 0, "totalSupply should be 0 on base ERC20 deployment") [staticcall]
│ └─ ← [Return]
├─ [222] ERC20::mint(ERC20BaseDeployTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], 1)
│ └─ ← [Revert] unrecognized function selector 0x40c10f19 for contract 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f, which has no fallback function.
├─ [0] VM::assertFalse(false, "mint should not exist on the base ERC20") [staticcall]
│ └─ ← [Return]
├─ [0] VM::assertEq(0, 0, "no revert data expected for non-existent function") [staticcall]
│ └─ ← [Return]
└─ ← [Stop]
[PASS] test_transferFromZeroBalance_revertsWithInsufficientBalance() (gas: 16545)
Traces:
[16545] ERC20BaseDeployTest::test_transferFromZeroBalance_revertsWithInsufficientBalance()
├─ [0] VM::startPrank(0x000000000000000000000000000000000000bEEF)
│ └─ ← [Return]
├─ [0] VM::expectRevert(custom error 0xf28dceb3: 00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000064e450d38c000000000000000000000000000000000000000000000000000000000000beef0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000)
│ └─ ← [Return]
├─ [5186] ERC20::transfer(0x000000000000000000000000000000000000cafE, 1)
│ └─ ← [Revert] ERC20InsufficientBalance(0x000000000000000000000000000000000000bEEF, 0, 1)
├─ [0] VM::stopPrank()
│ └─ ← [Return]
└─ ← [Stop]
[PASS] test_transferFrom_withAllowance_stillRevertsDueToZeroBalance() (gas: 37386)
Traces:
[51998] ERC20BaseDeployTest::test_transferFrom_withAllowance_stillRevertsDueToZeroBalance()
├─ [0] VM::startPrank(0x00000000000000000000000000000000000A11cE)
│ └─ ← [Return]
├─ [24985] ERC20::approve(0x00000000000000000000000000000000000005Ed, 5)
│ ├─ emit Approval(owner: 0x00000000000000000000000000000000000A11cE, spender: 0x00000000000000000000000000000000000005Ed, value: 5)
│ └─ ← [Return] true
├─ [0] VM::assertTrue(true, "approve should succeed") [staticcall]
│ └─ ← [Return]
├─ [1290] ERC20::allowance(0x00000000000000000000000000000000000A11cE, 0x00000000000000000000000000000000000005Ed) [staticcall]
│ └─ ← [Return] 5
├─ [0] VM::assertEq(5, 5, "allowance not recorded") [staticcall]
│ └─ ← [Return]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [0] VM::startPrank(0x00000000000000000000000000000000000005Ed)
│ └─ ← [Return]
├─ [0] VM::expectRevert(custom error 0xf28dceb3: 00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000064e450d38c00000000000000000000000000000000000000000000000000000000000a11ce0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000)
│ └─ ← [Return]
├─ [5853] ERC20::transferFrom(0x00000000000000000000000000000000000A11cE, 0x000000000000000000000000000000000000F00D, 5)
│ └─ ← [Revert] ERC20InsufficientBalance(0x00000000000000000000000000000000000A11cE, 0, 5)
├─ [0] VM::stopPrank()
│ └─ ← [Return]
├─ [1290] ERC20::allowance(0x00000000000000000000000000000000000A11cE, 0x00000000000000000000000000000000000005Ed) [staticcall]
│ └─ ← [Return] 5
├─ [0] VM::assertEq(5, 5, "allowance should remain unchanged after revert") [staticcall]
│ └─ ← [Return]
└─ ← [Stop]
Suite result: ok. 3 passed; 0 failed; 0 skipped; finished in 1.51ms (661.80µs CPU time)
Ran 1 test suite in 16.44ms (1.51ms CPU time): 3 tests passed, 0 failed, 0 skipped (3 total tests)