blockchain0x
Official Ruby SDK for Blockchain0x - the non-custodial AI-agent wallet platform on Base.
Pre-release:
0.0.1.alpha.0ships the operational essentials - HTTP transport,api_keysresource, andWebhooks.verify. The full surface (every resource + x402 client) lands in sub-plan 21.3 Phase C follow-up rows.
Install
gem install blockchain0x --pre
Or in a Gemfile:
gem 'blockchain0x', '~> 0.0.1.alpha'
Requires Ruby 3.0 or newer.
Quick start
require 'blockchain0x'
client = Blockchain0x::Client.new(api_key: ENV.fetch('BLOCKCHAIN0X_API_KEY'))
page = client.api_keys.list
page['data'].each { |k| puts "#{k['id']}\t#{k['prefix']}" }
The client pins the network from the API-key prefix (sk_test_* →
testnet, sk_live_* → mainnet). Override with
Blockchain0x::Client.new(api_key: ..., network: 'mainnet') when
running mixed-mode tests.
Verify webhook signatures
The single most important utility this SDK ships - drop it into the top of your webhook controller BEFORE touching the body.
# Rails example. Adapt for Sinatra / Hanami / Roda - the only
# requirement is the raw request body, NOT a parsed JSON hash.
class WebhooksController < ApplicationController
skip_before_action :verify_authenticity_token, only: :receive
def receive
result = Blockchain0x::Webhooks.verify(
headers: request.headers,
raw_body: request.raw_post,
secret: ENV.fetch('BLOCKCHAIN0X_WEBHOOK_SECRET'),
)
return head(:bad_request) unless result.ok?
# result.event_type / result.event_id / result.delivery_id populated.
Sidekiq.redis do |r|
r.lpush("events:#{result.event_type}", request.raw_post)
end
head :no_content
end
end
The verifier:
- Reads
X-Blockchain0x-Signaturein eithert=<ts>,v1=<hex>or bare-hex form (some load balancers strip commas). - Falls back to
X-Blockchain0x-Timestampwhen the signature is bare. - Rejects with
webhook.timestamp_outside_windowwhen drift exceeds 300 seconds. - Constant-time compares via
OpenSSL.fixed_length_secure_compare(or a manual byte-XOR fallback for older OpenSSL builds).
For exception-based flows pass raise_on_fail: true:
begin
result = Blockchain0x::Webhooks.verify(
headers: request.headers,
raw_body: request.raw_post,
secret: ENV.fetch('BLOCKCHAIN0X_WEBHOOK_SECRET'),
raise_on_fail: true,
)
rescue Blockchain0x::WebhookSignatureError => e
return render json: { code: e.code }, status: :bad_request
end
Errors
Two classes:
Blockchain0x::Error- base class; every SDK error inherits.Blockchain0x::APIKeyError- subclass for HTTP 401 / 403 envelopes whoseerror.codestarts withapikey.(e.g.apikey.scope_insufficient,apikey.wallet_not_assigned).
Always branch on .code, never regex-match .message:
begin
client.api_keys.list
rescue Blockchain0x::APIKeyError => e
if e.code == 'apikey.scope_insufficient'
# mint a fresh key with more scope
end
end
The module helper Blockchain0x.apikey_error?(error) returns true
when the exception's code begins with apikey. regardless of
subclass.
Retry behaviour
The transport retries on 429 and 5xx with exponential backoff
(0.5s → 1s → 2s → … → 30s cap, 3 retries by default).
Retry-After is honoured when the server sends it.
POST / PATCH / DELETE requests carry an Idempotency-Key
header - the SDK mints a UUID v4 if you do not supply one. Pass
idempotency_key: '...' to thread a stable key across SDK retries OR
across processes (e.g. a cron job that hashes its input
deterministically).
Workspace keys (sub-plan 21.3)
Two key shapes exist (see docs/concept-api-key-types.md for the full decision tree):
- Wallet-only - bound to ONE agent. Right shape for an autonomous AI agent that IS one wallet.
- Workspace - human-operator key that can carry workspace-level scopes AND assignments to N specific wallets.
The Ruby SDK forwards both shapes through client.api_keys. Once
the C-2-style create method lands beyond the alpha scaffold:
key = client.api_keys.create(
label: 'Treasury reconciliation',
workspace_scopes: ['read_workspace'],
wallet_assignments: [
{ agent_id: 'agt_trading', scopes: ['read_wallet_metadata'] },
{ agent_id: 'agt_settlement', scopes: ['read_wallet_metadata'] },
],
expires_in_days: 30,
)
puts key['secret'] # shown ONCE
Server-side RBAC: the minter cannot grant a scope they do not have
themselves. Over-grants reject with apikey.role_insufficient_for_grants
which is surfaced as a Blockchain0x::APIKeyError.
x402 (Phase C-7)
The sibling gem blockchain0x-x402 (Ruby port of
@blockchain0x/x402)
will ship the x402 client + Rack middleware in sub-plan 21.3 row C-7.
The wire format is identical across languages so a Ruby service can
accept payments from a Node client and vice-versa.
Codegen
Model classes are generated from
apps/backend/openapi/openapi.yaml via
openapi-generator-cli
with -g ruby --global-property=models,supportingFiles=false - see
codegen/README.md for the decision
rationale.
Source-of-truth + distribution
Source-of-truth: this directory in
Tosh-Labs/blockchain0x-app
under packages/sdk-ruby/.
Public mirror: Tosh-Labs/blockchain0x-ruby
(receives merges from this directory on dispatch of the
mirror-sdk-ruby workflow).
Distribution: rubygems via Trusted Publisher OIDC.
License
Apache-2.0.