{"_id":"@alea-drand/sdk","_rev":"2-b87e13e07686ff5b677b48405adacda1","name":"@alea-drand/sdk","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@alea-drand/sdk","version":"0.1.0","keywords":["solana","drand","randomness","verifiable-randomness","vrf","bls","bn254","cryptography","web3"],"license":"Apache-2.0","_id":"@alea-drand/sdk@0.1.0","maintainers":[{"name":"aaronisafk","email":"aaron@aaronkruger.com"}],"homepage":"https://alea.so","bugs":{"url":"https://github.com/alea-drand/alea/issues"},"dist":{"shasum":"b931dcfe451b66a3d78491e554e5607416e8a659","tarball":"https://registry.npmjs.org/@alea-drand/sdk/-/sdk-0.1.0.tgz","fileCount":36,"integrity":"sha512-mNXEJMAsY4MbuhCS+BnYOdNtwFqZFOb2DHfNUwKZgmAr1pVYiBC7rYWK7e0J+LjPy8n8DL+Np2irLuRlXpE5Ow==","signatures":[{"sig":"MEUCIQCVOYRT9w+IlW7JK8rqNWkVLRvCx60caxrHViqdTPF+IQIgKYRhpCt5B/aV6Ufl0plbGrcawxPWzy30ac8x+9pqisI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alea-drand%2fsdk@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":98578},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=18"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"gitHead":"8cf30849b13e33335b0c5f7782807ce3d635baed","scripts":{"test":"vitest run","build":"tsc -p tsconfig.json","prebuild":"npm run generate-idl","test:devnet":"ALEA_DEVNET_TESTS=1 vitest run tests/devnet_integration.test.ts","generate-idl":"node scripts/generate-idl-ts.mjs","prepublishOnly":"npm run build"},"_npmUser":{"name":"aaronisafk","email":"aaron@aaronkruger.com"},"repository":{"url":"git+https://github.com/alea-drand/alea.git","type":"git","directory":"sdk/typescript"},"_npmVersion":"10.8.2","description":"TypeScript SDK for Alea — drand BN254 randomness verification on Solana","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1","typescript":"^5.4","@types/node":"^25","@solana/web3.js":"^1.95.0","@coral-xyz/anchor":"^0.30.1"},"peerDependencies":{"@solana/web3.js":"^1.95.0","@coral-xyz/anchor":"^0.30.1","@solana/wallet-adapter-base":"^0.9.0"},"peerDependenciesMeta":{"@solana/wallet-adapter-base":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/sdk_0.1.0_1776657323967_0.38084028644876855","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alea-drand/sdk","version":"0.2.0","license":"Apache-2.0","description":"TypeScript SDK for Alea — drand BN254 randomness verification on Solana","homepage":"https://alea.so","repository":{"type":"git","url":"git+https://github.com/alea-drand/alea.git","directory":"sdk/typescript"},"bugs":{"url":"https://github.com/alea-drand/alea/issues"},"engines":{"node":">=18"},"keywords":["solana","drand","randomness","verifiable-randomness","vrf","bls","bn254","cryptography","web3"],"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"scripts":{"generate-idl":"node scripts/generate-idl-ts.mjs","prebuild":"npm run generate-idl","build":"tsc -p tsconfig.json","prepublishOnly":"npm run build","test":"vitest run","test:devnet":"ALEA_DEVNET_TESTS=1 vitest run tests/devnet_integration.test.ts"},"peerDependencies":{"@solana/web3.js":"^1.95.0","@coral-xyz/anchor":"^0.30.1","@solana/wallet-adapter-base":"^0.9.0"},"peerDependenciesMeta":{"@solana/wallet-adapter-base":{"optional":true}},"devDependencies":{"typescript":"^5.4","vitest":"^2.1","@types/node":"^25","@solana/web3.js":"^1.95.0","@coral-xyz/anchor":"^0.30.1"},"_id":"@alea-drand/sdk@0.2.0","gitHead":"354e7f0072fbddd3a76dc61d59523460abfa1616","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-3IgbmbXt0SL2uqDaElNgeGaYiRaTlHQ/DfM/2HJECPMrh7Zj0usbgF7Mpy1Fab1Amlsei6dJVtyh8gG0A4pLSw==","shasum":"5de2d20cda8451c6c301c8f3be171d28a9be4396","tarball":"https://registry.npmjs.org/@alea-drand/sdk/-/sdk-0.2.0.tgz","fileCount":36,"unpackedSize":106488,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@alea-drand%2fsdk@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDUbJtVJG5gCOwdNAqh85GnjgAN2AlbwMoNdszRRGyfUwIgEg5N0/hFhFMO/HN+SL+0Ow2NXmlbRIGKsTppbeXp9DU="}]},"_npmUser":{"name":"aaronisafk","email":"aaron@aaronkruger.com"},"directories":{},"maintainers":[{"name":"aaronisafk","email":"aaron@aaronkruger.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/sdk_0.2.0_1776670659036_0.12712373572599223"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-20T03:55:23.849Z","modified":"2026-04-20T07:37:39.501Z","0.1.0":"2026-04-20T03:55:24.127Z","0.2.0":"2026-04-20T07:37:39.173Z"},"bugs":{"url":"https://github.com/alea-drand/alea/issues"},"license":"Apache-2.0","homepage":"https://alea.so","keywords":["solana","drand","randomness","verifiable-randomness","vrf","bls","bn254","cryptography","web3"],"repository":{"type":"git","url":"git+https://github.com/alea-drand/alea.git","directory":"sdk/typescript"},"description":"TypeScript SDK for Alea — drand BN254 randomness verification on Solana","maintainers":[{"name":"aaronisafk","email":"aaron@aaronkruger.com"}],"readme":"# @alea-drand/sdk\n\nVerified drand randomness on Solana in one call.\n\n> **v0.1.x is DEVNET only — mainnet deployment pending.** Alea's program ID is cluster-agnostic; `DEVNET_PROGRAM_ID` and `MAINNET_PROGRAM_ID` point to the same bytes. Using a mainnet `Connection` before mainnet deploys fails at the Solana RPC layer (\"program not found\"). Read [CAVEATS.md](CAVEATS.md) before production use.\n\n## Install\n\n```bash\nnpm install @alea-drand/sdk @solana/web3.js @coral-xyz/anchor\n```\n\n`@solana/web3.js` and `@coral-xyz/anchor` are **peer dependencies** — consumers install them directly so your bundle has one copy of each (prevents class-identity issues with `PublicKey` instances across mismatched versions).\n\nRequires **Node 18+** and **ESM**. This package is ESM-only; use `import`, not `require()`. Works in browsers with any modern bundler (Vite, webpack 5, Next.js App Router, esbuild) — no polyfills needed.\n\n## Quick Start — Browser (user pays, via wallet-adapter)\n\n```typescript\nimport { getVerifiedRandomness } from \"@alea-drand/sdk\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useConnection } from \"@solana/wallet-adapter-react\";\n\nfunction RandomnessButton() {\n  const { connection } = useConnection();\n  const wallet = useWallet(); // WalletContextState\n\n  async function draw() {\n    // User approves a single popup. Returns 32 bytes of verified randomness.\n    const randomness = await getVerifiedRandomness({\n      connection,\n      signer: wallet,\n    });\n    console.log(`Randomness: ${Buffer.from(randomness).toString(\"hex\")}`);\n  }\n\n  return <button onClick={draw}>Draw</button>;\n}\n```\n\n## Quick Start — Server (developer pays, via Keypair)\n\n```typescript\nimport { getVerifiedRandomness } from \"@alea-drand/sdk\";\nimport { Keypair, Connection } from \"@solana/web3.js\";\nimport { readFileSync } from \"node:fs\";\n\nconst keypair = Keypair.fromSecretKey(\n  new Uint8Array(JSON.parse(readFileSync(process.env.KEYPAIR_PATH!, \"utf8\"))),\n);\nconst connection = new Connection(\"https://api.devnet.solana.com\", \"confirmed\");\n\nconst randomness = await getVerifiedRandomness({\n  connection,\n  signer: keypair,\n});\nconsole.log(Buffer.from(randomness).toString(\"hex\"));\n```\n\n## Testing on Devnet\n\nBefore integrating into prod, run the SDK against live devnet:\n\n```bash\n# 1. Install the SDK\nnpm install @alea-drand/sdk @solana/web3.js @coral-xyz/anchor\n\n# 2. Generate or load a Solana devnet keypair\nsolana-keygen new --outfile ~/.config/solana/alea-test.json\n\n# 3. Fund it with devnet SOL\nsolana airdrop 1 ~/.config/solana/alea-test.json --url devnet\n# Backup faucet if solana-labs is dry: 76 Devs Discord / LamportDAO\n\n# 4. Run the quick-start above against devnet\n# Expected: 32 bytes of randomness (hex) printed in under 10 seconds\n```\n\nLive devnet program: [`ALEAydzHd4cN2EWcdHKp4hehAE4B88b16gqVtVqsck2U`](https://explorer.solana.com/address/ALEAydzHd4cN2EWcdHKp4hehAE4B88b16gqVtVqsck2U?cluster=devnet).\n\n## API Reference\n\n### `getVerifiedRandomness(options)`\n\nHigh-level entry point. Fetches a drand beacon, submits a Solana transaction, and returns 32 bytes of verified randomness.\n\n```typescript\nasync function getVerifiedRandomness(options: {\n  connection: Connection;\n  signer: Keypair | Wallet;         // Keypair (Node) or wallet-adapter WalletContextState\n  programId?: PublicKey;            // defaults to DEVNET_PROGRAM_ID\n  round?: bigint;                   // defaults to latest available round\n  computeUnits?: number;            // defaults to 900_000\n}): Promise<Uint8Array>             // 32 bytes\n```\n\n### `verifyDrandBeacon(args)`\n\nLower-level IDL-based submission. Use when you have a round + signature fetched out-of-band.\n\n```typescript\nasync function verifyDrandBeacon(args: {\n  connection: Connection;\n  signer: Keypair | Wallet;\n  round: bigint;                    // [1, 2^64-1]\n  signature: Uint8Array;            // exactly 64 bytes, G1 uncompressed x||y\n  programId?: PublicKey;\n  computeUnits?: number;\n}): Promise<Uint8Array>             // 32 bytes\n```\n\n### `verifyDrandBeaconWithMeta(args)` — since 0.2.0\n\nSame as `verifyDrandBeacon` but returns the full tx metadata alongside the randomness. Use when you need to link to Explorer, report compute units / fees, or surface the slot to end users.\n\n```typescript\ntype VerifyMeta = {\n  randomness: Uint8Array;           // 32 bytes\n  tx: string;                       // base58 Solana tx signature\n  slot: number;                     // confirmed-commitment slot\n  computeUnitsUsed: number;         // from tx meta.computeUnitsConsumed\n  costLamports: number;             // from tx meta.fee\n};\n\nasync function verifyDrandBeaconWithMeta(args: {\n  connection: Connection;\n  signer: Keypair | Wallet;\n  round: bigint;\n  signature: Uint8Array;\n  programId?: PublicKey;\n  computeUnits?: number;\n  signal?: AbortSignal;\n  skipPreflight?: boolean;\n}): Promise<VerifyMeta>\n```\n\n### `getVerifiedRandomnessWithMeta(options)` — since 0.2.0\n\nOne-shot variant of `getVerifiedRandomness` that returns the drand round + drand signature + full on-chain meta in a single call. Used by the [alea.so](https://alea.so) public relayer; useful anywhere you want to display end-to-end provenance.\n\n```typescript\nasync function getVerifiedRandomnessWithMeta(options: {\n  connection: Connection;\n  signer: Keypair | Wallet;\n  programId?: PublicKey;\n  round?: bigint;\n  computeUnits?: number;\n  signal?: AbortSignal;\n  skipPreflight?: boolean;\n}): Promise<VerifyMeta & {\n  round: bigint;                    // drand round that was verified\n  signature: Uint8Array;            // drand sig (G1, 64 bytes)\n}>\n```\n\n### `fetchBeacon(round?)`\n\nFetches a drand beacon without Solana interaction. Uses 5-endpoint fallback with 3 retries, validates returned round matches requested.\n\n> **WARNING:** Returns UNVERIFIED data from the drand API. Use `getVerifiedRandomness()` for trustless randomness.\n\n```typescript\nasync function fetchBeacon(round?: bigint): Promise<{\n  round: bigint;\n  signature: Uint8Array;\n  unverifiedRandomness: string;  // hex — NOT on-chain verified\n}>\n```\n\n### `getCurrentRound()` / `getRoundAt(timestamp)`\n\nCompute drand round numbers (canonical `bigint`). `getCurrentRound` uses `Date.now()`.\n\n### `isRoundRecent(round, config, clock, maxAgeSeconds)`\n\nPure function — symmetric with Rust CPI `is_round_recent`. Call before `getVerifiedRandomness` to cheaply reject obvious replay attempts.\n\nFuture rounds (`roundTs > clock.unixTimestamp`) return `true` — this matches Rust's saturating-sub semantics so the TS and Rust checks agree at the round-emission edge.\n\n```typescript\nfunction isRoundRecent(\n  round: bigint,\n  config: { genesisTime: bigint; period: bigint },\n  clock: { unixTimestamp: bigint },\n  maxAgeSeconds: bigint,\n): boolean\n```\n\n### `createVerifyInstruction(options)`\n\nRaw instruction builder for advanced use (multi-ix composition, versioned transactions). Use `verifyDrandBeacon` for the common path.\n\n```typescript\nfunction createVerifyInstruction(options: {\n  round: bigint;\n  signature: Uint8Array;\n  payer: PublicKey;                 // signer — included in keys automatically\n  programId?: PublicKey;\n}): TransactionInstruction\n```\n\n### `getConfigAddress(programId?)`\n\nDerives the Alea Config PDA address (seeds `[b\"config\"]`).\n\n### Constants\n\n```typescript\nDRAND_CHAIN_HASH    // evmnet chain hash (hex, 64 chars)\nDRAND_GENESIS_TIME  // 1727521075 (Unix seconds)\nDRAND_PERIOD        // 3 (seconds per drand round)\nDRAND_ENDPOINTS     // 5 fallback URLs (can be replaced for custom trust)\nDEVNET_PROGRAM_ID   // ALEAydzHd4cN2EWcdHKp4hehAE4B88b16gqVtVqsck2U\nMAINNET_PROGRAM_ID  // === DEVNET_PROGRAM_ID; cluster comes from your Connection\n```\n\n### `AleaError`\n\n```typescript\nclass AleaError extends Error {\n  readonly code: number;\n}\n```\n\n### Error Codes\n\nOn-chain errors (from `alea-verifier`) + SDK-side errors (6100+):\n\n| Code | Name | Meaning |\n|------|------|---------|\n| 2001 | `ConstraintHasOne` | Signer is not the config authority (Anchor framework) |\n| 3010 | `AccountNotSigner` | Account passed without signature (Anchor framework) |\n| 6000 | `InvalidSignature` | BLS pairing check failed — wrong sig for this round |\n| 6001 | `InvalidG1Point` | Signature bytes are not on the BN254 G1 curve |\n| 6002 | `RoundZero` | Round must be > 0 (drand genesis sentinel) |\n| 6003 | `InvalidFieldElement` | **Reserved (unreachable in v1)** |\n| 6004 | `NoSquareRoot` | SVDW exhausted candidates (infrastructure failure) |\n| 6005 | `InvalidG2Point` | **Reserved (unreachable)** |\n| 6006 | `PairingError` | alt_bn128_pairing syscall failed (infrastructure) |\n| 6007 | `WrongChainHash` | `Config.chain_hash` does not match evmnet |\n| 6008 | `WrongPubkey` | `Config.pubkey_g2` does not match evmnet (or `alea-sdk` owner check) |\n| 6009 | `ReturnDataMissing` | **Reserved (unreachable under ADR 0030)** |\n| 6010 | `InvalidGenesisTime` | `Config.genesis_time` mismatch |\n| 6011 | `InvalidPeriod` | `Config.period` mismatch |\n| 6012 | `UnauthorizedInit` | initialize signer is not the upgrade authority |\n| **6100** | `DrandFetchFailed` | **SDK** — all drand endpoints failed after retries |\n| **6101** | `DrandRoundMismatch` | **SDK** — endpoint returned a different round than requested (possible compromise) |\n| **6102** | `InvalidInput` | **SDK** — input validation failed at SDK boundary |\n\nThe `ERRORS` map is exported and frozen at module load (`Object.freeze` + `Readonly<Record>`).\n\n## Consumer Responsibility\n\nThis SDK is for off-chain consumers. If you're building an on-chain program that CPIs to Alea, see the [`alea-sdk` Rust crate README](https://crates.io/crates/alea-sdk) for the **mandatory** `seeds::program` + `is_round_recent` constraints. Omitting either ships an exploitable program.\n\n## Program IDs\n\n| Network | Program ID |\n|---------|-----------|\n| Devnet  | `ALEAydzHd4cN2EWcdHKp4hehAE4B88b16gqVtVqsck2U` |\n| Mainnet | Same vanity ID; program not yet deployed on mainnet. Mainnet `Connection` will fail at the RPC layer with \"program not found\" until deploy. |\n\n## Zero Telemetry\n\n`@alea-drand/sdk` sends **no analytics, no telemetry, no phone-home**. Your only network calls are:\n- Drand API endpoints (for beacon fetch, 5 URLs with fallback — fully replaceable via `DRAND_ENDPOINTS` override)\n- Solana RPC endpoint of your choice (passed in as `connection`)\n\nNothing else. Verify by inspecting [`src/client.ts`](https://github.com/alea-drand/alea/blob/main/sdk/typescript/client.ts) and [`src/drand.ts`](https://github.com/alea-drand/alea/blob/main/sdk/typescript/drand.ts).\n\n## How to Verify This Package\n\nEvery release carries an [npm provenance attestation](https://docs.npmjs.com/generating-provenance-statements) signed by Sigstore + the GitHub Actions publish workflow. Confirm before use:\n\n```bash\nnpm audit signatures\n```\n\nOr visit [`@alea-drand/sdk` on npm](https://www.npmjs.com/package/@alea-drand/sdk) and look for the green \"Provenance\" badge. No badge → do not install.\n\n## Community & Support\n\n- **Bugs / feature requests:** [GitHub Issues](https://github.com/alea-drand/alea/issues)\n- **Questions / integrations:** [GitHub Discussions](https://github.com/alea-drand/alea/discussions)\n- **Security reports:** [GitHub Security Advisory](https://github.com/alea-drand/alea/security/advisories/new) (private)\n- **GitHub:** [alea-drand/alea](https://github.com/alea-drand/alea)\n\n## License\n\nApache 2.0 — see [LICENSE](LICENSE).\n","readmeFilename":"README.md"}