Ecency SDK
Use @ecency/sdk to read Hive data and broadcast transactions with built-in signing, RPC failover and React Query support.
The Ecency SDK (@ecency/sdk) is the Hive library that powers the Ecency web and mobile apps. It bundles a
transaction engine (signing, serialization, broadcasting), a multi-node RPC client with failover plus a large set of
React Query option builders and mutation hooks. You can use it in browsers, React
Native and Node.js.
It is published on npm under the MIT license. The source lives in
the ecency/vision-web monorepo under
packages/sdk.
At a glance
Section titled “At a glance”| Feature | Details |
|---|---|
| Transaction engine | Create, sign, serialize and broadcast Hive transactions, multi-signature, memo encryption |
| RPC layer | Multi-node failover with per-node health tracking, adaptive timeouts, optional hedged reads, quorum calls |
| Cryptography | secp256k1 keys, signatures, memo encryption and decryption |
| Query builders | 175+ query option builders |
| Mutation hooks | 90+ hooks for blockchain and Ecency write operations |
| Cache keys | Central QueryKeys object, one source of truth for cache keys |
| Auth methods | Private key, Keychain (and other extensions), HiveSigner, HiveAuth, custom broadcaster |
| Platforms | Web, React Native, Node.js (SSR) |
| Builds | Browser ESM plus Node ESM and CJS, with a lean @ecency/sdk/hive entry |
The transaction engine is built on an improved version of hive-tx by Mahdi Yari.
Installation
Section titled “Installation”npm install @ecency/sdk hivesigner# oryarn add @ecency/sdk hivesigner# orpnpm add @ecency/sdk hivesignerPeer dependencies:
| Package | Version | Required |
|---|---|---|
hivesigner |
^4.0.0 |
Yes |
@tanstack/react-query |
>=5 |
Optional, needed for the query builders and hooks |
react |
>=18 |
Optional, needed for the mutation hooks |
Without React, you can still use the transaction engine, RPC helpers and request functions in server jobs or scripts.
Quick start
Section titled “Quick start”import { ConfigManager, makeQueryClient, getAccountFullQueryOptions } from "@ecency/sdk";import { useQuery } from "@tanstack/react-query";
// 1. Once at startup: give the SDK the QueryClient your app usesconst queryClient = makeQueryClient();ConfigManager.setQueryClient(queryClient);
// 2. Use query builders in componentsfunction Profile({ username }: { username: string }) { const { data, isLoading } = useQuery(getAccountFullQueryOptions(username));
if (isLoading) return <div>Loading...</div>; return <div>{data?.name}</div>;}makeQueryClient() returns a QueryClient with refetchOnWindowFocus and refetchOnMount turned off. Pass your
own client if you want different defaults.
The @ecency/sdk/hive entry
Section titled “The @ecency/sdk/hive entry”If you only need the chain layer (transactions, keys, RPC calls), import from the @ecency/sdk/hive subpath. It
exports the hive-tx engine without the React Query modules:
import { Transaction, PrivateKey, callRPC, setNodes } from "@ecency/sdk/hive";
setNodes(["https://api.hive.blog", "https://api.deathwing.me"]);
const props = await callRPC("condenser_api.get_dynamic_global_properties", []);
const tx = new Transaction();await tx.addOperation("vote", { voter: "alice", author: "bob", permlink: "hello-world", weight: 10000,});tx.sign(PrivateKey.fromString(process.env.POSTING_WIF!));const result = await tx.broadcast();Main exports: Transaction, PrivateKey, PublicKey, Signature, Memo, callRPC, callRPCBroadcast,
callREST, callWithQuorum, RPCError, config, setNodes, setRestNodes, setRestNodesByApi,
setUserAgent, setResilience, setServerRpcProxy, utils and the operation types.
PrivateKey can be created with fromString(wif), fromSeed(seed), fromLogin(username, password, role) or
randomKey(). tx.broadcast(true) waits until the transaction is included in a block or dropped.
Configuration
Section titled “Configuration”ConfigManager holds the SDK’s shared settings. Call the setters once at startup.
import { ConfigManager, makeQueryClient } from "@ecency/sdk";
ConfigManager.setQueryClient(makeQueryClient());
// Replace the default RPC node listConfigManager.setHiveNodes([ "https://api.hive.blog", "https://api.deathwing.me", "https://rpc.mahdiyari.info",]);
// Use your own moderation account for anonymous reads (see below)ConfigManager.setDefaultObserver("my-app-moderation");
// Label your trafficConfigManager.setClientId("my-app");ConfigManager.setUserAgent("my-app-ssr (+https://example.com)");Setters
Section titled “Setters”| Setter | What it does |
|---|---|
setQueryClient(client) |
Sets one long-lived QueryClient for the whole app (browser and native apps). |
setQueryClientResolver(fn) |
Registers a function that returns the current request’s QueryClient. Use this for SSR so cached data dies with the request (for example, wrap your factory in React’s cache() in the Next.js App Router). |
setHiveNodes(nodes) |
Replaces the Hive RPC node list. |
setRestNodes(nodes) |
Replaces the list of nodes used for Hive REST APIs (no trailing slash). |
setRestNodesByApi(map) |
Pins a specific REST API to the nodes that serve it. An empty list removes the pin. |
setResilience(opts) |
Tunes read-call resilience: adaptive per-attempt timeouts (on by default) and hedged requests (off by default, enable with { hedge: true }). |
setServerRpcProxy(opts) |
On Node only, routes allowlisted read calls through your own read-through cache ({ url, headers, timeoutMs, methods }). Any proxy failure falls back to the nodes. Pass null to turn it off. |
setUserAgent(ua) |
Sets the User-Agent for server-side (Node) requests to Hive nodes. No effect in browsers or React Native. |
setPrivateApiHost(host) |
Sets the host for Ecency API calls. Defaults to https://ecency.com. |
setNewsletterHost(host) |
Sets the host for the newsletter routes. undefined falls back to the private API host. |
setPollsApiHost(host) |
Sets the host for the polls API. |
setImageHost(host) |
Sets the image host used for image URLs. Defaults to https://i.ecency.com. |
setDefaultObserver(account) |
Sets the observer account used for bridge calls when nobody is logged in. Must be a real account. |
setClientId(id) |
Sets the label sent in the X-Ecency-Client header on search and Ecency API requests. Defaults to ecency-sdk. |
setDmcaLists({ accounts, tags, posts }) |
Sets content filtering lists. Tags can be regex patterns (unsafe patterns are rejected), posts are matched as exact @author/permlink strings. |
Default RPC nodes
Section titled “Default RPC nodes”The SDK ships with 5 RPC nodes and fails over between them automatically:
https://api.hive.bloghttps://api.deathwing.mehttps://api.openhive.networkhttps://api.syncad.comhttps://rpc.mahdiyari.info
Read calls use a 5 second timeout per attempt and retry on other nodes. Broadcasts use a longer timeout.
Default observer and moderation
Section titled “Default observer and moderation”By default defaultObserver is "ecency". When nobody is logged in, bridge calls are made as that observer, so the
bridge marks posts from accounts on Ecency’s mute list (they come back with stats.gray set). Content is only
marked, not removed. If you run your own app, call setDefaultObserver with your own moderation account so your
anonymous visitors see your moderation choices instead of Ecency’s.
Client id
Section titled “Client id”setClientId sets a routing label, not a secret. It lets the server tell Ecency’s own apps apart from third-party
traffic. Set it to a name for your app. Don’t reuse Ecency’s own labels. If you need search at scale, use a
keyed HiveSearcher API key instead of Ecency’s public search proxy.
QueryKeys
Section titled “QueryKeys”All cache keys live in one QueryKeys export, grouped by domain. Use them to invalidate or read the cache.
import { QueryKeys } from "@ecency/sdk";
QueryKeys.posts.entry("/@alice/my-post");QueryKeys.accounts.full("alice");QueryKeys.posts.drafts("alice");
// Invalidate a whole domainqueryClient.invalidateQueries({ queryKey: QueryKeys.posts._prefix });
// Invalidate one entryqueryClient.invalidateQueries({ queryKey: QueryKeys.posts.entry("/@alice/my-post") });Domains: posts, accounts, notifications, core, communities, proposals, search, witnesses, wallet,
assets, market, analytics, promotions, resourceCredits, points, polls, operations, games,
quests, newsletter, support, badActors, curation, ai.
Queries
Section titled “Queries”Most reads are query option builders. They return plain objects, so you can pass them to useQuery,
useInfiniteQuery, queryClient.fetchQuery or prefetchQuery, on the client or the server.
import { useQuery, useInfiniteQuery } from "@tanstack/react-query";import { getPostQueryOptions, getPostsRankedQueryOptions, getPostsRankedInfiniteQueryOptions,} from "@ecency/sdk";
// One page of trending posts: (sort, startAuthor, startPermlink, limit, tag, observer)useQuery(getPostsRankedQueryOptions("trending", "", "", 20));
// Infinite feed for a tag: (sort, tag, limit, observer)useInfiniteQuery(getPostsRankedInfiniteQueryOptions("trending", "hive-engine"));
// A single post: (author, permlink, observer)useQuery(getPostQueryOptions("alice", "hello-world"));Because the cache is keyed, several components asking for the same data share one request. Use enabled to chain
dependent queries:
const { data: post } = useQuery(getPostQueryOptions(author, permlink));const { data: authorAccount } = useQuery({ ...getAccountFullQueryOptions(post?.author), enabled: !!post,});For server rendering, prefetch with queryClient.prefetchQuery(...) and hydrate on the client. Mutation hooks
(use*) are client-only; in the Next.js App Router keep them in "use client" components.
Mutations
Section titled “Mutations”Mutations are hooks built on useMutation. Each takes the username, an auth context and an optional broadcast mode:
import { useVote, useAccountUpdate } from "@ecency/sdk";
const { mutateAsync: vote } = useVote(username, auth);await vote({ author: "alice", permlink: "hello-world", weight: 10000 });
// Profile update: pass only the fields you want to changeconst { mutateAsync: updateProfile } = useAccountUpdate(username, auth);await updateProfile({ profile: { name: "Alice", about: "Hive enthusiast" },});useAccountUpdate takes Partial<{ profile, tokens }>. It re-reads the account from the chain first and merges
your changes into the existing metadata, so a partial update never wipes other fields.
The SDK picks the required authority (posting, active or owner) from the operation, so you don’t pass it yourself.
The broadcast mode is "async" by default: the hook returns once a node accepts the transaction. "sync" waits for
block inclusion and returns block_num and trx_num, but it is deprecated.
Broadcasting and authentication
Section titled “Broadcasting and authentication”Mutations take an AuthContextV2. The simplest form passes a key or token directly:
import type { AuthContextV2 } from "@ecency/sdk";
const auth: AuthContextV2 = { postingKey: "5K...", // WIF posting key (optional) accessToken: "hs-token", // HiveSigner access token (optional) loginType: "key",};For real apps, pass a platform adapter. The adapter tells the SDK where keys and tokens live, how to show messages and how to sign with wallets like Keychain or HiveAuth.
PlatformAdapter
Section titled “PlatformAdapter”| Field | Required | Purpose |
|---|---|---|
getUser(username) |
Yes | Returns your stored user object. |
getPostingKey(username) |
Yes | Returns the posting WIF, null for wallet logins (Keychain, HiveAuth), undefined if unknown. |
getActiveKey(username) |
No | Same for the active key (transfers, power down and other active ops). |
getOwnerKey(username) |
No | Same for the owner key (key changes, recovery). Usually only master password logins have it. |
getMemoKey(username) |
No | Same for the memo key (memo encryption). |
getAccessToken(username) |
Yes | Returns the HiveSigner access token, if any. |
getLoginType(username, authority?) |
Yes | Returns "key", "hivesigner", "keychain" or "hiveauth". Can depend on the authority the op needs. |
hasPostingAuthorization(username) |
No | Returns true if the user granted posting authority to your HiveSigner app account. Posting ops then go through the token, which is faster than a wallet popup. |
showError(message, type?) |
Yes | Shows an error in your UI. |
showSuccess(message) |
Yes | Shows a success message. |
showLoading(message) / hideLoading() |
No | Show and hide a loading indicator. |
showAuthUpgradeUI(authority, operation) |
No | Asks the user how to sign when their login can’t (for example, a posting key user making a transfer). Returns "hiveauth", "hivesigner", "keychain", "key" or false. |
broadcastWithKeychain(username, ops, keyType) |
No | Signs with a browser extension. keyType is lowercase. |
broadcastWithHiveAuth(username, ops, keyType) |
No | Signs with HiveAuth (QR code or app). |
broadcastWithHiveSigner(username, ops, keyType) |
No | Signs with HiveSigner hot signing (redirect, popup or WebView). Needed for active ops, because access tokens only carry posting authority. |
recordActivity(type, txId, blockNum?) |
No | Hook for your own activity tracking. |
invalidateQueries(keys) |
No | Invalidates cache keys after a mutation. |
grantPostingAuthority(username) |
No | Adds your app account to the user’s posting authority. Requires the active key. |
The broadcast functions return a TransactionConfirmation ({ id, block_num, trx_num, expired }).
Fallback between methods
Section titled “Fallback between methods”When an adapter is present, fallback is on unless you set enableFallback: false. The SDK:
- Reads the user’s login type with
getLoginType. - For posting ops, if
hasPostingAuthorizationreturnstrue, tries the HiveSigner token first. - Broadcasts with the user’s login method.
- If that fails with an auth error and the op needs posting or active authority, calls
showAuthUpgradeUI.
fallbackChain sets the order of methods to try when no login type is known. The default is
["key", "hiveauth", "hivesigner", "keychain", "custom"]. "custom" calls the broadcast function on the auth
context, for cases where you do the signing yourself.
Login integration
Section titled “Login integration”Your app owns the login screen. Once a user is logged in, store how they logged in and return it from the adapter. Here is a minimal adapter that supports Keychain, HiveAuth and HiveSigner.
import type { AuthContextV2, PlatformAdapter, TransactionConfirmation } from "@ecency/sdk";import { sendOperations } from "hivesigner";
type Login = { username: string; method: "keychain" | "hiveauth" | "hivesigner" | "key"; accessToken?: string; // HiveSigner postingKey?: string; // only for "key" logins};
const logins = new Map<string, Login>(); // your own storage
// Keychain expects "Posting" / "Active", the SDK passes lowercasefunction keychainBroadcast(username: string, ops: any[], keyType: string) { return new Promise<TransactionConfirmation>((resolve, reject) => { const role = keyType.charAt(0).toUpperCase() + keyType.slice(1); window.hive_keychain.requestBroadcast(username, ops, role, (resp: any) => { if (!resp.success) return reject(new Error(resp.message || "Cancelled")); resolve(resp.result); }); });}
export const adapter: PlatformAdapter = { getUser: async (u) => logins.get(u), getPostingKey: async (u) => { const l = logins.get(u); if (!l) return undefined; return l.method === "key" ? l.postingKey : null; }, getAccessToken: async (u) => logins.get(u)?.accessToken, getLoginType: async (u) => logins.get(u)?.method ?? null,
showError: (msg) => console.error(msg), showSuccess: (msg) => console.log(msg),
broadcastWithKeychain: keychainBroadcast, broadcastWithHiveAuth: async (username, ops, keyType) => { // Send a sign request through your HiveAuth client and wait for the approval return myHiveAuthClient.broadcast(username, ops, keyType); }, broadcastWithHiveSigner: async (username, ops) => { // Hot signing. Without a callback function, sendOperations only builds the // hivesigner.com sign URL (with a function it would open it in a new tab). const signUrl = sendOperations(ops, { callback: window.location.href }, undefined as never) as string; window.location.href = signUrl; // The page unloads here, so this promise never settles. The result comes back // through the redirect to the callback URL (see the note below). return new Promise<TransactionConfirmation>(() => {}); },};
export const auth: AuthContextV2 = { adapter };Then pass auth to any mutation hook, for example useVote(username, auth).
Notes:
- Browser extensions (Keychain and similar) can’t sign owner-authority operations. Use a key for those.
- A HiveSigner access token only carries posting authority. Active operations need
broadcastWithHiveSigner(hot signing) or another method. - Hot signing in this example is a full page redirect. After the user approves, hivesigner.com sends them back to the
callback URL with the transaction id added as an
idquery parameter (for example?id=<transaction id>). Read it on page load to confirm the result and refresh your data. The mutation that started the redirect never resolves, because the page that called it is gone. - See HiveSigner for getting an access token. The login methods guide shows how these methods look to users.
Signing one operation with a key
Section titled “Signing one operation with a key”For a one-off active operation signed with a key or master password, use useSignOperationByKey:
import { useSignOperationByKey } from "@ecency/sdk";
const { mutateAsync } = useSignOperationByKey(username);await mutateAsync({ operation: ["transfer", { from: "alice", to: "bob", amount: "1.000 HIVE", memo: "Thanks!" }], keyOrSeed: activeKeyOrMasterPassword,});Error handling
Section titled “Error handling”import { parseChainError, formatError, shouldTriggerAuthFallback, isResourceCreditsError, isNetworkError,} from "@ecency/sdk";
try { await mutateAsync(payload);} catch (error) { if (isResourceCreditsError(error)) { showMessage("Not enough Resource Credits to broadcast this transaction."); } else if (isNetworkError(error)) { showMessage("Network error, please check your connection."); } else { const [message, type] = formatError(error); showMessage(message); }}parseChainError returns a typed object with the error category. shouldTriggerAuthFallback tells you if the
error means another signing method might succeed.
Modules
Section titled “Modules”src/hive-tx/ Transaction engine (signing, serialization, RPC, cryptography)
src/modules/ accounts/ Account data, follows and mutes, profile updates, bookmarks, favorites ai/ AI assist, image generation, transcription, Listen audio analytics/ Activity, curation and leaderboard stats auth/ Auth helpers and types bad-actors/ List of known bad actor accounts bridge/ Bridge API helpers, alternate node verification communities/ Community data, subscriptions, roles, mutations core/ ConfigManager, QueryKeys, broadcasting, error parsing, utilities curation/ Curation desk feeds, recommendations and roster games/ Game status and claims hive-engine/ Hive Engine token queries integrations/ 3Speak, HiveSigner, HivePosh, Plausible stats market/ Price feeds, order book, trade history, market orders moderation/ Content moderation helpers, account reputation, external links newsletter/ Newsletter issues and digest subscriptions notifications/ Notification lists, settings, announcements operations/ Operation builders, authority mapping, signing points/ Ecency Points balance, transactions, transfers polls/ Polls and poll voting posts/ Posts and comments, voting, drafts, schedules, images, fragments private-api/ Ecency API helpers promotions/ Promotion and Boost+ pricing proposals/ Governance proposals and votes quests/ Daily quests and streak freezes resource-credits/ RC stats search/ Topic, account and post search support/ Support settings wallet/ Balances, delegations, transfers, conversions, savings witnesses/ Witness votes and proxyTypeScript
Section titled “TypeScript”The SDK is written in TypeScript and ships its types. Useful ones include Entry, Draft, Schedule,
Fragment, WaveEntry, FullAccount, AccountProfile, AuthContextV2, PlatformAdapter, AuthMethod,
TransactionConfirmation and BroadcastMode.
Related
Section titled “Related”- Ecency Wallets builds on the SDK for multi-chain wallets and asset queries.
- Render helper turns post Markdown into safe HTML.