2

I want to call an external RPC method from the offchain_worker. I checked multiple jsonrpc clients but none of them were no_std compatible. I want to know if there are any clients out there which can be used within the ocw of substrate.

I also tried to call the RPCs using sp_runtime::offchain::http::Request, but couldn't find an example of post method, any examples/help would be much appreciated.


[EDIT] just found an example of the latter part here, so please just let me know if there are no_std jsonrpc clients out there.

1 Answer 1

1

So looks like there's no RPC client ATM which supports no_std. the following is the implementation of an RPC method using sp_runtime::offchain::http::Request and serde:

use serde::{Deserialize, Serialize};
use scale_info::prelude::string::String;
use sp_runtime::{
  offchain::{
    http::Request,
    Duration,    
  },
};

#[derive(Serialize, Deserialize, Clone)]
struct RpcObject {
  jsonrpc: String,
  method: String,
  params: String, // could be a nested object
  id: String
}

#[derive(Serialize, Deserialize, Clone)]
struct RpcResponse {
  result: String,
  id: String
}

fn call_rpc_method(url: &str, method: String, params: String) -> Result<Vec<u8>, Error<T>> {
    let request_body = RpcObject {
      jsonrpc: String::from("2.0"),
      method: method.into(),
      params: params.into(),
      id: String::from("1")
    };
    let request_body_str = serde_json::to_string(&request_body).map_err(|_| <Error<T>>::Error)?;
    let request = Request::post(url, vec![request_body_str.clone()])
      .add_header("content-type", "application/json");

    let timeout = sp_io::offchain::timestamp()
      .add(Duration::from_millis(FETCH_TIMEOUT_PERIOD));

    let pending = request
      .deadline(timeout)
      .body(vec![request_body_str.clone()])
      .send() // Sending the request out by the host
      .map_err(|e| {
        log::error!("rpc request err: {:?}", e);
        <Error<T>>::Error
      })?;

    let response = pending
      .try_wait(timeout)
      .map_err(|e| {
        log::error!("rpc error: {:?}", e);
        <Error<T>>::HttpFetchingError
      })?
      .map_err(|e| {
        log::error!("rpc error: {:?}", e);
        <Error<T>>::HttpFetchingError
      })?;

    if response.code != 200 {
      log::error!("Unexpected http request status code: {}", response.code);
      return Err(<Error<T>>::HttpFetchingError);
    }

    // Next we fully read the response body and collect it to a vector of bytes.
    let resp_bytes = response.body().collect::<Vec<u8>>();
    Ok(resp_bytes)
  }



next you can use the response like:

let resut: RpcResponse = serde_json::from_slice(&res)
            .map_err(|e| {
              log::info!("deserialize error: {:?}", e);
              <Error<T>>::DeserializeToObjError
            })?;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.