pragma solidity ^0.8.20;
import { Token } from "./Token.sol";
import { Token2 } from "./Token2.sol";
import { Test, console } from "forge-std/Test.sol";
contract TokenDifferential is Test {
Token public customERC20;
Token2 public standardERC20;
function setUp() public {
customERC20 = new Token();
standardERC20 = new Token2();
}
function test_Differential_UnderflowHandling() public {
address user = makeAddr("user");
vm.expectRevert();
standardERC20.burn(user, 1);
customERC20.burn(user, 1);
uint256 userBal = customERC20.balanceOf(user);
assertEq(userBal, type(uint256).max);
console.log("User's Balance of CustomERC20 Token After Burn:", userBal);
}
function test_Differential_OverflowHandling() public {
address user = makeAddr("user");
uint256 maxAmount = type(uint256).max;
standardERC20.mint(user, maxAmount);
customERC20.mint(user, maxAmount);
uint256 userBalStandardToken = standardERC20.balanceOf(user);
uint256 userBalCustomToken = customERC20.balanceOf(user);
vm.expectRevert();
standardERC20.mint(user, 1);
uint256 userBalStandardTokenAfterRevert = standardERC20.balanceOf(user);
assertEq(userBalStandardTokenAfterRevert, userBalStandardToken);
customERC20.mint(user, 1);
uint256 userBalCustomTokenAfterSeconMint = customERC20.balanceOf(user);
assertEq(userBalCustomTokenAfterSeconMint, 0);
console.log("User's Standard ERC20 Balance After First Mint:", userBalStandardToken);
console.log("User's Custom ERC20 Balance After First Mint:", userBalCustomToken);
console.log("User's Standard ERC20 Balance After Second Mint:", userBalStandardTokenAfterRevert);
console.log("User's Custom ERC20 Balance After Second Mint:", userBalCustomTokenAfterSeconMint);
}
function test_Differential_TransferOverflowHandling() public {
address user1 = makeAddr("user1");
address user2 = makeAddr("user2");
uint256 maxAmount = type(uint256).max;
customERC20.mint(user1, maxAmount);
customERC20.mint(user2, 10);
uint256 user1CustomBalBefore = customERC20.balanceOf(user1);
uint256 user2CustomBalBefore = customERC20.balanceOf(user2);
vm.prank(user2);
customERC20.transfer(user1, 1);
uint256 user1CustomBalAfter = customERC20.balanceOf(user1);
uint256 user2CustomBalAfter = customERC20.balanceOf(user2);
assertEq(user1CustomBalAfter, 0);
console.log("User 1 Custom ERC20 Balance Before Transfer:", user1CustomBalBefore);
console.log("User 2 Custom ERC20 Balance Before Transfer:", user2CustomBalBefore);
console.log("User 1 Custom ERC20 Balance After Transfer:", user1CustomBalAfter);
console.log("User 2 Custom ERC20 Balance After Transfer:", user2CustomBalAfter);
}
}
function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
// ...
let accountBalance := sload(accountBalanceSlot)
+ if lt(accountBalance, value) { revert(0, 0) }
sstore(accountBalanceSlot, sub(accountBalance, value))
}
}
function _transfer(address from, address to, uint256 value) internal returns (bool success) {
assembly ("memory-safe") {
// ...
let toAmount := sload(toSlot)
+ if gt(toAmount, sub(not(0), value)) { revert(0, 0) }
sstore(toSlot, add(toAmount, value))
// ...
}
}