Installation
Copy
Ask AI
bun add @tevm/voltaire
Basic Store Pattern
Create reactive stores for Ethereum data:Copy
Ask AI
// lib/ethereum.ts
import { writable, derived } from 'svelte/store'
import { Address } from '@tevm/voltaire/Address'
import { Wei } from '@tevm/voltaire/Denomination'
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
const provider = createJsonRpcProvider('https://eth.llamarpc.com')
export function createBalanceStore(address: string) {
const { subscribe, set } = writable<string | null>(null)
const loading = writable(true)
const error = writable<Error | null>(null)
async function fetch() {
loading.set(true)
error.set(null)
try {
const addr = Address.from(address)
const balance = await provider.eth.getBalance({ address: addr })
set(Wei.toEther(balance))
} catch (e) {
error.set(e as Error)
} finally {
loading.set(false)
}
}
fetch()
return {
subscribe,
loading,
error,
refresh: fetch,
}
}
Copy
Ask AI
<!-- Balance.svelte -->
<script lang="ts">
import { createBalanceStore } from '$lib/ethereum'
export let address: string
$: balance = createBalanceStore(address)
</script>
{#if $balance.loading}
<p>Loading...</p>
{:else if $balance.error}
<p>Error: {$balance.error.message}</p>
{:else}
<p>Balance: {$balance} ETH</p>
<button on:click={balance.refresh}>Refresh</button>
{/if}
SvelteKit Load Functions
Fetch data server-side with SvelteKit:Copy
Ask AI
// routes/address/[address]/+page.server.ts
import { Address } from '@tevm/voltaire/Address'
import { Wei } from '@tevm/voltaire/Denomination'
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
import type { PageServerLoad } from './$types'
const provider = createJsonRpcProvider('https://eth.llamarpc.com')
export const load: PageServerLoad = async ({ params }) => {
const addr = Address.from(params.address)
const [balance, nonce, code] = await Promise.all([
provider.eth.getBalance({ address: addr }),
provider.eth.getTransactionCount({ address: addr }),
provider.eth.getCode({ address: addr }),
])
return {
address: params.address,
balance: Wei.toEther(balance),
nonce: Number(nonce),
isContract: code.length > 2,
}
}
Copy
Ask AI
<!-- routes/address/[address]/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types'
export let data: PageData
</script>
<h1>Address: {data.address}</h1>
<p>Balance: {data.balance} ETH</p>
<p>Nonce: {data.nonce}</p>
<p>Type: {data.isContract ? 'Contract' : 'EOA'}</p>
Reactive Polling
Poll for block updates:Copy
Ask AI
// lib/blocks.ts
import { readable } from 'svelte/store'
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
const provider = createJsonRpcProvider('https://eth.llamarpc.com')
export const latestBlock = readable<bigint | null>(null, (set) => {
const fetchBlock = async () => {
const blockNumber = await provider.eth.getBlockNumber()
set(blockNumber)
}
fetchBlock()
const interval = setInterval(fetchBlock, 12000)
return () => clearInterval(interval)
})
Copy
Ask AI
<script lang="ts">
import { latestBlock } from '$lib/blocks'
</script>
<p>Latest Block: {$latestBlock?.toString() ?? 'Loading...'}</p>
Derived Stores
Compute values from multiple stores:Copy
Ask AI
// lib/account.ts
import { derived, writable } from 'svelte/store'
import { Address } from '@tevm/voltaire/Address'
import { Wei } from '@tevm/voltaire/Denomination'
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
const provider = createJsonRpcProvider('https://eth.llamarpc.com')
export const currentAddress = writable<string | null>(null)
export const accountData = derived(
currentAddress,
($address, set) => {
if (!$address) {
set(null)
return
}
const fetchData = async () => {
const addr = Address.from($address)
const [balance, nonce] = await Promise.all([
provider.eth.getBalance({ address: addr }),
provider.eth.getTransactionCount({ address: addr }),
])
set({
address: $address,
balance: Wei.toEther(balance),
nonce: Number(nonce),
})
}
fetchData()
},
null as { address: string; balance: string; nonce: number } | null
)
Custom Async Store
Reusable async store factory:Copy
Ask AI
// lib/asyncStore.ts
import { writable, type Readable } from 'svelte/store'
interface AsyncStore<T> extends Readable<T | null> {
loading: Readable<boolean>
error: Readable<Error | null>
refresh: () => Promise<void>
}
export function asyncStore<T>(fetcher: () => Promise<T>): AsyncStore<T> {
const data = writable<T | null>(null)
const loading = writable(true)
const error = writable<Error | null>(null)
async function refresh() {
loading.set(true)
error.set(null)
try {
const result = await fetcher()
data.set(result)
} catch (e) {
error.set(e as Error)
} finally {
loading.set(false)
}
}
refresh()
return {
subscribe: data.subscribe,
loading: { subscribe: loading.subscribe },
error: { subscribe: error.subscribe },
refresh,
}
}
Copy
Ask AI
// Usage
import { asyncStore } from '$lib/asyncStore'
import { Address } from '@tevm/voltaire/Address'
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
const provider = createJsonRpcProvider('https://eth.llamarpc.com')
const balance = asyncStore(() =>
provider.eth.getBalance({
address: Address.from('0x...')
})
)
Actions for Transactions
Use Svelte actions for form submissions:Copy
Ask AI
<script lang="ts">
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
import { Address } from '@tevm/voltaire/Address'
let to = ''
let amount = ''
let status = ''
async function handleSend() {
status = 'Sending...'
try {
// Your transaction logic here
status = 'Sent!'
} catch (e) {
status = `Error: ${(e as Error).message}`
}
}
</script>
<form on:submit|preventDefault={handleSend}>
<input bind:value={to} placeholder="To address" />
<input bind:value={amount} placeholder="Amount ETH" />
<button type="submit">Send</button>
</form>
<p>{status}</p>
Context API
Share provider across components:Copy
Ask AI
// lib/context.ts
import { getContext, setContext } from 'svelte'
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
const PROVIDER_KEY = Symbol('provider')
export function setProvider(url: string) {
const provider = createJsonRpcProvider(url)
setContext(PROVIDER_KEY, provider)
return provider
}
export function getProvider() {
return getContext(PROVIDER_KEY)
}
Copy
Ask AI
<!-- +layout.svelte -->
<script lang="ts">
import { setProvider } from '$lib/context'
setProvider('https://eth.llamarpc.com')
</script>
<slot />
Svelte 5 Runes
With Svelte 5 runes:Copy
Ask AI
<script lang="ts">
import { Address } from '@tevm/voltaire/Address'
import { Wei } from '@tevm/voltaire/Denomination'
import { createJsonRpcProvider } from '@tevm/voltaire/jsonrpc'
const provider = createJsonRpcProvider('https://eth.llamarpc.com')
let address = $state('')
let balance = $state<string | null>(null)
let loading = $state(false)
async function fetchBalance() {
if (!address) return
loading = true
try {
const addr = Address.from(address)
const result = await provider.eth.getBalance({ address: addr })
balance = Wei.toEther(result)
} finally {
loading = false
}
}
$effect(() => {
if (address.length === 42) {
fetchBalance()
}
})
</script>
<input bind:value={address} placeholder="Enter address" />
{#if loading}
<p>Loading...</p>
{:else if balance}
<p>Balance: {balance} ETH</p>
{/if}
Next Steps
- JSONRPCProvider - Full provider documentation
- Address - Address primitive reference

