Search code examples
soliditydecentralized-applications

"Out of Gas" when calling function twice quickly, but not when calls are spaced out?


I have a smart contract, and one of the functions (queue) is meant to allow users to find "matches" with other users of the smart contract. The logic is that if you call queue and there is nobody waiting, you are now the queued user / wallet address. If you call queue and there is already a queued user, you clear them from the queue and set up the match.

This works fine if the first queue call is a few seconds before the second one, but if both users call queue at the same time, the second one always reverts with an Out of Gas error. Increasing the amount of gas does not solve the issue.

I would appreciate any ideas!

The code fails in the if block. If I remove most of the logic, it succeeds, but I can't figure out any rhyme or reason as to why.

if (awaitingMatch != address(0)) {
  userMap[awaitingMatch].opponent = msg.sender;
  userMap[awaitingMatch].matchedBlock = block.number;
  userMap[awaitingMatch].matchWins = 0;
  userMap[awaitingMatch].playAmount = msg.value;
  userMap[awaitingMatch].winsNeeded = winsToWin;

  userMap[msg.sender].opponent = awaitingMatch;
  userMap[msg.sender].matchedBlock = block.number; 
  userMap[msg.sender].matchWins = 0;
  userMap[msg.sender].winsNeeded = winsToWin;

  awaitingMatch = address(0);

  emit Match(msg.sender);
  emit Match(userMap[msg.sender].opponent);

// add this guy to the list awaiting a match, and set his desposit flag true
} else {
  awaitingMatch = msg.sender;
}

Solution

  • I think I have figured this out. The issue is that MetaMask tries to estimate the amount of gas that will be used for each transaction. MetaMask is quite good at this, and analyzes the state of the contract before estimating the gas. The if section (run by the second caller) does a lot more work than the else section (run by the first caller). If I make both calls at the same time, they both estimate that they'll run the lighter else section, but one of them will wind up running the first, more expensive if section.

    I think my best bet here is to tweak the amount of gas being supplied on any call to a function like this that could do quite different amounts of work depending on the moment the function is called.