Rujira
Domain library for Rujira, built on THORChain.
Provides shared types, query APIs, and event parsing for blockchain protocol data across 20+ chains.
Installation
Add rujira_ex to your list of dependencies in mix.exs:
def deps do
[
{:rujira_ex, "~> 0.7.0"}
]
end
Modules
Rujira.Amount— integer amounts normalized to 8 decimal placesRujira.Assets— blockchain asset resolution across 20+ chains (ETH, BTC, BSC, GAIA, SOL, etc.)Rujira.Coin— asset + amount pairsRujira.Fin— FIN DEX query API (pairs, order books, orders, ranges)Rujira.Events— multi-protocol event parser with typed envelopesRujira.Contracts— CosmWasm smart contract queries (cached)Rujira.Deployments— on-chain contract registry resolved live from THORChainRujira.Node— pluggable gRPC node abstractionRujira.Prices— pluggable price provider with oracle and FIN book fallbackRujira.Math— decimal arithmetic and numeric parsing
Configuration
config :rujira_ex,
node: MyApp.Node, # required — implement Rujira.Node behaviour
prices: Rujira.Prices.Default # optional — defaults to oracle + FIN mid-price fallback
See Caching for the cache's own tuning
knobs (retention, frontier_max_rows, max_markers, …).
Usage
Amounts and Coins
# Amounts are normalized to 8 decimal places (1.0 = 100_000_000)
{:ok, amount} = Rujira.Amount.new("1.5")
# Parse coin strings
{:ok, coin} = Rujira.Coin.new(%{asset: "ETH.ETH", amount: "1500000000"})
# Parse comma-separated coins
{:ok, coins} = Rujira.Coin.parse("1000rune,500uatom")
Asset Resolution
{:ok, asset} = Rujira.Assets.from_denom("uatom")
{:ok, asset} = Rujira.Assets.from_string("GAIA.ATOM")
decimals = Rujira.Assets.decimals(asset)
Event Parsing
# Parse raw blockchain events into typed structs
{:ok, event} = Rujira.Events.parse(%{type: "wasm-rujira-fin/trade", attributes: attrs})
# Pattern match on protocol-specific event data
%Rujira.Fin.Events.Event{address: addr, data: %Rujira.Fin.Events.Trade{} = trade} = event
FIN DEX Queries
{:ok, pairs} = Rujira.Fin.list_pairs()
{:ok, pair} = Rujira.Fin.get_pair(pair_address)
{:ok, %{book: book}} = Rujira.Fin.load_pair(pair, limit)
mid_price = book.center
Caching
Implement Rujira.Node (query/3) and call Rujira.Node.advance/1 on every
new height, before consuming it — the library fetches that block, invalidates
whatever it changed, and moves its head.
Reads without height: are served at the head. Before the first advance/1,
they return {:error, :no_head}. Pass height: for a historical read, and
pin it while indexing a block.
# indexer: advance first, then index at the pinned height
# a failed advance leaves the head where it was; the next call retries
with {:error, reason} <- Rujira.Node.advance(height) do
Logger.warning("advance to #{height} failed: #{inspect(reason)}")
end
{:ok, pairs} = Rujira.Fin.list_pairs(height: height)
# API consumer: reads without height: are served at the head
{:ok, pairs} = Rujira.Fin.list_pairs()
See Caching for the full model.
Guides
- Coding Conventions — style, naming, return values, numeric parsing
- Architecture — protocol structure, event pipeline, adding new protocols
Development
mix format --check-formatted
mix compile --warnings-as-errors
mix test
mix credo --strict
mix dialyzer
License
MIT — see LICENSE for details.