diff --git a/.github/workflows/dashmint-lab-ci.yml b/.github/workflows/dashmint-lab-ci.yml new file mode 100644 index 00000000..457ac9d0 --- /dev/null +++ b/.github/workflows/dashmint-lab-ci.yml @@ -0,0 +1,53 @@ +name: DashMint Lab CI + +on: + push: + branches: [main] + paths: + - 'example-apps/dashmint-lab/**' + - '.github/workflows/dashmint-lab-ci.yml' + pull_request: + branches: [main] + paths: + - 'example-apps/dashmint-lab/**' + - '.github/workflows/dashmint-lab-ci.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: dashmint-lab-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + name: test + build + runs-on: ubuntu-latest + timeout-minutes: 15 + defaults: + run: + working-directory: example-apps/dashmint-lab + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: .nvmrc + cache: npm + cache-dependency-path: example-apps/dashmint-lab/package-lock.json + + - name: Show Node/npm versions + run: | + node -v + npm -v + + - name: Install + run: npm ci + + - name: Test + run: npm test + + # tsc -b runs as part of build, so this covers the typecheck too. + - name: Build + run: npm run build diff --git a/.github/workflows/dashnote-ci.yml b/.github/workflows/dashnote-ci.yml new file mode 100644 index 00000000..f1f24d71 --- /dev/null +++ b/.github/workflows/dashnote-ci.yml @@ -0,0 +1,57 @@ +name: Dashnote CI + +# Unit tests and typechecked build. Playwright coverage lives in +# dashnote-e2e.yml, which serves the app via the dev server and so +# never runs tsc or the vitest suite. + +on: + push: + branches: [main] + paths: + - 'example-apps/dashnote/**' + - '.github/workflows/dashnote-ci.yml' + pull_request: + branches: [main] + paths: + - 'example-apps/dashnote/**' + - '.github/workflows/dashnote-ci.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: dashnote-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + name: test + build + runs-on: ubuntu-latest + timeout-minutes: 15 + defaults: + run: + working-directory: example-apps/dashnote + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: .nvmrc + cache: npm + cache-dependency-path: example-apps/dashnote/package-lock.json + + - name: Show Node/npm versions + run: | + node -v + npm -v + + - name: Install + run: npm ci + + - name: Test + run: npm test + + # tsc -b runs as part of build, so this covers the typecheck too. + - name: Build + run: npm run build diff --git a/.github/workflows/dashproof-lab-ci.yml b/.github/workflows/dashproof-lab-ci.yml new file mode 100644 index 00000000..4dcbf3c8 --- /dev/null +++ b/.github/workflows/dashproof-lab-ci.yml @@ -0,0 +1,53 @@ +name: DashProof Lab CI + +on: + push: + branches: [main] + paths: + - 'example-apps/dashproof-lab/**' + - '.github/workflows/dashproof-lab-ci.yml' + pull_request: + branches: [main] + paths: + - 'example-apps/dashproof-lab/**' + - '.github/workflows/dashproof-lab-ci.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: dashproof-lab-ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + check: + name: test + build + runs-on: ubuntu-latest + timeout-minutes: 15 + defaults: + run: + working-directory: example-apps/dashproof-lab + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version-file: .nvmrc + cache: npm + cache-dependency-path: example-apps/dashproof-lab/package-lock.json + + - name: Show Node/npm versions + run: | + node -v + npm -v + + - name: Install + run: npm ci + + - name: Test + run: npm test + + # tsc -b runs as part of build, so this covers the typecheck too. + - name: Build + run: npm run build diff --git a/1-Identities-and-Names/identity-withdraw-credits.mjs b/1-Identities-and-Names/identity-withdraw-credits.mjs index c2c0ffa7..dc6ceb82 100644 --- a/1-Identities-and-Names/identity-withdraw-credits.mjs +++ b/1-Identities-and-Names/identity-withdraw-credits.mjs @@ -9,7 +9,7 @@ console.log('Identity balance before withdrawal:', identity.balance); // Default: testnet faucet address. Replace or override via WITHDRAWAL_ADDRESS. const toAddress = process.env.WITHDRAWAL_ADDRESS || 'yXWJGWuD4VBRMp9n2MtXQbGpgSeWyTRHme'; -const amount = 1000000n; // Credits to withdraw (protocol minimum) +const amount = 1190000n; // Credits to withdraw (protocol minimum) const amountDash = Number(amount) / (1000 * 100000000); console.log(`Withdrawing ${amount} credits (${amountDash} DASH)`); diff --git a/CLAUDE.md b/CLAUDE.md index 74c52492..4ac78495 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,6 +27,10 @@ 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 + +bash scripts/run-tests.sh # Root suites + every example app's unit tests (no testnet writes) +bash scripts/run-tests.sh --e2e # ...plus each app's Playwright e2e (some specs write to testnet) +bash scripts/run-tests.sh --all # Everything, including root read-write (spends testnet credits) ``` ## Node / npm Toolchain @@ -40,6 +44,8 @@ npm ci The root package and each standalone example app declare `engines.node` for the tested Node minor version. The example apps have their own `package-lock.json` files, and npm's optional native/WASM dependency resolution can differ across Node/npm versions. For contributor work, prefer `npm ci` under `nvm use`; do not commit lockfile rewrites from another Node/npm version. +**Bumping `@dashevo/evo-sdk`:** use `bash scripts/bump-sdk.sh ` (under `nvm use`) — it updates every `package.json`, lockfile, and `*-lite.html` import. Then validate with `run-tests.sh`. Read-write and e2e tests write to testnet — only run them with user approval. + **Running a single tutorial directly:** ```bash diff --git a/example-apps/dashmint-lab/package-lock.json b/example-apps/dashmint-lab/package-lock.json index 6869488a..94d0ee55 100644 --- a/example-apps/dashmint-lab/package-lock.json +++ b/example-apps/dashmint-lab/package-lock.json @@ -8,7 +8,7 @@ "name": "dashmint-lab", "version": "0.0.0", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "@tailwindcss/vite": "^4.2.2", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -437,20 +437,20 @@ } }, "node_modules/@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "dependencies": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" }, "engines": { "node": ">=18.18" } }, "node_modules/@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==", "engines": { "node": ">=18.18" } diff --git a/example-apps/dashmint-lab/package.json b/example-apps/dashmint-lab/package.json index 8ffa7950..d8466ae1 100644 --- a/example-apps/dashmint-lab/package.json +++ b/example-apps/dashmint-lab/package.json @@ -21,7 +21,7 @@ "preview": "vite preview" }, "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "@tailwindcss/vite": "^4.2.2", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -31,13 +31,13 @@ "devDependencies": { "@eslint/js": "^9.39.4", "@playwright/test": "^1.59.1", - "dotenv": "^17.3.1", "@testing-library/react": "^16.3.2", "@types/node": "^24.12.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "@vitest/coverage-v8": "^4.1.5", + "dotenv": "^17.3.1", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", diff --git a/example-apps/dashmint-lab/public/dashmint-lite.html b/example-apps/dashmint-lab/public/dashmint-lite.html index c3be817c..16df1f99 100644 --- a/example-apps/dashmint-lab/public/dashmint-lite.html +++ b/example-apps/dashmint-lab/public/dashmint-lite.html @@ -119,7 +119,7 @@

Browse cards

// package and serves it as a browser-native ES module. Pinned to the same // version the React app at ../package.json depends on so both UIs behave // identically against the same testnet contract. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1'; + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@5.0.0'; // The token-enabled "card" data contract is already published on testnet by // the React app. Anyone querying with the same contract id hits the same diff --git a/example-apps/dashnote-starter/package-lock.json b/example-apps/dashnote-starter/package-lock.json index b428d201..8d2bb634 100644 --- a/example-apps/dashnote-starter/package-lock.json +++ b/example-apps/dashnote-starter/package-lock.json @@ -8,7 +8,7 @@ "name": "dashnote-starter", "version": "0.0.0", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "react": "^19.2.4", "react-dom": "^19.2.4" }, @@ -272,20 +272,20 @@ } }, "node_modules/@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "dependencies": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" }, "engines": { "node": ">=18.18" } }, "node_modules/@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==", "engines": { "node": ">=18.18" } diff --git a/example-apps/dashnote-starter/package.json b/example-apps/dashnote-starter/package.json index 36757eab..a507f89b 100644 --- a/example-apps/dashnote-starter/package.json +++ b/example-apps/dashnote-starter/package.json @@ -17,7 +17,7 @@ "preview": "vite preview" }, "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "react": "^19.2.4", "react-dom": "^19.2.4" }, diff --git a/example-apps/dashnote/package-lock.json b/example-apps/dashnote/package-lock.json index 5e839b89..fbccf3a8 100644 --- a/example-apps/dashnote/package-lock.json +++ b/example-apps/dashnote/package-lock.json @@ -8,7 +8,7 @@ "name": "dashnote", "version": "0.0.0", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "@tailwindcss/vite": "^4.2.2", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -437,20 +437,20 @@ } }, "node_modules/@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "dependencies": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" }, "engines": { "node": ">=18.18" } }, "node_modules/@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==", "engines": { "node": ">=18.18" } diff --git a/example-apps/dashnote/package.json b/example-apps/dashnote/package.json index 180a1ff5..b262656a 100644 --- a/example-apps/dashnote/package.json +++ b/example-apps/dashnote/package.json @@ -21,7 +21,7 @@ "preview": "vite preview" }, "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "@tailwindcss/vite": "^4.2.2", "react": "^19.2.4", "react-dom": "^19.2.4", diff --git a/example-apps/dashnote/public/dashnote-lite.html b/example-apps/dashnote/public/dashnote-lite.html index 0c443806..7233197a 100644 --- a/example-apps/dashnote/public/dashnote-lite.html +++ b/example-apps/dashnote/public/dashnote-lite.html @@ -129,7 +129,7 @@

Get note by ID

// package and serves it as a browser-native ES module. Pinned to the same // version the React app at ../package.json depends on so both UIs behave // identically against the same testnet contract. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1'; + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@5.0.0'; // The "note" data contract is already published on testnet by the React app. // Anyone querying with the same contract id hits the same documents. diff --git a/example-apps/dashproof-lab/package-lock.json b/example-apps/dashproof-lab/package-lock.json index 8b45d856..efae934a 100644 --- a/example-apps/dashproof-lab/package-lock.json +++ b/example-apps/dashproof-lab/package-lock.json @@ -8,7 +8,7 @@ "name": "dashproof-lab", "version": "0.0.0", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "@tailwindcss/vite": "^4.2.2", "react": "^19.2.4", "react-dom": "^19.2.4", @@ -437,20 +437,20 @@ } }, "node_modules/@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "dependencies": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" }, "engines": { "node": ">=18.18" } }, "node_modules/@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==", "engines": { "node": ">=18.18" } diff --git a/example-apps/dashproof-lab/package.json b/example-apps/dashproof-lab/package.json index 8bd90651..dc44233f 100644 --- a/example-apps/dashproof-lab/package.json +++ b/example-apps/dashproof-lab/package.json @@ -22,7 +22,7 @@ "preview": "vite preview" }, "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "@tailwindcss/vite": "^4.2.2", "react": "^19.2.4", "react-dom": "^19.2.4", diff --git a/example-apps/dashproof-lab/public/dashproof-lite.html b/example-apps/dashproof-lab/public/dashproof-lite.html index 583e2f3c..1a3a6efd 100644 --- a/example-apps/dashproof-lab/public/dashproof-lite.html +++ b/example-apps/dashproof-lab/public/dashproof-lite.html @@ -120,7 +120,7 @@

History by chainId

// package and serves it as a browser-native ES module. Pinned to the same // version the React app at ../package.json depends on so both UIs behave // identically against the same testnet contract. - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1'; + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@5.0.0'; // The "anchor" data contract is already published on testnet by the React app. // Anyone querying with the same contract id hits the same documents. diff --git a/example-apps/dashrate/package-lock.json b/example-apps/dashrate/package-lock.json index 63890ec5..a7dde3ea 100644 --- a/example-apps/dashrate/package-lock.json +++ b/example-apps/dashrate/package-lock.json @@ -8,7 +8,7 @@ "name": "dashrate", "version": "0.0.0", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "react": "^19.2.4", "react-dom": "^19.2.4" }, @@ -433,20 +433,20 @@ } }, "node_modules/@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "dependencies": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" }, "engines": { "node": ">=18.18" } }, "node_modules/@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==", "engines": { "node": ">=18.18" } diff --git a/example-apps/dashrate/package.json b/example-apps/dashrate/package.json index 197fbc69..a4a8cac7 100644 --- a/example-apps/dashrate/package.json +++ b/example-apps/dashrate/package.json @@ -21,7 +21,7 @@ "preview": "vite preview" }, "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "react": "^19.2.4", "react-dom": "^19.2.4" }, @@ -35,10 +35,10 @@ "@vitejs/plugin-react": "^6.0.1", "@vitest/coverage-v8": "^4.1.5", "eslint": "^9.39.4", - "jsdom": "^26.1.0", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.4.0", + "jsdom": "^26.1.0", "prettier": "^3.6.2", "typescript": "~6.0.2", "typescript-eslint": "^8.58.0", diff --git a/example-apps/dashrate/public/dashrate-lite.html b/example-apps/dashrate/public/dashrate-lite.html index 1c77019f..6f213f5c 100644 --- a/example-apps/dashrate/public/dashrate-lite.html +++ b/example-apps/dashrate/public/dashrate-lite.html @@ -138,7 +138,7 @@

Recent reviews

// The rest of the file (DOM wiring, render helpers) is plumbing. // ============================================================================ - import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.1.1'; + import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@5.0.0'; const CONTRACT_ID = 'BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP'; const DOC_TYPE = 'review'; diff --git a/example-apps/token-ops/package-lock.json b/example-apps/token-ops/package-lock.json index 993c2e83..6ef91fe5 100644 --- a/example-apps/token-ops/package-lock.json +++ b/example-apps/token-ops/package-lock.json @@ -8,7 +8,7 @@ "name": "token-ops", "version": "0.0.0", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "react": "^19.2.4", "react-dom": "^19.2.4", "sonner": "^2.0.7" @@ -435,20 +435,20 @@ } }, "node_modules/@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "dependencies": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" }, "engines": { "node": ">=18.18" } }, "node_modules/@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==", "engines": { "node": ">=18.18" } diff --git a/example-apps/token-ops/package.json b/example-apps/token-ops/package.json index d3d8ad56..eb79e520 100644 --- a/example-apps/token-ops/package.json +++ b/example-apps/token-ops/package.json @@ -22,7 +22,7 @@ "preview": "vite preview" }, "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "5.0.0", "react": "^19.2.4", "react-dom": "^19.2.4", "sonner": "^2.0.7" @@ -30,13 +30,13 @@ "devDependencies": { "@eslint/js": "^9.39.4", "@playwright/test": "^1.59.1", - "dotenv": "^17.3.1", "@testing-library/react": "^16.3.2", "@types/node": "^24.12.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", "@vitest/coverage-v8": "^4.1.5", + "dotenv": "^17.3.1", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", diff --git a/package-lock.json b/package-lock.json index c2cda9ad..6209afcc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "4.1.1", "license": "MIT", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "^5.0.0", "dotenv": "17.3.1" }, "devDependencies": { @@ -35,20 +35,20 @@ } }, "node_modules/@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "dependencies": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" }, "engines": { "node": ">=18.18" } }, "node_modules/@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==", "engines": { "node": ">=18.18" } @@ -1941,17 +1941,17 @@ "dev": true }, "@dashevo/evo-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-4.1.1.tgz", - "integrity": "sha512-DsfHXlLByyhvAerDknozg0HT4KHNsYP6rEy949aj/KGLLydb9bwJBHOWyn5oDsNR016tvr3oeT/jCkjMA4qoCA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/evo-sdk/-/evo-sdk-5.0.0.tgz", + "integrity": "sha512-xmNl2GM2GMZuo/B9Qa1O+Yi4rMXyd3ROtgRhyjjRT/dMtut9zV9ytpR6ppJcPnVjjxK1cLQItSQGkuAIga59rg==", "requires": { - "@dashevo/wasm-sdk": "4.1.1" + "@dashevo/wasm-sdk": "5.0.0" } }, "@dashevo/wasm-sdk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-4.1.1.tgz", - "integrity": "sha512-/be0D7qohZc9fHgYjbtzOg8KOajarzo0NtKCNybv4Z1xeV617Wo0CusnsxCzwbV7C/jDgPJiGel0KtcSTzQcfQ==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@dashevo/wasm-sdk/-/wasm-sdk-5.0.0.tgz", + "integrity": "sha512-omG45KRYo2TlJdDq/FrooGDwAOZ9kPo4j54P0qUHELfvGG0bpl49vODEY6fa749pzKRJnfsn2zoIV7WW+Lysjg==" }, "@eslint-community/eslint-utils": { "version": "4.4.0", diff --git a/package.json b/package.json index 95bdce08..b7a4d50e 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/dashpay/platform-tutorials#readme", "dependencies": { - "@dashevo/evo-sdk": "4.1.1", + "@dashevo/evo-sdk": "^5.0.0", "dotenv": "17.3.1" }, "devDependencies": { diff --git a/scripts/bump-sdk.sh b/scripts/bump-sdk.sh new file mode 100755 index 00000000..549b5e4d --- /dev/null +++ b/scripts/bump-sdk.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# Usage: bash scripts/bump-sdk.sh VERSION [--test] +# Example: bash scripts/bump-sdk.sh 4.2.0-dev.11 --test +set -euo pipefail + +usage() { echo 'Usage: bash scripts/bump-sdk.sh VERSION [--test]'; } +if [[ "${1:-}" == -h || "${1:-}" == --help ]]; then usage; exit 0; fi +if (( $# < 1 || $# > 2 )) || [[ "${2:---test}" != --test ]]; then + usage >&2 + exit 1 +fi + +cd "$(dirname "$0")/.." +shopt -s nullglob +pkg='@dashevo/evo-sdk' + +# Let npm resolve published versions, including dev/RC releases and tags. +version="$(npm view "$pkg@${1#v}" version --json | jq -er ' + if type == "string" then . else error("specify one published SDK version") end +')" + +dirs=() prefixes=() sections=() specs=() +for file in ./package.json example-apps/*/package.json; do + entry="$(jq -r --arg p "$pkg" ' + ["dependencies", "devDependencies", "optionalDependencies"][] as $s + | select(.[$s][$p] != null) | [$s, .[$s][$p]] | @tsv + ' "$file")" + [ -n "$entry" ] || continue + if [[ "$entry" == *$'\n'* ]]; then + echo "error: multiple SDK declarations in $file" >&2 + exit 1 + fi + IFS=$'\t' read -r section spec <<<"$entry" + if [[ ! "$spec" =~ ^([~^]?)[0-9][0-9A-Za-z.+-]*$ ]]; then + echo "error: unsupported SDK range '$spec' in $file" >&2 + exit 1 + fi + dirs+=("${file%/package.json}") + prefixes+=("${BASH_REMATCH[1]}") + sections+=("$section") + specs+=("$spec") +done + +for i in "${!dirs[@]}"; do + # Repair lite imports even when installation is skipped; dist/ is build output. + for page in "${dirs[i]}"/public/*-lite.html; do + sed -E "s|(https://esm\.sh/@dashevo/evo-sdk@)[^'\"]+|\1${prefixes[i]}$version|g" "$page" >"$page.tmp" + if cmp -s "$page" "$page.tmp"; then + unlink "$page.tmp" + else + mv "$page.tmp" "$page" + echo "Updated lite import: $page" + fi + done + lock="${dirs[i]}/package-lock.json" + if [[ "${specs[i]}" == "${prefixes[i]}$version" && -f "$lock" ]] && + locked="$(jq -er --arg p "$pkg" ' + .packages["node_modules/" + $p].version // .dependencies[$p].version // empty + ' "$lock")" && [[ "$locked" == "$version" ]]; then + echo "Already matching: ${dirs[i]} (${prefixes[i]}$version)" + continue + fi + case "${sections[i]}" in + dependencies) save=--save-prod ;; + devDependencies) save=--save-dev ;; + optionalDependencies) save=--save-optional ;; + esac + echo "Updating ${dirs[i]} to ${prefixes[i]}$version" + (cd "${dirs[i]}" && npm install "$pkg@$version" "$save" \ + --save-exact=false --save-prefix="${prefixes[i]}") +done + +# Run every project's tests even if another project fails. +if [[ "${2:-}" == --test ]]; then + status=0 + for dir in "${dirs[@]}"; do + if jq -e '.scripts.test | type == "string"' "$dir/package.json" >/dev/null; then + echo "Testing $dir" + if ! (cd "$dir" && npm run test); then + echo "Tests failed: $dir" >&2 + status=1 + fi + else + echo "Skipping tests: $dir (no test script)" + fi + done + exit "$status" +fi diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh new file mode 100755 index 00000000..6280ed65 --- /dev/null +++ b/scripts/run-tests.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# +# Run the repo's test suites: the root suites, then every example app that +# defines the scripts being run. Keeps going after a failure and reports which +# suites failed; exits 1 if any did. +# +# By default runs all non-write tests: the root read-only/version/lite suites, +# the root setupDashClient suite, and each app's unit tests. +# +# --e2e also run each app's Playwright e2e suite (does not install browsers) +# --all everything, including the root read-write tests and e2e +# +# Note: --all and --e2e write to testnet. They spend credits and register new +# on-chain state (identities, contracts, documents, tokens, names) that cannot +# be reclaimed; some app e2e specs perform writes too. + +cd "$(dirname "$0")/.." + +root_scripts=(test test:setup) +app_scripts=(test) + +for arg in "$@"; do + case "$arg" in + --e2e) + app_scripts=(test test:e2e) + ;; + --all) + root_scripts=(test:all test:setup) + app_scripts=(test test:e2e) + ;; + *) + echo "unknown option: $arg" >&2 + exit 2 + ;; + esac +done + +command -v jq >/dev/null || { echo "jq is required" >&2; exit 2; } + +failed=() + +for script in "${root_scripts[@]}"; do + echo "=== root $script" + npm run "$script" || failed+=("root:$script") +done + +for app in example-apps/*/; do + [ -f "$app/package.json" ] || continue + for script in "${app_scripts[@]}"; do + # An absent script is skipped; a present but non-string/blank one is a + # failure; a jq error means package.json itself is broken. + status=$(jq -r --arg s "$script" ' + (.scripts // {}) as $all + | if ($all | has($s) | not) then "absent" + elif ($all[$s] | type) == "string" and ($all[$s] | test("\\S")) then "valid" + else "invalid" end' "$app/package.json") || status=error + case $status in + valid) ;; + absent) continue ;; + invalid) failed+=("$app:$script (invalid script)"); continue ;; + *) failed+=("$app:package.json"); break ;; + esac + echo "=== $app $script" + (cd "$app" && npm run "$script") || failed+=("$app:$script") + done +done + +[ ${#failed[@]} -eq 0 ] || printf 'Failed:\n %s\n' "${failed[@]}" +exit $(( ${#failed[@]} > 0 )) diff --git a/test/setupDashClient.test.mjs b/test/setupDashClient.test.mjs index 640d7a66..95ea084b 100644 --- a/test/setupDashClient.test.mjs +++ b/test/setupDashClient.test.mjs @@ -23,10 +23,18 @@ const network = process.env.NETWORK || 'testnet'; // Testnet identity used by the tutorials (known to exist on-chain) const IDENTITY_ID = 'GgZekwh38XcWQTyWWWvmw6CEYFnLU7yiZFPWZEjqKHit'; +// Fixed BIP39 test vector. Used only where deterministic key derivation +// matters — never to assert that no identity exists, since this mnemonic is +// public and anyone can register an identity on it (as happened at index 0). const TEST_MNEMONIC = 'abandon abandon abandon abandon abandon abandon ' + 'abandon abandon abandon abandon abandon about'; +// Freshly generated per run, so it is guaranteed to have no on-chain identity +// at any index. Used by tests that assert absence. Never funded: these paths +// only derive keys and perform read-only lookups. +const UNREGISTERED_MNEMONIC = await wallet.generateMnemonic(); + /** * Derive identity authentication keys from a BIP39 mnemonic using DIP-9 paths. * Inlined here since setupDashClient.mjs doesn't export this as a standalone function. @@ -532,7 +540,7 @@ describe('IdentityKeyManager', function suite() { try { await IdentityKeyManager.create({ sdk, - mnemonic: TEST_MNEMONIC, + mnemonic: UNREGISTERED_MNEMONIC, // no identityId — forces auto-resolve, which will fail }); expect.fail('should have thrown'); @@ -592,7 +600,10 @@ describe('IdentityKeyManager', function suite() { describe('findNextIndex()', function () { it('should return 0 for mnemonic with no on-chain identity', async function () { - const idx = await IdentityKeyManager.findNextIndex(sdk, TEST_MNEMONIC); + const idx = await IdentityKeyManager.findNextIndex( + sdk, + UNREGISTERED_MNEMONIC, + ); expect(idx).to.equal(0); }); @@ -884,7 +895,7 @@ describe('setupDashClient()', function () { // requireIdentity: true path tries to auto-resolve and fails. const saved = clientConfig.mnemonic; try { - clientConfig.mnemonic = TEST_MNEMONIC; + clientConfig.mnemonic = UNREGISTERED_MNEMONIC; try { await setupDashClient(); // requireIdentity defaults to true expect.fail('should have thrown'); @@ -903,11 +914,8 @@ describe('setupDashClient()', function () { const saved = clientConfig.mnemonic; try { clientConfig.mnemonic = TEST_MNEMONIC; - // Both will fail with "No identity found" since TEST_MNEMONIC has no - // on-chain identity, but they fail at different derivation paths, - // proving identityIndex is forwarded. We just need it not to crash - // before reaching the identity lookup. - // Use requireIdentity: false to avoid the lookup and verify the index is stored. + // Use requireIdentity: false to skip the identity lookup entirely and + // verify the index is stored and reaches key derivation. const r0 = await setupDashClient({ requireIdentity: false, identityIndex: 0, @@ -986,7 +994,7 @@ describe('IdentityKeyManager.createForNewIdentity() auto-index', function () { const sdk = await createClient(network); const km = await IdentityKeyManager.createForNewIdentity({ sdk, - mnemonic: TEST_MNEMONIC, + mnemonic: UNREGISTERED_MNEMONIC, // no identityIndex — triggers findNextIndex }); expect(km.identityIndex).to.equal(0);