Skip to content

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.

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.

Terminal window
npm install @ecency/sdk hivesigner
# or
yarn add @ecency/sdk hivesigner
# or
pnpm add @ecency/sdk hivesigner

Peer 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.

import { ConfigManager, makeQueryClient, getAccountFullQueryOptions } from "@ecency/sdk";
import { useQuery } from "@tanstack/react-query";
// 1. Once at startup: give the SDK the QueryClient your app uses
const queryClient = makeQueryClient();
ConfigManager.setQueryClient(queryClient);
// 2. Use query builders in components
function 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.

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.

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 list
ConfigManager.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 traffic
ConfigManager.setClientId("my-app");
ConfigManager.setUserAgent("my-app-ssr (+https://example.com)");
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.

The SDK ships with 5 RPC nodes and fails over between them automatically:

  • https://api.hive.blog
  • https://api.deathwing.me
  • https://api.openhive.network
  • https://api.syncad.com
  • https://rpc.mahdiyari.info

Read calls use a 5 second timeout per attempt and retry on other nodes. Broadcasts use a longer timeout.

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.

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.

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 domain
queryClient.invalidateQueries({ queryKey: QueryKeys.posts._prefix });
// Invalidate one entry
queryClient.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.

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 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 change
const { 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.

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.

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 }).

When an adapter is present, fallback is on unless you set enableFallback: false. The SDK:

  1. Reads the user’s login type with getLoginType.
  2. For posting ops, if hasPostingAuthorization returns true, tries the HiveSigner token first.
  3. Broadcasts with the user’s login method.
  4. 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.

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 lowercase
function 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 id query 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.

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,
});
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.

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 proxy

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.