JSON-RPC Request Builder — Ruby source
Build valid JSON-RPC 2.0 requests, notifications, success responses, and error responses, plus batch arrays. Validate message structure.
This is the Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.
# json-rpc-builder — Ruby port: JSON-RPC 2.0 message builder + structural validation.
require 'json'
JSONRPC_VERSION = '2.0'
STANDARD_ERRORS = {
-32700 => 'Parse error', -32600 => 'Invalid Request', -32601 => 'Method not found',
-32602 => 'Invalid params', -32603 => 'Internal error', -32000 => 'Server error'
}.freeze
# :UNSET distinguishes "argument absent" from a legitimate JSON null.
Outcome = Struct.new(:ok, :json, :error, keyword_init: true)
def stringify(obj, indent = nil)
indent ? JSON.pretty_generate(obj) : JSON.generate(obj)
end
def build_request(method, params: :UNSET, id: 1, indent: nil)
unless method.is_a?(String) && !method.empty?
return Outcome.new(ok: false, json: '', error: 'method must be a non-empty string')
end
obj = { 'jsonrpc' => JSONRPC_VERSION, 'method' => method }
obj['params'] = params unless params.equal?(:UNSET)
obj['id'] = id
Outcome.new(ok: true, json: stringify(obj, indent))
end
# A notification is a request without an id: fire-and-forget, no reply.
def build_notification(method, params: :UNSET, indent: nil)
unless method.is_a?(String) && !method.empty?
return Outcome.new(ok: false, json: '', error: 'method must be a non-empty string')
end
obj = { 'jsonrpc' => JSONRPC_VERSION, 'method' => method }
obj['params'] = params unless params.equal?(:UNSET)
Outcome.new(ok: true, json: stringify(obj, indent))
end
def build_success_response(id, result, indent = nil)
obj = { 'jsonrpc' => JSONRPC_VERSION, 'result' => result, 'id' => id }
Outcome.new(ok: true, json: stringify(obj, indent))
end
def build_error_response(id, code, message = nil, data: :UNSET, indent: nil)
error = { 'code' => code, 'message' => message || STANDARD_ERRORS.fetch(code, 'Error') }
error['data'] = data unless data.equal?(:UNSET)
obj = { 'jsonrpc' => JSONRPC_VERSION, 'error' => error, 'id' => id }
Outcome.new(ok: true, json: stringify(obj, indent))
end
# Per spec §6 a batch is a non-empty array sent in one round-trip.
def build_batch(messages, indent = nil)
unless messages.is_a?(Array) && !messages.empty?
return Outcome.new(ok: false, json: '', error: 'batch must be a non-empty array')
end
Outcome.new(ok: true, json: stringify(messages, indent))
end
# Lightweight shape check — reports every problem, not just the first.
def validate_rpc(obj)
return [false, ['Not an object.']] unless obj.is_a?(Hash)
errors = []
errors << 'jsonrpc must be "2.0".' if obj['jsonrpc'] != JSONRPC_VERSION
errors << 'method must be a string.' if obj.key?('method') && !obj['method'].is_a?(String)
errors << 'cannot have both result and error.' if obj.key?('result') && obj.key?('error')
unless %w[method result error].any? { |k| obj.key?(k) }
errors << 'must have method, result, or error.'
end
[errors.empty?, errors]
end
if $PROGRAM_NAME == __FILE__
puts build_request('getBalance', params: ['0x1234']).json
puts build_notification('blockHeader').json
puts build_success_response(1, '0x1bc16d674ec80000').json
puts build_error_response(1, -32601).json
p validate_rpc('jsonrpc' => '1.0')
end
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →