Security for play-to-earn action game

Hi,

So I'm making a play-to-earn game, that is a web game and its logic runs on the user's machine. After the player finishes a session, the game will call the following mint function:

contract GoldCoin is ERC20, Ownable, ReentrancyGuard {
    
    uint256 public maxSupply = 200000000 * 10 ** decimals();
    mapping(address => uint256) public canClaimDate;     
    bytes32 private answer = 0x7b2be995daa5546c8be8af8968994a3e99baa3aba4c69818b3abbd9c8a9af88a;    

    constructor() ERC20("Gold Coin", "GC") {
        _mint(msg.sender, 100000000 * 10 ** decimals());
    }

    function mint(uint256 amount, string memory _pass) public nonReentrant{             
        require (amount <= 1000, "suspicious amount detected");        
        require (keccak256(abi.encodePacked(_pass)) == answer, "keyword doesn't match, stop trying to cheat and do some good in the world");
        require (block.timestamp >= canClaimDate[msg.sender], "24 hous haven't passed");
        require (maxSupply < amount + totalSupply(), "max supply exceeded");
        canClaimDate[msg.sender] = block.timestamp + 1 days;
        _mint(msg.sender, amount);
    }         
}

What I'm currently doing:

  • Limitating the amount of tokens that can be minted in a single function call, to what would be possible by playing.
  • Asking for a keyword that will be sent from the game, so it is more difficult to mint directly from the block explorer.
  • Limiting the minting to once per day, per player.

I know all this can be circumvented. What other measures would you put in place?
Passing all the logic through blockchain is not realistic for an action game, only for turn-based games.

Thanks!!!

1 Like

have you solved this? wondering the same thing as well because your answer is exposed on the contract!

you can use abi.decode to reverse answer. This means _pass is useless.
Also the function is public so anyone can get up to 1000 wei per day.
If the person uses a contract to do this then he can get a lot of tokens fast

1 Like

how do games like axie do this though? because players will earn SLP tokens from playing

Well idk but here is an idea of a game maybe it'll help.
A player can join a game and whenever the game is full (2 players) the server will see that and start the game. After the game was completed the server will call the smart contract and tell the smart contract who won. You can do whitelisted address so that only the server can set the winner. This would be an example but Idk how axie does it

1 Like

i went to axie website. indeed they have a 'visible' main contract for their ERC721 and a 'hidden' contract for claiming rewards

adding @minh_trng as we were just discussing this earlier
https://forum.openzeppelin.com/t/games-that-implement-in-game-minting-cant-a-user-simply-mint-items-directly-from-explorer-page

axie website

claim reward contract (not verified)

do you know how to reverse compile this? curious how they handle rewards here

ok seems like the SLP contract (which is the reward in the game) is totally hidden not even the code is shown!

Sadly not there is a "Decompile Bytecode" function but it probably won't help you a lot. You can go to contract and then there is a orange "Decompile Bytecode" button. The code is sometimes not making a lot of sense and it's looking more like Assembly then solidity.
Anyways there is no real way to see what's going on. You can just guess.

1 Like

thanks for the input!

update
there's a new SLP contract which does have the solidity code

yeah but that's just a normal token contract

contract SmoothLovePotion is ERC20Detailed, ERC20Mintable, ERC20GatewayWhitelist

seems like they have some custom designs here

yes but still it looks like a pretty normal token other than that

So the final solution is to not verify the rewards contract, so the password is not visible?

That would work if no one knows the name of the variable.

i assume the password would show up in cleartext when you use the "decompile bytecode" button that you mentioned earlier

But when sending the "claim" transaction on metamask, the function name appears, the parameters it takes, and the hex data. Is this not enough to claim directly on the contract without playing?

hey everyone wondering how to do this, i have implemented a custom API for my own game and i think it works.

seems to be the safest way to do this. security is on server side (not inside the game) so it is not hackable unless your server is hacked.

you will need NodeJs and ExpressJs (very basic JS) to setup this API

basically the work flow is like this

  1. deploy a verify signature contract (standalone contract) which will check that a secret wallet address signed a transaction with a 'secret message'
    reference tutorial: https: // www . youtube . com/watch?v=vYwYe-Gv_XI

  2. in the minting contract for the game item to be minted, it should require a 'true' check from the verify sig contract above before allowing minting

  3. now setup a Node app (using ExpressJs + ethers/web3 modules) which is an API app. the API app should accept POST request of a 'secret message' and sign this secret message with the secret wallet private key. (all these are server side so only your hosting provider and you know the private key). (its a POST that doesn't really post since this API app will have no database, but a POST call is more secure from what i understand as the secret message is hidden)

you can also setup custom logic here in the API

  1. when user mints something from the game, the game will send a POST request to the API. the game receives the signed transaction, and then feed this to the minting contract which then verifies it.

note: the game app will hold the secret message, but not the secret wallet private key.

the ExpressJs app will hold the private key