Arete documentation
React SDK
For the complete documentation index optimized for AI agents, see llms.txt or llms-full.txt. A markdown version of this page is available by appending.mdto the URL or sendingAccept: text/markdown.
For AI agents: the documentation index is at llms.txt (full corpus: llms-full.txt). A markdown source for this page is /sdks/react.md.
@usearete/react adds provider-managed connections and React hooks to the framework-agnostic @usearete/sdk client.
For an app that combines stacks, programs, and protocols in one session, start with Compose a session in React.
Protocol hooks
Section titled “Protocol hooks”Protocol hooks accept a protocol client directly, including a member of
session.protocols. useProtocolInstance follows canonical instance state
and the optional connected live row. useWalletActions and useAllowedSteps
report wallet-specific availability and rejection reasons.
useProtocolParticipant reads participant state, and useProtocolEvents
subscribes while mounted. Keep the client stable and close it when its owner
is disposed.
These hooks read and subscribe; call the generated client’s prepare/sign/submit flow for mutations. A permitted action can still be rejected during prepare. See Build a protocol interface with React for a complete component and Protocol API reference for hook signatures.
Install
Section titled “Install”npm install @usearete/react @usearete/sdk zodReact and @usearete/sdk are peer dependencies, so the application owns exactly one copy of the core SDK and @usearete/react shares it (a second copy would break the error and outcome checks mutation hooks rely on). Keep both packages on the same release. @usearete/react carries zustand as a normal dependency, so do not install zustand separately. Generated stack files import core SDK types and Zod schemas directly, which is why generated React consumers install all three packages above.
Arete’s fluent .use(), .useOne(), and .useMutation() methods are React
hooks. Standard Hooks linting cannot identify deeply nested member calls, so
@usearete/react/eslint-plugin.cjs exports an arete/fluent-hooks rule for
flat ESLint configurations. Enable it alongside the standard
react-hooks/rules-of-hooks rule. The React ORE template includes this setup.
import areteHooks from '@usearete/react/eslint-plugin.cjs';
export default [{ plugins: { arete: areteHooks }, rules: { 'arete/fluent-hooks': 'error' },}];An application also needs a generated stack definition. Discover a subscribe-ready package and install the target reported by its descriptor:
a4 explore catalog --query "<intent>" --kind stack --mode subscribe --jsona4 explore catalog stack <slug> --jsona4 install stack <slug> --ts \ --output ./src/generated/my-stack.ts \ --package-name @usearete/reactThe generated entry point exports the stack definition, entity types, schemas, program operations, addresses, reads, and package-specific helpers. Commit generated output, but do not edit it by hand.
Minimal setup
Section titled “Minimal setup”Wrap the application once, then call useArete with the generated stack. The stack contains its default WebSocket and HTTP endpoints, so no URL prop is required.
import { AreteProvider, useArete } from '@usearete/react';import { MY_STACK } from './generated/my-stack';
const publishableKey = import.meta.env.VITE_ARETE_PUBLISHABLE_KEY;if (!publishableKey) throw new Error('VITE_ARETE_PUBLISHABLE_KEY is required');
function Positions() { const arete = useArete(MY_STACK); const positions = arete.views.Position.list.use({ take: 20 });
if (arete.status === 'error') { return <button onClick={() => void arete.retry()}>Reconnect</button>; } if (positions.status !== 'ready') return <p>Connecting...</p>; if (positions.isEmpty) return <p>No matching positions.</p>; return <pre>{JSON.stringify(positions.data, null, 2)}</pre>;}
export default function App() { return ( <AreteProvider autoConnect stack={MY_STACK} auth={{ publishableKey }}> <Positions /> </AreteProvider> );}MY_STACK, Position, and the available query parameters are placeholders.
Use the generated import, view names, keys, and fields for the package you
installed.
Generated names and keys
Section titled “Generated names and keys”Generated TypeScript uses language-native camelCase even when source schemas or wire view paths use another convention. Use generated object paths rather than spelling protocol view identifiers or translating fields yourself.
Hosted Arete stacks require a publishable key for authentication and rate limiting. Local servers configured with allow_all may omit auth.
Read-only viewing does not require a wallet, but hosted browser access can still require a publishable key as shown above.
Provider
Section titled “Provider”AreteProvider owns connected clients and their stores. Supported provider props are:
| Prop | Purpose |
|---|---|
stack | Default stack for argument-less useArete() calls |
stackOptions | Default lookup options for the provider stack |
autoConnect | Start the initial connection; defaults to true |
autoReconnect | Recover an established connection after loss; defaults to true |
wallet | Wallet adapter used by generated program operations; does not reach session members |
auth | Authentication options such as publishableKey |
fetch | Custom fetch implementation |
validateFrames | Set false to silence rejected-frame warnings |
onFrameValidationError | Observe frames that were not stored: schema rejections, and patches for keys the client holds no entity for |
reconnectIntervals | Reconnect delays in milliseconds |
maxReconnectAttempts | Reconnect attempt limit |
maxEntriesPerView | Per-view store limit, or null for unlimited |
flushIntervalMs | Store update batching interval |
environment | 'production' (default) or { kind: 'dev', url } for the project’s dev environment; see below |
autoConnect and autoReconnect control separate lifecycle phases. Setting autoConnect={false} does not disable automatic recovery for a connection the app later establishes, but canRetry remains false because provider-owned replacement attempts would also stay disconnected; use the client lifecycle directly. Setting autoReconnect={false} does not prevent the provider’s initial connection.
environment points every client the provider creates (and an AreteSessionProvider below it) at the project’s dev environment through the edge a4 dev up serves. A browser reads no process variables, so pass it from your framework’s public variables:
<AreteProvider environment={import.meta.env.VITE_ARETE_ENVIRONMENT === 'dev' ? { kind: 'dev', url: import.meta.env.VITE_ARETE_DEV_URL } : 'production'}> <AreteEnvironmentBadge /> {/* renders only in dev mode */} <App /></AreteProvider>The page holds no Arete key in dev mode. useAreteEnvironment() returns 'dev' or 'production', and <AreteEnvironmentBadge /> shows a small “Dev environment” badge in dev mode and nothing in production. Sends need a sign-only wallet, such as useWalletStandardWallet.
Provider connection settings are captured when a shared client is created.
Changing them does not mutate an existing client; call retry() to replace it
using the latest provider configuration. Wallet updates are synchronized to
existing clients in place.
With a default stack declared, components call useArete() with no arguments:
<AreteProvider autoConnect stack={MY_STACK} stackOptions={{ url: 'ws://localhost:8877' }}> <App /></AreteProvider>
// Anywhere below — fully typed once the stack is registered:const arete = useArete();Passing that same stack explicitly also keeps its provider options, so local
endpoint overrides still apply: useArete(MY_STACK). Per-call options take
precedence, and a different explicit stack does not inherit these defaults.
Lookup options accept url, httpUrl, transport, transactions, chain,
and programs. Supply transactions when transaction inspection and submission
use a different service from the stack HTTP read endpoint.
Register the stack type once so argument-less calls are typed:
declare module '@usearete/react' { interface AreteDefaultStackRegistry { defaultStack: MyStack; }}Endpoint overrides can still go on the hook call, and always win:
const arete = useArete(MY_STACK, { url: 'ws://localhost:8877', httpUrl: 'http://localhost:8877',});useArete(stack) options are url, httpUrl, transport, transactions, chain, and programs. Keep a supplied programs object stable with a module constant or useMemo. A session member takes only stack (see Stacks in a session).
Attached programs follow the core identity rule. A program the stack already provides (the same object, or the same packageReleaseHash) resolves to the stack’s own program. A program with the same programSpecHash that cannot be proven identical, such as one you extended with extendProgram, replaces the stack’s under that key, and the SDK logs one warning. Any other program under one of the stack’s keys fails the connection with PROGRAM_KEY_CONFLICT, reported through arete.error; attach it under a key the stack does not use.
Shared calls
Section titled “Shared calls”Sibling components may each call useArete(MY_STACK) instead of threading one large result object through props. Calls with the same stack object and connection options share one provider-managed client and store. Concurrent initial calls also share the same connection attempt.
Changing the stack or attached-program object identity intentionally produces a different cache key. Keep generated stack imports at module scope.
Stack-bound React API
Section titled “Stack-bound React API”Single-stack apps can avoid both repeated constants and ambient module augmentation by binding the React API once:
import { createAreteReact } from '@usearete/react';import { ORE_STREAM_STACK } from './generated/ore-stack';
export const { Provider: OreProvider, useArete: useOre,} = createAreteReact(ORE_STREAM_STACK);useOre() carries the generated stack type directly and remains explicit at each consumer. Keep the generic AreteProvider and useArete(stack) API for multi-stack applications.
Wallets
Section titled “Wallets”Arete targets Wallet Standard wallets. Browser wallets that implement it register themselves, so the app does not list or import wallet-specific adapters. The recommended wiring, used by the React ORE template, is WalletProvider with an empty wallets list plus useSolanaWalletAdapter from @usearete/adapter-web3js/react:
npm install @usearete/adapter-web3js @solana/web3.js \ @solana/wallet-adapter-react @solana/wallet-adapter-react-uiimport type { ReactNode } from 'react';import { WalletProvider } from '@solana/wallet-adapter-react';import { WalletModalProvider } from '@solana/wallet-adapter-react-ui';import { useSolanaWalletAdapter } from '@usearete/adapter-web3js/react';import { AreteProvider } from '@usearete/react';import { MY_STACK } from './generated/my-stack';
import '@solana/wallet-adapter-react-ui/styles.css';
// useSolanaWalletAdapter reads the wallet context, so AreteProvider sits// one level below WalletProvider.function AreteShell({ children }: { children: ReactNode }) { const wallet = useSolanaWalletAdapter(); // undefined until a wallet connects return ( <AreteProvider stack={MY_STACK} auth={{ publishableKey }} wallet={wallet}> {children} </AreteProvider> );}
export default function App() { return ( // wallets={[]}: only Wallet Standard wallets, detected at runtime. <WalletProvider wallets={[]} autoConnect> <WalletModalProvider> <AreteShell> <Dashboard /> </AreteShell> </WalletModalProvider> </WalletProvider> );}Do not add wallet-specific adapter packages to wallets; Wallet Standard detection already covers wallets that support it. The web3.js adapter builds v0 transactions, so a wallet must support version 0; see the adapter README for its peer-dependency matrix.
React apps that use @wallet-standard/react instead of the Solana wallet adapter use useWalletStandardWallet from @usearete/react/wallet-standard; see Connect a Wallet Standard wallet. Its wallet works with AreteProvider as well as with a session.
Apps that do not use React or web3.js bridge a Wallet Standard account with createWalletStandardSigner from @usearete/adapter-kit and pass the signer to that package’s createWalletAdapter:
import { createWalletAdapter, createWalletStandardSigner } from '@usearete/adapter-kit';
const signer = createWalletStandardSigner({ wallet, account, chain: 'solana:mainnet' });const arete = createWalletAdapter({ transport: 'auto', signer });Sessions
Section titled “Sessions”Use a createSession session when the
app combines stacks with standalone programs or protocols, or wants one wallet
for all of them. Compose a session in React walks
through a complete app. In short:
AreteSessionProvideropens the session (definition, pluscreateSessionoptionswithoutwallet) when it mounts and closes it when it unmounts.options.authandoptions.fetchdefault to the enclosingAreteProvider’s. A new definition or new options reopen it; a newwalletprop only callssession.setWallet. Passsession={existing}instead ofdefinitionto share a session the app opened.useAreteSession<typeof DEFINITION>()returns{ session, status, error, ready, retry }.sessionisnulluntilstatusisopen.useOperationMutation(prepare, { wallet })runs a prepared instruction, transaction, or flow on the provider’s session. A run without a wallet fails withnot-readybefore anything is prepared; setallowExecutorWallet: trueto sign with the session’s wallet. Failures are anOperationMutationErrorwith a typedkind, such aswallet-rejected,wallet-feature-unsupported,wallet-chain-unsupported,wallet-version-unsupported, orwallet-modified.useWalletStandardWallet(account, { chain })from@usearete/react/wallet-standardturns a@wallet-standard/reactaccount into a sign-only wallet. Itserroris aWalletStandardSignerErrorwhosecodeissign_transaction_unsupported,chain_unsupported, oraccount_unavailable. This entry point needs@usearete/adapter-kit,@solana/kit, and@wallet-standard/ui-registry.AreteProvider’swalletprop does not reach session members. Set the session’s wallet onAreteSessionProvider.
Stacks in a session
Section titled “Stacks in a session”A stack member of the session (session.stacks.<key>) is already connected. Pass it to useArete for the same typed views, programs, reads and mutations as useArete(stack), over the member’s connection:
const { session } = useAreteSession<typeof APP>();const ore = useArete(session?.stacks.ore, { stack: APP.stacks.ore });const round = ore.views.OreRound.latest.use();Here APP is a session definition such as { stacks: { ore: ORE_STREAM_STACK } }, declared at module level.
Nothing connects a second time, and unmounting never disconnects the member: the session closes it. While the session opens the member is undefined and the result is useArete(stack)’s connecting state, with placeholder views and programs from stack (required whenever the member can be null or undefined). A reopen gives a new member, and the hooks move to it. Mutations sign with the session’s wallet (the provider’s wallet prop), and wait for the streamed slot they landed in only when the session connected the member over the WebSocket: the hooks read the member’s transport ('ws' or 'http') and its stack, so neither is an option once the member exists. See Live views from a session stack.
useArete result
Section titled “useArete result”The hook returns:
views: generated state, list, and custom view hooksprograms: generated account, instruction, operation, flow, and mutation hooksqueries: stack-level HTTP queriesread: callable one-shot extension reads with.use(...args, options?)React hooksreads: deprecated alias ofreadchain: generic Solana readsaddresses,constants,defaults, andmath: generated extension surfacesclient: connected low-level client, ornullwhile connectingstatus: preferred connection state for UI, including initial client creation and failed attemptsconnectionState,isConnected,isLoading,canRetry, anderrorsocketIssue: the latest structured WebSocket issueretry(): a deduplicated manual connection retry
Program and read hooks can be called unconditionally while the client is connecting. Submitting a disconnected mutation rejects with Arete client is not connected.
Lists and custom views
Section titled “Lists and custom views”const arete = useArete(ORE_STREAM_STACK);
const recent = arete.views.OreRound.latest.use({ take: 10 });const newest = arete.views.OreRound.latest.useOne();const allMiners = arete.views.OreMiner.list.use();List views return arrays. .useOne() and .use({ take: 1 }) return one entity or undefined.
Each parameter set is an independent protocol v2 query. Windows and filters on the same view do not share membership, while equivalent normalized queries share one reference-counted wire subscription:
const firstPage = arete.views.OreRound.latest.use({ take: 10 });const secondPage = arete.views.OreRound.latest.use({ take: 10, skip: 10 });During reconnect, each hook keeps its committed data and reports isRefreshing: true. A completed authoritative snapshot atomically replaces membership for only that query, including when the new result is empty.
State views
Section titled “State views”State views require their generated key object:
const round = arete.views.OreRound.state.use({ roundId: 335000n });const miner = arete.views.OreMiner.state.use( authority ? { authority } : undefined,);Pass undefined or null to disable a state subscription. A disabled hook returns data: undefined and isLoading: false; this is the normal wallet-disconnected pattern.
Hook options and results
Section titled “Hook options and results”Put list query configuration in the first params object: server-side key, partition, take, skip, filters, after, withSnapshot, and snapshotLimit, plus client-side schema and onSchemaValidationError. Keep enabled and initialData in the second options object. For compatibility, query fields present in the second argument are used when absent from params; params take precedence when both are supplied.
When a caller-supplied schema rejects an entity, onSchemaValidationError(diagnostic) receives { view, key?, entity, error }. The callback adds observability only: accepted entities still pass through unchanged, and rejected entities remain excluded.
Every view hook returns a discriminated result. Checking status and
isEmpty narrows data and error:
if (round.status === 'error') { console.error(round.error); // Error} else if (round.status === 'ready' && !round.isEmpty) { console.log(round.data.id.roundId); // entity, not undefined}status separates “the client has not connected yet” (connecting) from “subscribed, waiting for the first frame” (subscribing). isPending covers both, isReady marks a usable ready result, and isEmpty identifies a ready hook with no state entity or list entries. Errors may retain previously committed data, but isReady becomes false.
initialData is trusted as immediately usable ready data. State hooks accept one entity, list hooks accept an entity array, and useOne accepts one entity. The first live store update or completed empty snapshot replaces it. An empty list settles as []; an absent state entity settles as undefined. Do not seed transaction-authorizing data unless the application is willing to trust that seed.
await result.refresh() resolves after the refreshed subscription’s next complete snapshot is committed. Registration, send, and subscription failures reject, clear isRefreshing, and are exposed through error. A view refresh with no active matching subscription is a no-op.
The view hook objects themselves are also refresh targets. They refresh that view’s active subscriptions — keyed by argument for state views, all active subscriptions when omitted — and are a no-op when nothing is subscribed:
await arete.views.OreRound.state.refresh({ roundId });await arete.views.OreRound.state.refresh();await arete.views.OreRound.latest.refresh();To aggregate connection, view, and read results into loading, refreshing, and error status, use summarizeStatuses:
const streams = summarizeStatuses({ Board: board, Round: round, Miner: authority && miner });// streams.loading → ['Board'], streams.errors → ['Treasury: socket closed']// streams.refreshing → ['Round'] while committed data is being resynchronizedFalsy entries are skipped, which makes authority && miner the idiomatic way to gate a wallet-dependent source.
One-shot reads
Section titled “One-shot reads”Stack extensions can expose HTTP or RPC-backed reads. React wraps each function with an argument-keyed hook:
const preview = arete.read.solClaimPreview.use(authority);const quote = arete.read.quoteManualDeployment.use( input, { debounceMs: 300 },);The read runs again when its arguments change and exposes data, error, status, isPending, isReady, isEmpty, isLoading, isRefreshing, and refresh(). Status is 'disabled' | 'connecting' | 'loading' | 'ready' | 'refreshing' | 'error'. It is disabled while any required argument is null or undefined. Use .use(...args, { debounceMs, initialData }) for React-only behavior with options last; debounce applies to automatic argument changes, while refresh() always runs immediately. When options follow an omitted optional read argument, pass undefined in that argument’s position. The same function remains callable for imperative access: await arete.read.solClaimPreview(authority). The old arete.reads namespace remains as a deprecated alias.
ORE’s quoteManualDeployment reads current raw Miner and Automation accounts and computes an allocation preview. A quote is not a reservation and does not guarantee the Board will stay on the same round. Transaction preparation re-reads the raw Board account and rejects a stale explicit roundId before wallet approval.
Mutations
Section titled “Mutations”Generated raw instructions and application operations expose .useMutation():
const deploy = arete.programs.ore.transactions.mining .deployWithCheckpoint .useMutation();
await deploy.submit( { signer: authority, roundId, squares, amountPerSquare, }, { reconcile: { refresh: [ arete.views.OreBoard.state, arete.views.OreRound.state, arete.views.OreMiner.state, quote, ], }, },);Use mutation.mutate(args, options) from click and submit handlers when the
hook’s displayError state owns failure presentation. Use the rejecting
mutation.mutateAsync(args, options) or mutation.submit(args, options) for
imperative workflows that await or compose the result.
refresh accepts view hook objects (which refresh that view’s active subscriptions), view and read hook results, and plain callbacks — so a component that only writes does not need its own subscriptions just to refresh them.
The mutation prepares the operation, asks the wallet to sign, submits once, and tracks transaction receipts. Each generated operation callback updates completedReceipts, signatures, and the submitted phase before the final operation receipt resolves. Arete does not receive the wallet’s private key. The SDK does not automatically retry an ambiguous submission.
Useful result fields include phase, status, prepared, result, signatures, completedReceipts, displayError, failure, reconciliationError, isPreparing, isAwaitingWallet, and isReconciling.
Mutation phases
Section titled “Mutation phases”phase is the field to branch UI on. These are all of its values:
| Phase | Meaning |
|---|---|
idle | Nothing submitted yet, or reset() was called |
preparing | Building the operation (reading accounts, deriving addresses) before the wallet is involved |
awaiting-wallet | The wallet is signing, and the adapter is sending and confirming the transaction |
submitted | A transaction’s receipt landed; a multi-transaction flow returns to awaiting-wallet for the next one |
confirmed | Every transaction confirmed on chain |
reconciling | Waiting for the stream to process the confirmed slot and refreshing the supplied views and reads |
reconciled | The stream caught up and the refreshes completed |
confirmed-unreconciled | Confirmed on chain, but reconciliation timed out or failed; see reconciliationError |
not-submitted | Failed before anything reached the network (preparation, wallet rejection, or a definite send refusal) |
submitted-unknown | The transaction may have been sent; check signature before any retry, and never resend blindly |
chain-failed | Solana reported an on-chain error; displayError carries the parsed program error when available |
There is no separate submitting or confirming phase: awaiting-wallet covers signing, sending, and confirmation, because the wallet adapter performs all three in one call. The happy path is preparing → awaiting-wallet → submitted → confirmed → reconciling → reconciled; with reconciliation disabled or skipped it stops at confirmed. status is the coarser 'idle' | 'pending' | 'success' | 'error'.
See Transactions for transport, inspection, and failure-outcome details.
Parse form amounts without throwing
Section titled “Parse form amounts without throwing”safeToRawAmount(input, decimals) is re-exported by @usearete/react. Use it at form boundaries when invalid user input should become UI state rather than an exception:
import { safeToRawAmount } from '@usearete/react';
const parsed = safeToRawAmount({ ui: amountText }, 9);if (!parsed.success) { setAmountError(String(parsed.error)); return;}
const rawAmount = parsed.data;It returns { success: true, data } | { success: false, error }. Use the throwing toRawAmount variant when invalid input is a programmer error.
Stream reconciliation
Section titled “Stream reconciliation”Generated program mutation hooks reconcile by default in WebSocket mode:
- The transaction confirms on Solana.
- The hook finds the highest slot in its completed receipts.
- It waits up to 30 seconds for the connected stack’s processed-slot watermark to reach that slot.
- It refreshes the supplied hook results and waits for view snapshots and one-shot reads to complete.
The watermark proves the stack processed at least that slot. It does not guarantee that every entity changed or that a one-shot read reran; include the view hook objects, hook results, or reads that need refresh.
deploy.submit(args, { reconcile: false });deploy.submit(args, { reconcile: { timeoutMs: 10_000 } });deploy.submit(args, { reconcile: { refresh: preview.refresh } });deploy.submit(args, { reconcile: async (context) => { // A custom function replaces default processed-slot reconciliation. },});onConfirmed runs after chain confirmation. onSuccess runs after successful reconciliation, or immediately when reconciliation is disabled or skipped. A reconciliation timeout, refresh failure, or refreshed snapshot timeout settles as confirmed-unreconciled, is exposed through reconciliationError, and does not reject or roll back the confirmed transaction result.
Call retryReconciliation() after a confirmed-unreconciled result to repeat only processed-slot and refresh work. The hook reuses the confirmed receipts and original refresh targets; it never rebuilds, signs, or resubmits the transaction. canRetryReconciliation indicates when that recovery is available.
HTTP-only mutations and results without receipt slots skip default stream reconciliation.
Generated boundary
Section titled “Generated boundary”A stack build produces ProgramSpec, LiveSpec, and StackManifest artifacts under
.arete/; SDK generation consumes the exact StackManifest. The generated
directory can contain:
<stack>-core.ts: generated entities, schemas, views, and program modules<stack>.ts: public entry point and extension wiring- extension artifacts copied from the stack’s extension source
sdk-manifest.json: stable input, extension identities, artifact inventory, and generated content hashsdk-provenance.json: compiler provenance linked to the generated content
Regenerate after changing a stack, IDL, extension, or generator. For this monorepo’s ORE examples:
bash scripts/generate-example-sdks.shThat command rebuilds ore.program-spec.json,
OreStream.live-spec.json, and OreStream.stack-manifest.json from local Arete
source before emitting all example SDKs. CI runs generation twice and rejects
artifact, provenance, or output drift.
Migration from 0.3 to next
Section titled “Migration from 0.3 to next”- Legacy low-level
useViewanduseEntityhooks are removed; generatedarete.views.<Entity>.<view>.use(...)hooks are the supported subscription API. - Connection failures now report
status: 'error'; usecanRetryto decide when to show a retry action. - Mutation hooks add non-throwing
mutate()for event handlers and keep rejectingmutateAsync()/submit()for imperative composition.
Migration from 0.2 to 0.3
Section titled “Migration from 0.2 to 0.3”- Install and import
@usearete/react; legacy unscoped package examples are obsolete. - Remove the legacy provider-level WebSocket URL prop. Use generated endpoints or
useArete(stack, { url, httpUrl }). - Pass generated state key objects such as
{ roundId },{ authority }, or{ address }. Zero-argument state subscriptions are no longer supported. - Pass
undefinedornullto disable a state hook. Disabled hooks now reportisLoading: false. - Use generated camelCase fields such as
roundId,totalDeployed, andoreMetadata, not wire-format snake_case. - Generated program mutations now reconcile against the stream by default. Pass
reconcile: falsefor confirmation-only behavior. - Use
onConfirmedfor work that must happen after chain confirmation even if reconciliation later times out; reserveonSuccessfor reconciled success. - One-shot extension reads expose imperative calls and hooks together under
arete.read.<name>;arete.readsremains a deprecated alias. - Repeated
useAretecalls share a client when their stack and option identities match. Remove component-local connection ownership workarounds.
Testing
Section titled “Testing”@usearete/react/testing re-exports the core testing helpers and adds two React-specific ones:
createAreteTestConfig(harness, props?)returns<AreteProvider>props that connect every client to acreateWebSocketHarness()server, with no reconnects or batching delay. Everything else (views, reads, mutations, reconciliation) runs through the real provider and client.createMutationResultFixture(phase, overrides?)returns a complete, internally consistentuseMutation()result for any value inMUTATION_PHASES, so phase-specific UI can be rendered and tested without a wallet.
import { AreteProvider } from '@usearete/react';import { createAreteTestConfig, createWalletFixture, createWebSocketHarness,} from '@usearete/react/testing';
const ws = createWebSocketHarness();const wallet = createWalletFixture();render( <AreteProvider {...createAreteTestConfig(ws, { wallet })}> <Dashboard /> </AreteProvider>,);
// Poll with your test library's waitFor so React commits between checks.const subscription = await waitFor(() => ws.subscription('OreRound/latest'));ws.serve(subscription, [{ key: '1', data: round }]);Low-level exports
Section titled “Low-level exports”@usearete/react exports useConnectionState, useAreteContext, useAsyncRead (see Program reads with useAsyncRead), useInstructionMutation, reconciliation helpers, and selected core SDK APIs and types. Generated view hooks are the supported React subscription API. The package is not a complete mirror of @usearete/sdk; import the core package directly for the full low-level surface.