hop-sdk
v0.2.1
Published
Client SDK for the Polkadot HOP protocol
Downloads
2,689
Readme
hop-sdk
Client SDK for the Polkadot HOP protocol — send and claim data through ephemeral off-chain storage on Polkadot.
Status: HOP is live on the Polkadot Previewnet.
What is HOP
Transferring data peer-to-peer requires both parties to be online at the same time. Posting every offline transfer to the chain wastes block allowance, adds latency, and keeps data around far longer than needed.
HOP is an ephemeral off-chain data pool hosted by Bulletin Chain collators. The sender uploads data to a collator, signals the receiver through the statement store, and the receiver claims it directly — no on-chain transaction required. Data lives for up to 24 hours; if unclaimed, the collator promotes it to Bulletin Chain storage as a last-resort fallback.
Why this SDK
The HOP protocol may change at the runtime level — new chunking strategies, proof formats, or transport protocols.
hop-sdk gives you a stable send / claim API so your application code doesn't break when the protocol evolves. Call HopClient.connect('previewnet') and the SDK resolves the right node endpoints for you.
Install
npm install hop-sdkQuick start
import { HopClient, HopTicket } from 'hop-sdk';
// sender
const client = await HopClient.connect('previewnet');
const data = new TextEncoder().encode('hello HOP');
const [ticket] = await client.send(data);
// pass ticket bytes to the recipient
const bytes = ticket.encode();
// recipient
const recipient = await HopClient.connect('previewnet');
const restored = HopTicket.decode(bytes);
const payload = await recipient.claim(restored);
console.log(new TextDecoder().decode(payload)); // "hello HOP"
client.destroy();
recipient.destroy();API reference
HopClient
| Method | Signature | Description |
|---|---|---|
| connect | static async connect(env: HopEnvironment): Promise<HopClient> | Connect to a HOP node by environment name |
| send | async send(data: Uint8Array, recipientCount?: number): Promise<HopTicket[]> | Send data into the pool. Returns one ticket per recipient. Per-submission size cap is set by the chain (query via maxPromotionSize()). |
| maxPromotionSize | async maxPromotionSize(): Promise<number> | Max per-submission size in bytes, from HopRuntimeApi::max_promotion_size. |
| claim | async claim(ticket: HopTicket): Promise<Uint8Array> | Claim data from the pool using a ticket |
| destroy | destroy(): void | Release resources held by this client |
HopTicket
| Method | Signature | Description |
|---|---|---|
| encode | encode(): Uint8Array | Serialize to raw bytes |
| decode | static decode(bytes: Uint8Array): HopTicket | Deserialize from raw bytes |
Environments
| HopEnvironment | Network | Notes |
|---|---|---|
| local | Local dev node | For development against a local omni-node |
| previewnet | Polkadot Previewnet | For products |
Node endpoints are hardcoded per SDK release. They may change between versions, but your code stays the same — just call HopClient.connect('previewnet').
Tickets
A HopTicket is an opaque token returned by send(). Serialize it with encode() and restore it with HopTicket.decode():
const bytes = ticket.encode(); // Uint8Array (64 bytes)
const ticket = HopTicket.decode(bytes);Error handling
All SDK errors extend HopError, which has a code string and a message.
| Error class | Code | When |
|---|---|---|
| HopDataTooLargeError | DATA_TOO_LARGE | Data exceeds the chain's max_promotion_size |
| HopPoolFullError | POOL_FULL | The pool has no remaining capacity |
| HopDuplicateEntryError | DUPLICATE_ENTRY | Identical data is already in the pool |
| HopNotFoundError | NOT_FOUND | The requested entry does not exist in the pool |
| HopEmptyDataError | EMPTY_DATA | Submitted data is empty |
| HopInvalidSignatureError | INVALID_SIGNATURE | Server rejected the signature |
| HopNotRecipientError | NOT_RECIPIENT | Caller is not one of the entry's recipients |
| HopNoRecipientsError | NO_RECIPIENTS | Submit had no recipients |
| HopInvalidRecipientKeyError | INVALID_RECIPIENT_KEY | A recipient key failed SCALE decode |
| HopQuotaExceededError | QUOTA_EXCEEDED | User bandwidth/request quota exhausted |
| HopNotAuthorizedError | NOT_AUTHORIZED | Account is not authorized to promote this data |
| HopInvalidSignerError | INVALID_SIGNER | Signer bytes failed SCALE decode |
| HopAlreadyClaimedError | ALREADY_CLAIMED | Recipient already acknowledged this entry |
| HopInvalidHashLengthError | INVALID_HASH_LENGTH | Hash arg is not 32 bytes |
| HopTooManyRecipientsError | TOO_MANY_RECIPIENTS | More than 256 recipients in one submit |
| HopDuplicateRecipientError | DUPLICATE_RECIPIENT | A recipient appears more than once in the list |
| HopRateLimitedError | RATE_LIMITED | Per-account rate limit hit |
| HopInvalidTicketError | INVALID_TICKET | Ticket bytes are malformed or have wrong length (client-side) |
| HopNetworkError | NETWORK_ERROR | Transport-level failure (connection refused, timeout, etc.) |
import { HopPoolFullError } from 'hop-sdk';
try {
await client.send(data);
} catch (err) {
if (err instanceof HopPoolFullError) {
// wait and retry
}
throw err;
}Development
npm run build # build with tsup
npm test # run tests with vitest
npm run typecheck # type-check with tsc --noEmitLicense
Apache-2.0
