Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert details for error Reason(param-type) don't make it from the System to the client #3042

Closed
qbzzt opened this issue Aug 16, 2024 · 1 comment · Fixed by #3043
Closed

Comments

@qbzzt
Copy link
Contributor

qbzzt commented Aug 16, 2024

I have this Solidity code:

error Fail();
error ParamFailString(string);

contract TasksSystem is System {

  function errorRevert() public {
    revert Fail();
  }

  function stringParamRevert() public {
    revert ParamFailString("Parameter fail");
  }

When I call app__errorRevert, I catch this error:

{
  "details": "execution reverted: custom error 552670ff:",
  "metaMessages": [
    "Estimate Gas Arguments:",
    "  from:                  0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\n  to:                    0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\n  data:                  0xce1cd432\n  maxFeePerGas:          0 gwei\n  maxPriorityFeePerGas:  0 gwei\n  nonce:                 0"
  ],
  "shortMessage": "Execution reverted with reason: custom error 552670ff:.",
  "name": "EstimateGasExecutionError",
  "version": "[email protected]",
  "message": "Execution reverted with reason: custom error 552670ff:.\n\nEstimate Gas Arguments:\n  from:                  0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\n  to:                    0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\n  data:                  0xce1cd432\n  maxFeePerGas:          0 gwei\n  maxPriorityFeePerGas:  0 gwei\n  nonce:                 0\n\nDetails: execution reverted: custom error 552670ff:\nVersion: [email protected]",
  "cause": {
    "details": "execution reverted: custom error 552670ff:",
    "shortMessage": "Execution reverted with reason: custom error 552670ff:.",
    "name": "ExecutionRevertedError",
    "version": "[email protected]",
    "message": "Execution reverted with reason: custom error 552670ff:.\n\nDetails: execution reverted: custom error 552670ff:\nVersion: [email protected]",
    "cause": {
      "details": "execution reverted: custom error 552670ff:",
      "metaMessages": [
        "URL: http://127.0.0.1:8545",
        "Request body: {\"method\":\"eth_estimateGas\",\"params\":[{\"data\":\"0xce1cd432\",\"from\":\"0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\",\"maxFeePerGas\":\"0x0\",\"maxPriorityFeePerGas\":\"0x0\",\"nonce\":\"0x0\",\"to\":\"0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\"},\"pending\"]}"
      ],
      "shortMessage": "RPC Request failed.",
      "name": "RpcRequestError",
      "version": "[email protected]",
      "message": "RPC Request failed.\n\nURL: http://127.0.0.1:8545\nRequest body: {\"method\":\"eth_estimateGas\",\"params\":[{\"data\":\"0xce1cd432\",\"from\":\"0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\",\"maxFeePerGas\":\"0x0\",\"maxPriorityFeePerGas\":\"0x0\",\"nonce\":\"0x0\",\"to\":\"0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\"},\"pending\"]}\n\nDetails: execution reverted: custom error 552670ff:\nVersion: [email protected]",
      "cause": {
        "code": 3,
        "message": "execution reverted: custom error 552670ff:",
        "data": "0x552670ff"
      },
      "code": 3
    }
  },
  "attemptNumber": 1,
  "retriesLeft": 3
}

When I call app__stringParamRevert, the details don't have any additional information, just that the execution failed:

{
  "details": "execution reverted: ",
  "metaMessages": [
    "Estimate Gas Arguments:",
    "  from:                  0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\n  to:                    0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\n  data:                  0x4995f804\n  maxFeePerGas:          0 gwei\n  maxPriorityFeePerGas:  0 gwei\n  nonce:                 0"
  ],
  "shortMessage": "Execution reverted for an unknown reason.",
  "name": "EstimateGasExecutionError",
  "version": "[email protected]",
  "message": "Execution reverted for an unknown reason.\n\nEstimate Gas Arguments:\n  from:                  0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\n  to:                    0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\n  data:                  0x4995f804\n  maxFeePerGas:          0 gwei\n  maxPriorityFeePerGas:  0 gwei\n  nonce:                 0\n\nDetails: execution reverted: \nVersion: [email protected]",
  "cause": {
    "details": "execution reverted: ",
    "shortMessage": "Execution reverted for an unknown reason.",
    "name": "ExecutionRevertedError",
    "version": "[email protected]",
    "message": "Execution reverted for an unknown reason.\n\nDetails: execution reverted: \nVersion: [email protected]",
    "cause": {
      "details": "execution reverted: ",
      "metaMessages": [
        "URL: http://127.0.0.1:8545",
        "Request body: {\"method\":\"eth_estimateGas\",\"params\":[{\"data\":\"0x4995f804\",\"from\":\"0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\",\"maxFeePerGas\":\"0x0\",\"maxPriorityFeePerGas\":\"0x0\",\"nonce\":\"0x0\",\"to\":\"0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\"},\"pending\"]}"
      ],
      "shortMessage": "RPC Request failed.",
      "name": "RpcRequestError",
      "version": "[email protected]",
      "message": "RPC Request failed.\n\nURL: http://127.0.0.1:8545\nRequest body: {\"method\":\"eth_estimateGas\",\"params\":[{\"data\":\"0x4995f804\",\"from\":\"0x735B2F2c662eBEDFFA94027A7196F0559f7f18a4\",\"maxFeePerGas\":\"0x0\",\"maxPriorityFeePerGas\":\"0x0\",\"nonce\":\"0x0\",\"to\":\"0x8d8b6b8414e1e3dcfd4168561b9be6bd3bf6ec4b\"},\"pending\"]}\n\nDetails: execution reverted: \nVersion: [email protected]",
      "cause": {
        "code": 3,
        "message": "execution reverted: ",
        "data": "0xce1316ff0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000e506172616d65746572206661696c000000000000000000000000000000000000"
      },
      "code": 3
    }
  },
  "attemptNumber": 1,
  "retriesLeft": 3
}

I tried to use a uint256 parameter and it had the same results. Putting errors in a library didn't have any effect.

For an easy replication, use the react template and replace App.tsx with:

import { getTxpoolContent } from "viem/actions";
import { useMUD } from "./MUDContext";
import { useState } from "react"

const styleUnset = { all: "unset" } as const;

const ErrButton = attrs => {
  return (
    <>
      <button onClick={
        async event => {
          try {
            await attrs.fun([]);
          } catch (err) {
            attrs.set(JSON.stringify(err, undefined, 2))
          }
        }
      }>
        {attrs.msg}
      </button>
    </>
  )
}

export const App = () => {
  const {
    network: { worldContract, tables, useStore },
    systemCalls: { addTask, toggleTask, deleteTask },
  } = useMUD();

  const [result, setResult] = useState();

  const tasks = useStore((state) => {
    const records = Object.values(state.getRecords(tables.Tasks));
    records.sort((a, b) => Number(a.value.createdAt - b.value.createdAt));
    return records;
  });

  return (
    <>
      <table>
        <tbody>
          {tasks.map((task) => (
            <tr key={task.id}>
              <td align="right">
                <input
                  type="checkbox"
                  checked={task.value.completedAt > 0n}
                  title={task.value.completedAt === 0n ? "Mark task as completed" : "Mark task as incomplete"}
                  onChange={async (event) => {
                    event.preventDefault();
                    const checkbox = event.currentTarget;

                    checkbox.disabled = true;
                    try {
                      await toggleTask(task.key.id);
                    } finally {
                      checkbox.disabled = false;
                    }
                  }}
                />
              </td>
              <td>{task.value.completedAt > 0n ? <s>{task.value.description}</s> : <>{task.value.description}</>}</td>
              <td align="right">
                <button
                  type="button"
                  title="Delete task"
                  style={styleUnset}
                  onClick={async (event) => {
                    event.preventDefault();
                    if (!window.confirm("Are you sure you want to delete this task?")) return;

                    const button = event.currentTarget;
                    button.disabled = true;
                    try {
                      await deleteTask(task.key.id);
                    } finally {
                      button.disabled = false;
                    }
                  }}
                >
                  &times;
                </button>
              </td>
            </tr>
          ))}
        </tbody>
        <tfoot>
          <tr>
            <td>
              <input type="checkbox" disabled />
            </td>
            <td colSpan={2}>
              <form
                onSubmit={async (event) => {
                  event.preventDefault();
                  const form = event.currentTarget;
                  const fieldset = form.querySelector("fieldset");
                  if (!(fieldset instanceof HTMLFieldSetElement)) return;

                  const formData = new FormData(form);
                  const desc = formData.get("description");
                  if (typeof desc !== "string") return;

                  fieldset.disabled = true;
                  try {
                    await addTask(desc);
                    form.reset();
                  } finally {
                    fieldset.disabled = false;
                  }
                }}
              >
                <fieldset style={styleUnset}>
                  <input type="text" name="description" />{" "}
                  <button type="submit" title="Add task">
                    Add
                  </button>
                </fieldset>
              </form>
            </td>
          </tr>
        </tfoot>
      </table>

      <ErrButton
        fun={worldContract.write.app__stringRevert}
        msg="String revert (old style)"
        set={setResult}
      />
      <br />

      <ErrButton
        fun={worldContract.write.app__errorRevert}
        msg="Revert with error Fail()"
        set={setResult}
      />

      <ErrButton
        fun={worldContract.write.app__errorRevertLib}
        msg="Revert with error Errors.Fail()"
        set={setResult}
      />    
      <br />

      <ErrButton
        fun={worldContract.write.app__stringParamRevert}
        msg='Revert with error ParamFailString("Parameter fail")'
        set={setResult}
      />

      <ErrButton
        fun={worldContract.write.app__stringParamRevertLib}
        msg='Revert with error Errors.ParamFailString("Parameter fail")'
        set={setResult}
      />    
      <br />

      <ErrButton
        fun={worldContract.write.app__uintParamRevert}
        msg='Revert with error ParamFailUint(0x0BAD60A7)'
        set={setResult}
      />

      <ErrButton
        fun={worldContract.write.app__uintParamRevertLib}
        msg='Revert with error Errors.ParamFailUint(0x0BAD60A7)'
        set={setResult}
      />    
      <br />

      <h4>Message</h4>
      <pre>
      {result}
      </pre>
    </>
  );
};

And TasksSystem.sol with:

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;

import { System } from "@latticexyz/world/src/System.sol";
import { Tasks, TasksData } from "../codegen/index.sol";

error Fail();
error ParamFailString(string);
error ParamFailUint(uint256);

library Errors {
  error Fail();
  error ParamFailString(string);
  error ParamFailUint(uint256);
}


contract TasksSystem is System {
  function addTask(string memory description) public returns (bytes32 id) {
    id = keccak256(abi.encode(block.prevrandao, _msgSender(), description));
    Tasks.set(id, TasksData({ description: description, createdAt: block.timestamp, completedAt: 0 }));
  }

  function completeTask(bytes32 id) public {
    Tasks.setCompletedAt(id, block.timestamp);
  }

  function resetTask(bytes32 id) public {
    Tasks.setCompletedAt(id, 0);
  }

  function deleteTask(bytes32 id) public {
    Tasks.deleteRecord(id);
  }

  function stringRevert() public {
    revert("Old style revert, with a string");
  }

  function errorRevert() public {
    revert Fail();
  }

  function stringParamRevert() public {
    revert ParamFailString("Parameter fail");
  }

  function uintParamRevert() public {
    revert ParamFailUint(0x0BAD60A7);
  }

  function errorRevertLib() public {
    revert Errors.Fail();
  }

  function stringParamRevertLib() public {
    revert Errors.ParamFailString("Parameter fail");
  }

  function uintParamRevertLib() public {
    revert Errors.ParamFailUint(0x0BAD60A7);
  }    
}
@holic
Copy link
Member

holic commented Aug 16, 2024

might be related to wevm/viem#2624

I have a potential patch in MUD to work around this in the meantime, just need to write it up

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

2 participants