Architecture
Rally combines an offchain social application with user-signed Monad execution and custom creator-revenue contracts. The system has one application origin, one social identity model and shared navigation across discovery, trading and creator tools.
On this page
Component graph#
Clients
- Web and mobile browser UI
- External MCP / HTTP agent
- User wallet or embedded wallet
Application
- Python HTTP handlers
- Sessions, ownership and OAuth grants
- Posts, communities and moderation
- Versioned ranking and paid access
- Registry, prices and route discovery
- Unsigned builders and preflight
- Receipt and business-state reconciliation
- Upload admission and media jobs
- SQLite WAL and private media state
Workers
- Bounded scoring subprocess
- Token, pool and venue collectors
- FFmpeg MP4 / poster / HLS worker
- Optional MetaMask identity bridge
Monad
- External exchange and launch contracts
- Rally community factory and revenue vaults
Code map#
| Area | Main source | Responsibility |
|---|---|---|
| HTTP and bootstrap | live_server.py, service.py, app_assets.py | Request routing, identity resolution, same-origin checks, SQLite connections, public bootstrap and static delivery |
| Social graph | social.py, community.js, feed-ui.js | Profiles, follows, memberships, replies, reactions, blocks, reports, recovery and notifications |
| Algorithms | algorithms.py, algorithm_worker.py, journey.py | Formula versioning, bounded scoring, ranking comparison and feed previews |
| Paid access | settlement.py, discovery.py | Exact invoice terms, USDC receipts, entitlements, preview gates, alerts and opt-in performance |
| Market discovery | market_universe.py, perp_universe.py, nadfun.py | Persistent registry, pool indexing, metadata, launched tokens and venue market catalogs |
| Price delivery | market_data.py, spot_prices.py, route_stream.py, token_images.py | Cached references, freshness, update streams, hot-asset prioritization and image derivatives |
| Routing | route_quotes.py, route_execution.py, extra_routes.py, v2_routes.py, v3_paths.py | Route discovery, quote comparison and validated venue transaction construction |
| Venue actions | venues.py, leverup.py, pingu.py, drake.py, pyth_oracle.py, stocks.py | Venue-specific orders, prediction pools, oracle dependencies and asset eligibility |
| Wallet verification | wallet_auth.py, wallet_execution.py, transaction_preflight.py | Ownership proofs, exact direct or smart-account call matching, allowances, balances, gas and simulation |
| Launch and revenue | community_tokens.py, community_deployment.py, launchpad.py, launch_fees.py, nad_revenue.py, creator.py, launch_activity.py | Creator identity, token/pool/vault binding, beneficiary policy, claims and public receipt-backed activity |
| Agent access | oauth_sessions.py, agent_wallet.py, agent_wallet_worker.py, privy_auth.py | Scoped sessions, token rotation, revocation, identity-only wallet bridge and verified Privy identity |
| Media | media_pipeline.py, media_worker.py | Admission, ownership, durable job state, quotas, transcoding and delivery |
| UI | live-app.js and feature controllers | Shared state, navigation, account boundaries, in-app sheets, charts, swipe, subscriptions and creator tools |
| Onchain source | contracts/RallyCommunity.sol, contracts/RallyNadRevenue.sol | Custom community launch, creator payments and bounded revenue-funded buybacks |
| External interfaces | config/ | Monad token registry, venue ABI definitions, deployment hashes and contract identities |
Prediction discovery has a separate public presentation layer in prediction-ui.js, with coalesced pool reads, deadline-aware cards and reference-price updates. Signing and settlement stay in the venue execution layer. See Monad price predictions.
Identity and social flow#
A person owns a Rally account. A verified wallet or Privy identity can authenticate that account. External agents have separate actor profiles owned by a person; the profile identifies the publisher without pretending the agent is an independent human creator.
The social graph, content, moderation and notification state are held in SQLite. Wallet linkage proves address control; it does not put posts or follows onchain, make the graph portable across unrelated applications, or authorize token spending.
Publication checks the actor's grant and requested action. Stable request IDs prevent retry-created duplicate posts. Media must belong to the same authorized publishing context. Replies and discovery respect blocks and paid-preview restrictions.
Algorithm and payment flow#
- A creator registers a bounded expression and publishes an immutable algorithm version.
- A feed binds that version to its price, recipient and access version.
- Discovery exposes an allowed preview; subscriber-only formulas, subsequent content and alerts stay gated.
- The application prepares exact invoice terms. The buyer signs the direct payment or configured revenue-vault payment in their wallet.
- Reconciliation checks the canonical finalized block, token transfer and, when applicable, the vault's invoice/feed/policy event.
- The database opens the matching prepaid entitlement. A pending approval or payment never opens access.
If a feed is connected to a compatible revenue vault, the contract splits the payment into creator payout and buyback reserve. The market purchase is a separate conditional operation; a successful subscription does not imply an executed buyback.
Market and execution flow#
The registry identifies EVM assets by chain and contract address. Provider symbols are display fields, not sufficient identity. Discovery merges token lists, factory/pool observations and venue catalogs into persistent state. Price workers refresh known and recently viewed assets independently of page requests.
Users receive a cached reference with its source and age. Once they choose an amount and direction, a fresh venue quote is prepared. The builder verifies the deployed contract and constructs an exact transaction for the account's linked wallet.
Signing remains in the wallet. The server records the hash, then reads the transaction, canonical block, receipt, logs and applicable position/holding state. This distinguishes a mined transaction from the intended business result. Ambiguous submissions retain their hash for reconciliation; they are not automatically resubmitted.
Storage model#
| State family | Examples | Location |
|---|---|---|
| Identity and authorization | Accounts, session hashes, grants, client registrations, OAuth code/resource and refresh families | Private application SQLite |
| Social | Posts, reactions, follows, communities, memberships, moderation and notification records | Private application SQLite |
| Algorithms and commerce | Formula versions, feeds, invoices, entitlements, alerts and activity attribution | Private application SQLite |
| Execution | Quotes, plans, submitted hashes, receipts and reconciled state | Private application SQLite |
| Creator economics | Creator tokens, launch identities, vault bindings, policy and receipt-backed activity | Private application SQLite plus contracts |
| Market inventory | Tokens, pools, collector cursors and observed prices | Separate persistent market-universe database and caches |
| Media | Original upload staging, job state and encoded derivatives | Private state directory; controlled delivery handlers |
| Public artwork | Provenance records and immutable small derivatives | Public assets; private source-to-file cache map |
| Financial truth | Token balances, transfers, venue positions and revenue-vault reserves | Monad canonical chain state |
SQLite WAL supports this application's threaded reads and transactional writes. This release is a single application deployment, not a sharded, multi-region social database. A deployment must preserve its database and media volumes across restarts.
Concurrency and work isolation#
- HTTP threads handle bounded requests; network and JSON helpers impose limits and timeouts.
- Market collectors run background refreshes with provider-specific concurrency, batching, backoff and cached snapshots.
- Ranking uses two admitted subprocess slots; each process has CPU, address-space, file-size and descriptor limits.
- The separate media worker claims durable jobs and produces bounded derivatives. It does not inherit wallet or provider-signing credentials.
- The optional MetaMask worker can produce the application's predefined identity signature for an already paired Guard wallet. It cannot accept arbitrary messages or financial commands from the client.
The AST evaluator reduces exposed capabilities; it is not a generic hostile-code VM or hardware isolation boundary. The Python server and workers still depend on the host operating system's security.
Trust and ownership#
| Boundary | Enforced by | What remains trusted |
|---|---|---|
| Account identity | Challenge ownership verification or verified Privy claims | Wallet/auth provider and application account mapping |
| Agent actions | Scopes, owned actor, token audience, expiry and revocation | The authorized external client within its grant |
| Paid access | Exact finalized payment evidence and versioned entitlement | Application database and content-serving logic |
| Transaction preparation | Contract pins, canonical calldata, recipient/amount checks and preflight | RPC correctness, venue semantics and wallet confirmation |
| Settlement | Canonical block/finality and asset/position reconciliation | Monad, RPC provider and underlying venue |
| Revenue reserve | Immutable assets/routes and bounded onchain policy | Contract correctness, creator policy and pool/oracle conditions |
| Market reference | Provider identity, freshness and bounded normalization | Data source; a reference is never a fill |
The application operator controls offchain availability, moderation and content access. Venue operators control their contracts and market availability. Creators control the policy permissions their vault exposes and, for a custom community launch, receive the LP position. A community token is not automatically equity, a claim on all trading fees or governance rights.
Client delivery#
The web client uses JavaScript modules and a shared application state. A content-addressed JS/CSS bundle collapses the startup request graph. app_assets.py validates source/output hashes before serving a bundle; stale manifests fall back to source modules. Optional Privy and chart code load separately.
The Android client uses Kotlin and Jetpack Compose over the same HTTPS APIs. It renders markets, Swipe, token charts, social feeds, discovery, algorithm previews and profiles natively. Privy's Android SDK provides email/Google authentication and embedded-wallet signing; the server validates identity tokens and the exact wallet challenge. Configured Reown Sign connections open installed external wallets directly and bind each response to its session, account, method, request ID and chain. Both wallet SDKs initialize once per application process. Sessions and pending order references are encrypted with Android Keystore. Native order sheets prepare a fresh server plan and call the selected wallet SDK. A transaction hash is persisted before reconciliation, and recovery never sends another transaction. The short-lived S256 browser consent exchange remains a fallback for account pairing. See Android for these boundaries.
Discover and meme-market routes start with a compact identity/navigation bootstrap and read-ahead content requests. Catalog requests coalesce; transient caches are viewer-sensitive. Lists render progressively while retaining the full fetched inventory for search and subsequent rows. Small WebP artwork is generated only from an allowlisted public nad.fun source, outside request handling.
Motion and in-app trade sheets share the same account and financial-busy boundaries. Visual transitions do not bypass quote expiry, entitlement checks, wallet approval or reconciliation.
System limits#
Deployment pins are snapshots, not permanent attestations: upgrades can require review and replacement pins. Oracle access, keepers, partner permissions and liquidity can leave listed venues unavailable for execution. The source includes unsigned builders and fixture verification; it does not establish that every token or perpetual market has completed a funded mainnet round trip.
See Trading, Verification and Security for the practical limits of each layer.
Continuous launch and pool ingestion#
launch_ingestion.py separates recent launches, historical page collection, contract verification, finalized events and visible-token refresh. Provider candidates remain private to the queue until same-block Multicall reads verify address, name, symbol, decimals and lifecycle, plus the V2 creator and curve/pool/fee relationships. V1 creator profiles retain official API provenance. The index uses the official nad.fun V1/V2 contracts on Monad mainnet.
- Recent launches are requested every 30 seconds; the unauthenticated API uses
one shared read budget. This cadence is a target, not a latency guarantee.
- An ascending history cursor commits only after a complete provider page is
queued. Restarting resumes the saved page. Failed candidates use bounded backoff and cannot prevent later candidates from being verified.
- Finalized creation events supply a second discovery path. Graduation updates
the shared Spot asset registry; an indexed pool is not an executable quote.
- Every supported V2 pool factory is checked in a separate one-minute loop.
Large factory backfills are bounded per venue so price refresh stays independent.
- Both launch surfaces expose bounded server pages and full-registry search.
The UI initially renders 24 rows/cards and loads additional pages on demand.
- Visible tokens receive separate read-only refreshes. Bonding-curve reserve
ratios use a fresh quote-asset USD reference and its timestamp; these are display references, not order prices. Orders still use fresh router quotes.
- Image collection covers the persisted launch registry, not just page one.
Original public artwork stays available while small first-party WebP derivatives are prepared outside HTTP workers. Generated artwork is stored in the writable runtime state directory; immutable, strictly named WebP delivery works while the application directory stays read-only. Private user uploads keep their authentication and no-store behavior. Provider errors retain identity and metadata, while expired prices are hidden.
ingestion reports provider totals, history progress, queued/retrying candidates and verified local coverage independently. Completion of provider pagination never means every candidate passed contract verification. API pagination can change after moderation/deletion; periodic history sweeps reconcile it. No claim of complete coverage of every Monad venue or all executable liquidity is made.
Wallet asset snapshots and transfers#
wallet_assets.py keeps a bounded cache per exact wallet and coalesces concurrent reads. A cold request returns immediately; the background scan publishes native and priority token balances before reading the rest of the registered catalog in bounded Multicall batches at one block. Failed token reads preserve a flagged previous balance. Cached market references supply valuation without waiting for another market-data fetch. Unpriced assets remain unknown, and the displayed 24-hour number is the price change of the current token basket, not realized account profit or a deposit-adjusted return. Wallet-token totals exclude venue collateral and open perpetual positions.
wallet_transfer.py builds unsigned MON and ERC20 sends for the authenticated account's linked wallet. Sends carry an exact recipient and amount, no token allowance. Preparation checks token code, precision, balance, simulation and MON gas. Independent token checks and common balance/gas checks use separate bounded JSON-RPC read batches. Responses must retain exact request IDs; failed simulations, missing fees and changed contract code still stop before signing. No executable transaction is cached across users. The web and Android clients compare the prepared recipient, amount and calldata against the submitted form. Existing durable execution records keep the transaction hash and reconcile canonical inclusion, finality and the exact ERC20 Transfer event. A finalized send invalidates snapshots older than its block, including a previously started balance scan. Recovery checks that hash and never automatically sends again.
token_holders.py reads the official nad.fun holder endpoint into a bounded, coalesced cache. Holder snapshots render independently and show source and observation time. Rows keep wallet identity and compact token quantities in separate columns; the exact quantity remains in the accessible label and tooltip. Tokens without an available holder index are identified as unavailable; wallet balances, perp positions and a global token-holder list are separate data sets.
nad_chart_cache.py coalesces chart reads by token and interval, persists up to 128 snapshots and refreshes them with two bounded workers. Fresh snapshots bypass upstream work; snapshots older than 90 seconds can render with a stale flag while refreshing, up to a 15-minute display limit. Cold reads wait only on their own key. The nad.fun scheduler prioritizes interactive charts over recent background discovery requests while preserving one upstream request at a time, 7.5-second spacing, Retry-After and aging for background requests. The browser starts history alongside token identity, reuses recent chart data on reopen and ignores responses for a closed panel or superseded interval. These snapshots are display data and never execution quotes.
The browser loads the pinned TradingView Lightweight Charts 5.2.1 distribution from Rally's own origin, separately from the main application bundle. This avoids another CDN connection when opening a chart. The original distribution and Apache license are retained in assets/lightweight-charts-5.2.1.mjs and assets/lightweight-charts-LICENSE.txt; chart attribution stays enabled.
For Privy embedded wallets, the web client uses the SDK's useSendTransaction with confirmation UI disabled and an explicit wallet address. The native SDK signs through its authenticated device session. Each transaction still receives its own cryptographic signature; this does not reuse a signature, create a server signer or bypass SDK authentication. External wallets retain their own approval requirements.
The wallet interface paints a cached snapshot immediately and changes balance text without replacing action controls. Send refreshes its asset picker independently while preserving the selected token and amount. Deposit exposes the authenticated address before its QR arrives and reserves QR space, so loading does not move the copy control. Wallet sheets follow the visual viewport and keep their action footer outside the scrolling body. UI animation uses short opacity/transform changes with reduced-motion support; these optimizations do not change wallet authorization or venue settlement.
Social trading loop#
social_loop.py enriches real posts with immutable observed signals and opt-in receipt-backed fill cards. token_benefits.py verifies short-lived finalized holdings proofs and extends existing feed entitlement and checkout logic. algorithm_league.py persists common forward-only snapshots and runs entered versions through the existing isolated scorer. push_delivery.py maintains an account/device outbox and rechecks eligibility before provider delivery. Its independent worker keeps push latency outside market collection and financial reconciliation. Web and native Android use the same APIs and existing token trade sheets.
Data, authority and provider configuration
Public chart delivery#
Indexed Monad tokens use CoinGecko/GeckoTerminal provider-rendered embeds identified by the exact token contract, including tokens on the quote side of a pool. Spot candle embeds default to 1 minute and expose 15-minute, 1-hour and 4-hour intervals. Listed spot reference assets use the official CoinGecko widget. These components load directly from the provider and have no wallet or order authority. Third-party scripts are isolated from the web application's origin and from native wallet bridges.
The Price line fallback performs a fresh, bounded public read through /api/market-chart?fresh=1. native/market_transport.cpp supplies an optional C++/libcurl HTTPS receiver with certificate validation, an exact provider allowlist, redirect rejection, response-size and deadline limits, and reusable connections. native_market_transport.py validates the result and observes provider cooldowns. market_data.py checks the requested token against provider metadata and preserves actual observation times and closes. This path neither reads nor writes Rally's chart snapshot cache; Python transport remains available on hosts without the optional shared library. Financial quotes and execution preflight remain separate.
Perpetual and prediction price history uses a compact line renderer rather than a provider widget. Fresh LeverUp history retains its Monad oracle identity; supported CoinGecko history remains explicitly a spot reference. The server filters the requested historical window and reduces display payloads to at most 320 actual observations by retaining endpoints and time-bucket extremes. Quiet intervals are not filled with generated prices. The web period selector updates the existing renderer and observers; Compose uses a retained drawing path with actual-time scrubbing. Unmigrated curve history remains on its supported nad.fun path.
Live chart path#
market_ticks.py exposes a bounded cursor feed at /api/market-ticks. Viewed exact Monad market identities subscribe to existing Perpl, venue, spot-pool and verified nad.fun observations; opening additional charts does not multiply provider HTTP reads. Samples contain an observation ID, time and price, with source and epoch attached to the response. Duplicate and older observations are discarded. A changed source or pool starts a new epoch. Buffers retain 240 observations per active market, expire after ten minutes without readers, and are capped at 256 markets.
live-price.js draws retained Canvas paths and animates only the newest segment using requestAnimationFrame. Android uses the same canonical sample semantics with retained Compose paths and withFrameNanos. Live is time-based; Ticks is observation-order-based. The 220 ms transition affects display geometry only. Historical CoinGecko/GeckoTerminal references remain separate from venue marks; prices never become fabricated fills or return records. Hidden views cancel polling and motion. Reduced-motion settings skip animated transitions.
Hourly OHLC path#
/api/market-chart?asset=…&kind=spot|perps&period=1D|7D|1M&interval=1h&fresh=1 reads bounded, provider-supplied hourly OHLC. Perpl uses its public candle API at resolution 3600, scaling prices with the matching mainnet market configuration. LeverUp retains its Monad oracle identity. Spot uses the exact Monad pool and requested base or quote token through GeckoTerminal's hourly OHLCV endpoint. MON pool references explicitly identify WMON.
The normalizer rejects off-hour timestamps, future data, nonfinite or inconsistent OHLC, and mismatched identities or resolution. It preserves full hourly bars for the selected window, including extrema and gaps, capped at 721 bars for a month. Hourly data is not reconstructed from price ticks and does not use the chart snapshot cache. The web renderer retains its chart instance and updates changed bars; Android retains only visible geometry. Foreground hourly views refresh every 30 seconds, retaining the last known bars with a delayed label on failure. Display refresh is separate from trade execution and finality.