{"_id":"@0xsingularity/web3.js","_rev":"3-1d86e14941c7846bb9e9d00eacf12c17","name":"@0xsingularity/web3.js","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@0xsingularity/web3.js","version":"0.1.0","keywords":["singularity","solana","svm","web3","blockchain"],"author":{"name":"bajpainaman","email":"naman@entropytoorder.xyz"},"license":"MIT","_id":"@0xsingularity/web3.js@0.1.0","maintainers":[{"name":"singularitywastaken","email":"namanbajpai23@gmail.com"}],"dist":{"shasum":"939dfd2739e7a922a523126e993ec5c1c4bfe5a0","tarball":"https://registry.npmjs.org/@0xsingularity/web3.js/-/web3.js-0.1.0.tgz","fileCount":30,"integrity":"sha512-m4R87lUw9SjoLecRZUHKjZ+8UWtVhbVZLOuf4zvd8w/rAioPfoPVLb9KV+jlYgB3TYZqVjZSvI6XJS5n2QSvEg==","signatures":[{"sig":"MEUCIQCTRT3GnQ+G3mHJ/s5phCQRPxL1h1gnfDh0LhwGS3WohQIgBZs5iAusxAbcq2nBqHETwri99njZkxnPOLZjM7odsKg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":78091},"main":"dist/index.js","type":"commonjs","types":"dist/index.d.ts","engines":{"node":">=18"},"scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"singularitywastaken","email":"namanbajpai23@gmail.com"},"_npmVersion":"9.2.0","description":"Thin drop-in wrapper around @solana/web3.js preconfigured for the Singularity chain.","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1.0","typescript":"^5.6.0","@types/node":"^20.0.0","@solana/web3.js":"^1.98.0"},"peerDependencies":{"@solana/web3.js":"^1.87.0"},"_npmOperationalInternal":{"tmp":"tmp/web3.js_0.1.0_1785608893456_0.15992307610333234","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@0xsingularity/web3.js","version":"0.1.1","keywords":["singularity","solana","svm","web3","blockchain"],"author":{"name":"bajpainaman","email":"naman@entropytoorder.xyz"},"license":"MIT","_id":"@0xsingularity/web3.js@0.1.1","maintainers":[{"name":"singularitywastaken","email":"namanbajpai23@gmail.com"}],"dist":{"shasum":"511c91d7a3fd9400b7190f83e1d41f8ec3b05ac5","tarball":"https://registry.npmjs.org/@0xsingularity/web3.js/-/web3.js-0.1.1.tgz","fileCount":30,"integrity":"sha512-sGbkfvt/7BVL9cOUeoyAYtWOs7ruAFG1ujYYOgLTCjDSw4rmWePkaSNL/D0KgV+pA0bknQgu9VMK8a4tHHnWMA==","signatures":[{"sig":"MEQCIEps1YHUOA8vVm1ld8UOW6SGgJmClJvYxQU900QinFUrAiApI69SdYbpmLYPCIgRN2H3kLS5QyCsSVBNyB1MhlMgHA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81154},"main":"dist/index.js","type":"commonjs","types":"dist/index.d.ts","browser":{"./dist/wallet.js":false},"engines":{"node":">=18"},"gitHead":"71cf28b057c29a5a0d6d5f1171454193f9a7737e","scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:756f1448-0300-489b-98ed-3d982bca67d2"}},"_npmVersion":"12.0.2","description":"Thin drop-in wrapper around @solana/web3.js preconfigured for the Singularity chain.","directories":{},"_nodeVersion":"22.23.1","_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.2.6","typescript":"^5.6.0","@types/node":"^20.0.0","@solana/web3.js":"^1.98.0"},"peerDependencies":{"@solana/web3.js":"^1.87.0"},"_npmOperationalInternal":{"tmp":"tmp/web3.js_0.1.1_1785620077582_0.9188719858732524","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@0xsingularity/web3.js","version":"0.2.0","description":"Thin drop-in wrapper around @solana/web3.js preconfigured for the Singularity chain.","license":"MIT","author":{"name":"bajpainaman","email":"naman@entropytoorder.xyz"},"keywords":["singularity","solana","svm","web3","blockchain"],"type":"commonjs","main":"dist/index.js","types":"dist/index.d.ts","browser":{"./dist/wallet.js":false},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","typecheck":"tsc -p tsconfig.json --noEmit","test":"vitest run","prepublishOnly":"npm run build"},"peerDependencies":{"@solana/web3.js":"^1.87.0"},"devDependencies":{"@solana/web3.js":"^1.98.0","@types/node":"^20.0.0","typescript":"^5.6.0","vitest":"^3.2.6"},"engines":{"node":">=18"},"gitHead":"b5efefce198d28e6a4c3c590db7191d27108f284","_id":"@0xsingularity/web3.js@0.2.0","_nodeVersion":"22.23.1","_npmVersion":"12.0.2","dist":{"integrity":"sha512-jMMQ0kb0EOFCzfWwi3+zEg+Y5YOEZcgvgzj1XX12Lf+uYK5YEG9vF3j6p8GXwAwSXD/0MAKrfSWFzhUJuSXtKw==","shasum":"c4b126dfad0c60d0f2847fce6abbabc74f844302","tarball":"https://registry.npmjs.org/@0xsingularity/web3.js/-/web3.js-0.2.0.tgz","fileCount":50,"unpackedSize":170107,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIB0UhgxNSiZ9Y0HPm9MjkUCkCq2wzgE2Kvv7CIYR1sBbAiEAui4+Km2o0FvIF8JqC+BmoQXJdy+SOnA0magrWsxlsiM="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:756f1448-0300-489b-98ed-3d982bca67d2"}},"directories":{},"maintainers":[{"name":"singularitywastaken","email":"namanbajpai23@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/web3.js_0.2.0_1785623030437_0.5561063471421972"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-01T18:28:13.259Z","modified":"2026-08-01T22:23:50.739Z","0.1.0":"2026-08-01T18:28:13.604Z","0.1.1":"2026-08-01T21:34:37.718Z","0.2.0":"2026-08-01T22:23:50.585Z"},"author":{"name":"bajpainaman","email":"naman@entropytoorder.xyz"},"license":"MIT","keywords":["singularity","solana","svm","web3","blockchain"],"description":"Thin drop-in wrapper around @solana/web3.js preconfigured for the Singularity chain.","maintainers":[{"name":"singularitywastaken","email":"namanbajpai23@gmail.com"}],"readme":"# `@0xsingularity/web3.js`\n\nA thin, drop-in wrapper around [`@solana/web3.js`](https://github.com/solana-labs/solana-web3.js) for the **Singularity** chain.\n\nSingularity speaks the Solana JSON-RPC protocol, so stock `@solana/web3.js` already works against it. This package does not fork it, patch it, or bundle it — it re-exports it and adds the endpoint configuration, SING amounts, wallet files, a faucet helper, a client for the chain's own JSON-RPC namespace, and a set of helpers that encode the chain's divergences from Solana in code so you cannot forget them.\n\n> Version 0.2.0, published on npm as `@0xsingularity/web3.js`.\n\n## Install\n\n```bash\nnpm install @0xsingularity/web3.js @solana/web3.js\n```\n\n`@solana/web3.js` is a peer dependency, so you supply it.\n\n## Use it as a drop-in\n\nChange the import. That's the migration.\n\n```diff\n-import { Keypair, SystemProgram, Transaction } from '@solana/web3.js';\n+import { Keypair, SystemProgram, Transaction } from '@0xsingularity/web3.js';\n```\n\nEvery named export of `@solana/web3.js` is re-exported unchanged, by identity — `singularity.Keypair === solana.Keypair`. Nothing is overridden or reimplemented.\n\nThen connect:\n\n```ts\nimport { connect, checkCompat } from '@0xsingularity/web3.js';\n\nconst connection = connect();            // RPC + WS endpoints preset, commitment 'confirmed'\nawait checkCompat(connection);           // warns loudly if this isn't actually Singularity\n\nconst slot = await connection.getSlot();\n```\n\n`connect()` returns an ordinary `Connection`. Every upstream method behaves as documented upstream.\n\n```ts\nconnect({ endpoint: 'http://127.0.0.1:8899' });  // point somewhere else\nconnect({ commitment: 'finalized' });            // any ConnectionConfig option passes through\n```\n\nSetting `wsEndpoint` matters: from an HTTPS URL, upstream would derive `wss://host:8900/`, which is wrong here. `connect()` sets the correct root-path WebSocket URL for you.\n\n## Wallets, shared with the CLI\n\n```ts\nimport { createWallet, loadWallet, loadOrCreateWallet } from '@0xsingularity/web3.js';\n\nconst wallet = createWallet();            // ~/.config/singularity/id.json\nconsole.log(wallet.publicKey.toBase58());\n\nconst keypair = loadWallet();             // the same file the CLI reads\n```\n\nThe file is the ordinary `solana-keygen` format — a JSON array of 64 bytes. A wallet made by `singularity wallet new` loads here, and a wallet made here works with `solana --keypair`. The default path honours `SINGULARITY_WALLET`, exactly like the CLI.\n\n`createWallet` refuses to overwrite an existing file unless you pass `{ force: true }`, because overwriting a keypair destroys funds. These functions use `node:fs`, so they are Node-only; the rest of the package runs in a browser. Browser bundlers replace the wallet module with an empty stub (via the `browser` field), so importing the browser-safe exports never pulls in `node:fs`.\n\n## SING, not SOL\n\n```ts\nimport {\n  LAMPORTS_PER_SING, singToLamports, lamportsToSing, formatSing,\n  getBalanceSing, requestAirdrop,\n} from '@0xsingularity/web3.js';\n\nconst balance = await getBalanceSing(connection, wallet.publicKey);   // 1.5\nformatSing(1_500_000_000);                                            // \"1.5\"\n\nconst signature = await requestAirdrop(connection, wallet.publicKey, 1);\n```\n\n`requestAirdrop` converts SING to lamports, calls `connection.requestAirdrop`, and polls until the transfer confirms. The faucet's rejections come back typed:\n\n| Error | When |\n|---|---|\n| `FaucetCapError` | Above the 10 SING per-request cap (code `-32602`) |\n| `FaucetRateLimitError` | A daily cap was reached (code `-32005`) |\n| `FaucetError` | Any other faucet rejection |\n| `AirdropConfirmationError` | Accepted, then failed or timed out — carries the `signature` |\n\n```ts\ntry {\n  await requestAirdrop(connection, wallet.publicKey, 50);\n} catch (error) {\n  if (error instanceof FaucetCapError) { /* ask for less */ }\n}\n```\n\nThe scale is nine decimals, the same as SOL, so upstream's `LAMPORTS_PER_SOL` is numerically correct. `LAMPORTS_PER_SING` is that number under the right name.\n\n## `eto_*` — the chain's own RPC\n\nSingularity runs five VMs on one state layer and one token standard, and it exposes that through methods Solana does not have. `@solana/web3.js` cannot reach them, so this package ships a small typed client for the same endpoint.\n\n```ts\nimport { connect, EtoRpc } from '@0xsingularity/web3.js';\n\nconst eto = EtoRpc.fromConnection(connect());\n\nconst stats = await eto.getStats();\nstats.block_height;      // 2124558\nstats.vm_support;        // ['svm', 'evm', 'wasm', 'move', 'zk']\n\nconst tx = await eto.getTransaction(signature);   // null when unknown\ntx?.vm;                  // 'svm'\n\nawait eto.getUniversalToken(tokenAccount);        // the token-ACCOUNT address, not the mint\nawait eto.getUniversalTokensByOwner(owner);       // [] when none\nawait eto.getAccountTransactions(address);        // first page (25); [] when none\nawait eto.getAccountTransactions(address, { limit: 100, offset: 100 }); // page 2 of 100\nawait eto.search('some-address-or-signature');    // hits carry a `type`\n```\n\n| Method | JSON-RPC | Returns |\n|---|---|---|\n| `getStats()` | `eto_getStats` | `EtoStats` |\n| `getTransaction(sig)` | `eto_getTransaction` | `EtoTransaction \\| null` |\n| `getUniversalToken(tokenAccount)` | `eto_getUniversalToken` | `EtoUniversalToken \\| null` |\n| `getUniversalTokensByOwner(owner)` | `eto_getUniversalTokensByOwner` | `EtoUniversalToken[]` |\n| `getAccountTransactions(address, {limit, offset}?)` | `eto_getAccountTransactions` | `EtoTransaction[]` (paginated: server default 25/page, cap 100) |\n| `search(query)` | `eto_search` | `EtoSearchResult[]` |\n| `call(method, params)` | anything | `unknown` |\n\nEvery method here was confirmed against the live endpoint on 2026-08-01, and the response types name only the fields actually observed. Each type also allows unknown keys, so a field this package has not seen still reaches you — treat every field as optional at runtime. Use `call()` for a method that has no wrapper yet.\n\n> `getUniversalToken` takes the base58 **token-account** address (the `account` field of the result), not the mint. Passing a `0x` hex mint returns `-32602 invalid base58 account pubkey`.\n\nThe client uses the global `fetch` (Node 18+). Pass `{ fetch }` to supply your own.\n\n## Divergences, encoded in code\n\nThe chain docs describe Singularity's differences from Solana as prose you have to remember. These helpers encode them so they either do the right thing automatically or fail loudly.\n\n**Read a token balance — `readUniversalToken`.** One 76-byte layout, at fixed offsets, that all five VMs read directly, so you get a balance with no token-account parsing:\n\n```ts\nimport { readUniversalToken, VmOrigin } from '@0xsingularity/web3.js';\n\nconst header = await readUniversalToken(connection, tokenAccount);\nif (header) {\n  header.amount;                    // bigint\n  header.vmOrigin === VmOrigin.SVM; // 0 SVM, 1 EVM, 2 WASM, 3 Move\n}\n```\n\n`readUniversalToken` is the safe path: the Universal Token Header has **no discriminator, no magic, no length prefix**, so *any* 76+ byte account decodes into a structurally valid-looking token — on the live chain, a WASM program account decodes with its module bytes in the `mint` field. `readUniversalToken` gates on the account's owning program (`UNIVERSAL_TOKEN_PROGRAM_ID`) before decoding, so a non-token account returns `null`. The lower-level `decodeUniversalTokenHeader(bytes)` is a **structural decode that trusts the caller** — use it only on bytes you already know are a token account; it cannot tell you whether they are. The header defines only four `vmOrigin` values (SVM/EVM/WASM/Move); the runtime's fifth VM, ZK, has no value in the layout, so `vmOriginName` returns `'unknown'` for anything else rather than throwing.\n\n**Pack big batches — `packInstructions`.** Singularity has **no per-block compute limit** — Solana caps a block near 48M compute units and a single write lock at 12M; here a block is bounded only by payload bytes (15 MiB) and count, so a caller can submit many large packed transactions with no block-CU ceiling. That is the value: pack many instructions into the fewest **per-transaction** units (the cap is 1 MiB, `SVM_MAX_TX_BYTES`). `packInstructions` does that and splits only when needed.\n\n```ts\nimport { packInstructions, serializeUnchecked } from '@0xsingularity/web3.js';\n\nconst { transactions, maxSerializedBytes } = packInstructions(instructions, { feePayer });\n```\n\nOne honest limit: upstream `Transaction.serialize()` refuses anything over Solana's 1,232-byte packet cap, and upstream `Message.serialize()` writes the instruction section into a fixed 1,232-byte buffer while indexing accounts with a single byte (max 256 keys). So a packed transaction **does** exceed Solana's 1,232-byte total-wire cap — account keys and signatures live outside that buffer — but tops out around a couple of KB in practice, **not** 1 MiB. Reaching a true 1 MiB transaction would mean hand-encoding the message and bypassing the upstream codec, which this package does not do. Send the results with `sendAndConfirm`, or with `serializeUnchecked`, which produces the wire bytes upstream `serialize()` would reject.\n\n**Confirm at one-hop speed — `sendAndConfirm`.** Finality is one hop and every confirmed transaction reports `confirmationStatus: 'finalized'`, so upstream's WebSocket `processed → confirmed → finalized` staging is unnecessary. `sendAndConfirm` sends once and tightly polls `getSignatureStatuses`:\n\n```ts\nimport { sendAndConfirm } from '@0xsingularity/web3.js';\n\nconst sig = await sendAndConfirm(connection, tx, [payer], { pollIntervalMs: 100 });\n```\n\nMeasured live: the first confirm in a process is roughly 2x faster than `sendAndConfirmTransaction`, because it skips the WebSocket handshake (≈311 ms → ≈138 ms). On a warm socket the two are comparable; tune `pollIntervalMs` to taste.\n\n**429 backoff, on by default.** The public endpoint rate-limits per IP and the docs say to back off on HTTP 429. `connect()` wraps the connection's fetch to retry 429 with exponential backoff and jitter, honoring `Retry-After`. Opt out with `connect({ retryOn429: false })`, or tune it: `connect({ retryOn429: { maxRetries: 3 } })`.\n\n**Machine-readable caveats.** The RPC divergences and absent instructions are exported as typed constants so a linter or preflight check can consult them instead of trusting a human to have read the docs: `SINGULARITY_PLACEHOLDER_METHODS`, `SINGULARITY_DIVERGENT_METHODS`, `SINGULARITY_SILENT_TRUNCATION` (with `checkSilentTruncation` / `warnIfSilentlyTruncated` for the only two truncations a client cannot otherwise detect), and `SINGULARITY_ABSENT_INSTRUCTIONS`.\n\n## What this package adds\n\n| Export | Purpose |\n|---|---|\n| `connect(config?)` / `SingularityConnection` | Preconfigured `Connection` |\n| `checkCompat(connection, opts?)` | Verifies `getGenesisHash`; warns, or throws with `{ throwOnMismatch: true }` |\n| `createWallet` / `loadWallet` / `loadOrCreateWallet` / `saveWallet` / `defaultWalletPath` | Keypair files the CLI also reads |\n| `requestAirdrop` / `getBalanceSing` | Faucet and balance, in SING, with typed errors |\n| `LAMPORTS_PER_SING` / `singToLamports` / `lamportsToSing` / `formatSing` | SING amounts (`singToLamports` rejects sub-lamport dust) |\n| `EtoRpc` | Typed client for the `eto_*` namespace |\n| `readUniversalToken` / `decodeUniversalTokenHeader` / `vmOriginName` / `VmOrigin` / `UNIVERSAL_TOKEN_PROGRAM_ID` | Universal Token Header: owner-gated safe read + raw decoder |\n| `packInstructions` / `serializeUnchecked` | Pack instructions past Solana's 1232-byte cap |\n| `sendAndConfirm` | One-hop confirm by tight polling |\n| `withRateLimitBackoff` | The 429 backoff `connect()` installs |\n| `SINGULARITY_PLACEHOLDER_METHODS` / `SINGULARITY_DIVERGENT_METHODS` / `SINGULARITY_SILENT_TRUNCATION` / `SINGULARITY_ABSENT_INSTRUCTIONS` | Machine-readable divergence caveats |\n| `SINGULARITY_MAINNET` | Cluster descriptor (endpoints, genesis hash, chain id, epoch length) |\n| `SINGULARITY_RPC_URL` / `SINGULARITY_WS_URL` | `https://rpc.entropytoorder.xyz` / `wss://rpc.entropytoorder.xyz/` |\n| `SINGULARITY_GENESIS_HASH` | `48e8a6FHB3CANQetLYE83frAu5zUy12uBiE75bdq5tYo` |\n| `SINGULARITY_CHAIN_ID` | `0x454f` (17743) |\n| `SINGULARITY_SLOTS_PER_EPOCH` | `14400` |\n| `SINGULARITY_LIMITS` | Server-enforced request and transaction caps |\n| `SINGULARITY_SUPPORTED_SUBSCRIPTIONS` | WebSocket methods that exist here |\n| `SINGULARITY_ACCEPTED_ELF_FLAGS` | `[0x0, 0x20]` — see below |\n\n## What differs from Solana\n\nRead this section once. It is short on purpose; consult the chain docs for the full detail.\n\n**Programs must be SBPF v1, or they silently do nothing.** The loader accepts ELF `e_flags` of `0x0` (v1) or the legacy `0x20` marker, and **rejects** the plain version encodings (`1`, `2`, `3`) that current Agave toolchains emit by default. A program built with a modern default toolchain **deploys successfully and then never executes**. Verify before deploying:\n\n```bash\nreadelf -h target/deploy/my_program.so    # want: Flags: 0x0\n```\n\nor use `singularity check-program my_program.so` from the companion `singularity-cli` package. This is chain issue **#763** and it is the single most expensive mistake you can make here.\n\n**No TPU / QUIC.** `getClusterNodes` returns `null` TPU addresses deliberately. Submit transactions over RPC; `solana program deploy` may need `--use-rpc`.\n\n**Preflight always runs.** `skipPreflight`, `preflightCommitment` and `maxRetries` are accepted by the client and ignored server-side. Do not rely on skipping simulation.\n\n**Transactions may be up to 1 MiB**, not Solana's 1,232 bytes.\n\n**Request caps:** 100 addresses per `getMultipleAccounts`, 256 calls per JSON-RPC batch.\n\n**`voteSubscribe` is not implemented**; `blockSubscribe` is operator-gated. `signatureSubscribe`, `logsSubscribe`, `accountSubscribe`, `programSubscribe`, `slotSubscribe` and `rootSubscribe` all work.\n\n**`Clock.unix_timestamp` is real unix seconds, but derived from the slot** (genesis + slot × 230 ms). Deadline and duration checks behave correctly; the value drifts from wall-clock time across chain halts. Do not use it as a trusted wall clock.\n\n**The token is SING.** Anything re-exported from upstream labels amounts \"SOL\", because the Solana codebase has no way to know a deployment's ticker. `LAMPORTS_PER_SOL` is still numerically correct. Use `LAMPORTS_PER_SING`, `formatSing` and `getBalanceSing` when the label matters.\n\n## Develop\n\n```bash\nnpm install\nnpm run typecheck   # tsc --noEmit, strict\nnpm run build       # emits dist/ with .d.ts\nnpm test            # vitest, 154 tests, no network access\n```\n\nTests cover the constants, the drop-in re-export surface, `connect()` (including the WebSocket-endpoint regression and 429 backoff), `checkCompat()`, wallet round-trips through a temporary directory, the SING conversion maths (including sub-lamport dust rejection), the faucet error mapping, the exact JSON-RPC envelope `EtoRpc` builds, the Universal Token Header decoder (against a live account fixture) and its owner-gated read, instruction packing, and `sendAndConfirm`. None of them touch the network.\n","readmeFilename":"README.md"}