How to mimic ethers.js callStatic with JsonRCP

I am directly constructing view/read only contract queries successfully with eth_call, now I am wondering how to call a contract state changing function/method in a simulation mode?

use case: uniswap quoter contract quoteExactInputSingle function is to return quotes for a given pool, and mean to be executed only on the local node (as opposed to initiate an EVM state transition)

Here is the excerpt from the linked uniswap references:

To get around this difficulty, we can use the callStatic method provided by ethers.js . callStatic is a useful method that submits a state-changing transaction to an Ethereum node, but asks the node to simulate the state change, rather than to execute it. Our script can then return the result of the simulated state change.`

hey @steven.varga this is the geth state override see eth_call | Go Ethereum

1 Like

Thank you for the quick reply. Unfortunately I am still stuck, here is the MWE:
the eth_call fails, but the transaction returns the correct hash.
UPDATE: I just noticed that there is a payload, which never gets to me.

{'jsonrpc': '2.0', 'id': 5, 'error': {'code': 3, 'data': '0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010556e6578706563746564206572726f7200000000000000000000000000000000', 'message': 'execution reverted: Unexpected error'

This may be something to do with python’s web3 implementation, propagating the error because of the revert.

from web3 import Web3, HTTPProvider
from Crypto.Hash import keccak
import hashlib
import requests
import json

QUOTER = '0xb27308f9F90D607463bb33eA1BeBb41C27CE5AB6'   
WALLET = FILL_IN_ADDRESS
INFURA = 'https://ropsten.infura.io/v3/YOUR_ACCOUNT'
PRIVATE_KEY = YOUR_KEY

# on ropsten verified with https://etherscan.io/address/0x1F98431c8aD98523631AE4a59f267346ea31F984#readContract
WETH = '0xc778417E063141139Fce010982780140Aa0cD5Ab'
DAI = '0xaD6D458402F60fD3Bd25163575031ACDce07538D'

w3 = Web3(Web3.HTTPProvider(INFURA))
quoter  = w3.eth.contract(address=QUOTER,
    abi=json.loads('[{"inputs":[{"internalType":"address","name":"_factory","type":"address"},{"internalType":"address","name":"_WETH9","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH9","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"quoteExactInput","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"name":"quoteExactInputSingle","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"quoteExactOutput","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"uint24","name":"fee","type":"uint24"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint160","name":"sqrtPriceLimitX96","type":"uint160"}],"name":"quoteExactOutputSingle","outputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"amount0Delta","type":"int256"},{"internalType":"int256","name":"amount1Delta","type":"int256"},{"internalType":"bytes","name":"path","type":"bytes"}],"name":"uniswapV3SwapCallback","outputs":[],"stateMutability":"view","type":"function"}]'))

tx = quoter.functions.quoteExactInputSingle(USDC, WETH, 3000, 1000000000000, 0).build_transaction({
    'from': WALLET,
    'gas': 462000,
    'value': 1,
    'nonce': 24, 
})
## fails with: {'jsonrpc': '2.0', 'id': 5, 'error': {'code': -32000, 'message': 'execution reverted'}}
value = w3.eth.call(tx)

exit(0)  

## also fails:
print(w3.eth.estimate_gas(tx)) 

# OK: 
# <bound method HexBytes.hex of HexBytes('0xcdeda5150469eb2200ca368626dc6518d29d6538f87abc9cb332e1580b649c7e')>
tx_signed = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
print(w3.eth.send_raw_transaction(tx_signed.rawTransaction).hex)