0

I'm trying to limit the access of functions in my contract to only normal user addresses, to prevent the 'revert-and-retry attack' described here.

Quick excerpt from that link:

victim.tryMyLuck();
require(victim.conditionOutcome() == favorable);

In other words, the attacker can choose to commit a transaction only when the outcome of a “random” trial is favorable, and abort otherwise. The only cost in the latter case is minor: the gas spent to execute the transaction. The attack works even if there is value transfer in the tryMyLuck() trial: if the transaction aborts, its effects are reverted.

Back to the question: Is it possible to require() transactions to come from user addresses and block contracts?

4
  • Not an answer but consider using custom errors instead of revert reason strings. Compared to revert reason strings, custom errors are easier to work with, more gas efficient, and more elegant. Commented Jul 10, 2022 at 8:14
  • Im not very sure about this solution but you can test if tx.origin is the msg.sender
    – Majd TL
    Commented Jul 10, 2022 at 10:21
  • Use are your own risk, 0% sure if this is a safe condition without security issues
    – Majd TL
    Commented Jul 10, 2022 at 10:27
  • 2
    Hi Enzo! Welcome to Ethereum Stackexchange! Adding such condition could be detrimental to your product in the long term. It will be blocking all contracts for interaction. There are other solution, like using an oracle, or a commit-reveal strategy.
    – Ismael
    Commented Jul 12, 2022 at 13:20

3 Answers 3

2

You can require that a call comes from a contract address by checking that the callers contract size (calledEXTCODESIZE) exceeds zero .

But you can't require that it come from a user address. The problem with the second statement is that a contract has a contract size of zero during construction.

1
  • This is definitely the best explanation. It kind of sucks that there is no way to tell if the caller is a user/contract.
    – Enzo
    Commented Sep 22, 2022 at 1:12
0

If you want to limit the access of functions in your Smart contract to only normal user addresses.

Insert this in 1st line of your function:

require(msg.sender.code.length == 0, "Only normal user allowed, Smart Contracts not allowed");

msg.sender return the Address of the one who call the function

0xAddress.code return ByteCode of any address. Which is nothing for normal user Address.

e.g. if OnlyTrueUsersCanCall() function will be called, it would return true for normal users, and false for bots/SmartContracts. In true part, you can include further steps.

pragma solidity >= 0.8.0 < 0.9.0;
contract StoreData  {
  function OnlyTrueUserCanCall() public view returns(bool){
    if(msg.sender.code.length==0){
      return(true);
      // do your stuff here
    }else{
      return(false);
    //in case of bot or smart conract call the function
    }
  }
}

Solidity Bots can not call the function

3
  • 1
    As @ikijong explained, this won't work as you could just call the contract in the constructor arguments (while the contract code length is 0)
    – Enzo
    Commented Jul 10, 2022 at 8:19
  • what does that means ? are you saing that msg.sender can be only call in constructor() ?
    – aakash4dev
    Commented Jul 10, 2022 at 8:31
  • This code.length function can be forged by putting the code you want in the constructor of the contract calling the StoreData contract. There is more information provided by @ikijong in his answer.
    – Enzo
    Commented Jul 10, 2022 at 9:13
0

The open zeppelin address library has an “isContract” function you can use to check whether or not a call is coming from a contract or EOA

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