Closed shreethejaBandit closed 1 year ago
Did you setup the contract to have the generated Go file to handle method call types for the contract:
//Step 1: Create abi file by running: solc --abi Store.sol > store.abi
//Step 2: Create bin file by running: solc --bin Store.sol > store.bin
//Step 3: Remove comments above the abi and bin files.
//Step 4: Generate Go contract interaction file by running: abigen --bin=store.bin --abi=store.abi --pkg=store --out=store.go
//Step 5: Run: getSetEvent.go
?
See this example:
https://github.com/MarcusWentz/Web3_Get_Set_Contract_Metamask/tree/main/Scripts/go
Yes that is for contract call i have tried that method too but this doesnt need abigen stuff this directly calls rpc method eth_call with passing raw data. To make this issue simpler i have shared a raw example.
If the reference to the abigen method needed attached the code below
func (rpc *EvmRpc) callTokenUri(contractAddress string, tokenId string) (string, error) {
contract, err := erc721.NewMainCaller(common.HexToAddress(contractAddress), rpc)
if err != nil {
return "", err
}
token, err := strconv.Atoi(tokenId)
if err != nil {
return "", err
}
uri, err := contract.TokenURI(&bind.CallOpts{}, big.NewInt(int64(token)))
if err != nil {
return "", err
}
return uri, nil
}
Here erc721 is the contract generated from abigen method tried with this method too on eth bayc contract worked fine but in case of shardeum fails with above error.
I was able to run:
package main
import (
"fmt"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
client, _ := ethclient.Dial("https://liberty20.shardeum.org")
defer client.Close()
contractAddr := common.HexToAddress("0x8f01876ccd727fd703787ed477b7f71e1e3adbb1")
callMsg := ethereum.CallMsg{
To: &contractAddr,
Data: common.FromHex("0x8da5cb5b"),
}
fmt.Printf("callMsg: ", callMsg)
}
Are you able to use the equivalent to:
client.CallContract(context.Background(), callMsg, nil)
in Javascript, Python and Rust in the meantime while we look into this?
fmt.Printf("callMsg: ", callMsg)
This just prints the call arguments actual call is done by this line
client.CallContract(context.Background(), callMsg, nil)
Which throws error. JS Python looks good but go is throwing some problems is this related to the issue #23 ?
This shouldn't be a problem from package this is official go-ethereum client of geth
Does the callMsg printed output look correct?
Does the curl request work with this data as mentioned in https://github.com/Shardeum/shardeum-bug-reporting/issues/23?
I found another bug while testing this with a possible solution as well: https://github.com/Shardeum/shardeum-bug-reporting/issues/29
Does the callMsg printed output look correct?
This is the call message output it seems to be proper is it accessList should be handled on sharded network.
Does the curl request work with this data as mentioned in #23?
Yes Curl works fine as attached below
Thank you for testing the curl request.
We will look into what might be causing this issue.
Please use Javascript and Python (or maybe even Rust) in the meantime until this is fixed.
The curl request in text form to test:
curl 'https://liberty20.shardeum.org/' -H 'content-type: application/json' --data-raw '{"method":"eth_call","params":[
{
"to":"0x8f01876ccd727fd703787ed477b7f71e1e3adbb1",
"data":"0x8da5cb5b"
},"latest"],"id":46,"jsonrpc":"2.0"}'
Result:
{"jsonrpc":"2.0","id":46,"result":"0x000000000000000000000000911027f9a8e982b8f6e6d6e3e844bae26490fca4"}
It is possible the Owner function data might not match this contract for some reason.
I recommend doing a contract call like this based on generated contract types:
func getstoredData(contract *store.Store) (storedData *big.Int) {
storedData, err := contract.StoredData(&bind.CallOpts{})
if err != nil {
log.Fatal(err)
}
return
}
to find the Owner.
Golang example:
https://github.com/MarcusWentz/Web3_Get_Set_Contract_Metamask/blob/main/Scripts/go/getSetEvent.go
It is possible the Owner function data might not match this contract for some reason.
I recommend doing a contract call like this based on generated contract types:
func getstoredData(contract *store.Store) (storedData *big.Int) { storedData, err := contract.StoredData(&bind.CallOpts{}) if err != nil { log.Fatal(err) } return }
to find the Owner.
Golang example:
https://github.com/MarcusWentz/Web3_Get_Set_Contract_Metamask/blob/main/Scripts/go/getSetEvent.go
The Original Method Follows the same as mentioned still the same error.
In Javascript I get:
const ethers = require("ethers");
(async () => {
const provider = new ethers.providers.JsonRpcProvider("https://liberty20.shardeum.org")
const contractAddress = "0x8f01876ccd727fd703787ed477b7f71e1e3adbb1";
const rawCallData = "0x8da5cb5b";
let callReturnData = await provider.call({
to: contractAddress,
data: rawCallData
});
console.log(callReturnData);
console.log(typeof(callReturnData));
})();
which returns:
0x000000000000000000000000911027f9a8e982b8f6e6d6e3e844bae26490fca4
string
I will try to test this in Rust next to see if it has any type errors like Golang using ethers.rs:
https://docs.rs/ethers-providers/latest/ethers_providers/struct.Provider.html#method.call
Is this contract deployed to Goerli as well?
If not, please deploy the contract to Goerli, then share the contract address on Goerli so we can test the return value and type as well.
Is this contract deployed to Goerli as well?
If not, please deploy the contract to Goerli, then share the contract address on Goerli so we can test the return value and type as well.
Yes Goerli tested against all standard networks and JS also no problem only on golang we get this error. same code with goerlli rpc will work.
Rust test working:
// use std::env;
use std::str::FromStr;
use std::str;
use ethers::types::transaction::eip2718::TypedTransaction;
use ethers_providers::{Provider};
use ethers_core::types::{Address};
use ethers::{
prelude::*,
};
use eyre::Result;
#[tokio::main]
async fn main() -> Result<()> {
// let rpc_goerli_infura_https = env::var("goerliHTTPS_InfuraAPIKey").expect("$goerliHTTPS_InfuraAPIKey is not set");
// let provider = Provider::<Http>::try_from(rpc_goerli_infura_https).expect("could not instantiate HTTP Provider");
let rpc_shardeum_https = "https://liberty20.shardeum.org/";
let provider = Provider::<Http>::try_from(rpc_shardeum_https).expect("could not instantiate HTTP Provider");
let chain_id_connected = provider.get_chainid().await?;
println!("Got chain_id_connected: {}", chain_id_connected);
let mut tx_raw = TypedTransaction::Legacy(TransactionRequest::new());
if chain_id_connected == U256::from(5) {
let contract_address = "0x326C977E6efc84E512bB9C30f76E30c160eD06FB".parse::<Address>()?; //Chainlink Address Goerli
let from_wallet = "0x66C1d8A5ee726b545576A75380391835F8AAA43c";
let hex_string_function = "balanceOf(address)";
let hex_string_function_hashed = ethers::utils::keccak256(hex_string_function);
let function_selector: String = prefix_hex::encode(&hex_string_function_hashed[0..4]);
let padded_zeroes: &str = "000000000000000000000000";
let slice_wallet_to_add_to_data: &str = &from_wallet[2..42];
let raw_string = function_selector + padded_zeroes + slice_wallet_to_add_to_data;
let raw_call_data = Bytes::from_str(&raw_string).unwrap();
tx_raw.set_to(contract_address);
tx_raw.set_data(raw_call_data);
}
if chain_id_connected == U256::from(8081) {
let contract_address = "0x8f01876ccd727fd703787ed477b7f71e1e3adbb1".parse::<Address>()?;
let raw_call_data = Bytes::from_str("0x8da5cb5b").unwrap(); //ERC-20: balanceOf(0x66C1d8A5ee726b545576A75380391835F8AAA43c)
tx_raw.set_to(contract_address);
tx_raw.set_data(raw_call_data);
}
println!("{:?}", tx_raw);
let call_return_data = provider.call(&tx_raw,None).await?;
println!("{:?}", call_return_data);
// println!("{:?}", call_return_data.whatisthis); //Shows type as "ethers::types::Bytes" in error message. Credit: https://stackoverflow.com/a/21747400
Ok(())
}
Returns:
Legacy(TransactionRequest { from: None, to: Some(Address(0x8f01876ccd727fd703787ed477b7f71e1e3adbb1)), gas: None, gas_price: None, value: None, data: Some(Bytes(0x8da5cb5b)), nonce: None, chain_id: Some(8081) })
Bytes(0x000000000000000000000000911027f9a8e982b8f6e6d6e3e844bae26490fca4)
I see the response is type Bytes (Javascript showed type String):
error[E0609]: no field `whatisthis` on type `ethers::types::Bytes`
--> src/main.rs:50:39
|
50 | println!("{:?}", call_return_data.whatisthis); //Shows type as "ethers::types::Bytes" in error message. Credit: https://stackoverflow...
| ^^^^^^^^^^ unknown field
|
= note: available fields are: `0
It is possible that Golang is sensitive to this return type potentially being wrong when trying to unmarshal the JSON RPC response.
Golang
client.CallContract(context.Background(), callMsg, nil)
test:
package main
import (
"context"
"encoding/hex"
"fmt"
"log"
"os"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/common"
)
func main() {
client, err := ethclient.Dial(os.Getenv("goerliHTTPS_InfuraAPIKey"))
// client, err := ethclient.Dial("https://liberty20.shardeum.org")
if err != nil {
log.Fatal(err)
}
//Goerli
tx_to := common.HexToAddress("0x326C977E6efc84E512bB9C30f76E30c160eD06FB")
tx_data := common.FromHex("0x70a0823100000000000000000000000066C1d8A5ee726b545576A75380391835F8AAA43c")
//Liberty 2.X
// tx_to := common.HexToAddress("0x8f01876ccd727fd703787ed477b7f71e1e3adbb1")
// tx_data := common.FromHex("0x8da5cb5b")
callMsg := ethereum.CallMsg{
To: &tx_to,
Data: tx_data,
}
fmt.Printf("callMsg: ")
fmt.Println(callMsg)
res, err := client.CallContract(context.Background(), callMsg, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("eth_call: " + hex.EncodeToString(res) + "\n")
}
Goerli:
callMsg: {0x0000000000000000000000000000000000000000 0x326C977E6efc84E512bB9C30f76E30c160eD06FB 0 <nil> <nil> <nil> <nil> [112 160 130 49 0 0 0 0 0 0 0 0 0 0 0 0 102 193 216 165 238 114 107 84 85 118 167 83 128 57 24 53 248 170 164 60] []}
eth_call: 0000000000000000000000000000000000000000000000008ac7230489e80000
Liberty 2.X:
callMsg: {0x0000000000000000000000000000000000000000 0x8F01876CCd727Fd703787eD477B7f71E1E3ADbb1 0 <nil> <nil> <nil> <nil> [141 165 203 91] []}
2023/02/14 12:59:37 Busy or error
exit status 1
Golang test with eth_call working by sending raw JSON to the RPC client:
package main
import (
"fmt"
"log"
// "os"
"github.com/ethereum/go-ethereum/rpc"
)
func main() {
// client, err := rpc.DialHTTP(os.Getenv("goerliHTTPS_InfuraAPIKey"))
client, err := rpc.DialHTTP("https://liberty20.shardeum.org")
if err != nil {
log.Fatal(err)
}
type request struct {
To string `json:"to"`
Data string `json:"data"`
}
var result string
// req := request{"0x326C977E6efc84E512bB9C30f76E30c160eD06FB", "0x70a0823100000000000000000000000066C1d8A5ee726b545576A75380391835F8AAA43c"}
req := request{"0x8f01876ccd727fd703787ed477b7f71e1e3adbb1", "0x8da5cb5b"}
if err := client.Call(&result, "eth_call", req, "latest"); err != nil {
log.Fatal(err)
}
fmt.Printf(result + "\n")
}
Goerli:
0x0000000000000000000000000000000000000000000000008ac7230489e80000
Liberty 2.X:
0x000000000000000000000000911027f9a8e982b8f6e6d6e3e844bae26490fca4
Test with contract IDEX.sol on Ethereum Mainnet:
https://etherscan.io/address/0xcc13fc627effd6e35d2d2706ea3c4d7396c610ea#code
from:
https://ethereum.stackexchange.com/a/63813
Betatnet 1.X contract address:
https://explorer-sphinx.shardeum.org/account/0xa66cc96316a4df10b96dc3e62dae184d04e93ad9
RPC eth_call
in Golang works with client.Call(&result, "eth_call", req, "latest")
:
package main
import (
"fmt"
"log"
"os"
"github.com/ethereum/go-ethereum/rpc"
)
func main() {
client, err := rpc.DialHTTP(os.Getenv("mainnetHTTPS_InfuraAPIKey"))
// client, err := rpc.DialHTTP("https://sphinx.shardeum.org/")
if err != nil {
log.Fatal(err)
}
type request struct {
To string `json:"to"`
Data string `json:"data"`
}
var result string
req := request{"0xcc13fc627effd6e35d2d2706ea3c4d7396c610ea", "0x8da5cb5b"}
// req := request{"0xA66CC96316A4dF10b96Dc3e62dAE184d04E93Ad9", "0x8da5cb5b"}
if err := client.Call(&result, "eth_call", req, "latest"); err != nil {
log.Fatal(err)
}
fmt.Printf(result + "\n")
}
client.CallContract(context.Background(), callMsg, nil)
returns Busy or error
:
package main
import (
"context"
"encoding/hex"
"fmt"
"log"
"os"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/common"
)
func main() {
client, err := ethclient.Dial(os.Getenv("mainnetHTTPS_InfuraAPIKey"))
// client, err := ethclient.Dial("https://sphinx.shardeum.org/")
if err != nil {
log.Fatal(err)
}
//Ethereum Mainnet
tx_to := common.HexToAddress("0xcc13fc627effd6e35d2d2706ea3c4d7396c610ea")
//Betanet 1.X
// tx_to := common.HexToAddress("0xA66CC96316A4dF10b96Dc3e62dAE184d04E93Ad9")
tx_data := common.FromHex("0x8da5cb5b")
callMsg := ethereum.CallMsg{
To: &tx_to,
Data: tx_data,
}
fmt.Printf("callMsg: ")
fmt.Println(callMsg)
res, err := client.CallContract(context.Background(), callMsg, nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("eth_call: " + hex.EncodeToString(res) + "\n")
}
The golang eth implementation aways sends the message with the "From" address, so if you do not provide it it will send the request with the zero value for address: 0x0000000000000000000000000000000000000000
like
curl 'https://sphinx.shardeum.org/' -H 'content-type: application/json' --data-raw '{"method":"eth_call","params":[ { "from": "0x0000000000000000000000000000000000000000", "to":"0x8f01876ccd727fd703787ed477b7f71e1e3adbb1", "data":"0x8da5cb5b" },"latest"],"id":46,"jsonrpc":"2.0"}'
which gives a {"jsonrpc":"2.0","id":46,"error":{"code":500,"message":"Busy or error"}} response from our network
fix: make our rpc identify 0x0000000000000000000000000000000000000000 as no Value (null)
workaround: use a address in the golang request
func main() {
client, err := ethclient.Dial("https://sphinx.shardeum.org/%22/)
defer client.Close()
contractAddr := common.HexToAddress("0x8f01876ccd727fd703787ed477b7f71e1e3adbb1")
callMsg := ethereum.CallMsg{
From: common.HexToAddress("0xfC321461191148FfC45Df25829717795909Ef8Ce"),
To: &contractAddr,
Data: common.FromHex("0x8da5cb5b"),
}
res, err := client.CallContract(context.Background(), callMsg, nil)
if err != nil {
fmt.Println("Error Found 😊
fmt.Print(err)
return
}
fmt.Printf("Owner: %s\n", common.BytesToAddress(res).Hex())
}
this will give you a 200 response!
Thanks, this helps for now
Shardeum RPC URL throws error when contract read methods are called via go client which is a official version of Geth client.
Impact : Read And Write To blockchain From Golang Applications are not possible.
How to reproduce the issue?
The above mentioned snippet calls owner() function from a contract deployed onto shardeum. Works fine on metamask and other chains and even on JS applications.
to run this code just type in command
go run main.go
Throws error saying : busy or error
What other resources can you share regarding this issue?