Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

err.reason undefined when require() fails with reason in a view #2321

Closed
1 task done
theoretical2019 opened this issue Aug 21, 2019 · 3 comments
Closed
1 task done

Comments

@theoretical2019
Copy link


Issue

If a view fails require() with a reason, the reason isn't available in the error. In other words, in a catch(err) block, err.reason is always undefined if a view failed. If the Solidity code passed a reason in the require() that failed, then err.reason should be the reason from Solidity.

Steps to Reproduce

  • Create a Solidity smart contract with a view.
  • Have a statement of the form require(cond, "ThisShouldFail") in the view.
  • In JS, call the view inside a try / catch block. Use view arguments such that cond evaluates to false.
  • In the catch(err) block, err.reason is undefined.

Here's an example:

$ truffle test

Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/ReasonTest.sol



  Contract: ReasonTest
current_value(): <BN: 5>
Fail require() in a transaction:
err.reason: TooSmall
Fail require() in a view:
err.reason: undefined
    ✓ Demonstrate the bug (89ms)


  1 passing (106ms)

$ cat contracts/ReasonTest.sol
pragma solidity >=0.4.21 <0.6.0;

/**
 * A simple smart contract that holds a value which decreases by 10.
 */
contract ReasonTest
{
   uint public value;

   constructor(uint initial_value) public
   {
      value = initial_value;
   }

   function current_value() public view
      returns (uint)
   {
      return value;
   }

   function next_value() public view
      returns (uint)
   {
      require( value >= 10, "TooSmall" );
      return value-10;
   }

   function update_value() public
      returns (uint)
   {
      require( value >= 10, "TooSmall" );
      value -= 10;
      return value;
   }
}
$ cat test/reason.js

const ReasonTest = artifacts.require("ReasonTest");

contract("ReasonTest", function(accounts)
{
   it( "Demonstrate the bug",
      async function()
      {
         let instance = await ReasonTest.deployed();

         // Print out current_value(), we see it's smaller than 10, so require(value <= 10) should fail
         console.log("current_value():", await instance.current_value());

         // When require() fails in a transaction, we get err.reason as expected (in Truffle 5)
         console.log("Fail require() in a transaction:");
         try
         {
            await instance.update_value();
         }
         catch(err)
         {
            console.log("err.reason:", err.reason);
         }

         // When require() fails in a view, err.reason is not available
         console.log("Fail require() in a view:");
         try
         {
            await instance.next_value();
         }
         catch(err)
         {
            console.log("err.reason:", err.reason);
         }
      });
});

Expected Behavior

Output of above-given files should be:

Fail require() in a transaction:
err.reason: TooSmall
Fail require() in a view:
err.reason: TooSmall

Actual Results

Output actually is:

Fail require() in a transaction:
err.reason: TooSmall
Fail require() in a view:
err.reason: undefined

Environment

I think only Truffle version is relevant to this situation. It is:

$ truffle version
Truffle v5.0.32 (core: 5.0.32)
Solidity - 0.5.10 (solc-js)
Node v10.16.0
Web3.js v1.2.1

I'm using Truffle's built-in Ethereum environment (no external Geth / Parity / Ganache / etc.)

Here's details of the external environment just in case it matters:

$ cat /etc/issue
Ubuntu 18.04.2 LTS \n \l
$ node --version
v10.16.0
$ npm --version
6.9.0
@gnidan
Copy link
Contributor

gnidan commented Aug 21, 2019

Awesome write-up! Thank you! We'll get this triaged.

@eggplantzzz
Copy link
Contributor

@theoretical2019 Yes thanks again for such a good report! I found the bug, for calls Truffle was not getting the reason. I'll open up a PR for this and hopefully we'll get it released next week!

@eggplantzzz
Copy link
Contributor

@theoretical2019 So we figured out what was going on with this issue. So it turns out that for sends, Truffle receives the status of the transaction through the receipt data I believe. This is how Truffle can tell whether it was successful or reverted. With calls (which would occur in the situation where a function declared view) there is no status information and thus Truffle won't be able to tell if it "fails" a requires conditions. We decided it might be dangerous to not replicate this behavior and so decided to leave it as it is for now.

Check out the conversation in the closed PR above (PR #2340) if you would like to see what was discussed. Thanks again for opening this though!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants