View Source Icon.RPC.Request (ICON 2.0 SDK v0.2.3)
This module defines a basic JSON RPC request payloads.
building-a-request
Building a Request
The requests built using build/3
are prepared to be encoded as a JSON
accepted by the ICON 2.0 JSON RPC v3. For building a request we need three
things:
- The name of the method we're calling.
- The parameters we're sending along the method.
- Some options to both validate the parameters, sign any transaction and build the actual request.
e.g. let's say we want to query a block by its height, we would do the following:
iex> method = "icx_getBlockByHeight"
iex> params = %{height: 42}
iex> schema = %{height: :pos_integer}
iex> Icon.RPC.Request.build(method, params, schema: schema)
%Icon.RPC.Request{
id: 1_639_382_704_065_742_380,
method: "icx_getBlockByHeight",
options: %{
schema: %{height: :pos_integer},
identity: #Identity<
node: "https://ctz.solidwallet.io",
network_id: "0x01 (Mainnet)",
debug: false
>,
url: "https://ctz.solidwallet.io/api/v3"
},
params: %{height: 42}
}
Note: The previous example is for documentation purpose. This functionality is already present in
Icon.RPC.Request.Goloop
, so no need to build the request ourselves.
And, when using the function Jason.encode!
, we'll get the following JSON:
{
"id": 1639382704065742380,
"jsonrpc": "2.0",
"method": "icx_getBlockByHeight",
"params": {
"height": "0x2a"
}
}
signing-a-transaction
Signing a Transaction
Transactions need to be signed before sending them to the node. With the signature is possible to verify it comes from the wallet requesting it e.g. let's say we want to sent 1 ICX from one wallet to another:
iex> request = Icon.RPC.Request.build("icx_sendTransaction", ...)
%Icon.RPC.Request{
id: 1_639_382_704_065_742_380,
method: "icx_sendTransaction",
options: ...,
params: %{
from: "hx2e243ad926ac48d15156756fce28314357d49d83",
to: "hxdd3ead969f0dfb0b72265ca584092a3fb25d27e0",
value: 1_000_000_000_000_000_000,
...
}
}
then we can sign it as follows:
iex> {:ok, request} = Icon.RPC.Request.sign(request)
{
:ok,
%Icon.RPC.Request{
id: 1_639_382_704_065_742_380,
method: "icx_sendTransaction",
options: ...,
params: %{
from: "hx2e243ad926ac48d15156756fce28314357d49d83",
to: "hxdd3ead969f0dfb0b72265ca584092a3fb25d27e0",
value: 1_000_000_000_000_000_000,
signature: "Kut8d4uXzy0UPIU13l3OW5Ba3WNuq6B6w7+0v4XR4qQNv1Cy3qOmn7ih4TZrXZGT3qhkaRM/WCL+qmWyh86/tgA="
...
}
}
}
and also verify it to check everything is correct after signing it:
iex> Icon.RPC.Request.verify(request)
true
Note: In order to sign a transaction, the option
identity
is mandatory and it needs to be built using aprivate_key
e.g.iex> Icon.RPC.Identity.new(private_key: "8ad9...") #Identity<[ node: "https://ctz.solidwallet.io", network_id: "0x1 (Mainnet)", debug: false, address: "hxfd7e4560ba363f5aabd32caac7317feeee70ea57", private_key: "8ad9..." ]>
sending-the-request
Sending the Request
Once we're satisfied with our request, we can send it to the ICON 2.0
blockchain using the send/1
function:
iex> Icon.RPC.Request.send(request)
{:ok, "0xd579ce6162019928d874da9bd1dbf7cced2359a5614e8aa0bf7cf75f3770504b"}
where the response()
will depend on the actual method being called.
Link to this section Summary
Types
RPC ID.
RPC Method.
RPC option.
RPC options.
RPC call parameters.
A JSON RPC response.
A JSON RPC request.
Functions
Adds step limit to a transaction. If no value is provided, then it will request for a node estimation.
Builds an RPC request given a method
, some parameters
and general
options
.
Sends a remote procedure call to an ICON 2.0 node.
Serializes a transaction request
.
Signs request
.
Whether a request
is signed correctly or not.
Link to this section Types
@type id() :: pos_integer()
RPC ID.
@type method() :: binary()
RPC Method.
@type option() :: {:schema, module() | Icon.Schema.t()} | {:timeout, non_neg_integer()} | {:identity, Icon.RPC.Identity.t()} | {:url, binary()}
RPC option.
@type options() :: [option()]
RPC options.
@type params() :: map()
RPC call parameters.
A JSON RPC response.
@type t() :: %Icon.RPC.Request{ id: id :: pos_integer(), method: method :: method(), options: options :: %{ :identity => Icon.RPC.Identity.t(), :url => binary(), optional(:schema) => module() | Icon.Schema.t(), optional(:timeout) => non_neg_integer() }, params: params :: params() }
A JSON RPC request.
Link to this section Functions
@spec add_step_limit(t(), nil | Icon.Schema.Types.Loop.t()) :: {:ok, t()} | {:error, Icon.Schema.Error.t()}
Adds step limit to a transaction. If no value is provided, then it will request for a node estimation.
Builds an RPC request given a method
, some parameters
and general
options
.
The method
and parameters
depend on the JSON RPC method we're calling,
while the options
have extra instructions for the actual request.
Options:
timeout
- Whether we should have a timeout on the call or not. This timeout only applies to methods we can wait on the result e.g.icx_waitTransactionResult
andicx_sendTransactionAndWait
.schema
-Icon.Schema
for verifying the callparameters
.identity
-Icon.RPC.Identity
of the wallet performing the action. A full identity is not required for most readonly calls. However, it is necessary to have a full wallet configure for sending transactions to the ICON 2.0 blockchain.url
- This endpoint is set automatically by the request builder.
encoding
Encoding
Though having the schema
option is not mandatory, it is necessary whenever
we're calling a method
with parameters
in order to convert them to the
ICON 2.0 type representation as well as serializing transactions. For more
information, check Icon.Schema
module.
example
Example
The following example shows how to build a request for querying a block by
height
:
iex> method = "icx_getBlockByHeight"
iex> params = %{height: 42}
iex> schema = %{height: :pos_integer}
iex> Icon.RPC.Request.build(method, params, schema: schema)
%Icon.RPC.Request{
id: 1_639_382_704_065_742_380,
method: "icx_getBlockByHeight",
options: %{
schema: %{height: :pos_integer},
identity: #Identity<
node: "https://ctz.solidwallet.io",
network_id: "0x01 (Mainnet)",
debug: false
>,
url: "https://ctz.solidwallet.io/api/v3"
},
params: %{height: 42}
}
@spec send(t()) :: {:ok, response()} | {:error, Icon.Schema.Error.t()}
Sends a remote procedure call to an ICON 2.0 node.
@spec serialize(t()) :: {:ok, binary()} | {:error, Icon.Schema.Error.t()}
Serializes a transaction request
.
When building a transaction signature, one of the steps of the process is serializing the transaction. In general, the serialization process goes as follows:
- Convert the JSON RPC method parameters to the ICON representation.
- Serialize them.
E.g. a request like the following:
%Icon.RPC.Request{
id: 1_641_400_211_292_452_380,
method: "icx_sendTransaction",
options: ...,
params: %{
from: "hx2e243ad926ac48d15156756fce28314357d49d83",
to: "hxdd3ead969f0dfb0b72265ca584092a3fb25d27e0",
nid: 1,
version: 3,
timestamp: ~U[2022-01-05 16:30:11.292452Z],
stepLimit: 100_000,
value: 1_000_000_000_000_000_000
}
}
would be serialized as follows:
icx_sendTransaction.from.hx2e243ad926ac48d15156756fce28314357d49d83.nid.0x1.stepLimit.0x186a0.timestamp.0x5d4d844874124.to.hxdd3ead969f0dfb0b72265ca584092a3fb25d27e0.value.0xde0b6b3a7640000.version.0x3
The serialization rules are simple:
- Values should be encoded to the ICONs encoding e.g. the integer
1
would be converted to"0x1"
. <key>
/<value>
pairs in maps should be converted to"<key>.<value>"
string e.g.{:a, 1}
would be converted to"a.0x1"
- All keys in a map should be in alphabetical order.
- All maps except the top level one should be surrounded by braces e.g.
%{a: 1}
would be converted to"{a.0x1}"
. - Lists should be surrounded by brackets and its elements should be separated
by
.
e.g.[1,2,3]
would be converted to"[0x1.0x2.0x3]"
. - The top level map should be preceded by
"icx_sendTransaction."
prefix e.g.%{from: "hx...", ...}
would be converted to"icx_sendTransaction.from.hx..."
- Any of the characters
\
,{
,}
,[
,]
and.
should be escaped by adding a\
before them e.g.%{message: "..."}
would be encoded as{message.\.\.\.}
.
@spec sign(t()) :: {:ok, t()} | {:error, Icon.Schema.Error.t()}
Signs request
.
Signing a request does the following:
- Serializes the parameters see
serialize/1
, - Hash the serialized parameters with
SHA3_256
digest algorithm twice. - Generate a SECP256K1 signature with the hash.
- Encode the signature in Base 64.
- Add the encoded signature to the transaction parameters.
Note:
Curvy
is the library used by this API for the signature. It generates compact signatures in the form ofVRS
while ICON expectsRSV
signatures. This modules handles the conversion between these formats transparently.
Whether a request
is signed correctly or not.