Hello devs! I am pleased to announce that the EVM RPC Canister is now live. Thank you all for your feedback and comments while we developed this service.
The canister is still in beta, and while we put it through some real-world battle testing, it will remain managed by DFINITY. Once we are confident it can serve the needs of all of your dapps safely and performantly, we will publish a GA release and put it under NNS control.
Big thank you to @THLO@gregory-demay@Manu and most of all @rvanasa for all of the hard work in getting this out. I don’t think I can give enough credit to @rvanasa — he really put his heart and soul into this to give you all a fast and easy way to bridge your canisters to EVM smart contracts. Incredible work, Ryan!
As you begin your integrations, please let us know of any issues you come across. We will address feedback as quickly as possible, but will give priority to bugs and security issues.
Hey I have a major question, does the canister rely on making on request to one Ethereum provider? Or does it make multiple requests and compare them? I don’t see any way to control that so far in the Candid file.
If there is no capability to use multiple providers and have the responses compared, I would like to understand the reasoning and suggest that this be implemented so that applications can choose to increase the security of their Ethereum integration until we have a direct integration.
This is the case specifically for the candid RPC methods. The general RPC endpoint accepts a provider id as a param and will leave the normalization / canonicalization up to the developer.
Is there something that isn’t obvious when you run one of the candid RPC methods? Perhaps @rvanasa can help out
It’s possible to select any number or combination of these RPC providers. Here is an example dfx command for making an RPC request with all 5 built-in Ethereum providers:
Note that using more RPC providers increases the chance of encountering HTTP outcall consensus errors, but this may be worth it for the added security depending on the situation.
We are in the process of rolling out documentation and examples for how to use the canister; hopefully this is useful as a reference point for the time being.
Okay I understand now, I didn’t see the vec for providing the providers.
I am trying to get a basic example working, and eth_getBlockByNumber so far no matter what provider I try is returning "missing field baseFeePerGas:
{"Consistent":{"Err":{"HttpOutcallError":{"InvalidHttpJsonRpcResponse":{"status":200,"body":"{\"jsonrpc\":\"2.0\",\"id\":13,\"result\":{\"difficulty\":\"0x400000000\",\"extraData\":\"0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa\",\"gasLimit\":\"0x1388\",\"gasUsed\":\"0x0\",\"hash\":\"0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"mixHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"nonce\":\"0x0000000000000042\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x21c\",\"stateRoot\":\"0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544\",\"timestamp\":\"0x0\",\"totalDifficulty\":\"0x400000000\",\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]}}\n","parsingError":["missing field `baseFeePerGas` at line 1 column 1473"]}}}}}
Be sure to check out @dfx-json’s announcement blog post which gives some great background for the EVM RPC canister along with detailed installation instructions.
Documentation is now available for those interested in the full capabilities of the EVM RPC canister.
Let us know if you run into any issues or have any questions; your feedback is highly important during this beta testing phase. Feel free to open an issue or even contribute a PR on the project’s GitHub repository.
let source = #Custom {
chainId = 42161;
services = [{ url = "https://1rpc.io/arb"; headers = null }];
};
Cycles.add(1000000000);
let result = await EvmRpc.eth_getBlockByNumber(source, null, #Latest);
A full list of chain ids and RPC services can be found on ChainList.org.
We appreciate your involvement during this beta testing phase and encourage you to reach out with any questions or requests here or on the GitHub repository.
+1. It’s possible to use most networks listed on ChainList by specifying a custom provider (relevant documentation), although support varies depending on the specific chain and RPC method.
We don’t have a clear timeline for when it will be out of beta. Basically, we want to gather enough data to make sure the canister is a) safe for production use and b) polished enough from a DX standpoint for production use. The more feedback devs like you can give us, the sooner we can get it out of beta, so I encourage everyone to run it through the wringer.
Okay I am confused again here. Why is JsonRpcSource different than RpcSource for Request? If I want to do a raw Json RPC Request, not one of the pre-provided methods like eth_feeHistory, why is the Source different? Does JsonRpcSource also do consensus among various providers?
I was trying to do eth_getBalance and realized there wasn’t a candid method provided, so I switched to making a raw Request, put the Source is very different, that’s where this confusion is coming from.
The Text is just JSON Text specifying what you want to do. The nat64 is the expected return size that the function likely uses in the HTTP outcall to allocate space and charge for gas.
If you are getting a balance you can set it pretty low because the response is likely a just at 256bit Number. If you are calling a function that might have a bigger size you may need to make it bigger…I this setup to try to get the recommended gas fee but it turned out being pretty useless as most L2 just have this hard coded:
/// Retrieve the latest block on the Ethereum blockchain.
public func getEthGas() : async Nat {
nonce_api_id += 1;
// Select RPC services
let services = #EthMainnet(#Alchemy);
// Call `eth_getBlockByNumber` RPC method (unused cycles will be refunded)
Cycles.add(1000000000);
let result = await EvmRpc.request(services, "{\"id\": " # Nat.toText(nonce_api_id) # ", \"jsonrpc\": \"2.0\", \"method\": \"eth_maxPriorityFeePerGas\"}", 1000);
switch result {
// Consistent, successful results
case (#Ok(val)){
let myJSON = JSON.parse(val);
Debug.print(debug_show(myJSON));
let ?(#Object(array)) = myJSON;
Debug.print(debug_show(array));
let ?myNode = Array.find<(Text, JSON.JSON)>(array, func (x) {
switch x {
case ("result", _){
true;
};
case (_, _){
false;
};
};
});
Debug.print(debug_show(myNode));
let #String(myHex) = myNode.1;
Debug.print(debug_show(myHex));
let ?justbytes = Text.stripStart(myHex, #text("0x"));
Debug.print(debug_show(justbytes));
let #ok(myBytes) = HEX.decode(justbytes);
Debug.print(debug_show(myBytes));
let myNat = Conversion.bytesToNat(myBytes);
Debug.print(debug_show("ethgas" # debug_show(myNat)));
myNat;
};
// Consistent error message
case (#Err(err)) 15;
};
};
Thanks for the info on the last parameter, I’m mostly concerned with the first parameter, the RpcSource or JsonRpcSource, I don’t understand why they’re different and it’s confusing.
I’m more and more coming at this from a teaching angle, these small things cause confusion when introducing developers new to the IC.
This indeed sounds like outdated documentation. In the latest EVM RPC version, JsonRpcSource is removed in favor of names which more clearly explain the distinction (RpcService and RpcServices).
Since everything appears up-to-date in the developer docs and GitHub repository, definitely let me know if any other website still uses the original Candid interface so we can fix that.
The nat64 arg is the maximum number of bytes in the response, which is used to determine the cost of the RPC request. I opened a PR to clarify this in the Candid interface (#181).
Hopefully this helps, and please feel free to mention any other usability issues. The goal is to smooth out these details before we lock the canister into the NNS.