Cascade from Injective
How any Injective dApp uses Cascade as its permanent storage layer.
This is the reusable primitive the rest of this section builds on. The shape is the same whether you're shipping an NFT marketplace, a DAO, a derivatives venue, or prediction markets: Injective holds state and pointers, Cascade holds the bytes, and an action_id ties them together.
The action_id pointer pattern
Cascade's unit of storage is an action. A MsgRequestAction on Lumera registers an artifact's metadata and returns a numeric action_id. Once stored, the artifact is retrievable forever by GET /download/<action_id> against any cascade-aware gateway. See How Cascade Works for the full lifecycle.
Inside your Injective contract, every Cascade artifact is referenced by a single value: that action_id, stored as a String in CosmWasm state.
That single String is the entirety of the cross-chain coupling. The contract enforces only that the field is non-empty. It does not enforce that the artifact resolves, because it cannot read across chains synchronously. Resolution and integrity are read-time concerns, handled by an indexer or the frontend, off the consensus path.
Two ways to write to Cascade
There are two production patterns for getting bytes onto Cascade from an Injective context. They are not mutually exclusive. Many deployments use both, choosing per artifact type.
Pattern A: user-signed (the user signs the Lumera tx)
The user holds both an inj1… and a lumera1… address in their wallet. The dApp signs two transactions for a single action: first a Lumera tx that uploads to Cascade and returns an action_id, then an Injective tx that submits that action_id to your contract.
Choose this when provenance matters where every artifact carries a direct, on-chain link back to the user's own Lumera key. The cost is one extra signature and a small ulume fee the user pays.
Pattern B: server-signed via cascade-api (recommended)
A backend holds a funded Lumera signing key and uploads on behalf of users. The user signs only the Injective transaction, the upload is a server-side HTTP call. The simplest way to run this is to forward to the Lumera-operated cascade-api at api.lumera.help, which holds the Lumera key, signs MsgRequestAction, and pays the ulume fee for you.
Choose this when UX matters: one signature instead of two, no Lumera address required for the user, and a single rate-limited signing key you can monitor and rotate. This is the path the Inscribe reference implementation uses end-to-end, and it's the fastest way to ship.
The trade-off is provenance: every artifact is signed by the operator's key, not the user's. If you need on-chain proof that user X uploaded artifact Y, record the binding on Injective by having your contract store (user, action_id) so the pairing is the on-chain proof rather than the Lumera signature.
The server-side forward, in detail
A minimal backend just streams the multipart body to the cascade-api and returns its response. The browser sends the file, the backend forwards it under its bearer token, and the cascade-api signs MsgRequestAction, broadcasts on Lumera, and replies with action_id + tx_hash + block_height + task_id (typically 6–15s). The backend relays that response back to the browser, which then submits the action_id on Injective.
There is no IBC round-trip on this path. The cascade-api signs on Lumera directly. The bearer token is your operator key, scoped by the cascade-api's quota.
If you'd rather hold the Lumera key yourself instead of using the hosted cascade-api, the equivalent with @lumera-protocol/sdk-js is:
Pattern C: contract-driven via ICA (advanced)
There is a third pattern, where a CosmWasm contract on Injective writes to Cascade autonomously over IBC: the contract packs a MsgRequestAction, sends it across the IBC channel to a registered Interchain Account on Lumera, and the ICA executes it.
This is not on the hot path, and you should not reach for it first. An earlier prototype of the reference implementation dispatched Cascade writes this way via cw-ica-controller and waited for a Hermes relayer. Reserve Pattern C for canonical, contract-only artifacts where there is genuinely no user in the loop and the contract itself must be the signer.
Rule of thumb: use A when on-chain provenance to the user's key is required, B for everything else (and as your default), and consider C only for contract-deterministic canonical writes where IBC latency is acceptable.
Reading from Cascade
Reading is independent of the write pattern. Once an artifact has an action_id, anyone can fetch it, with no authentication, from any cascade-aware gateway:
| Endpoint | Auth | Returns |
|---|---|---|
GET https://api.lumera.help/download/{action_id} | none | raw bytes, Content-Type sniffed |
GET <your-backend>/cascade/{action_id} | your choice | raw bytes, ideally with an immutable cache header |
| Lumera SDK | wallet signature | reconstructed bytes streamed |
Wiring the pointer into your contract
The Cascade integration on the CosmWasm side is small. Accept the action_id as an opaque String at write time and validate only that it is non-empty:
From there, two conventions make the data easy to consume: emit the CID as a wasm event attribute so an indexer can pick it up, and expose CIDs in queries so a read-only client can drive a viewer without one. That is the essential contract-side surface: the contract does not download from Cascade, does not verify the artifact, does not even care that it's a Cascade CID. It holds a string; verification and resolution belong to the indexer and the frontend.
Build your own integration has the code for both, plus two production conventions: modelling the workflow as a state machine, and locking funds atomically with the CID write.
Funding the writes
Cascade writes are paid in ulume. Which account pays depends on the pattern:
| Account | What it pays for | How to fund |
|---|---|---|
The user's lumera1… (Pattern A) | Each MsgRequestAction they sign | Faucet |
| Your service's Lumera key (Pattern B) | Each MsgRequestAction your service signs on behalf of users | Faucet |
The ICA's lumera1… (Pattern C) | Each Cascade write the contract triggers | Top up over IBC from your Injective treasury |
A Cascade upload costs roughly 10–15k ulume on testnet.