> Nilla fork: SDK nilla-180.1 (upstream 180 compatible), provider nilla-wallet, RDNS app.nilla.wallet. Relative wallet/SDK links below resolve on this Nilla-controlled origin. Cross-origin apps must substitute a verified NILLA_ORIGIN; no published hostname is configured. Upstream repositories are attribution, not wallet-origin fallbacks. See IDENTITY-PHASE2.md and THIRD_PARTY_NOTICES.md. For Nilla wallet-built KRON buys, use the reviewed-intent adapter described in [TRANSACTION-INTENT-PHASE3.md](TRANSACTION-INTENT-PHASE3.md). A txid is submitted, not settled. Refreshes must satisfy the approved economic bounds. # Nilla Wallet SDK (NILLA) — dApp connect **SDK repo (plug-and-play):** https://github.com/mrzeku2000XTTT/kcc20-sdk **Docs site:** ./docs.html Live wallet: . SDK: ./sdk.js?v=nilla-180.1 VEYRA: ./veyra.html Demo: ./dapp-demo.html Wallet app: https://github.com/mrzeku2000XTTT/KCC20-wallet Nilla Wallet is a **hosted PWA**. It is **not** a Google/Chrome extension. Keys never leave the wallet origin. A dApp (TTT, KasDistro, or yours) **builds the unsigned PSKT**, then hands it to this wallet. The user reviews an Approve sheet and PIN-signs (or KasWare, if they turned that on). This is the same model: find the route, build the tx, wallet signs. ## What exists today | Piece | What it is | |---|---| | `sdk.js` | One script. Creates `window.kcc20`. Opens the PWA (popup / iframe / `web+kcc20:`). Talks `postMessage` `ns:'kcc20'`. | | Host | `wallet/js/dappConnect.js` — Approve / Reject overlay. Native `signPsktJson` or KasWare. | | KIP-12 | `sdk.js` announces `kaspa:provider` (`rdns: app.nilla.wallet`) so a wallet-agnostic picker can list **Nilla Wallet** next to KasWare / Kastle. | | Protocol | Installed PWA handles `web+kcc20:` (see `manifest.json`). | KIP-12 itself is still a **draft** ([kaspanet/kips#21](https://github.com/kaspanet/kips/pull/21)). KasWare remains an **extension** inject (`window.kasware.signPskt`). Use KCC20 when you do not want to require an extension. ## Install in a dApp ```html ``` ```js const kcc = window.kcc20; // require kcc.sdkVersion === "nilla-180.1" const accounts = await kcc.connect(); // popup: user Approves, then the window closes const address = accounts[0]; const network = await kcc.getNetwork(); // silent — popup stays closed const pubKey = await kcc.getPublicKey(); // silent — from the Connect session const utxos = await kcc.getUtxoEntries(address); // silent — public Kaspa UTXOs for that address const signed = await kcc.signPskt({ txJsonString, // rusty-kaspa Safe JSON you built options: { signInputs: [{ index: 0, sighashType: 1 }] } }); // popup: user Signs const { txId } = await kcc.pushTx(signed); // optional — or broadcast yourself ``` **Popup vs silent.** Connect / Sign / Send / Broadcast open the PWA. After Connect succeeds the popup **closes on purpose** so reads stay silent. To **self-send KAS** (TTT app-store unlock) call `sendKaspa` — it **reopens** the wallet, shows a Sign sheet, and only broadcasts after the user confirms. Or `openWallet({ screen: 'send', to })` to focus Send and leave the window open. `getAccounts` / `getBalance` / … still work with the popup closed. Pin **sdk.js?v=nilla-180.1**. KasWare-shaped aliases on the same object: `requestAccounts`, `signPskt`, `pushTx`, `getAccounts`, `getNetwork`, `getPublicKey`, `getUtxoEntries`, `getBalance`. ### Wallet-agnostic discovery (KIP-12) ```js window.addEventListener('kaspa:provider', (ev) => { const { info, provider } = ev.detail || {}; if (!info || !provider) return; // info.rdns === 'app.nilla.wallet' → KCC20 PWA wallets.set(info.rdns || info.uuid, { info, provider }); }); window.dispatchEvent(new Event('kaspa:requestProvider')); ``` Load `sdk.js` **before** you dispatch `kaspa:requestProvider`. ## Rules that keep funds safe 1. **You build. They sign.** Never send a private key. Never ask KCC20 to invent the route. 2. **Pass `signInputs`.** `index` is the **global** `tx.inputs[]` slot (0-based), not “the Nth wallet input.” Only list the user’s P2PK funding inputs. Do **not** list covenant / pool / curve / inventory inputs. If you omit the list (or list extras), v167 skips already-signed and P2SH inputs and signs only unsigned P2PK owned by this wallet. 3. **SIGHASH_ALL (1) only** on this build. 4. **User must Approve** connect, each sign, and each broadcast. 5. Network strings from `window.kcc20.getNetwork()` are `kaspa_mainnet` / `kaspa_testnet_10`. The KIP-12 provider normalizes to `mainnet` / `testnet-10`. ## Methods | Method | Result | |---|---| | `connect()` / `requestAccounts()` | `string[]` addresses | | `getAccounts()` | same, no extra prompt if already allowed | | `getNetwork()` | `kaspa_mainnet` or `kaspa_testnet_10` | | `switchNetwork(id)` | user confirms | | `getPublicKey()` | hex | | `getUtxoEntries(address?)` | UTXOs | | `getBalance(address?)` | `{ confirmed, unconfirmed, address }` sompi | | `getHoldings()` | KAS + KCC20 bags | | `signPskt({ txJsonString, options })` | signed Safe JSON **string** | | `pushTx(signedJson)` | `{ txId, node }` — **object**, not a hex string. Read `result.txId`. | | `sendToken({ tick, amount, dest })` | KCC20 send (used by TTT Fund) | | `sendKaspa({ to, amount })` / `request('sendKaspa', …)` | **v169.** Reopens the popup, Sign sheet for a plain KAS send (self-send allowed). `amount` optional (defaults 0.002 KAS). Returns `{ txId, amount, from, to, explorer }`. User always confirms. | | `openWallet({ screen, to, amount })` / `request('openWallet', …)` | **v169.** Reopen/focus the wallet. `screen`: `send` \| `home` \| `tokens`. Prefills Send. Does **not** auto-broadcast; window stays open. | | `disconnect()` | drop origin | ## KasDistro (You → Apps) Site: https://kasdistro.com In-wallet tab: You → Apps → KasDistro (iframe, same as TTT). Logo: their `kasdistro-logo.png`. Scrapeable page: ./kasdistro.html KasDistro pays **KAS to many `kaspa:q` addresses**. They load `sdk.js?v=nilla-180.1`, call `connect()` on a user click, then either: - `sendKaspa({ to, amount })` for one recipient, or - build one unsigned Safe JSON with many outputs and `signPskt` + `pushTx` (they build, we sign). Do **not** use `buyKron` or `sendToken` for KAS distro. Keys never leave the PWA. ## What this is not - Not a Chrome Web Store extension. No `chrome.runtime`. - Not Tap2Tip. KCC20 already does the builder PSKT handoff. - Not a hosted signer. If the PWA is killed or locked, signing stops until the user reopens it. - KasWare PSKT issues (extension-only, input-index / Safe JSON mismatches) are why native PIN sign exists. Prefer native KCC20 for builder PSKTs.