Skip to content

Commit

Permalink
test: call Candid-RPC methods from Motoko E2E canister (#104)
Browse files Browse the repository at this point in the history
* Set up Motoko Candid-RPC E2E tests

* Refactor
  • Loading branch information
rvanasa authored Dec 20, 2023
1 parent 34d78f6 commit 3e9cebd
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 4 deletions.
89 changes: 89 additions & 0 deletions e2e/motoko/Main.mo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Evm "mo:evm";
import Debug "mo:base/Debug";
import Text "mo:base/Text";
import Blob "mo:base/Blob";
import Cycles "mo:base/ExperimentalCycles"

shared ({ caller = installer }) actor class Main() {
let mainnet = Evm.Rpc(
Expand All @@ -25,6 +26,7 @@ shared ({ caller = installer }) actor class Main() {
let json = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_gasPrice\",\"params\":null,\"id\":1}";
let maxResponseBytes : Nat64 = 1000;

// `requestCost()`
let cyclesResult = await EvmRpcCanister.requestCost(source, json, maxResponseBytes);
let cycles = switch cyclesResult {
case (#Ok cycles) { cycles };
Expand All @@ -33,12 +35,14 @@ shared ({ caller = installer }) actor class Main() {
};
};

// `request()` without cycles
let resultWithoutCycles = await EvmRpcCanister.request(source, json, maxResponseBytes);
assert switch resultWithoutCycles {
case (#Err(#ProviderError(#TooFewCycles { expected }))) expected == cycles;
case _ false;
};

// `request()` with cycles
let result = await mainnet.request("eth_gasPrice", #Array([]), 1000);
label validate {
switch result {
Expand All @@ -58,6 +62,91 @@ shared ({ caller = installer }) actor class Main() {
Debug.trap(debug_show result);
};

// Candid-RPC methods
type RpcResult<T> = { #Ok : T; #Err : EvmRpcCanister.RpcError };
type MultiRpcResult<T> = {
#Consistent : RpcResult<T>;
#Inconsistent : [(EvmRpcCanister.RpcService, RpcResult<T>)];
};

func assertOk<T>(method : Text, result : MultiRpcResult<T>) {
switch result {
case (#Consistent(#Ok _)) {};
case (#Consistent(#Err err)) {
Debug.trap("received error for " # method # ": " # debug_show err);
};
case (#Inconsistent(results)) {
for ((service, result) in results.vals()) {
switch result {
case (#Ok(_)) {};
case (#Err(err)) {
Debug.trap("received error in inconsistent results for " # method # ": " # debug_show err);
};
};
};
};
};
};

let candidRpcCycles = 1_000_000_000_000;
let ethMainnetSource = #EthMainnet(null);

Cycles.add(candidRpcCycles);
assertOk(
"eth_getLogs",
await EvmRpcCanister.eth_getLogs(
ethMainnetSource,
{
addresses = ["0xdAC17F958D2ee523a2206206994597C13D831ec7"];
fromBlock = null;
toBlock = null;
topics = null;
},
),
);
Cycles.add(candidRpcCycles);
assertOk(
"eth_getBlockByNumber",
await EvmRpcCanister.eth_getBlockByNumber(ethMainnetSource, #Latest),
);
Cycles.add(candidRpcCycles);
assertOk(
"eth_getTransactionReceipt",
await EvmRpcCanister.eth_getTransactionReceipt(ethMainnetSource, "0xdd5d4b18923d7aae953c7996d791118102e889bea37b48a651157a4890e4746f"),
);
Cycles.add(candidRpcCycles);
assertOk(
"eth_getTransactionCount",
await EvmRpcCanister.eth_getTransactionCount(
ethMainnetSource,
{
address = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
block = #Latest;
},
),
);
Cycles.add(candidRpcCycles);
assertOk(
"eth_feeHistory",
await EvmRpcCanister.eth_feeHistory(
ethMainnetSource,
{
blockCount = 3;
newestBlock = #Latest;
rewardPercentiles = null;
},
),
);
Cycles.add(candidRpcCycles);
assertOk(
"eth_sendRawTransaction",
await EvmRpcCanister.eth_sendRawTransaction(
ethMainnetSource,
"0xf86c098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a76400008025a028ef61340bd939bc2195fe537567866003e1a15d3c71ff63e1590620aa636276a067cbe9d8997f761aecb703304b3800ccf555c9f3dc64214b297fb1966a3b6d83",
),
);

// Signature verification
let a1 = "0xc9b28dca7ea6c5e176a58ba9df53c30ba52c6642";
let a2 = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045";

Expand Down
8 changes: 4 additions & 4 deletions lib/motoko/src/lib.mo
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ module {
#JsonRpcError : JsonRpcError;
};

public type Result<T> = {
public type RpcResult<T> = {
#ok : T;
#err : Error;
};
Expand Down Expand Up @@ -104,7 +104,7 @@ module {
let actorSource = wrapActorSource(source);

var nextId : Nat = 0;
public func request(method : Text, params : JSON.JSON, maxResponseBytes : Nat64) : async Result<JSON.JSON> {
public func request(method : Text, params : JSON.JSON, maxResponseBytes : Nat64) : async RpcResult<JSON.JSON> {
nextId += 1;
// prettier-ignore
let payload = JSON.show(#Object([
Expand Down Expand Up @@ -132,8 +132,8 @@ module {
};
};

public func requestPlain(payload : Text, maxResponseBytes : Nat64) : async Result<Text> {
func requestPlain_(payload : Text, maxResponseBytes : Nat64, cycles : Nat) : async Result<Text> {
public func requestPlain(payload : Text, maxResponseBytes : Nat64) : async RpcResult<Text> {
func requestPlain_(payload : Text, maxResponseBytes : Nat64, cycles : Nat) : async RpcResult<Text> {
Cycles.add(cycles);
switch (await actor_.request(actorSource, payload, maxResponseBytes)) {
case (#Ok ok) { #ok ok };
Expand Down

0 comments on commit 3e9cebd

Please sign in to comment.