use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum CompatLevel {
Exact,
LocalIndex,
BestEffort,
Shim,
SdkWrapper,
Planned,
Skipped,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Namespace {
Das,
Enhanced,
Tx,
Rpc,
Staking,
Webhooks,
Ws,
Wallet,
Compat,
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum Transport {
#[default]
JsonRpc,
Rest,
Ws,
SdkWrapper,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MethodEntry {
pub method: &'static str,
pub namespace: Namespace,
pub helius_sdk_path: &'static str,
pub compat: CompatLevel,
#[serde(default)]
pub transport: Transport,
pub since_version: Option<&'static str>,
pub source_doc: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
pub notes: Option<&'static str>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default)]
pub struct ManifestSummary {
pub exact: u32,
pub local_index: u32,
pub best_effort: u32,
pub shim: u32,
pub sdk_wrapper: u32,
pub planned: u32,
pub skipped: u32,
pub total: u32,
}
#[must_use]
pub fn summarize(entries: &[MethodEntry]) -> ManifestSummary {
let mut s = ManifestSummary {
total: entries.len() as u32,
..ManifestSummary::default()
};
for e in entries {
match e.compat {
CompatLevel::Exact => s.exact += 1,
CompatLevel::LocalIndex => s.local_index += 1,
CompatLevel::BestEffort => s.best_effort += 1,
CompatLevel::Shim => s.shim += 1,
CompatLevel::SdkWrapper => s.sdk_wrapper += 1,
CompatLevel::Planned => s.planned += 1,
CompatLevel::Skipped => s.skipped += 1,
}
}
s
}
#[must_use]
pub fn manifest() -> &'static [MethodEntry] {
MANIFEST
}
const MANIFEST: &[MethodEntry] = &[
MethodEntry {
method: "getAsset",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAsset",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das/getasset",
notes: Some(
"Hybrid: uncompressed NFTs (Token Metadata, MplCore) work cold — resolve via upstream getAccountInfo + decoder on cache miss, then enrich with off-chain JSON (image/description/attributes/files) fetched from the asset's uri (http(s):// + file://, fail-soft, cached). Compressed NFTs require local Bubblegum indexing (no upstream equivalent exists) and return null for trees we haven't observed. LOCAL_INDEX reflects the cNFT constraint.",
),
},
MethodEntry {
method: "getAssetBatch",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAssetBatch",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das",
notes: Some("Sequential fanout over getAsset (up to 1000 ids). Same hybrid behavior: uncompressed entries hit upstream on miss, compressed require indexing."),
},
MethodEntry {
method: "getAssetProof",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAssetProof",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das/getassetproof",
notes: Some(
"cNFT merkle proof computed from the local indexer. Trees must be registered via --index-tree or tidepool_indexTree before resolution works.",
),
},
MethodEntry {
method: "getAssetProofBatch",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAssetProofBatch",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das",
notes: Some("Shares TreeState materialization across ids in the batch."),
},
MethodEntry {
method: "getAssetsByOwner",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAssetsByOwner",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das/getassetsbyowner",
notes: Some(
"Returns assets the proxy has indexed via prior getAsset / getAssetBatch calls. Owners never touched return empty.",
),
},
MethodEntry {
method: "getAssetsByAuthority",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAssetsByAuthority",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das",
notes: None,
},
MethodEntry {
method: "getAssetsByCreator",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAssetsByCreator",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das",
notes: Some("`onlyVerified` filters to creators whose verified flag is true."),
},
MethodEntry {
method: "getAssetsByGroup",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getAssetsByGroup",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das",
notes: Some("For MplCore: groupKey=\"collection\"."),
},
MethodEntry {
method: "searchAssets",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.searchAssets",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das/searchassets",
notes: Some(
"AND-combines owner / authority / creator / grouping / interface / burnt filters. Smallest-index-first narrowing keeps multi-filter queries fast.",
),
},
MethodEntry {
method: "getNftEditions",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getNftEditions",
compat: CompatLevel::LocalIndex,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das",
notes: Some("Cold call on an unknown master mint fetches + indexes the master's Edition PDA via getAsset first, then serves from the index. Print editions are accumulated lazily as they're individually fetched via getAsset — list reflects what Tidepool has observed."),
},
MethodEntry {
method: "getTokenAccounts",
namespace: Namespace::Das,
helius_sdk_path: "helius.das.getTokenAccounts",
compat: CompatLevel::Shim,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/das/gettokenaccounts",
notes: Some("Forwards to getTokenAccountsByOwner (owner filter) or getProgramAccounts memcmp (mint-only filter). Queries both SPL Token and Token-2022. Paginates locally; no cursor support."),
},
MethodEntry {
method: "getTransactions",
namespace: Namespace::Enhanced,
helius_sdk_path: "helius.enhanced.getTransactions",
compat: CompatLevel::BestEffort,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/enhanced-transactions/gettransactions",
notes: Some("REST: POST /v0/transactions. Body: { transactions: [sig, ...] }. Narrow classifier — TRANSFER / COMPRESSED_NFT_* / NFT_MINT / UNKNOWN; SWAP/STAKE/DEFI parsers not implemented."),
},
MethodEntry {
method: "getTransactionsByAddress",
namespace: Namespace::Enhanced,
helius_sdk_path: "helius.enhanced.getTransactionsByAddress",
compat: CompatLevel::BestEffort,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/enhanced-transactions",
notes: Some("REST: GET /v0/addresses/:address/transactions. Resolves signatures via getSignaturesForAddress + runs classifier. Same narrow scope as getTransactions."),
},
MethodEntry {
method: "getTransfersByAddress",
namespace: Namespace::Enhanced,
helius_sdk_path: "helius.rpc.getTransfersByAddress",
compat: CompatLevel::BestEffort,
transport: Transport::JsonRpc,
since_version: Some("0.3.0"),
source_doc: "https://www.helius.dev/docs/api-reference/getTransfersByAddress",
notes: Some("JSON-RPC. Flattens native+SPL transfers per signature into per-event records with `from/to`, `mint`, `amount`, `uiAmount`. Helius's claim of 'full history from genesis' isn't matched — Tidepool only sees what Surfpool has streamed. SPL `decimals` defaults to 0 until we wire a token-info cache. p-token batch transfers surface as one event per inner transfer."),
},
MethodEntry {
method: "getTransactionsForAddress",
namespace: Namespace::Enhanced,
helius_sdk_path: "helius.rpc.getTransactionsForAddress",
compat: CompatLevel::BestEffort,
transport: Transport::JsonRpc,
since_version: Some("0.3.0"),
source_doc: "https://www.helius.dev/blog/introducing-gettransactionsforaddress",
notes: Some("JSON-RPC. Combined getSignaturesForAddress + getTransaction + classify. Supersedes the REST `getTransactionsByAddress`. Supports `limit`, `paginationToken`, `minSlot`, `maxSlot`, `status` (success/failure). Same history limitation as getTransfersByAddress."),
},
MethodEntry {
method: "getPriorityFeeEstimate",
namespace: Namespace::Tx,
helius_sdk_path: "helius.tx.getPriorityFeeEstimate",
compat: CompatLevel::BestEffort,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/priority-fee-api",
notes: Some("Local percentile ladder (min/low/medium/high/veryHigh/unsafeMax) computed over getRecentPrioritizationFees samples."),
},
MethodEntry {
method: "sendSmartTransaction",
namespace: Namespace::Tx,
helius_sdk_path: "helius.tx.sendSmartTransaction",
compat: CompatLevel::SdkWrapper,
transport: Transport::SdkWrapper,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/sending-transactions/optimizing-transactions",
notes: Some(
"helius-sdk client-side composition: simulate → priority fee → getLatestBlockhash → send → confirm. Every underlying call passes through.",
),
},
MethodEntry {
method: "broadcastTransaction",
namespace: Namespace::Tx,
helius_sdk_path: "helius.tx.broadcastTransaction",
compat: CompatLevel::SdkWrapper,
transport: Transport::SdkWrapper,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/sending-transactions",
notes: None,
},
MethodEntry {
method: "pollTransactionConfirmation",
namespace: Namespace::Tx,
helius_sdk_path: "helius.tx.pollTransactionConfirmation",
compat: CompatLevel::SdkWrapper,
transport: Transport::SdkWrapper,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/sending-transactions",
notes: None,
},
MethodEntry {
method: "sendTransactionWithSender",
namespace: Namespace::Tx,
helius_sdk_path: "helius.tx.sendTransactionWithSender",
compat: CompatLevel::Shim,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/sending-transactions/sender",
notes: Some(
"Forwards to upstream sendTransaction. Helius's production impl runs the tx through its parallel Jito-relay fleet for faster landing; locally we can't reproduce that, so inclusion latency matches whatever Surfpool gives you. The method works — callers still get a signature back.",
),
},
MethodEntry {
method: "getProgramAccountsV2",
namespace: Namespace::Rpc,
helius_sdk_path: "helius.getProgramAccountsV2",
compat: CompatLevel::Shim,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/rpc/http/getprogramaccountsv2",
notes: Some("Forwards to getProgramAccounts; sorts by pubkey and slices locally by cursor+limit. Cursor is the last pubkey from the prior page."),
},
MethodEntry {
method: "getTokenAccountsByOwnerV2",
namespace: Namespace::Rpc,
helius_sdk_path: "helius.getTokenAccountsByOwnerV2",
compat: CompatLevel::Shim,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/rpc/http/gettokenaccountsbyownerv2",
notes: Some("Forwards to getTokenAccountsByOwner; cursor semantics match getProgramAccountsV2."),
},
MethodEntry {
method: "createStakeTransaction",
namespace: Namespace::Staking,
helius_sdk_path: "helius.staking.createStakeTransaction",
compat: CompatLevel::SdkWrapper,
transport: Transport::SdkWrapper,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/staking/how-to-stake-with-helius-programmatically",
notes: None,
},
MethodEntry {
method: "createUnstakeTransaction",
namespace: Namespace::Staking,
helius_sdk_path: "helius.staking.createUnstakeTransaction",
compat: CompatLevel::SdkWrapper,
transport: Transport::SdkWrapper,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/staking/how-to-stake-with-helius-programmatically",
notes: None,
},
MethodEntry {
method: "createWebhook",
namespace: Namespace::Webhooks,
helius_sdk_path: "helius.webhooks.createWebhook",
compat: CompatLevel::Shim,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/webhooks",
notes: Some("REST: POST /v0/webhooks. Local polling simulator — spawns a background task that polls getSignaturesForAddress every 500ms and POSTs a simplified tx envelope to the user URL."),
},
MethodEntry {
method: "getAllWebhooks",
namespace: Namespace::Webhooks,
helius_sdk_path: "helius.webhooks.getAllWebhooks",
compat: CompatLevel::Shim,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/webhooks",
notes: Some("REST: GET /v0/webhooks. In-memory registry by default; persistent behind --db."),
},
MethodEntry {
method: "getWebhookByID",
namespace: Namespace::Webhooks,
helius_sdk_path: "helius.webhooks.getWebhookByID",
compat: CompatLevel::Shim,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/webhooks",
notes: Some("REST: GET /v0/webhooks/:id."),
},
MethodEntry {
method: "editWebhook",
namespace: Namespace::Webhooks,
helius_sdk_path: "helius.webhooks.editWebhook",
compat: CompatLevel::Shim,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/webhooks",
notes: Some("REST: PUT /v0/webhooks/:id. Merges into existing record; restarts the polling task with fresh cursor state."),
},
MethodEntry {
method: "deleteWebhook",
namespace: Namespace::Webhooks,
helius_sdk_path: "helius.webhooks.deleteWebhook",
compat: CompatLevel::Shim,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/webhooks",
notes: Some("REST: DELETE /v0/webhooks/:id."),
},
MethodEntry {
method: "signatureSubscribe",
namespace: Namespace::Ws,
helius_sdk_path: "helius.ws.signatureNotifications",
compat: CompatLevel::Exact,
transport: Transport::Ws,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/rpc/websocket-methods",
notes: Some(
"WebSocket reverse-proxied to Surfpool's native impl. Tidepool's WS port (`port+1`) forwards every frame to upstream. confirmTransaction() / sendAndConfirm() work transparently.",
),
},
MethodEntry {
method: "signatureUnsubscribe",
namespace: Namespace::Ws,
helius_sdk_path: "helius.ws.close",
compat: CompatLevel::Exact,
transport: Transport::Ws,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/rpc/websocket-methods",
notes: Some("Reverse-proxied to Surfpool's native impl."),
},
MethodEntry {
method: "accountSubscribe",
namespace: Namespace::Ws,
helius_sdk_path: "helius.ws.accountNotifications",
compat: CompatLevel::Exact,
transport: Transport::Ws,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/rpc/websocket-methods",
notes: Some("Reverse-proxied to Surfpool's native impl."),
},
MethodEntry {
method: "logsSubscribe",
namespace: Namespace::Ws,
helius_sdk_path: "helius.ws.logsNotifications",
compat: CompatLevel::Exact,
transport: Transport::Ws,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/rpc/websocket-methods",
notes: Some("Reverse-proxied to Surfpool's native impl. All filters supported by Surfpool work: `all`, `allWithVotes`, `{ mentions: [pubkey] }`."),
},
MethodEntry {
method: "logsUnsubscribe",
namespace: Namespace::Ws,
helius_sdk_path: "helius.ws.close",
compat: CompatLevel::Exact,
transport: Transport::Ws,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/rpc/websocket-methods",
notes: Some("Reverse-proxied to Surfpool's native impl."),
},
MethodEntry {
method: "getBalances",
namespace: Namespace::Wallet,
helius_sdk_path: "helius.wallet.getBalances",
compat: CompatLevel::Shim,
transport: Transport::Rest,
since_version: Some("1.0.0"),
source_doc: "https://www.helius.dev/docs/api-reference/wallet-api",
notes: Some("REST: GET /v0/addresses/:address/balances. Fans out to getBalance + getTokenAccountsByOwner (SPL + Token-2022). USD pricing fields are null by design — no local price feed."),
},
MethodEntry {
method: "tidepool_info",
namespace: Namespace::Compat,
helius_sdk_path: "(tidepool extension)",
compat: CompatLevel::Exact,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://github.com/vibestackmd/tidepool",
notes: Some("Returns this manifest + runtime summary."),
},
MethodEntry {
method: "tidepool_indexTree",
namespace: Namespace::Compat,
helius_sdk_path: "(tidepool extension)",
compat: CompatLevel::Exact,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://github.com/vibestackmd/tidepool",
notes: Some(
"Runtime tree registration for the cNFT indexer. Params: { tree: string, maxSignatures?, pageSize? }.",
),
},
MethodEntry {
method: "tidepool_exportTreeSnapshot",
namespace: Namespace::Compat,
helius_sdk_path: "(tidepool extension)",
compat: CompatLevel::Exact,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://github.com/vibestackmd/tidepool",
notes: Some(
"Export one tree's indexed state as a portable snapshot envelope (base64-wrapped JSON). Params: { tree: string }. Pair with tidepool_loadTreeSnapshot for fresh-boot preload.",
),
},
MethodEntry {
method: "tidepool_loadTreeSnapshot",
namespace: Namespace::Compat,
helius_sdk_path: "(tidepool extension)",
compat: CompatLevel::Exact,
transport: Transport::JsonRpc,
since_version: Some("1.0.0"),
source_doc: "https://github.com/vibestackmd/tidepool",
notes: Some(
"Apply a previously-dumped snapshot to the local store. Params: { snapshot: SnapshotBlob }. Overwrites any existing state for that tree.",
),
},
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn manifest_is_non_empty_and_has_all_shipped_methods() {
let m = manifest();
assert!(m.len() > 15);
assert!(m.iter().any(|e| e.method == "getAsset"));
assert!(m.iter().any(|e| e.method == "getAssetProof"));
assert!(m.iter().any(|e| e.method == "signatureSubscribe"));
assert!(m.iter().any(|e| e.method == "tidepool_indexTree"));
}
#[test]
fn summarize_counts_every_entry_exactly_once() {
let m = manifest();
let s = summarize(m);
let sum = s.exact
+ s.local_index
+ s.best_effort
+ s.shim
+ s.sdk_wrapper
+ s.planned
+ s.skipped;
assert_eq!(sum, s.total);
assert_eq!(s.total as usize, m.len());
}
#[test]
fn every_shipped_entry_has_a_since_version() {
for e in manifest() {
match e.compat {
CompatLevel::Planned | CompatLevel::Skipped => {
assert!(
e.since_version.is_none(),
"planned/skipped should not have since_version: {}",
e.method
);
}
_ => {
assert!(
e.since_version.is_some(),
"shipped method missing since_version: {}",
e.method
);
}
}
}
}
}