cryptofanboy
cryptofanboy

Reputation: 1

What part of this code is causing buying and selling to be connected?

I've been trying to learn more about coding in order to educate and protect myself from a common scam called a honeypot where people can buy in but not sell. For me to understand how they work, I've basically been trying to recreate the general code format and then break it down piece by piece. I'm not a great coder, but this is what I've put together from research (DO NOT BUY - FOR EDUCATIONAL PURPOSES ONLY):

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
import "@openzeppelin/[email protected]/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/[email protected]/token/ERC20/extensions/ERC20Snapshot.sol";
import "@openzeppelin/[email protected]/access/AccessControl.sol";
import "@openzeppelin/[email protected]/security/Pausable.sol";
import "@openzeppelin/[email protected]/token/ERC20/extensions/draft-ERC20Permit.sol";
import "@openzeppelin/[email protected]/token/ERC20/extensions/ERC20Votes.sol";
import "@openzeppelin/[email protected]/token/ERC20/extensions/ERC20FlashMint.sol";

contract PepeTime is ERC20, ERC20Burnable, ERC20Snapshot, AccessControl, Pausable, ERC20Permit, ERC20Votes, ERC20FlashMint {
    bytes32 public constant SNAPSHOT_ROLE = keccak256("SNAPSHOT_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    
    bool public pauseSelling;
    address public creator;

    constructor() ERC20("Pepe Time", "PPT") ERC20Permit("Pepe Time") {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(SNAPSHOT_ROLE, msg.sender);
        _grantRole(PAUSER_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);

        creator = msg.sender;
    }

    function snapshot() public onlyRole(SNAPSHOT_ROLE) {
        _snapshot();
    }

    function pause() public onlyRole(PAUSER_ROLE) {
        _pause();
    }

    function unpause() public onlyRole(PAUSER_ROLE) {
        _unpause();
    }
    
    function toggleSelling() public {
        require(msg.sender == creator, "Only the contract creator can perform this action");
        pauseSelling = !pauseSelling;
    }

    function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    modifier canTransfer(address from, address to) {
        if (pauseSelling && from != creator) {
            require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Selling is currently paused for other wallets");
        }
        _;
    }

    function _beforeTokenTransfer(address from, address to, uint256 amount)
        internal
        whenNotPaused
        override(ERC20, ERC20Snapshot)
        canTransfer(from, to)
    {
        super._beforeTokenTransfer(from, to, amount);
    }

    // The following functions are overrides required by Solidity.

    function _afterTokenTransfer(address from, address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._afterTokenTransfer(from, to, amount);
    }

    function _mint(address to, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._mint(to, amount);
    }

    function _burn(address account, uint256 amount)
        internal
        override(ERC20, ERC20Votes)
    {
        super._burn(account, amount);
    }
}

I've tested it myself and the only thing that doesn't work is that when selling is paused, buying gets turned off too. But I know that's not right because I've fallen for these honeypot tricks myself and I've been able to buy in. My question is how would this contract differ from a honeypot that allows buying even when selling is paused? Why does the pauseSelling function affect buying as well?

Thank you for your help, and again, this is for educational purposes. I'm trying to understand token coding, please don't buy anything.

I made it so selling can be paused, but then it also pauses buying for all wallets. I think there may be an issue with one of the transfer functions.

I made it so selling can be paused, but now it ends up pausing all buys as well.

Upvotes: 0

Views: 238

Answers (1)

Petr Hejda
Petr Hejda

Reputation: 43491

how would this contract differ from a honeypot that allows buying even when selling is paused? Why does the pauseSelling function affect buying as well?

First let's define "buying" and "selling". In terms of ERC20 tokens, there's only transfer functions, but they alone don't indicate whether the specific transfer happens as part of a larger trade (e.g. I transfer you 10 of ABC and you transfer me 20 of DEF => you're effectively selling DEF tokens and buying ABC while I'm selling ABC and buying DEF) - or whether it's a standalone transfer.

So this buying and selling usually happens through decentralized exchanges (DeX) like Uniswap and others. The DeX contract validates that transfers of both tokens happen as expected - otherwise the overall sale reverts.

Example - User wants to sell 10 A tokens for 20 B tokens:

  1. User calls DeX swap() function
  2. DeX.swap() invokes A.transferFrom(user, DeX, 10) - transferring 10 A tokens from the user to the exchange (mind that the user needs to give prior approval to the DeX)
  3. DeX.swap() also invokes B.transfer(user, 20) - transfering 20 B tokens from the exchange (caller of the transfer function) to the user
  4. There's several validations and other functions (e.g. calculating the price impact of this trade) and if any othem fail, the overall trade fails

Now, you can see that the exchange invokes either transfer() or transferFrom() function on a token contract depending on the direction of the trade. In other words:

  • user selling - exchange calls transferFrom(user, ...)
  • user buying - exchange calls transfer(user, ...)

And these scam tokens often have some kind of validation to decide whether to permit the transfer or not:

  • if the called function is transferFrom()
    • and if the caller is an exchange
      • and if the argument (from whom to transfer tokens) is not our admin address
        • then it's a user trying to sell => revert
  • in all other cases (e.g. user buying, or admin selling) => allow the transfer

Upvotes: 0

Related Questions