Package Exports
- @vultisig/rujira
- @vultisig/rujira/assets
- @vultisig/rujira/ccl
- @vultisig/rujira/ghost
- @vultisig/rujira/orderbook
- @vultisig/rujira/range
- @vultisig/rujira/signer
- @vultisig/rujira/staking
- @vultisig/rujira/swap
- @vultisig/rujira/utils
Readme
@vultisig/rujira
TypeScript SDK for interacting with Rujira (FIN) on THORChain.
It provides:
- FIN swap quotes + execution (CosmWasm)
- Secured asset ("synth-like") deposit helpers (inbound address + memo)
- Withdrawal helpers (THORChain
MsgDepositwithdrawal flow) - Route discovery + convenience easy routes
Status: Early/alpha. APIs may change.
Installation
# npm
npm i @vultisig/rujira
# yarn
yarn add @vultisig/rujira
# pnpm
pnpm add @vultisig/rujiraIf you are working inside this monorepo, use the workspace dependency:
{
"dependencies": {
"@vultisig/rujira": "workspace:^"
}
}Secured assets (what they are)
THORChain can custody L1 assets in inbound vaults and represent them on THORChain as secured assets (bank balances / denoms).
Typical flow:
- You send an L1 transfer (e.g. BTC) to a THORChain inbound address
- You include a memo that tells THORChain which THOR address should receive the secured asset
- THORChain credits your THOR address with the corresponding secured denom
- Those secured denoms can be swapped on FIN (Rujira orderbook DEX)
- You can withdraw back to L1 by broadcasting a THORChain
MsgDepositwith a withdraw memo
Notes:
- THORChain secured balances are commonly stored with 8 decimals.
- This package accepts human-friendly asset notation like
BTC.BTC/ETH.ETHand maps to FIN denoms when available via the built-in asset registry.
Quick start (read-only)
import { RujiraClient } from '@vultisig/rujira'
const client = new RujiraClient({ network: 'mainnet' })
await client.connect()
// 1) Discover FIN routes / contracts
const quote = await client.swap.getQuote({
fromAsset: 'THOR.RUNE',
toAsset: 'BTC.BTC',
amount: '100000000', // base units (example)
})
console.log('Expected out:', quote.expectedOutput)Quick start (with Vultisig signing)
To execute swaps/withdrawals you need a Cosmos signer. When using a Vultisig vault, use VultisigRujiraProvider.
import { Vultisig } from '@vultisig/sdk'
import { RujiraClient, VultisigRujiraProvider } from '@vultisig/rujira'
// 1) Initialize Vultisig SDK + pick a vault
const sdk = new Vultisig({ /* ... */ })
await sdk.initialize()
const vault = await sdk.getActiveVault()
if (!vault) throw new Error('No active vault')
// 2) Create a Rujira client with a Vultisig-backed signer
const signer = new VultisigRujiraProvider(vault)
const client = new RujiraClient({ network: 'mainnet', signer })
await client.connect()
// 3) FIN swap (secured denoms on THORChain)
const thorDestination = await vault.address('THORChain')
const quote = await client.swap.getQuote({
fromAsset: 'BTC.BTC',
toAsset: 'ETH.ETH',
amount: '100000',
destination: thorDestination,
slippageBps: 100,
})
const swapResult = await client.swap.execute(quote, { slippageBps: 100 })
console.log('Swap tx hash:', swapResult.txHash)Deposits (secure L1 → THORChain)
Get inbound address + memo
import { RujiraClient } from '@vultisig/rujira'
const client = new RujiraClient({ network: 'mainnet' })
await client.connect()
const deposit = await client.deposit.prepare({
fromAsset: 'BTC.BTC',
amount: '100000', // base units; used for validation / dust threshold checks
thorAddress: 'thor1...',
})
console.log('Send to:', deposit.inboundAddress)
console.log('Memo:', deposit.memo)List all inbound addresses
const inbound = await client.deposit.getInboundAddresses()
console.log(inbound.map(i => ({ chain: i.chain, address: i.address })))Check secured balances on THORChain
const balances = await client.deposit.getBalances('thor1...')
console.log(balances)Withdrawals (THORChain → L1)
Withdrawals are executed on THORChain via MsgDeposit with a withdraw memo. When using Vultisig, withdrawal execution requires a VultisigRujiraProvider (it exposes vault access for MsgDeposit signing).
import { RujiraClient, VultisigRujiraProvider } from '@vultisig/rujira'
const client = new RujiraClient({ network: 'mainnet', signer: new VultisigRujiraProvider(vault) })
await client.connect()
const prepared = await client.withdraw.prepare({
asset: 'BTC.BTC',
amount: '100000',
l1Address: 'bc1...',
})
const result = await client.withdraw.execute(prepared)
console.log('Withdraw tx hash:', result.txHash)Easy routes (convenience)
Use pre-defined routes to avoid thinking about pairs.
import { EASY_ROUTES, listEasyRoutes, getRoutesSummary } from '@vultisig/rujira'
console.log(getRoutesSummary())
console.log(listEasyRoutes())
const route = EASY_ROUTES.RUNE_TO_USDCAPI reference (high level)
new RujiraClient(options)
export interface RujiraClientOptions {
config?: Partial<RujiraConfig>
signer?: RujiraSigner
rpcEndpoint?: string
contractCache?: {
load?: () => Promise<Record<string, string>> | Record<string, string>
save?: (finContracts: Record<string, string>) => Promise<void> | void
}
debug?: boolean
swapOptions?: RujiraSwapOptions
}Key methods:
connect()/disconnect()isConnected()/canSign()getAddress()getBalance(address, denom)/getAllBalances(address)
Modules:
client.swap.getQuote(params)/client.swap.execute(quote, options)client.deposit.prepare(params)/client.deposit.getInboundAddresses()/client.deposit.getBalances(thorAddress)client.withdraw.prepare(params)/client.withdraw.execute(prepared)
VultisigRujiraProvider
A Cosmos signer implementation that delegates signing to a Vultisig vault.
import { VultisigRujiraProvider } from '@vultisig/rujira'
const signer = new VultisigRujiraProvider(vault /*, chainId? */)GraphQL / discovery rate limits (optional)
Rujira contract discovery may use GraphQL. If you run into HTTP 429 rate limits, you can provide an API token to the GraphQL client used by discovery.
import { RujiraDiscovery } from '@vultisig/rujira'
const discovery = new RujiraDiscovery({
network: 'mainnet',
graphql: { apiKey: process.env.RUJIRA_API_KEY },
})
const contracts = await discovery.discoverContracts()Examples
See:
packages/rujira/examples/packages/rujira/docs/
License
MIT