diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..460d111 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,118 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Tutorial code for [Dash Platform](https://docs.dash.org/platform) demonstrating how to interact with the Dash network using `@dashevo/evo-sdk`. Covers identities, DPNS names, data contracts, and documents. + +## Commands + +```bash +npm test # Read-only tests (~2min, safe to run anytime) +npm run test:read-write # Write tests (destructive, consumes testnet credits, ~5min) +npm run test:all # Both suites sequentially +npm run test:setup # Mocha tests for setupDashClient configuration + +npm run lint # TypeScript type-check all JS files (tsc) +npm run fmt # Format with Prettier +``` + +**Running a single tutorial directly:** + +```bash +node connect.mjs +node 1-Identities-and-Names/identity-retrieve.mjs +``` + +**Running a single test file:** + +```bash +node --test --test-timeout=120000 test/read-only.test.mjs +``` + +## Architecture + +### Tutorial Structure + +Each tutorial is a standalone `.mjs` ESM file with top-level `await`. The pattern is consistent: + +```javascript +import { setupDashClient } from '../setupDashClient.mjs'; + +const { sdk, keyManager } = await setupDashClient(); + +try { + // Tutorial logic — use sdk and keyManager + console.log(result.toJSON()); +} catch (e) { + console.error('Something went wrong:\n', e.message); +} +``` + +### `setupDashClient.mjs` + +The central helper (~500 lines) that all tutorials import. It handles: + +- Loading `.env` via `dotenv` +- BIP39 wallet derivation from `PLATFORM_MNEMONIC` +- DIP-13 key path derivation +- SDK instantiation (`createClient(network)`) +- Key manager setup — returns `{ sdk, keyManager, addressKeyManager }` + +`keyManager.identityId` resolves automatically from the mnemonic. `keyManager.getAuth()` returns the identity, key, and signer needed for signing transactions. + +> **Note:** The in-memory key pattern in `setupDashClient` is for tutorials only — not suitable for production. + +### Test Framework + +Tests use Node.js built-in test runner. Each test runs a tutorial as a **subprocess** via `test/run-tutorial.mjs` and validates: + +- Exit code is 0 +- `stdout`/`stderr` match expected regex patterns +- Process was not killed (no timeout) + +`test/assertions.mjs` provides helpers like `extractId()` and `extractKeyId()` to capture values from output for use in subsequent dependent tests. + +**Read-write tests** maintain a shared state object to pass IDs (contract IDs, document IDs, etc.) between sequential dependent tests. + +### Derivation Paths + +All key derivation uses standard Dash paths. External wallets/tools must use the same paths for compatibility. + +| Key type | Testnet path | Mainnet path | +| - | - | - | +| Platform address (BIP44) | `m/44'/1'/0'/0/i` | `m/44'/5'/0'/0/i` | +| Identity master key (DIP-13) | `m/9'/1'/5'/0'/0'/0'/0'` | `m/9'/5'/5'/0'/0'/0'/0'` | +| Identity keys 0–4 (DIP-13) | `m/9'/1'/5'/0'/0'/0'/k'` | `m/9'/5'/5'/0'/0'/0'/k'` | + +Where `i` = address index and `k` = key index (0=MASTER, 1=HIGH auth, 2=CRITICAL auth, 3=TRANSFER, 4=ENCRYPTION). + +No BIP39 passphrase is used. + +### Environment Variables + +Copy `.env.example` to `.env`. Key variables: + +| Variable | Purpose | +| - | - | +| `PLATFORM_MNEMONIC` | BIP39 mnemonic; required for all write operations | +| `NETWORK` | `testnet` (default) or `mainnet` | +| `DATA_CONTRACT_ID` | Output of `contract-register-minimal.mjs` | +| `DOCUMENT_ID` | Output of `document-submit.mjs` | +| `RECIPIENT_ID` | Identity ID for credit transfers | +| `RECIPIENT_PLATFORM_ADDRESS` | `tdash1...` address for send-funds | + +Read-only tests skip gracefully when `PLATFORM_MNEMONIC` is unset. + +### Directory Layout + +- **Root** — shared utilities (`setupDashClient.mjs`, `connect.mjs`, `create-wallet.mjs`, `view-wallet.mjs`, `send-funds.mjs`) +- **`1-Identities-and-Names/`** — identity registration, top-up, key management, DPNS name registration/lookup +- **`2-Contracts-and-Documents/`** — data contract variants (minimal, indexed, binary, timestamps, history, NFT), document CRUD, NFT operations +- **`test/`** — test runner, assertions, read-only and read-write test suites +- **`docs/`** — HTML/JS interactive tutorial runner (separate from Node tutorials) + +### Linting / Types + +TypeScript (`tsconfig.json`) checks JS files with `strict: true`, `noUnusedLocals: true`, targeting Node16 modules. ESLint uses `airbnb-base`. Prettier uses single quotes, 2-space tabs, trailing commas. diff --git a/README.md b/README.md index 3069ac0..7d76583 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ npm ci 1. Fund the platform address using the bridge URL printed in the previous step 1. Create a `.env` file (see [`.env.example`](./.env.example)) and set `PLATFORM_MNEMONIC` to the mnemonic from step 2. Set `NETWORK` if needed (defaults to `testnet`). +1. To inspect an existing wallet after configuring `PLATFORM_MNEMONIC`, run `node view-wallet.mjs` Proceed with the [Identities and Names tutorials](./1-Identities-and-Names/) first and the [Contracts and Documents tutorials](./2-Contracts-and-Documents/) next. They align with the @@ -80,6 +81,21 @@ npm run test:read-write npm run test:all ``` +### Importing an existing wallet + +If you already have a Dash identity created with another tool (e.g. [Dash +Bridge](https://bridge.thepasta.org/)), you can use it directly by setting `PLATFORM_MNEMONIC` to +your existing mnemonic. Run `node view-wallet.mjs` to confirm the derived address and identity ID. + +For compatibility, the external tool must use the same derivation paths (no BIP39 passphrase): + +| Key type | Testnet | Mainnet | +| - | - | - | +| Platform address (BIP44) | `m/44'/1'/0'/0/i` | `m/44'/5'/0'/0/i` | +| Identity keys (DIP-13) | `m/9'/1'/5'/0'/0'/0'/k'` | `m/9'/5'/5'/0'/0'/0'/k'` | + +The first platform address (`i=0`) must be funded for top-up and send-funds operations. + ## Contributing PRs accepted. diff --git a/view-wallet.mjs b/view-wallet.mjs new file mode 100644 index 0000000..fc25806 --- /dev/null +++ b/view-wallet.mjs @@ -0,0 +1,36 @@ +import { + createClient, + clientConfig, + AddressKeyManager, + IdentityKeyManager, +} from './setupDashClient.mjs'; + +const { mnemonic, network } = clientConfig; + +if (!mnemonic) { + console.error('No mnemonic found. Set PLATFORM_MNEMONIC in your .env file.'); + process.exit(1); +} + +try { + const sdk = await createClient(network); + const addressKeyManager = await AddressKeyManager.create({ sdk, mnemonic, network }); + const { bech32m, path } = addressKeyManager.primaryAddress; + + let identityId = 'No identity found for this mnemonic'; + try { + const keyManager = await IdentityKeyManager.create({ sdk, mnemonic, network }); + identityId = keyManager.identityId; + } catch (e) { + if (!e.message?.includes('No identity found for the given mnemonic')) throw e; + } + + // ⚠️ Never log mnemonics in real applications + console.log('Network: ', network); + console.log('Mnemonic: ', mnemonic); + console.log(`First address: ${bech32m} (${path})`); + console.log('Fund address: ', `https://bridge.thepasta.org/?address=${bech32m}`); + console.log('Identity ID: ', identityId); +} catch (e) { + console.error('Something went wrong:\n', e.message); +}