In order for a software application to interact with the Ethereum blockchain (by reading blockchain data and/or sending transactions to the network), it must connect to an Ethereum node.
For this purpose, every Ethereum client implements a JSON-RPC specification, so there are a uniform set of methods that applications can rely on.
JSON-RPC is a stateless, light-weight remote procedure call (RPC) protocol. It defines several data structures and the rules around their processing. It is transport agnostic in that the concepts can be used within the same process, over sockets, over HTTP, or in many various message passing environments. It uses JSON (RFC 4627) as data format.
Ethereum clients each may utilize different programming languages when implementing the JSON-RPC specification. See individual client documentation for further details related to specific programming languages. We recommend checking the documentation of each client for the latest API support information.
Read the full JSON-RPC API spec on GitHub.
Hex value encoding
Two key data types get passed over JSON: unformatted byte arrays and quantities. Both are passed with a hex encoding but with different requirements for formatting.
When encoding quantities (integers, numbers): encode as hex, prefix with “0x”, the most compact representation (slight exception: zero should be represented as “0x0”).
Here are some examples:
- 0x41 (65 in decimal)
- 0x400 (1024 in decimal)
- WRONG: 0x (should always have at least one digit – zero is “0x0”)
- WRONG: 0x0400 (no leading zeroes allowed)
- WRONG: ff (must be prefixed 0x)
When encoding unformatted data (byte arrays, account addresses, hashes, bytecode arrays): encode as hex, prefix with “0x”, two hex digits per byte.
Here are some examples:
- 0x41 (size 1, “A”)
- 0x004200 (size 3, “B”)
- 0x (size 0, “”)
- WRONG: 0xf0f0f (must be even number of digits)
- WRONG: 004200 (must be prefixed 0x)
The default block parameter
The following methods have an extra default block parameter:
When requests are made that act on the state of Ethereum, the last default block parameter determines the height of the block.
The following options are possible for the defaultBlock parameter:
- HEX String – an integer block number
- String “earliest” for the earliest/genesis block
- String “latest” – for the latest mined block
- String “pending” – for the pending state/transactions
Deploying a contract using JSON_RPC
This section includes a demonstration of how to deploy a contract using only the RPC interface. There are alternative routes to deploying contracts where this complexity is abstracted away—for example, using libraries built on top of the RPC interface such as web3.js and web3.py. These abstractions are generally easier to understand and less error-prone, but it is still helpful to understand what is happening under the hood.
The following is a straightforward smart contract called Multiply7 that will be deployed using the JSON-RPC interface to an Ethereum node. This tutorial assumes the reader is already running a Geth node. More information on nodes and clients is available here. Please refer to individual client documentation to see how to start the HTTP JSON-RPC for non-Geth clients. Most clients default to serving on localhost:8545.
The first thing to do is make sure the HTTP RPC interface is enabled. This means we supply Geth with the -http flag on startup. In this example we use the Geth node on a private development chain. Using this approach we don’t need ether on the real network.
This will start the HTTP RPC interface on http://localhost:8545.
We can verify that the interface is running by retrieving the Coinbase address and balance using curl. Please note that data in these examples will differ on your local node. If you want to try these commands, replace the request params in the second curl request with the result returned from the first.
Because numbers are hex encoded, the balance is returned in wei as a hex string. If we want to have the balance in ether as a number we can use web3 from the Geth console.
Now that there is some ether on our private development chain, we can deploy the contract. The first step is to compile the Multiply7 contract to byte code that can be sent to the EVM. To install solc, the Solidity compiler, follow the Solidity documentation. (You might want to use an older solc release to match the version of compiler used for our example.)
The next step is to compile the Multiply7 contract to byte code that can be send to the EVM.
Now that we have the compiled code we need to determine how much gas it costs to deploy it. The RPC interface has an eth_estimateGas method that will give us an estimate.
And finally deploy the contract.
The transaction is accepted by the node and a transaction hash is returned. This hash can be used to track the transaction. The next step is to determine the address where our contract is deployed. Each executed transaction will create a receipt. This receipt contains various information about the transaction such as in which block the transaction was included and how much gas was used by the EVM. If a transaction creates a contract it will also contain the contract address. We can retrieve the receipt with the eth_getTransactionReceipt RPC method.
Our contract was created on 0x4d03d617d700cf81935d7f797f4e2ae719648262. A null result instead of a receipt means the transaction has not been included in a block yet. Wait for a moment and check if your miner is running and retry it.
Interacting with smart contracts
In this example we will be sending a transaction using eth_sendTransaction to the multiply method of the contract.
eth_sendTransaction requires several arguments, specifically from, to and data. From is the public address of our account, and to is the contract address. The data argument contains a payload that defines which method must be called and with which arguments. This is where the ABI (application binary interface) comes into play. The ABI is a JSON file that defines how to define and encode data for the EVM.
The bytes of the payload defines which method in the contract is called. This is the first 4 bytes from the Keccak hash over the function name and its argument types, hex encoded. The multiply function accepts an uint which is an alias for uint256. This leaves us with:
The next step is to encode the arguments. There is only one uint256, say, the value 6. The ABI has a section which specifies how to encode uint256 types.
int<M>: enc(X) is the big-endian two’s complement encoding of X, padded on the higher-order (left) side with 0xff for negative X and with zero > bytes for positive X such that the length is a multiple of 32 bytes.
This encodes to 0000000000000000000000000000000000000000000000000000000000000006.
Combining the function selector and the encoded argument our data will be 0xc6888fa10000000000000000000000000000000000000000000000000000000000000006.
This can now be sent to the node:
Since a transaction was sent, a transaction hash was returned. Retrieving the receipt gives:
The receipt contains a log. This log was generated by the EVM on transaction execution and included in the receipt. The multiply function shows that the Print event was raised with the input times 7. Since the argument for the Print event was a uint256 we can decode it according to the ABI rules which will leave us with the expected decimal 42. Apart from the data it is worth noting that topics can be used to determine which event created the log:
This was just a brief introduction into some of the most common tasks, demonstrating direct usage of the JSON-RPC.
- JSON-RPC specification
- Nodes and clients
- Backend APIs
- Execution clients