🚀 A high-level Pixa blockchain proxy layer with entity-based storage, WASM content sanitization, encrypted key vault, and session management. Built on top of
@pixagram/dpixa.
Version: 3.4.0
- Overview
- Architecture
- Getting Started
- Authentication & Sessions
- API Groups
- Content Sanitization
- Entity Storage Deep Dive
- Events
- Error Handling
- Exports
- Configuration Reference
PixaProxyAPI is the primary client-side interface for Pixagram applications that need to read from and write to the Pixa blockchain. It wraps the low-level @pixagram/dpixa client with:
- Entity-based caching — Accounts, posts, and comments are stored in typed LacertaDB collections, indexed by canonical IDs. Queries are cached as ID arrays and resolved from the entity stores on subsequent reads.
- Automatic content sanitization — Every entity passes through a WASM-powered sanitization pipeline (
pixa-content) before entering the database. No raw, unsanitized content is ever served from cache. - Encrypted key vault — Private keys can be stored in a PBKDF2-encrypted LacertaDB vault, unlocked with a user PIN.
- Session management — Multi-account sessions with expiration, refresh, and switch support.
- Multi-strategy RPC fallback — Each API method tries
condenser_api, thendatabase_api, thenpixamind(Hivemind bridge), returning the first successful result. - Browser + Node.js — Runs in both environments with automatic platform detection for crypto and stream APIs.
Introduced in v3.4.0, the entity storage architecture replaces the flat key-value cache with a relational system that separates entities from queries.
┌─────────────────────────────────────────────────────────────────┐
│ Entity Stores (LacertaDB) │
│ │
│ accounts_store posts_store comments_store │
│ ┌──────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ key: "alice" │ │ key: "alice_post" │ │ key: "bob_re1" │ │
│ │ display_name │ │ html (sanitized) │ │ html (strict) │ │
│ │ sanitized_* │ │ images, links │ │ images, links │ │
│ │ parsed_meta │ │ plainText excerpt │ │ parsed_meta │ │
│ │ reputation_* │ │ tags, app │ │ _stored_at │ │
│ │ _stored_at │ │ _stored_at │ │ │ │
│ └──────────────┘ └──────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Query Cache (LacertaDB) │
│ │
│ key: "trending:tag=art:limit=20" │
│ value: { ids: ["alice_post", "bob_photo", ...], │
│ entity_type: "posts", timestamp: 1706000000 } │
└─────────────────────────────────────────────────────────────────┘
Query results are stored as arrays of entity IDs rather than duplicating full entity data. When a cached query is hit:
- Look up the query key in
query_cache - If fresh (within TTL), retrieve the ID array
- Resolve each ID from the appropriate entity store
- If all entities are present and fresh, return them directly — zero RPC calls
- If any entity is stale or missing, re-fetch the entire query from chain
Nothing enters an entity store without being sanitized first. The SanitizationPipeline class processes raw blockchain data through the pixa-content WASM engine:
| Entity Type | Sanitization Applied |
|---|---|
| Account | parseMetadata on json_metadata / posting_json_metadata, sanitizeBiography on bio, sanitizeUsername on name, profile image/cover extraction, reputation calculation |
| Post (depth=0) | renderPost (full Markdown→HTML, image extraction, link extraction, XSS filtering), extractPlainText (500-char excerpt), metadata parsing for tags/app |
| Comment (depth>0) | renderComment (stricter subset — no headings, tables, iframes), metadata parsing, reputation |
User calls api.tags.getDiscussionsByTrending({ tag: "art", limit: 20 })
│
▼
┌──────────────────────────┐
│ Build query cache key: │
│ "trending:tag=art: │
│ limit=20" │
└──────────┬───────────────┘
│
┌──────▼──────┐ ┌──────────────┐
│ Query Cache │──fresh──▶│ Resolve IDs │──▶ Return sanitized entities
│ lookup │ │ from entity │
└──────┬──────┘ │ stores │
│ stale/miss └──────────────┘
▼
┌──────────────────────┐
│ Fetch from chain │ condenser_api → database_api → pixamind
│ (multi-fallback) │
└──────────┬───────────┘
▼
┌──────────────────────┐
│ SanitizationPipeline │ sanitizeContent() auto-detects post vs comment
│ for each raw entity │
└──────────┬───────────┘
▼
┌──────────────────────┐
│ Upsert into entity │ posts_store / comments_store
│ store + cache IDs │ query_cache stores ID array
└──────────┬───────────┘
▼
Return sanitized entities
PixaProxyAPI requires the following peer dependencies:
npm install @pixagram/dpixa @pixagram/lacerta-db pixa-contentimport PixaProxyAPI from './pixaproxyapi.js';
const api = new PixaProxyAPI();
await api.initialize({
// RPC nodes (optional — defaults to Pixagram's CORS proxy)
nodes: [
'https://api.pixagram.io',
'https://api.hivekings.com'
],
// Chain configuration (optional)
chainId: 'your-chain-id',
addressPrefix: 'PIX',
// Timeouts
timeout: 30000,
failoverThreshold: 3,
sessionTimeout: 30 * 60 * 1000, // 30 minutes
pinTimeout: 5 * 60 * 1000, // 5 minutes
// WASM sanitizer path (optional)
wasmPath: '/path/to/pixa_content_bg.wasm',
// Internal domains for link classification
internalDomains: ['pixa.pics', 'pixagram.io'],
// Performance monitoring (optional)
enablePerformanceMonitoring: true,
});After initialization, all API groups are available as properties on the instance:
api.database // DatabaseAPI
api.tags // TagsAPI
api.blocks // BlocksAPI
api.globals // GlobalsAPI
api.accounts // AccountsAPI
api.market // MarketAPI
api.authority // AuthorityAPI
api.votes // VotesAPI
api.content // ContentAPI
api.witnesses // WitnessesAPI
api.follow // FollowAPI
api.broadcast // BroadcastAPI
api.auth // AuthAPI
api.formatter // FormatterAPI
api.blockchain // BlockchainAPI
api.rc // ResourceCreditsAPI
api.communities // CommunitiesAPI
api.keys // AccountByKeyAPI
api.transaction // TransactionStatusAPIYou can update configuration at runtime:
api.updateConfig({
SESSION_TIMEOUT: 60 * 60 * 1000, // 1 hour
PIN_TIMEOUT: 10 * 60 * 1000, // 10 minutes
internalDomains: ['pixa.pics', 'app.pixagram.io'],
// Override entity/query TTLs
ENTITY_TTL: {
accounts: 12 * 60 * 60 * 1000, // 12 hours
posts: 3 * 60 * 1000, // 3 minutes
comments: 60 * 1000, // 1 minute
},
QUERY_TTL: {
trending: 2 * 60 * 1000, // 2 minutes
feed: 15 * 1000, // 15 seconds
}
});The fastest way to authenticate. Keys are held in memory (not persisted to encrypted vault):
// Login with master password
const result = await api.quickLogin('alice', 'masterpassword123', 'master', {
userAgent: 'myapp/1.0',
});
console.log(result);
// {
// success: true,
// account: 'alice',
// sessionId: 'alice_1706000000_abc123def',
// keyType: 'master',
// validation: { valid: true, publicKey: 'PIX7abc...', ... }
// }
// Login with individual WIF key
const result2 = await api.quickLogin('alice', '5KQwrPbwd...', 'posting');For persistent key storage, initialize the encrypted vault:
// First time: create vault with PIN
await api.initializeVault('123456', {
iterations: 1000000, // PBKDF2 iterations (default: 1M)
fastMode: false, // true = 5000 iterations (dev only)
onProgress: (pct) => console.log(`${pct}% derived`),
});
// Store keys in vault during login
await api.quickLogin('alice', 'masterpassword', 'master', {
storeInVault: true,
});
// Later: unlock with PIN
const unlock = await api.unlockWithPin('123456', {
keyType: 'posting',
account: 'alice',
});
// { success: true, account: 'alice' }// Restore previous session on app start
const account = await api.restoreSession();
// Returns account name if session valid, null otherwise
// Check login status
const loggedIn = await api.isLoggedIn();
// Check if PIN unlock is needed
const status = await api.requiresUnlock('posting');
// { needsUnlock: false, unlockType: null, account: 'alice' }
// { needsUnlock: true, unlockType: 'pin', account: 'alice' }
// { needsUnlock: true, unlockType: 'key', account: 'alice' }
// Get active account
const active = await api.getActiveAccount();
// Logout
await api.logout();Low-level database API passthrough.
| Method | Parameters | Returns | Description |
|---|---|---|---|
call(method, params) |
string, any[] |
Promise<any> |
Raw RPC call to database_api |
getDatabaseInfo() |
— | Promise<object> |
Node database information |
const info = await api.database.call('get_dynamic_global_properties', []);Discussion discovery by sorting algorithm. All discussion methods use entity store + query cache (v3.4.0).
| Method | Parameters | Returns | Description |
|---|---|---|---|
getTrendingTags(afterTag?, limit?) |
string, number |
Promise<Tag[]> |
Get trending tags |
getDiscussionsByTrending(query) |
DiscussionQuery |
Promise<Post[]> |
Trending discussions |
getDiscussionsByCreated(query) |
DiscussionQuery |
Promise<Post[]> |
Newest discussions |
getDiscussionsByHot(query) |
DiscussionQuery |
Promise<Post[]> |
Hot discussions |
getDiscussionsByPromoted(query) |
DiscussionQuery |
Promise<Post[]> |
Promoted discussions |
getDiscussionsByPayout(query) |
DiscussionQuery |
Promise<Post[]> |
Approaching payout |
getDiscussionsByVotes(query) |
DiscussionQuery |
Promise<Post[]> |
By vote count |
getDiscussionsByActive(query) |
DiscussionQuery |
Promise<Post[]> |
Recently active |
getDiscussionsByChildren(query) |
DiscussionQuery |
Promise<Post[]> |
By comment count |
getDiscussionsByMuted(query) |
DiscussionQuery |
Promise<Post[]> |
Muted (not available) |
DiscussionQuery shape:
{
tag: 'photography', // Tag name or username
limit: 20, // Results count (max 100)
start_author: '', // Pagination cursor: author
start_permlink: '', // Pagination cursor: permlink
}Example:
// Get 20 trending posts in #pixelart
const posts = await api.tags.getDiscussionsByTrending({
tag: 'pixelart',
limit: 20,
});
// Each post is already sanitized with html, images, links, wordCount
posts.forEach(post => {
console.log(post.title, post.wordCount, post.images.length);
console.log(post.html); // Safe HTML, ready to render
});
// Paginate
const page2 = await api.tags.getDiscussionsByTrending({
tag: 'pixelart',
limit: 20,
start_author: posts[posts.length - 1].author,
start_permlink: posts[posts.length - 1].permlink,
});| Method | Parameters | Returns | Description |
|---|---|---|---|
getBlock(blockNum) |
number |
Promise<SignedBlock> |
Full block with transactions |
getBlockHeader(blockNum) |
number |
Promise<BlockHeader> |
Block header only |
getOpsInBlock(blockNum, onlyVirtual?) |
number, boolean |
Promise<AppliedOperation[]> |
Operations from a block |
const block = await api.blocks.getBlock(12345678);
console.log(`Witness: ${block.witness}, Txs: ${block.transactions.length}`);| Method | Parameters | Returns | Description |
|---|---|---|---|
getDynamicGlobalProperties() |
— | Promise<DynamicGlobalProperties> |
Current blockchain state |
getChainProperties() |
— | Promise<ChainProperties> |
Witness-voted chain params |
getFeedHistory() |
— | Promise<FeedHistory> |
PIXA/PXS price feed history |
getCurrentMedianHistoryPrice() |
— | Promise<Price> |
Current median PIXA/PXS price |
getHardforkVersion() |
— | Promise<string> |
Current hardfork version |
getRewardFund(name?) |
string |
Promise<RewardFund> |
Reward pool info |
getVestingDelegations(account, from?, limit?) |
string, string, number |
Promise<VestingDelegation[]> |
Delegations made by account |
getConfig() |
— | Promise<object> |
Node compile-time config |
getVersion() |
— | Promise<object> |
Node version info |
const props = await api.globals.getDynamicGlobalProperties();
console.log(`Head block: ${props.head_block_number}`);
console.log(`PIXA supply: ${props.current_supply}`);
const price = await api.globals.getCurrentMedianHistoryPrice();
console.log(`1 PIXA = ${price.base} PXS`);Account methods use the entity store — accounts are sanitized and cached with a 24-hour TTL by default.
| Method | Parameters | Returns | Description |
|---|---|---|---|
getAccounts(accounts, forceRefresh?) |
string[], boolean |
Promise<SanitizedAccount[]> |
Get detailed account info |
lookupAccounts(lowerBound, limit?) |
string, number |
Promise<string[]> |
Autocomplete account names |
lookupAccountNames(accounts) |
string[] |
Promise<Account[]> |
Lookup multiple accounts by name |
getAccountCount() |
— | Promise<number> |
Total accounts on chain |
getAccountHistory(account, from?, limit?, bitmask?) |
string, number, number, [number,number] |
Promise<Array> |
Account operation history |
getAccountReputations(lowerBound?, limit?) |
string, number |
Promise<Array> |
Batch reputation lookup |
getAccountNotifications(account, limit?) |
string, number |
Promise<Notification[]> |
Account notifications (Pixamind) |
Sanitized Account Properties (v3.4.0):
Accounts returned from getAccounts include these extra fields produced by the sanitization pipeline:
const [account] = await api.accounts.getAccounts(['alice']);
// Standard blockchain fields are all present (balance, vesting_shares, etc.)
// Plus sanitized fields:
account._entity_id // 'alice'
account._entity_type // 'account'
account._stored_at // timestamp of DB insertion
account.reputation_score // computed reputation (e.g. 65.42)
account.display_name // sanitized from metadata profile.name
account.sanitized_about // HTML-stripped bio (max 512 chars)
account.sanitized_name // validated username
account.profile_image // extracted profile image URL
account.cover_image // extracted cover image URL
account.sanitized_location // stripped location string
account.sanitized_website // stripped website string
account.parsed_metadata // { profile: {...}, tags: [...], extra: {...} }// Force refresh (bypasses entity store cache)
const fresh = await api.accounts.getAccounts(['alice'], true);| Method | Parameters | Returns | Description |
|---|---|---|---|
getOrderBook(limit?) |
number |
Promise<OrderBook> |
Internal market order book |
getOpenOrders(account) |
string |
Promise<Order[]> |
Account's open orders |
getTicker() |
— | Promise<Ticker> |
Market ticker |
getTradeHistory(start, end, limit?) |
string, string, number |
Promise<Trade[]> |
Recent trades |
getMarketHistory(bucketSeconds, start, end) |
number, string, string |
Promise<Bucket[]> |
OHLCV candles |
getMarketHistoryBuckets() |
— | Promise<number[]> |
Available bucket sizes |
| Method | Parameters | Returns | Description |
|---|---|---|---|
getOwnerHistory(account) |
string |
Promise<Array> |
Owner key change history |
getRecoveryRequest(account) |
string |
Promise<object|null> |
Pending recovery request |
getWithdrawRoutes(account, type?) |
string, string |
Promise<Array> |
Power-down routes |
getAccountBandwidth(account, type) |
string, string |
Promise<object> |
Bandwidth usage |
getSavingsWithdrawFrom(account) |
string |
Promise<Array> |
Pending savings withdrawals (outgoing) |
getSavingsWithdrawTo(account) |
string |
Promise<Array> |
Pending savings deposits (incoming) |
verifyAuthority(stx) |
SignedTransaction |
Promise<boolean> |
Verify transaction signatures |
| Method | Parameters | Returns | Description |
|---|---|---|---|
getActiveVotes(author, permlink) |
string, string |
Promise<Vote[]> |
Active votes on a post/comment |
getAccountVotes(account) |
string |
Promise<Vote[]> |
Votes cast by an account |
Content methods use the entity store + query cache. Posts and comments are sanitized and cached separately.
| Method | Parameters | Returns | Description |
|---|---|---|---|
getContent(author, permlink) |
string, string |
Promise<SanitizedPost|SanitizedComment|null> |
Single post or comment |
getContentReplies(author, permlink) |
string, string |
Promise<SanitizedComment[]> |
Replies to a post/comment |
getDiscussionsByAuthorBeforeDate(author, startPermlink, beforeDate, limit?) |
string, string, string, number |
Promise<SanitizedPost[]> |
Author's posts before date |
getRepliesByLastUpdate(author, startPermlink, limit?) |
string, string, number |
Promise<SanitizedComment[]> |
Replies by last update |
getDiscussionsByComments(query) |
DiscussionQuery |
Promise<SanitizedComment[]> |
User's comments |
getDiscussionsByBlog(query) |
DiscussionQuery |
Promise<SanitizedPost[]> |
User's blog posts |
getDiscussionsByFeed(query) |
DiscussionQuery |
Promise<SanitizedPost[]> |
User's feed |
getAccountPosts(account, sort?, limit?, options?) |
string, string, number, object |
Promise<SanitizedPost[]> |
Generic account posts |
getState(path) |
string |
Promise<any> |
Legacy state endpoint |
// Fetch a single post (checks entity store first)
const post = await api.content.getContent('alice', 'my-pixel-art');
console.log(post.html); // Sanitized HTML
console.log(post.images); // [{ src, alt, is_base64, index }]
console.log(post.links); // [{ href, text, domain, is_external }]
console.log(post.plainText); // Plain text excerpt (max 500 chars)
console.log(post.tags); // ['pixelart', 'art', 'creative']
console.log(post.author_reputation); // 67.5
// Fetch replies (cached as comment entities)
const replies = await api.content.getContentReplies('alice', 'my-pixel-art');
replies.forEach(reply => {
console.log(`@${reply.author}: ${reply.html}`);
});| Method | Parameters | Returns | Description |
|---|---|---|---|
getWitnessByAccount(account) |
string |
Promise<Witness> |
Witness info for account |
getWitnessesByVote(from, limit?) |
string, number |
Promise<Witness[]> |
Witnesses ranked by votes |
lookupWitnessAccounts(lowerBound, limit?) |
string, number |
Promise<string[]> |
Autocomplete witness names |
getWitnessCount() |
— | Promise<number> |
Total witness count |
getActiveWitnesses() |
— | Promise<string[]> |
Current witness schedule |
getWitnessSchedule() |
— | Promise<object> |
Detailed witness schedule |
| Method | Parameters | Returns | Description |
|---|---|---|---|
getFollowers(account, startFollower?, type?, limit?) |
string, string, string, number |
Promise<Follow[]> |
Account followers |
getFollowing(account, startFollowing?, type?, limit?) |
string, string, string, number |
Promise<Follow[]> |
Accounts being followed |
getFollowCount(account) |
string |
Promise<{follower_count, following_count}> |
Follow counts |
getFeedEntries(account, startEntryId?, limit?) |
string, number, number |
Promise<FeedEntry[]> |
Feed entries |
getBlogEntries(account, startEntryId?, limit?) |
string, number, number |
Promise<BlogEntry[]> |
Blog entries |
getRebloggedBy(author, permlink) |
string, string |
Promise<string[]> |
Accounts that reblogged |
getBlogAuthors(account) |
string |
Promise<Array> |
Authors in user's blog |
getSubscriptions(account) |
string |
Promise<Array> |
Community subscriptions |
const counts = await api.follow.getFollowCount('alice');
console.log(`Followers: ${counts.follower_count}, Following: ${counts.following_count}`);All broadcast methods automatically retrieve the required private key from the key manager. The user must be logged in.
| Method | Parameters | Returns | Description |
|---|---|---|---|
vote(voter, author, permlink, weight) |
string, string, string, number |
Promise<TransactionConfirmation> |
Vote on content (weight: -10000 to 10000) |
comment(params) |
CommentParams |
Promise<TransactionConfirmation> |
Create post or reply |
commentOptions(params) |
CommentOptionsParams |
Promise<TransactionConfirmation> |
Set beneficiaries, payout settings |
deleteComment(author, permlink) |
string, string |
Promise<TransactionConfirmation> |
Delete a post or comment |
// Create a post
await api.broadcast.comment({
parentAuthor: '', // Empty for root post
parentPermlink: 'pixelart', // Category
author: 'alice',
permlink: 'my-new-pixel-art',
title: 'My New Pixel Art',
body: '# Hello!\n\nCheck out this pixel art...',
jsonMetadata: {
tags: ['pixelart', 'art'],
image: ['https://pixa.pics/image.png'],
app: 'pixagram/3.4.0',
},
});
// Vote 100%
await api.broadcast.vote('alice', 'bob', 'bobs-post', 10000);
// Set beneficiaries (must be called before first vote)
await api.broadcast.commentOptions({
author: 'alice',
permlink: 'my-new-pixel-art',
maxAcceptedPayout: '1000000.000 PXS',
percentPxs: 10000,
allowVotes: true,
allowCurationRewards: true,
extensions: [[0, { beneficiaries: [{ account: 'pixagram', weight: 500 }] }]],
});| Method | Parameters | Returns | Description |
|---|---|---|---|
transfer(from, to, amount, memo?) |
string, string, string, string |
Promise<TransactionConfirmation> |
Transfer PIXA or PXS |
transferToVesting(from, to, amount) |
string, string, string |
Promise<TransactionConfirmation> |
Power up |
withdrawVesting(account, vestingShares) |
string, string |
Promise<TransactionConfirmation> |
Power down (use "0.000000 VESTS" to cancel) |
delegateVestingShares(delegator, delegatee, vestingShares) |
string, string, string |
Promise<TransactionConfirmation> |
Delegate/undelegate |
transferToSavings(from, to, amount, memo?) |
string, string, string, string |
Promise<TransactionConfirmation> |
Transfer to savings |
transferFromSavings(from, requestId, to, amount, memo?) |
string, number, string, string, string |
Promise<TransactionConfirmation> |
Initiate savings withdrawal (3-day) |
cancelTransferFromSavings(from, requestId) |
string, number |
Promise<TransactionConfirmation> |
Cancel pending withdrawal |
recurrentTransfer(params) |
RecurrentParams |
Promise<TransactionConfirmation> |
Set up recurring transfer |
claimRewardBalance(account, rewardPixa, rewardPxs, rewardVests) |
string, string, string, string |
Promise<TransactionConfirmation> |
Claim pending rewards |
// Transfer 10 PIXA
await api.broadcast.transfer('alice', 'bob', '10.000 PIXA', 'Thanks!');
// Power up
await api.broadcast.transferToVesting('alice', 'alice', '100.000 PIXA');
// Claim all rewards
const [acc] = await api.accounts.getAccounts(['alice']);
await api.broadcast.claimRewardBalance(
'alice',
acc.reward_pixa_balance,
acc.reward_pxs_balance,
acc.reward_vesting_balance
);| Method | Parameters | Returns | Description |
|---|---|---|---|
follow(follower, following) |
string, string |
Promise<TransactionConfirmation> |
Follow a user |
unfollow(follower, following) |
string, string |
Promise<TransactionConfirmation> |
Unfollow a user |
mute(follower, following) |
string, string |
Promise<TransactionConfirmation> |
Mute a user |
reblog(account, author, permlink) |
string, string, string |
Promise<TransactionConfirmation> |
Reblog/resteem a post |
customJson(params) |
CustomJsonParams |
Promise<TransactionConfirmation> |
Broadcast custom JSON |
| Method | Parameters | Returns | Description |
|---|---|---|---|
updateAccount2(params) |
UpdateAccountParams |
Promise<TransactionConfirmation> |
Update account (keys, metadata) |
updateProfile(account, profileObject) |
string, object |
Promise<TransactionConfirmation> |
Update profile metadata |
accountCreate(params) |
AccountCreateParams |
Promise<TransactionConfirmation> |
Create new account |
accountCreateWithDelegation(params) |
AccountCreateWithDelegationParams |
Promise<TransactionConfirmation> |
Create account with delegation |
accountWitnessVote(account, witness, approve?) |
string, string, boolean |
Promise<TransactionConfirmation> |
Vote for witness |
accountWitnessProxy(account, proxy) |
string, string |
Promise<TransactionConfirmation> |
Set witness proxy |
witnessUpdate(params) |
WitnessUpdateParams |
Promise<TransactionConfirmation> |
Update witness config |
setWithdrawVestingRoute(fromAccount, toAccount, percent, autoVest?) |
string, string, number, boolean |
Promise<TransactionConfirmation> |
Power-down routing |
| Method | Parameters | Returns | Description |
|---|---|---|---|
limitOrderCreate(params) |
LimitOrderParams |
Promise<TransactionConfirmation> |
Create limit order |
limitOrderCancel(owner, orderId) |
string, number |
Promise<TransactionConfirmation> |
Cancel limit order |
convertPixa(owner, amount, requestId) |
string, string, number |
Promise<TransactionConfirmation> |
Convert PIXA to PXS |
| Method | Parameters | Returns | Description |
|---|---|---|---|
sendOperations(operations, key) |
Array, PrivateKey|string |
Promise<TransactionConfirmation> |
Broadcast raw operations |
Cryptographic utilities (no RPC calls).
| Method | Parameters | Returns | Description |
|---|---|---|---|
isWif(key) |
string |
boolean |
Check if string is a valid WIF private key |
toWif(username, password, role) |
string, string, string |
string |
Derive WIF from username/password/role |
wifToPublic(wif) |
string |
string |
Get public key from WIF |
signMessage(message, wif) |
string, string |
string |
Sign a message |
verifySignature(message, signature, publicKey) |
string, string, string |
boolean |
Verify a signature |
encodeMemo(senderPrivateKey, recipientPublicKey, message) |
string, string, string |
string |
Encrypt a memo |
decodeMemo(recipientPrivateKey, encryptedMemo) |
string, string |
string |
Decrypt a memo |
generateKeys(username, password) |
string, string |
KeySet |
Generate all 4 key pairs |
// Generate all keys from master password
const keys = api.auth.generateKeys('alice', 'masterpassword');
// {
// owner: '5K...', ownerPublic: 'PIX7...',
// active: '5K...', activePublic: 'PIX7...',
// posting: '5K...', postingPublic: 'PIX7...',
// memo: '5K...', memoPublic: 'PIX7...',
// }Formatting and conversion utilities.
| Method | Parameters | Returns | Description |
|---|---|---|---|
reputation(rawReputation) |
number |
number |
Convert raw reputation to display score (e.g. 65.42) |
vestToSteem(vestingShares, totalVestingShares, totalVestingFundSteem) |
string|number ×3 |
number |
Convert VESTS to PIXA |
steemToVest(steem, totalVestingShares, totalVestingFundSteem) |
string|number ×3 |
number |
Convert PIXA to VESTS |
formatAsset(amount, symbol, precision?) |
number, string, number |
string |
Format asset string (e.g. "100.000 PIXA") |
getVestingSharePrice(props) |
DynamicGlobalProperties |
Price |
VESTS↔PIXA price from global props |
getVests(account, subtractDelegated?, addReceived?) |
Account, boolean, boolean |
number |
Effective vesting shares |
Real-time blockchain streaming with async generators.
| Method | Parameters | Returns | Description |
|---|---|---|---|
getBlockHeader(blockNum) |
number |
Promise<BlockHeader> |
Block header |
getBlock(blockNum) |
number |
Promise<SignedBlock> |
Full block |
getTransaction(txId) |
string |
Promise<SignedTransaction> |
Transaction by ID |
getTransactionHex(tx) |
Transaction |
Promise<string> |
Transaction hex encoding |
getCurrentBlockNum(mode?) |
string |
Promise<number> |
Current block number |
getCurrentBlockHeader(mode?) |
string |
Promise<BlockHeader> |
Current block header |
getCurrentBlock(mode?) |
string |
Promise<SignedBlock> |
Current full block |
getBlockNumbers(options?) |
StreamOptions |
AsyncGenerator<number> |
Stream block numbers |
getBlocks(options?) |
StreamOptions |
AsyncGenerator<SignedBlock> |
Stream full blocks |
getOperations(options?) |
StreamOptions |
AsyncGenerator<AppliedOperation> |
Stream all operations |
getBlockNumberStream() |
StreamOptions |
ReadableStream |
Node.js readable stream |
getBlockStream() |
StreamOptions |
ReadableStream |
Node.js readable stream |
getOperationsStream() |
StreamOptions |
ReadableStream |
Node.js readable stream |
Mode: 'irreversible' (default, confirmed) or 'latest' (may be reversed in fork).
// Stream new operations
for await (const op of api.blockchain.getOperations()) {
const [opType, opData] = op.op;
if (opType === 'transfer') {
console.log(`${opData.from} → ${opData.to}: ${opData.amount}`);
}
if (opType === 'vote') {
console.log(`${opData.voter} voted ${opData.weight} on @${opData.author}/${opData.permlink}`);
}
}
// Process a specific range
for await (const block of api.blockchain.getBlocks({ from: 1000000, to: 1000100 })) {
console.log(`Block ${block.block_id}: ${block.transactions.length} txs`);
}Resource credits (transaction bandwidth) management.
| Method | Parameters | Returns | Description |
|---|---|---|---|
getResourceParams() |
— | Promise<object> |
Current RC parameters |
getResourcePool() |
— | Promise<object> |
Global RC pool state |
findRcAccounts(accounts) |
string[] |
Promise<RCAccount[]> |
RC info for accounts |
getRCMana(account) |
string |
Promise<Manabar> |
Calculated RC mana (regenerated) |
getVPMana(account) |
string |
Promise<Manabar> |
Calculated voting power mana |
calculateRCCost(operationType, operationData?) |
string, object |
Promise<number> |
Estimate RC cost for an operation |
const rc = await api.rc.getRCMana('alice');
console.log(`RC: ${rc.percentage}% (${rc.current_mana} / ${rc.max_mana})`);
const vp = await api.rc.getVPMana('alice');
console.log(`Voting Power: ${vp.percentage}%`);
const cost = await api.rc.calculateRCCost('comment', { body: 'Hello world!' });
console.log(`Estimated RC cost: ${cost}`);Community features (requires Pixamind/Hivemind). Uses entity store + query cache (v3.4.0).
| Method | Parameters | Returns | Description |
|---|---|---|---|
getCommunity(name, observer?) |
string, string |
Promise<CommunityDetail|null> |
Community details |
listCommunities(options?) |
object |
Promise<Community[]> |
List communities |
getSubscriptions(account) |
string |
Promise<Subscription[]> |
User's community subscriptions |
getRankedPosts(options?) |
RankedPostsOptions |
Promise<SanitizedPost[]> |
Ranked posts (cached + sanitized) |
getAccountPosts(account, sort?, options?) |
string, string, object |
Promise<SanitizedPost[]> |
Account posts (cached + sanitized) |
| Method | Parameters | Returns | Description |
|---|---|---|---|
getKeyReferences(keys) |
(string|PublicKey)[] |
Promise<{accounts: string[][]}> |
Find accounts by public key |
const result = await api.keys.getKeyReferences(['PIX7abc123...']);
console.log(`Key belongs to: ${result.accounts[0].join(', ')}`);| Method | Parameters | Returns | Description |
|---|---|---|---|
findTransaction(transactionId, expiration?) |
string, string |
Promise<{status: string}> |
Check transaction status |
isConfirmed(transactionId) |
string |
Promise<boolean> |
Whether transaction is confirmed |
Status values: 'within_irreversible_block', 'within_reversible_block', 'expired_irreversible', 'expired_reversible', 'too_old', 'unknown'.
The main PixaProxyAPI instance exposes convenience methods that delegate to the internal WASM-powered ContentSanitizer. In v3.4.0 these also delegate to SanitizationPipeline when available.
Process a raw post through the full sanitization pipeline.
const rawPost = await api.content.getContent('alice', 'my-post');
const processed = api.processPost(rawPost, {
include_images: true,
max_image_count: 0, // 0 = unlimited
internal_domains: ['pixa.pics'],
});
// processed.html — sanitized HTML (XSS-safe)
// processed.images — [{ src, alt, is_base64, index }]
// processed.links — [{ href, text, domain, is_external }]
// processed.wordCount — word count
// processed.plainText — plain text excerpt
// processed.tags — parsed tags array
// processed.author_reputation — computed reputation scoreNote: In v3.4.0, if the entity was already fetched through
api.content.*orapi.tags.*, it is already sanitized andprocessPostreturns it as-is (no double-processing).
Stricter sanitization for comments — no headings, tables, or iframes.
const processed = api.processComment(rawComment);Strip all HTML/Markdown formatting.
const text = api.extractPlainText('# Hello\n\nSome **bold** text');
// "Hello Some bold text"TF-IDF extractive summarization.
const result = api.summarizeContent(longPostBody, 3);
// {
// summary: 'Top 3 sentences...',
// keywords: ['pixel', 'art', 'blockchain'],
// sentences: [{ text, score }, ...]
// }Parse json_metadata string (handles PIXA/HIVE/STEEM variants).
const meta = api.parseMetadata(account.json_metadata);
// { profile: { name, about, ... }, tags: [...], extra: {...} }HTML→plain text biography with length limit.
const bio = api.sanitizeBiography('<p>Hello <b>world</b></p>', 128);
// "Hello world"Validate and sanitize a username (3–16 chars, a-z0-9.-).
api.sanitizeUsername('Alice.Smith') // 'alice.smith'
api.sanitizeUsername('ab') // '' (too short)
api.sanitizeUsername('INVALID!!') // ''| Entity Type | Store Collection | Key Format | Example |
|---|---|---|---|
| Account | accounts_store |
username |
"alice" |
| Post | posts_store |
${author}_${permlink} |
"alice_my-pixel-art" |
| Comment | comments_store |
${author}_${permlink} |
"bob_re-alice-my-pixel-art-001" |
| Query | query_cache |
${namespace}:${sorted_params} |
"trending:limit=20:tag=art" |
All entities carry metadata fields:
{
_entity_id: 'alice', // Canonical key
_entity_type: 'account', // 'account' | 'post' | 'comment'
_stored_at: 1706000000000, // Insertion timestamp (ms)
// ... all original + sanitized fields
}The pipeline is constructed with references to the ContentSanitizer and FormatterAPI:
| Method | Input | Output |
|---|---|---|
sanitizeAccount(raw) |
Raw account from RPC | Account with display_name, sanitized_about, parsed_metadata, reputation_score, etc. |
sanitizePost(raw, options?) |
Raw discussion (depth=0) | Post with html, images, links, wordCount, plainText, tags, app |
sanitizeComment(raw, options?) |
Raw comment (depth>0) | Comment with html (strict), images, links, wordCount |
sanitizeContent(raw, options?) |
Any raw content | Auto-detects post vs comment by depth / parent_author |
| Method | Description |
|---|---|
get(type, entityId) |
Get entity if fresh (within TTL), otherwise null |
upsert(type, sanitizedEntity) |
Insert or update a sanitized entity |
upsertMany(type, entities) |
Batch upsert |
resolve(type, ids[]) |
Batch resolve — returns array in same order as IDs |
invalidate(type, entityId) |
Delete a single entity |
invalidateAll(type) |
Clear all entities of a type |
| Method | Description |
|---|---|
QueryCacheManager.buildKey(namespace, params) |
Static. Build deterministic cache key from query descriptor |
get(queryKey, ttlCategory?) |
Retrieve cached ID array if fresh |
store(queryKey, ids[], entityType) |
Cache a query result as ID array |
invalidate(queryKey) |
Delete a single cached query |
invalidateByPrefix(prefix) |
Delete all queries matching a namespace |
Default TTLs (all in milliseconds):
Entity TTLs (ENTITY_TTL):
| Entity | Default TTL | Description |
|---|---|---|
accounts |
24 hours | Account data changes infrequently |
posts |
5 minutes | Posts can receive votes/edits |
comments |
2 minutes | Comments are more ephemeral |
Query TTLs (QUERY_TTL):
| Query Type | Default TTL | Description |
|---|---|---|
trending |
5 minutes | Trending algorithm changes slowly |
created |
30 seconds | New posts appear frequently |
hot |
3 minutes | Hot algorithm |
promoted |
5 minutes | Promoted changes slowly |
active |
1 minute | Activity-based |
blog |
1 minute | User blog |
feed |
30 seconds | User feed updates often |
comments |
1 minute | Comment listings |
content |
1 minute | Single content lookups |
content_replies |
1 minute | Reply listings |
account_lookup |
10 minutes | Account autocomplete |
Subscribe to events via api.on(), api.off(), api.once():
| Event | Payload | Description |
|---|---|---|
session_created |
{ account, sessionId, pinEnabled } |
New session started |
session_restored |
{ account, pinEnabled, keysLoaded, needsPIN? } |
Previous session restored |
session_ended |
{ account } |
Session terminated |
session_expired |
{ account } |
Session TTL expired |
account_switched |
{ account } |
Switched active account |
vault_initialized |
{ timestamp } |
Encrypted vault created |
pin_unlocked |
{ account } |
Vault unlocked with PIN |
key_required |
{ account, type, callback } |
Key needed — UI should prompt user |
profile_updated |
{ account, profile } |
Profile metadata updated |
api.on('session_expired', ({ account }) => {
console.log(`Session expired for @${account}`);
// Redirect to login
});
api.on('key_required', async ({ account, type, callback }) => {
// Show UI prompt for key
const key = await showKeyPrompt(account, type);
callback(key, false, false);
});All errors extend PixaAPIError:
| Error Class | Code | Description |
|---|---|---|
PixaAPIError |
varies | Base error with code and data properties |
KeyNotFoundError |
KEY_NOT_FOUND |
Required key not available |
VaultNotInitializedError |
VAULT_NOT_INITIALIZED |
Vault operation before init |
SessionExpiredError |
SESSION_EXPIRED |
Session TTL exceeded |
SessionNotFoundError |
SESSION_NOT_FOUND |
No active session |
try {
await api.broadcast.transfer('alice', 'bob', '10.000 PIXA', '');
} catch (error) {
if (error instanceof KeyNotFoundError) {
console.log(`Need ${error.data.keyType} key for @${error.data.account}`);
} else if (error.code === 'SESSION_EXPIRED') {
// Re-authenticate
} else {
console.error(error.message);
}
}import PixaProxyAPI from './pixaproxyapi.js';// Error classes & config
import {
CONFIG,
PixaAPIError,
KeyNotFoundError,
VaultNotInitializedError,
SessionExpiredError,
SessionNotFoundError,
} from './pixaproxyapi.js';
// Re-exported from @pixagram/dpixa
import {
PrivateKey,
PublicKey,
Signature,
Asset,
Price,
Memo,
cryptoUtils,
utils,
Types,
BlockchainMode,
getVestingSharePrice,
getVests,
} from './pixaproxyapi.js';
// Utility functions
import {
normalizeAccount,
getRandomBytes,
bytesToHex,
} from './pixaproxyapi.js';The full CONFIG object with defaults:
{
// Key derivation
PBKDF2_ITERATIONS: 1_000_000,
// Legacy cache TTLs (used by CacheManager for non-entity data)
CACHE_TTL: {
posts: 60_000,
accounts: 86_400_000,
comments: 60_000,
trending: 300_000,
feed: 30_000,
communities: 3_600_000,
rewards: 300_000,
search: 600_000,
blocks: 30_000,
witnesses: 300_000,
market: 60_000,
global_props: 10_000,
},
// Entity store TTLs (v3.4.0)
ENTITY_TTL: {
accounts: 86_400_000, // 24 hours
posts: 300_000, // 5 minutes
comments: 120_000, // 2 minutes
},
// Query cache TTLs (v3.4.0)
QUERY_TTL: {
trending: 300_000,
created: 30_000,
hot: 180_000,
promoted: 300_000,
active: 60_000,
blog: 60_000,
feed: 30_000,
comments: 60_000,
votes: 60_000,
children: 60_000,
cashout: 60_000,
content: 60_000,
content_replies: 60_000,
account_lookup: 600_000,
notifications: 30_000,
},
// Session & auth
SESSION_TIMEOUT: 1_800_000, // 30 minutes
PIN_TIMEOUT: 300_000, // 5 minutes
MIN_PIN_LENGTH: 6,
// Network
DEFAULT_NODES: ['https://cors-header-proxy-with-api.omnibus-39a.workers.dev'],
APP_NAME: 'pixagram/3.4.0',
CHAIN_ID: null,
ADDRESS_PREFIX: 'PIX',
// Pagination
PAGINATION_LIMIT: 20,
}Pixagram SA — Share and Enjoy! 🚀