Search code examples
soliditychainlinkgo-ethereum

Remix: Returned error: {"jsonrpc":"2.0","error":"invalid opcode: PUSH0", "id":2405507186007008}


After runing my private chain node in geth and Chainlink node in my Ubuntu, I would like to test the function of Chainlink Any API(https://docs.chain.link/any-api/get-request/examples/single-word-response, Single Word Response).

I ran these commands to run nodes:

## SHELL1
cd ~/myChain/localChain/node1 && geth --datadir data --gcmode "archive" --syncmode=full --networkid 4190 --http --http.addr 0.0.0.0 --http.port 6789 --http.corsdomain "*" --ws --port 30305 --allow-insecure-unlock --unlock edd96278959aA8B27DdC14FD70ACb31f7e7beC2F --keystore ./keystore console

## SHELL2
cd ~/myChain/chainlink/.chainlink && docker run --net host -u=root  -p 6688:6688 -v ~/.chainlink:/chainlink -it --env-file=.env smartcontract/chainlink:1.11.0 local n

I successfully deployed my LinkToken contract, operator contract and created a new job(GET>uint256) in my Chainlink node UI Operator. They are as followed:

LinkToken.sol

pragma solidity ^0.4.11;

import "https://github.com/smartcontractkit/chainlink/contracts/src/v0.4/ERC677Token.sol";
import { StandardToken as linkStandardToken } from "https://github.com/smartcontractkit/chainlink/contracts/src/v0.4/vendor/StandardToken.sol";


contract LinkToken is linkStandardToken, ERC677Token {

  uint public constant totalSupply = 10**27;
  string public constant name = "ChainLink Token";
  uint8 public constant decimals = 18;
  string public constant symbol = "LINK";

  function LinkToken()
    public
  {
    balances[msg.sender] = totalSupply;
  }

  /**
  * @dev transfer token to a specified address with additional data if the recipient is a contract.
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  * @param _data The extra data to be passed to the receiving contract.
  */
  function transferAndCall(address _to, uint _value, bytes _data)
    public
    validRecipient(_to)
    returns (bool success)
  {
    return super.transferAndCall(_to, _value, _data);
  }

  /**
  * @dev transfer token to a specified address.
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint _value)
    public
    validRecipient(_to)
    returns (bool success)
  {
    return super.transfer(_to, _value);
  }

  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  function approve(address _spender, uint256 _value)
    public
    validRecipient(_spender)
    returns (bool)
  {
    return super.approve(_spender,  _value);
  }

  /**
   * @dev Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amount of tokens to be transferred
   */
  function transferFrom(address _from, address _to, uint256 _value)
    public
    validRecipient(_to)
    returns (bool)
  {
    return super.transferFrom(_from, _to, _value);
  }


  // MODIFIERS

  modifier validRecipient(address _recipient) {
    require(_recipient != address(0) && _recipient != address(this));
    _;
  }

}

operator.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
import "@chainlink/contracts/src/v0.7/Operator.sol";

job GET>uint256 (I have already change the "YOUR_ORACLE_CONTRACT_ADDRESS" with my operator contract address)

type = "directrequest"
schemaVersion = 1
name = "Get > Uint256 - (TOML)"
maxTaskDuration = "0s"
contractAddress = "YOUR_ORACLE_CONTRACT_ADDRESS"
minIncomingConfirmations = 0
observationSource = """
    decode_log   [type="ethabidecodelog"
                  abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"
                  data="$(jobRun.logData)"
                  topics="$(jobRun.logTopics)"]

    decode_cbor  [type="cborparse" data="$(decode_log.data)"]
    fetch        [type="http" method=GET url="$(decode_cbor.get)" allowUnrestrictedNetworkAccess="true"]
    parse        [type="jsonparse" path="$(decode_cbor.path)" data="$(fetch)"]

    multiply     [type="multiply" input="$(parse)" times="$(decode_cbor.times)"]

    encode_data  [type="ethabiencode" abi="(bytes32 requestId, uint256 value)" data="{ \\"requestId\\": $(decode_log.requestId), \\"value\\": $(multiply) }"]
    encode_tx    [type="ethabiencode"
                  abi="fulfillOracleRequest2(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes calldata data)"
                  data="{\\"requestId\\": $(decode_log.requestId), \\"payment\\":   $(decode_log.payment), \\"callbackAddress\\": $(decode_log.callbackAddr), \\"callbackFunctionId\\": $(decode_log.callbackFunctionId), \\"expiration\\": $(decode_log.cancelExpiration), \\"data\\": $(encode_data)}"
                  ]
    submit_tx    [type="ethtx" to="YOUR_ORACLE_CONTRACT_ADDRESS" data="$(encode_tx)"]

    decode_log -> decode_cbor -> fetch -> parse -> multiply -> encode_data -> encode_tx -> submit_tx
"""

LinkToken contract is used to transfer LINK token to other address, and Operator contract is used to interact with my Chainlink node. By following the document of Chainlink, I will deploy ATestnetConsumer contract then request the API data.

However, I got an error in Remix like this:

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending? Returned error: {"jsonrpc":"2.0","error":"invalid opcode: PUSH0", "id":2405507186007008}

PUSH0 Error in Remix And my geth console also got this error: PUSH0 Error in Geth

I tried to reset my geth node and Chainlink node and redo all the same steps. However, they don't make sense.


Solution

  • With Solc version 0.8.20, the default EVM version is set to "Shanghai". A new opcode, PUSHO, was added to the Ethereum Virtual Machine in the Shanghai upgrade. That means the PUSH0 opcode can now be part of the contract's bytecode. If your private chain does not support it, it will error with the "Invalid opcode" error.

    The solution is to manually set the EVM version to some previous version, for example, "Paris" (the version before, also known as The Merge), instead of "Shanghai", which is the default now.

    Remix:

    • Go to the compilation tab
    • Toggle the "Advanced Configurations" dropdown
    • Toggle the "EVM VERSION" dropdown menu and select "paris" instead of "default"

    Solc:

    • Run solc --evm-version <VERSION> <CONTRACT>
    • for example solc --evm-version paris contract.sol

    Hardhat:

    • Adjust the hardhat config file like this
    // hardhat.config.ts
    solidity: {
        compilers: [
            {
                version: '0.8.20',
                settings: {
                    evmVersion: 'paris'
                }
            }
        ]
    }
    

    Foundry:

    • Adjust the foundry.toml file like this
    evm_version = 'paris'