pragma solidity ^0.8.18;
import {Test} from "forge-std/Test.sol";
import {TokenDivider} from "../src/TokenDivider.sol";
import {MockNFT} from "./mocks/MockNFT.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
contract TokenDividerTest is Test {
    TokenDivider public tokenDivider;
    MockNFT public mockNft;
    address public fracTokenAddress;
    
    address public user1 = makeAddr("user1");
    address public user2 = makeAddr("user2");
    uint256 constant SELL_PRICE = 1 ether;
    uint256 constant SELL_AMOUNT = 100;
    
    receive() external payable {}
    function setUp() public {
        tokenDivider = new TokenDivider();
        mockNft = new MockNFT();
        
        vm.startPrank(user1);
        mockNft.mint(user1, 1);
        mockNft.approve(address(tokenDivider), 1);
        tokenDivider.divideNft(address(mockNft), 1, 1000);
        fracTokenAddress = tokenDivider.getErc20InfoFromNft(address(mockNft)).erc20Address;
        IERC20(fracTokenAddress).approve(address(tokenDivider), type(uint256).max);
        vm.stopPrank();
        
        vm.deal(address(this), 10 ether);
    }
    function testBuyOrderVulnerability() public {
        uint256 initialSellerBalance = user1.balance;
        
        
        vm.prank(user1);
        tokenDivider.sellErc20(address(mockNft), SELL_PRICE, SELL_AMOUNT);
        emit log("\n=== Before Buy Order ===");
        emit log_named_uint("User1 ETH Balance", user1.balance);
        emit log_named_uint("User1 Token Balance", IERC20(fracTokenAddress).balanceOf(user1));
        emit log_named_uint("User2 Token Balance", IERC20(fracTokenAddress).balanceOf(user2));
        emit log_named_uint("Contract Token Balance", IERC20(fracTokenAddress).balanceOf(address(tokenDivider)));
        
        vm.startPrank(user2);
        vm.deal(user2, 2 ether);
        
        
        vm.mockCall(
            fracTokenAddress,
            abi.encodeWithSelector(IERC20.transfer.selector, user2, SELL_AMOUNT),
            abi.encode(false)
        );
        tokenDivider.buyOrder{value: SELL_PRICE + 0.02 ether}(0, user1);
        emit log("\n=== After Buy Order ===");
        emit log_named_uint("User1 ETH Received", user1.balance - initialSellerBalance);
        emit log_named_uint("User1 Token Balance", IERC20(fracTokenAddress).balanceOf(user1));
        emit log_named_uint("User2 Token Balance", IERC20(fracTokenAddress).balanceOf(user2));
        emit log_named_uint("Contract Token Balance", IERC20(fracTokenAddress).balanceOf(address(tokenDivider)));
        
        vm.stopPrank();
    }
}
forge test -vvv --match-test testBuyOrderVulnerability -vvv
[⠰] Compiling...
No files changed, compilation skipped
Ran 1 test for test/TokenDivider.t.sol:TokenDividerTest
[PASS] testBuyOrderVulnerability() (gas: 224068)
Logs:
=== Before Buy Order ===
  User1 ETH Balance: 0
  User1 Token Balance: 900
  User2 Token Balance: 0
  Contract Token Balance: 100
=== After Buy Order ===
  User1 ETH Received: 995000000000000000
  User1 Token Balance: 900
  User2 Token Balance: 0
  Contract Token Balance: 100
Traces:
  [3570733] TokenDividerTest::setUp()
    ├─ [1819251] → new TokenDivider@0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
    │   ├─ emit OwnershipTransferred(previousOwner: 0x0000000000000000000000000000000000000000, newOwner: TokenDividerTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496])
    │   └─ ← [Return] 8968 bytes of code
    ├─ [816931] → new MockNFT@0x2e234DAe75C793f67A35089C9d99245E1C58470b
    │   └─ ← [Return] 3855 bytes of code
    ├─ [0] VM::startPrank(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF])
    │   └─ ← [Return]
    ├─ [47187] MockNFT::mint(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], 1)
    │   ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], tokenId: 1)
    │   └─ ← [Stop]
    ├─ [25046] MockNFT::approve(TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 1)
    │   ├─ emit Approval(owner: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], approved: TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], tokenId: 1)
    │   └─ ← [Stop]
    ├─ [711076] TokenDivider::divideNft(MockNFT: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 1, 1000)
    │   ├─ [572] MockNFT::ownerOf(1) [staticcall]
    │   │   └─ ← [Return] user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF]
    │   ├─ [572] MockNFT::ownerOf(1) [staticcall]
    │   │   └─ ← [Return] user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF]
    │   ├─ [1174] MockNFT::name() [staticcall]
    │   │   └─ ← [Return] "MockNFT"
    │   ├─ [1196] MockNFT::symbol() [staticcall]
    │   │   └─ ← [Return] "MNFT"
    │   ├─ [452565] → new ERC20ToGenerateNftFraccion@0x104fBc016F4bb334D775a19E8A6510109AC63E00
    │   │   └─ ← [Return] 2031 bytes of code
    │   ├─ [46894] ERC20ToGenerateNftFraccion::mint(TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 1000)
    │   │   ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], value: 1000)
    │   │   └─ ← [Stop]
    │   ├─ [30860] MockNFT::safeTransferFrom(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 1, 0x)
    │   │   ├─ emit Transfer(from: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], to: TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], tokenId: 1)
    │   │   ├─ [664] TokenDivider::onERC721Received(TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], 1, 0x)
    │   │   │   └─ ← [Return] 0x150b7a02
    │   │   └─ ← [Stop]
    │   ├─ [572] MockNFT::ownerOf(1) [staticcall]
    │   │   └─ ← [Return] TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]
    │   ├─ emit NftDivided(nftAddress: MockNFT: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], amountErc20Minted: 1000, erc20Minted: ERC20ToGenerateNftFraccion: [0x104fBc016F4bb334D775a19E8A6510109AC63E00])   
    │   ├─ [25203] ERC20ToGenerateNftFraccion::transfer(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], 1000)
    │   │   ├─ emit Transfer(from: TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], to: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], value: 1000)
    │   │   └─ ← [Return] true
    │   └─ ← [Stop]
    ├─ [854] TokenDivider::getErc20InfoFromNft(MockNFT: [0x2e234DAe75C793f67A35089C9d99245E1C58470b]) [staticcall]
    │   └─ ← [Return] ERC20Info({ erc20Address: 0x104fBc016F4bb334D775a19E8A6510109AC63E00, tokenId: 1 })
    ├─ [24734] ERC20ToGenerateNftFraccion::approve(TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])       
    │   ├─ emit Approval(owner: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], spender: TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], value: 115792089237316195423570985008687907853269984665640564039457584007913129639935 [1.157e77])
    │   └─ ← [Return] true
    ├─ [0] VM::stopPrank()
    │   └─ ← [Return]
    ├─ [0] VM::deal(TokenDividerTest: [0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496], 10000000000000000000 [1e19])
    │   └─ ← [Return]
    └─ ← [Stop]
  [285351] TokenDividerTest::testBuyOrderVulnerability()
    ├─ [0] VM::prank(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF])
    │   └─ ← [Return]
    ├─ [158617] TokenDivider::sellErc20(MockNFT: [0x2e234DAe75C793f67A35089C9d99245E1C58470b], 1000000000000000000 [1e18], 100)
    │   ├─ emit OrderPublished(amount: 100, seller: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], nftPegged: MockNFT: [0x2e234DAe75C793f67A35089C9d99245E1C58470b])
    │   ├─ [32420] ERC20ToGenerateNftFraccion::transferFrom(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], 100)
    │   │   ├─ emit Transfer(from: user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF], to: TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f], value: 100)
    │   │   ├─  storage changes:
    │   │   │   @ 0x266f7372a76c4363ea04e26dfd6a2a19d44ec1cadb0d91060c1fb98ef6adc19b: 1000 → 900
    │   │   │   @ 0x996b860ef646da948c7d745162e3851398cba818db7bc8c7a4d9d465a54dfa11: 0 → 100
    │   │   └─ ← [Return] true
    │   ├─  storage changes:
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4055: 0 → 100
    │   │   @ 0xffa349e12903d55d44b03963e778e0466a588c951f33196e2aad9d758af52678: 0 → 1
    │   │   @ 0x3e35f3d712e6159836b1bf1ead524757949cb2df7e6fe5147bd05f40e1049396: 1000 → 900
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4052: 0 → 0x00000000000000000000000029e3b139f4393adda86303fcdaa35f60bb7092bf
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4053: 0 → 0x000000000000000000000000104fbc016f4bb334d775a19e8a6510109ac63e00
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4054: 0 → 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000
    │   └─ ← [Stop]
    ├─ emit log(val: "\n=== Before Buy Order ===")
    ├─ emit log_named_uint(key: "User1 ETH Balance", val: 0)
    ├─ [559] ERC20ToGenerateNftFraccion::balanceOf(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF]) [staticcall]
    │   └─ ← [Return] 900
    ├─ emit log_named_uint(key: "User1 Token Balance", val: 900)
    ├─ [2559] ERC20ToGenerateNftFraccion::balanceOf(user2: [0x537C8f3d3E18dF5517a58B3fB9D9143697996802]) [staticcall]
    │   └─ ← [Return] 0
    ├─ emit log_named_uint(key: "User2 Token Balance", val: 0)
    ├─ [559] ERC20ToGenerateNftFraccion::balanceOf(TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
    │   └─ ← [Return] 100
    ├─ emit log_named_uint(key: "Contract Token Balance", val: 100)
    ├─ [0] VM::startPrank(user2: [0x537C8f3d3E18dF5517a58B3fB9D9143697996802])
    │   └─ ← [Return]
    ├─ [0] VM::deal(user2: [0x537C8f3d3E18dF5517a58B3fB9D9143697996802], 2000000000000000000 [2e18])
    │   └─ ← [Return]
    ├─ [0] VM::mockCall(ERC20ToGenerateNftFraccion: [0x104fBc016F4bb334D775a19E8A6510109AC63E00], 0xa9059cbb000000000000000000000000537c8f3d3e18df5517a58b3fb9d91436979968020000000000000000000000000000000000000000000000000000000000000064, 0x0000000000000000000000000000000000000000000000000000000000000000)
    │   └─ ← [Return]
    ├─ [69920] TokenDivider::buyOrder{value: 1020000000000000000}(0, user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF])
    │   ├─ emit OrderSelled(buyer: user2: [0x537C8f3d3E18dF5517a58B3fB9D9143697996802], price: 1000000000000000000 [1e18])
    │   ├─ [0] user1::fallback{value: 995000000000000000}()
    │   │   └─ ← [Stop]
    │   ├─ [55] TokenDividerTest::receive{value: 10000000000000000}()
    │   │   └─ ← [Stop]
    │   ├─ [0] ERC20ToGenerateNftFraccion::transfer(user2: [0x537C8f3d3E18dF5517a58B3fB9D9143697996802], 100)
    │   │   └─ ← [Return] false
    │   ├─  storage changes:
    │   │   @ 0xffa349e12903d55d44b03963e778e0466a588c951f33196e2aad9d758af52678: 1 → 0
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4053: 0x000000000000000000000000104fbc016f4bb334d775a19e8a6510109ac63e00 → 0
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4052: 0x00000000000000000000000029e3b139f4393adda86303fcdaa35f60bb7092bf → 0
    │   │   @ 0x916164bb747da7612753fdea4750bf6dffb9d8ce7252274385dd43052cf26491: 0 → 100
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4054: 0x0000000000000000000000000000000000000000000000000de0b6b3a7640000 → 0
    │   │   @ 0x89f4dda5c819fbddd16bb9ad04ae6f4f53e9598fdcd85a64e2934551e1ca4055: 100 → 0
    │   └─ ← [Stop]
    ├─ emit log(val: "\n=== After Buy Order ===")
    ├─ emit log_named_uint(key: "User1 ETH Received", val: 995000000000000000 [9.95e17])
    ├─ [559] ERC20ToGenerateNftFraccion::balanceOf(user1: [0x29E3b139f4393aDda86303fcdAa35F60Bb7092bF]) [staticcall]
    │   └─ ← [Return] 900
    ├─ emit log_named_uint(key: "User1 Token Balance", val: 900)
    ├─ [559] ERC20ToGenerateNftFraccion::balanceOf(user2: [0x537C8f3d3E18dF5517a58B3fB9D9143697996802]) [staticcall]
    │   └─ ← [Return] 0
    ├─ emit log_named_uint(key: "User2 Token Balance", val: 0)
    ├─ [559] ERC20ToGenerateNftFraccion::balanceOf(TokenDivider: [0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f]) [staticcall]
    │   └─ ← [Return] 100
    ├─ emit log_named_uint(key: "Contract Token Balance", val: 100)
    ├─ [0] VM::stopPrank()
    │   └─ ← [Return]
    ├─  storage changes:
    │   @ 0x996b860ef646da948c7d745162e3851398cba818db7bc8c7a4d9d465a54dfa11: 0 → 100
    └─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.28ms (778.50µs CPU time)
Ran 1 test suite in 1.24s (1.28ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)
Implement mandatory token transfer verification to ensure buyer receives tokens before finalizing transaction.