Overview
A conceptual map of the system. Read this before diving into the Engine or SDK sections.
The three actors
Every Shain session involves three actors on-chain:
- Holder — a Solana wallet holding at least the minimum balance of
$SHAIN. Holders sign their own transactions; Shain never custodies keys. - Program — the deployed Anchor program. Owns two PDA types (config singleton, per-holder session) and arbitrates entry into the private route.
- Treasury — a PDA-owned associated token account that receives session fees. Accrues balance over time; disposition rules are documented in the program itself.
The session lifecycle
- Holder checks that their wallet carries ≥
min_holdingof$SHAIN. - Holder signs
start_session()— the program transferssession_feefrom the holder's token account to the treasury ATA and writes aShainSessionPDA withexpires_at = now + 24h. - For the next 24 hours, any dapp may gate a private call behind
gated_action(tag). The program asserts the session is still live and increments the caller's action counter. - After expiry, any signer may call
close_session(). The session PDA is closed and its rent lamports are refunded to the session owner.
What gated_action does
gated_action(tag: u64)is the integration hook. It does exactly one thing on-chain: it asserts that the caller has a session that is not yet expired, then increments the session's action counter for instrumentation.
The tag is an opaque u64. Shain itself does not interpret it — integrating dapps use it to label call sites, the same way you'd use log keys. Use the SDK's tagFromCallsite(module, fn) helper for a stable deterministic value without managing a registry.
pub fn gated_action(ctx: Context<GatedAction>, tag: u64) -> Result<()>;
// Accounts:
// [signer] user — must match session.owner
// [writable] shain_session — PDA [b"shain_session", user.key().as_ref()]What the contract knows
The config PDA ShainConfig carries the global parameters the authority seeded at deployment:
| Field | Type | Default | Role |
|---|---|---|---|
session_duration | i64 | 86400 (24h) | Window length in seconds |
session_fee | u64 | 1_000_000 | Fee charged on open, in base units |
min_holding | u64 | 10_000_000 | Required balance to open |
total_sessions | u64 | 0 | Lifetime counter across all holders |
total_fees_collected | u64 | 0 | Running treasury accrual |
These are all set once at initialize time and are not adjustable through any other instruction.
What Shain does not do
- It does not mix tokens. There is no pool, no commitment tree, no relayer. Your balances stay in your wallet.
- It does not anonymize you. Your wallet address remains attributable. What Shain attenuates is the ability of mempool watchers to correlate your actions during the session window.
- It does not give you privacy forever. Sessions expire. This is a feature — the goal is trading without a parasite, not anonymity as an identity.
Next
- How sessions work — the session state machine step by step.
- Engine / Architecture — concrete data structures and CPI flow.