Smart Contract and Backend Interaction

Hi All,

I’m very new to the forum, so, this might be wrong place to ask. If so, please move to the correct folder.

We are developing a game in which we have resources that the players collect by using certain NFT tools. These resources (say, Wood and Gold) are collected in the backend. The player can craft new items using these resources. But we are facing two problems:

  1. If the user crafts new item in the game, we have to pay the transaction fee.
  2. If the players converts the resources to tokenized versions (these tokens will be in his wallet), he will call the contract, but this time, the contract won’t know the required amounts of the resources.

So, my questions are:

  1. Is there way to make the user pay the fees without converting in-game assets to tokenized versions?
  2. Is there a way for the smart contract to interact with backend?

Thanks in advance.

Hi @isos81
I might misunderstand your problem, but here is my thought.

As I understand, users have some resources (Wood and Gold) recorded in your game server (backend / centralized database), and you want to make them able to mint NFTs by consuming some resources, is it correct? The current solution is the server will update the record and mint an NFT to the address, which leads to the fee for the server.

A solution I saw before (but I don’t remember from where) is that you can lock some resources in the database for a time, make a time-limited signature in the game server, and send the signature to the user. The user then uses the signature to call the minting function of the smart contract, the function will verify if the signature is signed by the server and hasn’t expired and the mint an NFT to the caller. In this way, the server won’t interact with the contract so won’t cause a fee. Since you can track the timestamp in the latest block in the server and release resources if the signature expired and wasn’t used, the resources won’t lose if minting was failed.

Does it make sense?

A link might help:

2 Likes

Hi @flyinglimao,

You didn’t.

Yes, exactly!

This is what we’ve thought of. However, the problem is, how will the backend know whether the mint was successful or not?

Couldn’t get this. Can you please eloborate?

Absolutely. In fact, we can’t think of any other way. Now the only problem that remains is to let the backend know the result of minting.

Thanks!

This is what we’ve thought of. However, the problem is, how will the backend know whether the mint was successful or not?

The easiest but maybe inefficient way I know is to emit an event with the signature or some identifier, then you can listen to the event. The code might look like this (it can’t work but should express the logic):

// on chain
contract NFT {
  event UsedSignature(bytes32);

  function mintWithSignature(bytes signature, bytes payload) public {
    require(verifySignature(signature, payload), "signature invalid");
    _mint(msg.sender, tokenId);
    emit UsedSignature(signature);
  }

  // more detailed verify function
  function verifySignature(bytes signature, bytes payload) {
    // check if the signature is singed with server private key
    if (!ecdsaVerify(serverPublicKey, payload, signature)) return false;
    // check if the signature didn't expire
    if (getExpireTime(payload) < block.timestamp) return false; 
    // ...
    return true;
  }
}
// in game server
ethereum
  .contractAt(contractAddress)
  .addEventListener('UsedSignature', (signature) => {
    onMintSuccess(db.lookUp(signature));
  });
// you should also release resources for expired signatures
setInterval(() => {
  const latestBlockTimestamp = ethereum.getLatestBlock().timestamp;
  db.where(record => record.expireTime > latestBlockTimestamp)
     .each(record => onMintFail(record));
}, 1000);

Since the UsedSignature event is only emitted in the mint function, the server can listen to the event and trust it (your contract).
In case your server isn’t always active, you can query events regularly.

// in game server cron job
ethereum
  .contractAt(contractAddress)
  .queryEvents('UsedSignature', (events) => {
    events.forEach(signature => onMintSuccess(signature));
  });
3 Likes

Thank you very much @flyinglimao. You helped us a lot.

3 Likes