# KCC20 Wallet SDK (SCORPION) — dApp connect

**SDK repo (plug-and-play):** https://github.com/mrzeku2000XTTT/kcc20-sdk  
**Docs site:** https://kcc-20-wallet.vercel.app/docs.html  
Live wallet: https://kcc-20-wallet.vercel.app  
SDK: https://kcc-20-wallet.vercel.app/sdk.js?v=174
VEYRA: https://kcc-20-wallet.vercel.app/veyra.html  
Demo: https://kcc-20-wallet.vercel.app/dapp-demo.html  
Wallet app: https://github.com/mrzeku2000XTTT/KCC20-wallet

KCC20 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.kcc20.wallet`) so a wallet-agnostic picker can list **KCC20 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
<script src="https://kcc-20-wallet.vercel.app/sdk.js?v=169"></script>
```

```js
const kcc = window.kcc20; // require kcc.sdkVersion === "169"
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=169**.

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.kcc20.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: https://kcc-20-wallet.vercel.app/kasdistro.html

KasDistro pays **KAS to many `kaspa:q` addresses**. They load `sdk.js?v=169`, 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.
