Cascade
Guides

Node.js Environment

Using the Lumera JS SDK in Node.js for server-side applications, CLI tools, and backend services.

Overview

The @lumera-protocol/sdk-js SDK works in both browser and Node.js environments. This guide covers the Node.js setup for server-side applications, CLI tools, and backend services where programmatic signing with DirectSecp256k1HdWallet replaces browser wallet extensions.

Setup

npm install @lumera-protocol/sdk-js @cosmjs/proto-signing @cosmjs/stargate @cosmjs/amino

Programmatic Signing

In Node.js there is no Keplr or Leap wallet. Instead, use DirectSecp256k1HdWallet for transaction signing and build a custom signer that supports signArbitrary (ADR-036) for Cascade's off-chain signatures:

signer.ts
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { Secp256k1HdWallet, makeSignDoc as makeAminoSignDoc } from "@cosmjs/amino";
 
export async function createNodeSigner(mnemonic: string) {
  const directWallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "lumera" });
  const aminoWallet = await Secp256k1HdWallet.fromMnemonic(mnemonic, { prefix: "lumera" });
  const [account] = await directWallet.getAccounts();
 
  const signer = {
    getAccounts: () => directWallet.getAccounts(),
    signDirect: (addr: string, doc: any) => directWallet.signDirect(addr, doc),
    signAmino: (addr: string, doc: any) => aminoWallet.signAmino(addr, doc),
    async signArbitrary(chainId: string, signerAddress: string, data: string) {
      const signDoc = makeAminoSignDoc(
        [{
          type: "sign/MsgSignData",
          value: {
            signer: signerAddress,
            data: Buffer.from(data).toString("base64"),
          },
        }],
        { gas: "0", amount: [] },
        "",
        "",
        0,
        0
      );
      const { signature } = await aminoWallet.signAmino(signerAddress, signDoc);
      return { signed: data, signature: signature.signature, pub_key: signature.pub_key };
    },
  };
 
  return { signer, account };
}

Upload a File

upload.ts
import { createLumeraClient } from "@lumera-protocol/sdk-js";
import { createNodeSigner } from "./signer";
 
const { signer, account } = await createNodeSigner(process.env.LUMERA_MNEMONIC!);
 
const client = await createLumeraClient({
  preset: "testnet",
  signer: signer as any,
  address: account.address,
  gasPrice: "0.025ulume",
});
 
const file = await fs.promises.readFile("./my-document.pdf");
const expirationTime = String(Math.floor(Date.now() / 1000) + 24 * 60 * 60);
 
const result = await client.Cascade.uploader.uploadFile(new Uint8Array(file), {
  fileName: "my-document.pdf",
  isPublic: true,
  expirationTime,
  taskOptions: { pollInterval: 2000, timeout: 300000 },
});
 
console.log("Stored with action ID:", result.action_id);

Download a File

download.ts
import { createLumeraClient } from "@lumera-protocol/sdk-js";
import { createNodeSigner } from "./signer";
 
const { signer, account } = await createNodeSigner(process.env.LUMERA_MNEMONIC!);
 
const client = await createLumeraClient({
  preset: "testnet",
  signer: signer as any,
  address: account.address,
  gasPrice: "0.025ulume",
});
 
const stream = await client.Cascade.downloader.download("your-action-id");
const reader = stream.getReader();
const chunks: Uint8Array[] = [];
 
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  if (value) chunks.push(value);
}
 
const fileBytes = new Uint8Array(chunks.reduce((acc, c) => acc + c.length, 0));
let offset = 0;
for (const chunk of chunks) {
  fileBytes.set(chunk, offset);
  offset += chunk.length;
}
 
await fs.promises.writeFile("./downloaded-file.pdf", fileBytes);

Server-Side Alternatives

For server-side applications, you can also use the native Go SDK or Rust SDK.

Next Steps

Edit this page

On this page