Skip to content

Commit bb51758

Browse files
thephezclaude
andauthored
docs(tutorials): sync to Evo SDK 4.0.0-rc.2 and document token-burn scarcity (#152)
* docs(tutorials): sync with tutorial repo -Bump to Evo SDK v4.0-rc.2 (3.1 was renamed 4.0) and Node 22. -Add the DashMint token-cost flow: a fixed-supply token configuration burned on card create, with creationRestrictionMode opened to anyone who can pay the token cost. - Add note update revision checking, raise the withdrawal amount to the protocol minimum, and drop the obsolete note message maxLength. * docs(tutorials): document DashMint token-burn scarcity model Add a token flow section and Transfer DashMint tokens walkthrough to DashMint Lab, register both blocks in the sync map, and align the intro, TL;DR, and contract-schema prose with token-gated minting. For Dashnote, restructure the update-note steps, note the expectedRevision guard, fix the message maxLength description, and bump the Node prerequisite to 22. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d480726 commit bb51758

10 files changed

Lines changed: 348 additions & 37 deletions

File tree

_static/dashmint-lite.html

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,24 @@ <h2>Browse cards</h2>
119119
// package and serves it as a browser-native ES module. Pinned to the same
120120
// version the React app at ../package.json depends on so both UIs behave
121121
// identically against the same testnet contract.
122-
import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@3.1.0-dev.1';
122+
import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.0.0-rc.2';
123123

124-
// The "card" data contract is already published on testnet by the React app.
125-
// Anyone querying with the same contract id hits the same documents.
126-
const CONTRACT_ID = '4eJR4pgV9mQdyoodfTTwFUp3SYBRJbUrJ5X1ViN2zBhY';
124+
// The token-enabled "card" data contract is already published on testnet by
125+
// the React app. Anyone querying with the same contract id hits the same
126+
// documents.
127+
const CONTRACT_ID = '5hK6SMfN4m2vU1t9qhvngUUQjsXeMNwr8MZdFeGBH8Aa';
127128
const DOC_TYPE = 'card';
128129

129130
// Connect to testnet. testnetTrusted() uses the SDK's bundled list of trusted
130131
// nodes — no node URL or config needed. connect() does the gRPC handshake
131132
// + initial sync. No identity or signing is required for read-only queries.
133+
//
134+
// Workaround: pin the platform protocol version for evo-sdk dev.6 so the
135+
// SDK doesn't ask testnet for a newer protocol it can't decode. Mirrors
136+
// PLATFORM_VERSION_OVERRIDE in setupDashClient-core.mjs. Remove once a
137+
// fixed SDK release lands.
132138
async function connectSdk() {
133-
const sdk = EvoSDK.testnetTrusted();
139+
const sdk = EvoSDK.testnetTrusted({ version: 11 });
134140
await sdk.connect();
135141
return sdk;
136142
}

_static/dashnote-lite.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ <h2>Get note by ID</h2>
129129
// package and serves it as a browser-native ES module. Pinned to the same
130130
// version the React app at ../package.json depends on so both UIs behave
131131
// identically against the same testnet contract.
132-
import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@3.1.0-dev.1';
132+
import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.0.0-rc.2';
133133

134134
// The "note" data contract is already published on testnet by the React app.
135135
// Anyone querying with the same contract id hits the same documents.

_static/dashproof-lite.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ <h2>History by chainId</h2>
120120
// package and serves it as a browser-native ES module. Pinned to the same
121121
// version the React app at ../package.json depends on so both UIs behave
122122
// identically against the same testnet contract.
123-
import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@3.1.0-dev.1';
123+
import { EvoSDK } from 'https://esm.sh/@dashevo/evo-sdk@4.0.0-rc.2';
124124

125125
// The "anchor" data contract is already published on testnet by the React app.
126126
// Anyone querying with the same contract id hits the same documents.

docs/tutorials/connecting-to-testnet.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Platform services are provided via a combination of HTTP and gRPC connections to
1212

1313
## Prerequisites
1414

15-
- An installation of [NodeJS v20 or higher](https://nodejs.org/en/download/)
15+
- An installation of [NodeJS v22 or higher](https://nodejs.org/en/download/)
1616

1717
## Connect via Dash SDK
1818

docs/tutorials/example-apps/dashmint-lab.md

Lines changed: 284 additions & 18 deletions
Large diffs are not rendered by default.

docs/tutorials/example-apps/dashnote.md

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ If you just want the mental model: read the architecture table, then `createNote
3535
- A configured client: [Setup SDK Client](../setup-sdk-client.md) — Dashnote re-uses `setupDashClient-core.mjs`
3636
- A registered identity: [Register an Identity](../identities-and-names/register-an-identity.md)
3737
- Familiarity with data contracts: [Register a Data Contract](../contracts-and-documents/register-a-data-contract.md)
38-
- Node >= 20 and a funded testnet identity (BIP-39 mnemonic + identity index) for write operations
38+
- Node >= 22 and a funded testnet identity (BIP-39 mnemonic + identity index) for write operations
3939
- Read-only browse works without any credentials against the bundled default contract
4040

4141
## Clone and run
@@ -255,6 +255,7 @@ Each operation file is intentionally small. The app-level pattern is: validate i
255255
* SDK method: sdk.documents.create({ document, identityKey, signer })
256256
*/
257257
import type { Logger } from "../lib/logger";
258+
import { PLATFORM_VERSION_OVERRIDE } from "../../../../platformVersion.mjs";
258259
import { loadSdkModule } from "./sdkModule";
259260
import type { DashKeyManager, DashSdk } from "./types";
260261
@@ -297,7 +298,7 @@ export async function createNote({
297298
298299
const json =
299300
typeof document.toJSON === "function"
300-
? (document.toJSON() as Record<string, unknown>)
301+
? (document.toJSON(PLATFORM_VERSION_OVERRIDE) as Record<string, unknown>)
301302
: {};
302303
const noteId = String(json.$id ?? json.id ?? "");
303304
if (!noteId) {
@@ -310,7 +311,13 @@ export async function createNote({
310311

311312
### Update a note
312313

313-
`updateNote.ts` is the canonical fetch-then-bump-revision write. It calls `sdk.documents.get` to read the on-chain revision, increments it by one, builds a new `Document` with the same id and ownerId, and submits via `sdk.documents.replace`. Replays without bumping the revision are rejected by the state transition.
314+
`updateNote.ts` is the canonical fetch-then-bump-revision write:
315+
316+
- Call `sdk.documents.get` to read the current on-chain revision.
317+
- Increment it by one and build a new `Document` with the same id and ownerId.
318+
- Submit via `sdk.documents.replace`. Replays without bumping the revision are rejected by the state transition.
319+
320+
The optional `expectedRevision` parameter guards against a concurrent edit: if the on-chain revision no longer matches what the caller last loaded, the update is refused with a "reload and try again" error instead of silently overwriting the newer version.
314321

315322
```{code-block} typescript
316323
:caption: updateNote.ts
@@ -320,6 +327,10 @@ export async function createNote({
320327
* Update an existing note. Fetches the current document to bump its revision,
321328
* then submits a replace state transition.
322329
*
330+
* Pass `expectedRevision` to refuse the update if the network's revision
331+
* doesn't match — i.e. the note was changed on the network after the local
332+
* copy was loaded.
333+
*
323334
* SDK methods:
324335
* sdk.documents.get(contractId, documentTypeName, documentId)
325336
* sdk.documents.replace({ document, identityKey, signer })
@@ -335,6 +346,7 @@ export interface UpdateNoteParams {
335346
noteId: string;
336347
title?: string;
337348
message: string;
349+
expectedRevision?: number;
338350
log?: Logger;
339351
}
340352
@@ -345,6 +357,7 @@ export async function updateNote({
345357
noteId,
346358
title,
347359
message,
360+
expectedRevision,
348361
log,
349362
}: UpdateNoteParams): Promise<bigint> {
350363
log?.(`Saving note ${noteId}…`);
@@ -354,8 +367,18 @@ export async function updateNote({
354367
throw new Error(`Note ${noteId} not found.`);
355368
}
356369
370+
const currentRevision = BigInt(existingDoc.revision ?? 0);
371+
if (
372+
expectedRevision !== undefined &&
373+
currentRevision !== BigInt(expectedRevision)
374+
) {
375+
throw new Error(
376+
`Note changed on network (you had revision ${expectedRevision}, network is at ${currentRevision}). Reload your notes and try again.`,
377+
);
378+
}
379+
357380
const { Document } = await loadSdkModule();
358-
const revision = BigInt(existingDoc.revision ?? 0) + 1n;
381+
const revision = currentRevision + 1n;
359382
const trimmedTitle = title?.trim();
360383
const document = new Document({
361384
properties: {
@@ -431,7 +454,7 @@ export async function deleteNote({
431454
The note contract is intentionally minimal: one document type, two user-editable fields, two indices to support the recent-notes list. Key choices worth calling out:
432455

433456
- `documentsMutable: true` and `canBeDeleted: true` — notes are editable and deletable.
434-
- `maxLength: 120` for `title` and `maxLength: 10000` for `message` are **UTF-8 byte budgets**, not character counts. The editor's progress bar reflects bytes; emoji and non-ASCII sequences consume more of the budget than ASCII.
457+
- `maxLength: 120` for `title` caps the title; `message` carries no `maxLength` and is instead bounded by Platform's per-field byte limit. The editor's progress bar tracks the `message` byte count against that limit — emoji and non-ASCII sequences consume more of the budget than ASCII.
435458
- `byOwnerUpdated` (`$ownerId`, `$updatedAt`) is the index the recent-notes list paginates on; `byOwnerCreated` is its created-time sibling.
436459

437460
`registerContract` builds the `DataContract`, calls `setConfig()` to lock in those choices, then publishes via `sdk.contracts.publish`. `ensureContract` is the lazy wrapper used by the login flow: re-use a saved contract ID if one is present, otherwise register a fresh one.
@@ -464,7 +487,6 @@ export const NOTE_SCHEMAS = {
464487
},
465488
message: {
466489
type: "string",
467-
maxLength: 10000,
468490
position: 1,
469491
},
470492
},

docs/tutorials/identities-and-names/withdraw-an-identity-balance.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ console.log('Identity balance before withdrawal:', identity.balance);
3232
// Default: testnet faucet address. Replace or override via WITHDRAWAL_ADDRESS.
3333
const toAddress =
3434
process.env.WITHDRAWAL_ADDRESS || 'yXWJGWuD4VBRMp9n2MtXQbGpgSeWyTRHme';
35-
const amount = 190000n; // Credits to withdraw
35+
const amount = 1000000n; // Credits to withdraw (protocol minimum)
3636
const amountDash = Number(amount) / (1000 * 100000000);
3737
3838
console.log(`Withdrawing ${amount} credits (${amountDash} DASH)`);

docs/tutorials/introduction.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Building on Dash Platform requires first registering an Identity and then regist
1212

1313
The tutorials in this section are written in JavaScript and use [Node.js](https://nodejs.org/en/about/). The following prerequisites are necessary to complete the tutorials:
1414

15-
- [Node.js](https://nodejs.org/en/) (v20+)
15+
- [Node.js](https://nodejs.org/en/) (v22+)
1616
- Familiarity with JavaScript asynchronous functions using [async/await](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await)
1717
- The [Dash JavaScript SDK](https://www.npmjs.com/package/@dashevo/evo-sdk) (see [Connecting to a Network](../tutorials/connecting-to-testnet.md#1-install-the-dash-sdk))
1818

docs/tutorials/setup-sdk-client.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import {
6767
SecurityLevel,
6868
wallet,
6969
} from '@dashevo/evo-sdk';
70+
import { PLATFORM_VERSION_OVERRIDE } from './platformVersion.mjs';
7071
7172
/** @typedef {import('@dashevo/evo-sdk').Identity} Identity */
7273
/** @typedef {import('@dashevo/evo-sdk').IdentityPublicKey} IdentityPublicKey */
@@ -144,6 +145,8 @@ export async function dip13KeyPath(network, identityIndex, keyIndex) {
144145
// SDK client helpers
145146
// ---------------------------------------------------------------------------
146147
148+
export { PLATFORM_VERSION_OVERRIDE };
149+
147150
/**
148151
* Create and connect an EvoSDK client for the selected network.
149152
*
@@ -152,9 +155,11 @@ export async function dip13KeyPath(network, identityIndex, keyIndex) {
152155
*/
153156
export async function createClient(network = 'testnet') {
154157
const factories = /** @type {Record<string, () => EvoSDK>} */ ({
155-
testnet: () => EvoSDK.testnetTrusted(),
156-
mainnet: () => EvoSDK.mainnetTrusted(),
157-
local: () => EvoSDK.localTrusted(),
158+
testnet: () =>
159+
EvoSDK.testnetTrusted({ version: PLATFORM_VERSION_OVERRIDE }),
160+
mainnet: () =>
161+
EvoSDK.mainnetTrusted({ version: PLATFORM_VERSION_OVERRIDE }),
162+
local: () => EvoSDK.localTrusted({ version: PLATFORM_VERSION_OVERRIDE }),
158163
});
159164
160165
const factory = factories[network];

scripts/tutorial-sync/tutorial-code-map.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ mappings:
199199
caption: contract.ts
200200
language: typescript
201201

202+
- source: example-apps/dashmint-lab/src/dash/dashMintToken.ts
203+
doc: example-apps/dashmint-lab.md
204+
block_id:
205+
caption: dashMintToken.ts
206+
language: typescript
207+
202208
- source: example-apps/dashmint-lab/src/dash/withAuthedCard.ts
203209
doc: example-apps/dashmint-lab.md
204210
block_id:
@@ -217,6 +223,12 @@ mappings:
217223
caption: transferCard.ts
218224
language: typescript
219225

226+
- source: example-apps/dashmint-lab/src/dash/transferDashMintTokens.ts
227+
doc: example-apps/dashmint-lab.md
228+
block_id:
229+
caption: transferDashMintTokens.ts
230+
language: typescript
231+
220232
- source: example-apps/dashmint-lab/src/dash/setPrice.ts
221233
doc: example-apps/dashmint-lab.md
222234
block_id:

0 commit comments

Comments
 (0)