CKB/Ethereum RPC Interface Usage

·

Interacting with blockchain networks programmatically has become a cornerstone of decentralized application (dApp) development. Whether you're retrieving transaction data, checking token balances, or deploying smart contracts, Ethereum's JSON-RPC interface provides the essential gateway. This guide walks through practical implementations using Go and public Ethereum nodes—without requiring a locally synced client.

By leveraging services like public RPC endpoints, developers can efficiently query and interact with the Ethereum blockchain in real time. The following sections explore core operations such as fetching transfer records, querying ERC20 token data like USDT, checking balances, signing transactions, and even deploying and interacting with smart contracts—all using Go and standard Ethereum libraries.


Querying Ethereum Transfer Records

One of the most common tasks in blockchain analysis is retrieving transaction details from a block. Since running a full Ethereum node can be resource-intensive, we use a public node provider to access the network.

Using the ethclient package from go-ethereum, you can connect to a remote node via Infura or similar services. Each transaction in Ethereum does not directly include the sender’s address—it must be derived from the digital signature.

To recover the sender:

  1. Extract the public key from the transaction signature.
  2. Derive the Ethereum address from the public key.

Here’s how to fetch the latest block and print sender, receiver, and value for each transaction:

package main

import (
 "context"
 "log"
 "math/big"
 "github.com/ethereum/go-ethereum/core/types"
 "github.com/ethereum/go-ethereum/ethclient"
 "github.com/godump/doa"
)

func main() {
 ethClient := doa.Try(ethclient.Dial("https://mainnet.infura.io/v3/5c17ecf14e0d4756aa81b6a1154dc599"))
 blockNumber := doa.Try(ethClient.BlockNumber(context.Background()))
 block := doa.Try(ethClient.BlockByNumber(context.Background(), big.NewInt(int64(blockNumber))))

 for _, tx := range block.Transactions() {
  sender := doa.Try(types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx))
  amount, _ := tx.Value().Float64()
  log.Printf("%s -> %s %14.6f ETH", sender, tx.To(), amount/1e18)
 }
}

👉 Learn how to securely manage Ethereum interactions with advanced tools


Fetching USDT (ERC20) Transfer Logs

Tether (USDT) operates as an ERC20 token on Ethereum. To track transfers, we parse event logs emitted by the contract. The Transfer event emits from, to, and value—accessible via the Log entries in transactions.

The event signature (topic) for Transfer(address,address,uint256) is:

0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

We filter logs using this topic and decode the data:

query := ethereum.FilterQuery{
 Addresses: []common.Address{common.HexToAddress("0xdac17f958d2ee523a2206206994597c13d831ec7")},
 Topics: [][]common.Hash{
  {common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")},
 },
 FromBlock: big.NewInt(int64(blockNumber)),
 ToBlock: big.NewInt(int64(blockNumber)),
}

txlog := doa.Try(ethClient.FilterLogs(context.Background(), query))
for _, e := range txlog {
 amount := new(big.Int).SetBytes(e.Data)
 valuef, _ := amount.Float64()
 log.Printf("%s %s -> %s %.6f USDT", e.TxHash, common.HexToAddress(e.Topics[1].Hex()), common.HexToAddress(e.Topics[2].Hex()), valuef/1e6)
}

This method allows efficient monitoring of USDT movements without parsing every transaction.


Checking USDT Balance of an Address

ERC20 tokens use the balanceOf(address) function to return token balances. To call this function via RPC:

  1. Compute the function selector: first 4 bytes of keccak256("balanceOf(address)")70a08231.
  2. Encode the target address as 32-byte right-padded hexadecimal.
  3. Concatenate selector + encoded address.
  4. Use CallContract to simulate execution.

Example: Query balance of F977814e90dA44bFA03b6295A0616a897441aceC:

contract := common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7")
ret := doa.Try(ethClient.CallContract(context.Background(), ethereum.CallMsg{
 To: &contract,
 Data: doa.Try(hex.DecodeString("70a08231000000000000000000000000F977814e90dA44bFA03b6295A0616a897441aceC")),
}, big.NewInt(int64(blockNumber))))

balance := new(big.Int).SetBytes(ret)
log.Println("USDT Balance:", float64(balance.Int64())/1e6)

Random Address Balance Checker (Educational Use)

For demonstration purposes, this code generates random private keys and checks if their associated addresses hold any ETH:

for {
 pri := doa.Try(crypto.GenerateKey())
 adr := crypto.PubkeyToAddress(pri.PublicKey)
 val, err := ethClient.BalanceAt(context.Background(), adr, nil)
 if err != nil { continue }
 log.Println("Private Key:", hex.EncodeToString(crypto.FromECDSA(pri)), "Address:", adr, "Balance:", val)
 if val.Cmp(big.NewInt(0)) > 0 { break }
}

⚠️ Note: This is purely illustrative. The odds of finding a funded key are astronomically low and should not be used for real-world attempts.


Signing and Verifying Messages

Digital signatures are fundamental to blockchain security. Using secp256k1, you can sign a message hash and verify its authenticity:

msg := make([]byte, 32)
rand.Read(msg)
sig := doa.Try(crypto.Sign(msg, prikey))
pubKeyBytes := crypto.FromECDSAPub(&pubkey)

// Verify using ecrecover
recoveredPub, _ := crypto.Ecrecover(msg, sig)
doa.Doa(slices.Equal(recoveredPub, pubKeyBytes))

This pattern underpins transaction authentication and wallet verification.


Sending ETH Transactions

To send ETH, you must:

Example sending 1 ETH to null address:

tx := types.NewTransaction(nonce, recv, value, gasLimit, gasPrice, data)
txSign := doa.Try(types.SignTx(tx, types.NewEIP155Signer(id), prikey))
doa.Nil(client.SendTransaction(context.Background(), txSign))

👉 Discover secure ways to interact with Ethereum using trusted platforms


Deploying a Smart Contract

Compile Solidity code (e.g., a simple storage contract) into bytecode and deploy:

pragma solidity >=0.8.2 <0.9.0;
contract Storage {
 uint256 number;
 function set(uint256 num) public { number = num; }
 function get() public view returns (uint256) { return number; }
}

Deploy via RPC:

data := doa.Try(os.ReadFile("storage"))
tx := types.NewContractCreation(nonce, value, gasLimit, gasPrice, data)
txSign := doa.Try(types.SignTx(tx, types.NewEIP155Signer(id), prikey))
doa.Nil(client.SendTransaction(context.Background(), txSign))

After mining, retrieve the contract address from the receipt.


Interacting With Deployed Contracts

Call functions like set(42) and get() by encoding function selectors and parameters:

data = append(data, hash([]byte("set(uint256)"))[:4]...)
data = append(data, math.U256Bytes(big.NewInt(42))...)

Use CallContract to read state (get()) without gas cost.


Setting Up a Local Development Node

For testing, run a local Ethereum dev chain:

git clone https://github.com/ethereum/go-ethereum --branch release/1.13
cd go-ethereum && make geth
geth --dev --http

Attach console:

geth attach http://localhost:8545

Send test transactions:

eth.sendTransaction({from: eth.accounts[0], to: '0x...', value: web3.toWei(1, 'ether')})

Ideal for debugging dApps offline.


Frequently Asked Questions

Q: Can I use public RPC nodes for production?
A: Yes, but consider rate limits and reliability. For high-demand applications, run your own node or use premium API tiers.

Q: How do I get the function signature for ERC20 methods?
A: Hash the function string with Keccak-256 and take the first 4 bytes. Tools like web3.utils.sha3("balanceOf(address)") help automate this.

Q: Is it safe to expose my private key in code?
A: No. Never hardcode private keys. Use secure vaults or environment variables in production.

Q: What is the difference between CallContract and sending a transaction?
A: CallContract reads data locally (free), while sending a transaction writes to blockchain (costs gas).

Q: Why do I need to recover the sender from a transaction?
A: Ethereum transactions only include signatures; the sender’s address isn’t stored directly and must be derived.

Q: Can I monitor all USDT transfers in real time?
A: Yes, by setting up event listeners with SubscribeFilterLogs for continuous stream processing.

👉 Access powerful blockchain tools to streamline your development workflow


Core Keywords:

This guide equips developers with foundational skills to interact with Ethereum programmatically—enabling efficient dApp development, analytics, and automation.