-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
822 lines (776 loc) · 51.7 KB
/
Copy pathindex.html
File metadata and controls
822 lines (776 loc) · 51.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>EffectStream — Build Multi-Chain dApps</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/reset.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/reveal.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/theme/black.css" id="theme" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/plugin/highlight/monokai.css" />
<!-- Official EffectStream fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" />
<style>
/* ===== Official EffectStream palette (effectstream-home) ===== */
:root {
--bg: #0d0d12;
--bg-subtle: #13131a;
--surface: rgba(255, 255, 255, 0.04);
--surface-hover: rgba(255, 255, 255, 0.07);
--border: rgba(255, 255, 255, 0.08);
--border-hover: rgba(255, 255, 255, 0.15);
--cyan: #06d6a0; /* primary accent */
--purple: #8b5cf6; /* secondary accent */
--magenta: #ec4899; /* highlight */
--teal: #19b17b;
--text: #f0f0f5;
--text-secondary: rgba(240, 240, 245, 0.55);
--text-tertiary: rgba(240, 240, 245, 0.35);
--warn: #f59e0b;
}
.reveal-viewport { background: var(--bg); }
.reveal {
font-family: "Inter", -apple-system, "Segoe UI", Roboto, sans-serif;
font-size: 30px; color: var(--text);
}
.reveal h1, .reveal h2, .reveal h3 {
font-family: "Outfit", "Inter", sans-serif;
text-transform: none; letter-spacing: -0.02em; color: var(--text);
}
.reveal h1 { font-size: 1.9em; font-weight: 700; }
.reveal h2 { font-size: 1.35em; font-weight: 600;
background: linear-gradient(135deg, var(--cyan), var(--purple));
-webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.reveal h3 { color: var(--purple); font-size: 1.0em; font-weight: 600; }
.gradient { background: linear-gradient(135deg, var(--cyan), var(--purple));
-webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; }
.reveal section img { border: none; box-shadow: none; background: transparent; }
.reveal strong { color: var(--cyan); }
.reveal em { color: var(--purple); font-style: normal; }
.reveal code { font-family: "JetBrains Mono", monospace; }
.reveal pre { width: 100%; font-size: 0.55em; box-shadow: none; }
.reveal pre code { max-height: 520px; border-radius: 12px; padding: 16px;
font-family: "JetBrains Mono", monospace; }
.reveal ul { width: 100%; }
.reveal li { margin: 0.25em 0; }
.reveal a { color: var(--cyan); }
.tag {
display: inline-block; padding: 3px 14px; border-radius: 999px;
font-size: 0.5em; letter-spacing: 0.1em; text-transform: uppercase; font-weight: 600;
background: rgba(6, 214, 160, 0.10); color: var(--cyan);
border: 1px solid rgba(6, 214, 160, 0.30); margin-bottom: 0.5em;
}
.muted { color: var(--text-secondary); }
.small { font-size: 0.7em; }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 1.0em; align-items: stretch; text-align: left; }
.grid3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 0.9em; align-items: stretch; }
.card {
background: var(--surface); border: 1px solid var(--border);
border-radius: 16px; padding: 0.7em 0.95em; text-align: left;
}
.card h3 { margin: 0 0 0.25em; }
.flow {
display: flex; align-items: center; justify-content: center; gap: 0.5em;
flex-wrap: wrap; font-size: 0.8em; margin: 0.6em 0;
}
.flow .node {
background: rgba(6, 214, 160, 0.08); border: 1px solid rgba(6, 214, 160, 0.30);
border-radius: 12px; padding: 0.5em 0.8em; color: var(--text);
}
.flow .arrow { color: var(--purple); font-size: 1.4em; }
.reveal table { font-size: 0.62em; }
.reveal table th { color: var(--cyan); }
.play { color: var(--cyan); font-weight: 600; }
.kbd { font-family: "JetBrains Mono", monospace; background:#1c1c24; border:1px solid var(--border-hover); border-radius:6px; padding:1px 7px; font-size:0.8em; }
/* ===== "Shape of it" pipeline diagram ===== */
.pipe { display: flex; flex-direction: column; align-items: center; gap: 0.35em; margin-top: 0.4em; }
.pipe .layer {
width: 88%; border-radius: 14px; padding: 0.55em 0.8em; text-align: center;
background: var(--surface); border: 1px solid var(--border);
}
.pipe .layer .lbl { font-size: 0.5em; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-tertiary); display:block; margin-bottom: 0.25em; }
.pipe .chains { display: flex; flex-wrap: wrap; gap: 0.4em; justify-content: center; }
.pipe .chip {
font-size: 0.62em; padding: 0.2em 0.7em; border-radius: 999px;
background: rgba(255,255,255,0.05); border: 1px solid var(--border-hover); color: var(--text);
}
.pipe .arrow { color: var(--purple); font-size: 1.1em; line-height: 1; }
.pipe .engine { border-color: rgba(6,214,160,0.35); background: rgba(6,214,160,0.07); }
.pipe .stm { border-color: rgba(139,92,246,0.40); background: rgba(139,92,246,0.08); }
.pipe .note { font-size: 0.62em; color: var(--text-secondary); }
.batch-loop { font-size: 0.6em; color: var(--magenta); margin-top: 0.3em; }
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<!-- ===================== TITLE ===================== -->
<section data-background-gradient="radial-gradient(ellipse at 50% 35%, #16162200 0%, #0d0d12 70%), linear-gradient(135deg, #0d0d12 0%, #13131a 55%, #160f24 100%)">
<span class="tag">Multi-Chain Web3 Engine</span>
<h1 class="gradient">EffectStream</h1>
<h3 style="color:var(--text); opacity:0.85; margin-top:-0.1em;">Build multi-chain dApps without the bridges</h3>
<p class="muted small" style="margin-top:1.2em;">A Web3 engine for dApps, games & autonomous worlds<br/>EVM · Midnight · Cardano · Bitcoin · Celestia · Avail · NEAR</p>
<aside class="notes">
Intro: I'm Eddie. Today — what multi-chain dApps are and why they're hard, how EffectStream
solves it, a couple of demos that are live today, then I'll launch a cross-chain template
from scratch, and finish with how you can try it. Roughly 25 minutes, then Q&A.
Presenting tip: S opens speaker notes, arrow keys move.
</aside>
</section>
<!-- ===================== AGENDA ===================== -->
<section>
<h2>What we'll cover</h2>
<div class="grid2" style="font-size:0.85em;">
<ul>
<li><strong>1.</strong> Why multi-chain dApps</li>
<li><strong>2.</strong> How EffectStream works</li>
<li><strong>3.</strong> Demos in the wild</li>
</ul>
<ul>
<li><strong>4.</strong> <span class="play">Live:</span> launch an EVM + Midnight template</li>
<li><strong>5.</strong> Tools, links, try it yourself</li>
</ul>
</div>
<p class="muted small">Open source · TypeScript · one command to run a full local multi-chain stack</p>
<aside class="notes">
Five parts. I'll keep the slides light and spend the real time on the live launch in part 4.
If the demo gods are kind, you'll see a fresh app reading two chains at once in about ten minutes.
</aside>
</section>
<!-- ===================== SEGMENT 1: WHY ===================== -->
<section>
<section data-background-color="#13131a">
<span class="tag">Part 1</span>
<h2>Why multi-chain dApps?</h2>
<p class="muted">Context, and the problem everyone keeps hitting</p>
<aside class="notes">Three minutes of context. What's broken about building on-chain apps today.</aside>
</section>
<section>
<h2>The problem with "pick one chain"</h2>
<ul>
<li>Your users & liquidity are <strong>spread across many chains</strong> — pick one and you cut off the rest.</li>
<li>The usual fix is <em>bridging</em> assets over… and bridges are the <strong>#1 hack target</strong> in crypto — it's hard to do right.</li>
<li><strong>No single chain has it all</strong> — some are slow, some expensive, some lack the assets, or the features your app needs.</li>
<li>Every chain has its own VM, wallets, tooling → supporting another chain means a <strong>rewrite</strong>.</li>
</ul>
<aside class="notes">
Set up the pain. Liquidity is fragmented. The dominant cross-chain UX is asking a user to bridge,
and bridges are hard to get right — they hold custody and have been drained for billions. And no
single chain has everything: some are slow, some expensive, some don't have the assets or features
you need. So devs either commit to one chain or rewrite for each — both bad.
</aside>
</section>
<section>
<h2>What people actually want</h2>
<div class="flow">
<span class="node">Reach users<br/>on every chain</span>
<span class="arrow">+</span>
<span class="node">Use each chain's<br/>strengths</span>
<span class="arrow">+</span>
<span class="node">No bridging<br/>No custody risk</span>
</div>
<ul class="small">
<li><strong>EVM</strong> — deep markets & existing tokens · <strong>Midnight</strong> — privacy / ZK</li>
<li><strong>Celestia</strong> — cheap data availability · <strong>Cardano</strong> — native assets</li>
</ul>
<p style="margin-top:0.6em;">A <strong>multi-chain dApp</strong> = one app that <em>reads from many chains</em> and folds them into <strong>one consistent state</strong>.</p>
<aside class="notes">
Flip to what we want. Use each chain for what it's best at — EVM for markets and tokens,
Midnight for privacy, Celestia for cheap DA, Cardano for native assets — without forcing users
to move funds. That's the definition I'll use: one app, reads many chains, one consistent state.
That's exactly what EffectStream lets you build.
</aside>
</section>
</section>
<!-- ===================== SEGMENT 2: HOW ===================== -->
<section>
<section data-background-color="#13131a">
<span class="tag">Part 2</span>
<h2>How EffectStream manages it</h2>
<p class="muted">The short tech version</p>
<aside class="notes">Four minutes. The core mental model and the three pieces that make it work.</aside>
</section>
<section>
<h2>The core model</h2>
<div class="flow" style="font-size:0.95em;">
<span class="node">Many chains<br/><span class="muted small">EVM · Midnight · …</span></span>
<span class="arrow">→</span>
<span class="node">Sync layer<br/><span class="muted small">normalize blocks</span></span>
<span class="arrow">→</span>
<span class="node">State machine<br/><span class="muted small">your TS logic</span></span>
<span class="arrow">→</span>
<span class="node">Indexed state<br/><span class="muted small">SQL / API</span></span>
</div>
<p>Read from many chains → fold into <strong>one deterministic state machine</strong> → query the result.</p>
<p class="small muted">It's a <em>sovereign rollup</em>: inputs are ordered on-chain (data availability), your logic runs in your node, the resulting state is queryable.</p>
<aside class="notes">
One sentence to remember: read many chains, fold into one deterministic state machine, query the result.
The "sovereign rollup" framing: the chains give you ordering and data availability — they store the inputs —
but YOUR code decides what those inputs mean. Same inputs, same code, same state on every node. Deterministic.
</aside>
</section>
<section>
<h2>Three pieces</h2>
<div class="grid3">
<div class="card">
<h3>Sync</h3>
<p class="small">Per-chain fetchers (viem for EVM, UTXORpc for Cardano, Midnight SDK, Bitcoin, Celestia…) poll finalized blocks and normalize them into one common format.</p>
</div>
<div class="card">
<h3>State machine</h3>
<p class="small">You write a typed <em>grammar</em> of commands + one handler each, in TypeScript. The runtime routes each input and commits SQL <strong>atomically per block</strong>.</p>
</div>
<div class="card">
<h3>Batcher</h3>
<p class="small">Users sign inputs and POST them over HTTP. The batcher packs many into <strong>one on-chain tx</strong>. Storage-first — survives crashes.</p>
</div>
</div>
<aside class="notes">
Three pieces. Sync: a fetcher per chain that turns very different blocks into one common shape.
State machine: this is what you actually write — a grammar of allowed commands, a generator function
per command, and every block your yielded SQL queries commit in a single transaction. Batcher: solves
throughput — instead of every chess move being its own gas-paying tx, users sign and POST, the batcher
bundles them into one transaction, and it persists everything to disk first so nothing is lost.
</aside>
</section>
<section>
<h2>The payoff: <span style="color:var(--es-ok)">non-custodial</span></h2>
<ul>
<li>All real interactions land <strong>on the chains</strong>, not inside your node.</li>
<li>Users keep <strong>full custody of their L1 assets</strong> the whole time.</li>
<li>“<em>Even if your app gets hacked, users' L1 assets are not at risk.</em>”</li>
</ul>
<p class="small muted">Plus: complex logic runs in TypeScript (not Solidity), so game state, physics, and rich rules are finally practical.</p>
<aside class="notes">
The big payoff and the differentiator versus most L2s: it's non-custodial. The framework never
holds user funds — assets stay on L1. So the blast radius of a bug is your app state, not people's money.
That's huge for brands and for trust. And because your logic is TypeScript, not the EVM, you can do things
that are impractical on-chain — real physics, real game rules.
</aside>
</section>
<section>
<h2>The shape of it</h2>
<div class="pipe">
<div class="layer">
<span class="lbl">Chains · ordering + data availability</span>
<div class="chains">
<span class="chip">EVM</span><span class="chip">Midnight</span><span class="chip">Cardano</span>
<span class="chip">Bitcoin</span><span class="chip">Celestia</span><span class="chip">Avail</span><span class="chip">NEAR</span>
</div>
</div>
<span class="arrow">▼ <span class="note">primitives watch the events you care about</span></span>
<div class="layer engine">
<span class="lbl">Sync</span><strong>Normalize every chain → one deterministic order</strong>
</div>
<span class="arrow">▼</span>
<div class="layer stm">
<span class="lbl">State machine</span><strong>Your TypeScript logic</strong> — same inputs, same state, every node
</div>
<span class="arrow">▼</span>
<div class="layer">
<span class="lbl">Database + API</span>Queryable application state
</div>
<div class="batch-loop">↳ Batcher: users sign inputs → bundled into one on-chain tx → back to the top</div>
</div>
<aside class="notes">
New, accurate picture — read it top to bottom. The chains at the top only do two jobs: order the inputs
and store them (data availability). Primitives are typed watchers that pick out the events you care about.
Sync normalizes all those different chains into one deterministic stream. Your TypeScript state machine
folds that stream into application state — deterministic, so every node agrees. The result is queryable
via a database and API. And the batcher (the pink loop) is how user inputs get on-chain efficiently:
sign, bundle, one transaction, which then flows back in at the top.
</aside>
</section>
</section>
<!-- ===================== SEGMENT 3: DEMOS ===================== -->
<section>
<section data-background-color="#13131a">
<span class="tag">Part 3</span>
<h2>Demos in the wild</h2>
<p class="muted">Real apps & templates, working today</p>
<aside class="notes">Three minutes — three things that actually exist and run, so this isn't just theory.</aside>
</section>
<section>
<span class="tag">Game · multichain · Arbitrum + Midnight</span>
<h2 style="margin-bottom:0.25em;">Safe Solver</h2>
<iframe src="https://drive.google.com/file/d/1KhkfE4dM5dI3Wo0P3Ij8V3rOTLdHaGX1/preview"
style="width:64%; height:400px; border:0; border-radius:14px; box-shadow:0 8px 40px rgba(0,0,0,0.5);"
allow="autoplay" allowfullscreen></iframe>
<p class="small" style="margin-top:0.5em;">
Same logic over Arbitrum <em>and</em> Midnight blocks · deterministic randomness per block ·
<a href="https://safesolver.midnight.fun/" target="_blank" rel="noopener" style="color:var(--cyan); font-weight:600;">play it live ▶</a>
</p>
<aside class="notes">
Safe Solver is the canonical multichain example — the pattern the other templates build on.
The same game logic computes over two chains' blocks at once, and the random puzzle is derived
deterministically per block so everyone agrees without a server. I'll play the clip rather than
host it live; the live link is there if anyone wants to try it after.
</aside>
</section>
<section>
<span class="tag">Proof at scale · Arbitrum + Cardano</span>
<h2 style="margin-bottom:0.25em;">Tarochi</h2>
<iframe src="https://drive.google.com/file/d/1--jE8nVOyhrPqh5IWmahys2aCrKn23aI/preview"
style="width:64%; height:380px; border:0; border-radius:14px; box-shadow:0 8px 40px rgba(0,0,0,0.5);"
allow="autoplay" allowfullscreen></iframe>
<p class="small" style="margin-top:0.5em;">
A fully on-chain autonomous game — the <strong>volume proof</strong>:
<strong>over 1M+ transactions</strong>, gas subsidized · 4-layer rollup stack + Cardano DEX · built in GameMaker.
</p>
<aside class="notes">
Tarochi is the "does this actually scale" answer. Over a million transactions, gas subsidized so users
didn't feel it. It stacks four layers — Ethereum → Arbitrum → Xai → Tarochi — and also watches Cardano
for DEX activity. The takeaway: this isn't a toy — it ran a real game at real volume.
</aside>
</section>
<section>
<span class="tag">Dev environment · EVM + Cardano</span>
<h2 style="margin-bottom:0.25em;">EVM-Cardano Explorer</h2>
<iframe src="https://drive.google.com/file/d/1dmXshwpqeXi9sez5ekfqfrlXBdjVVABW/preview"
style="width:64%; height:380px; border:0; border-radius:14px; box-shadow:0 8px 40px rgba(0,0,0,0.5);"
allow="autoplay" allowfullscreen></iframe>
<p class="small" style="margin-top:0.5em;">
A full local <strong>EVM + Cardano</strong> dev env from one command — Hardhat + <strong>YACI DevKit</strong> + <strong>Dolos</strong>.
Mint NFTs, send ADA, one unified feed — <em>minutes, not the days</em> a Cardano indexer sync usually takes.
</p>
<aside class="notes">
The developer-experience demo. Standing up a local Cardano dev environment historically meant syncing an
indexer like Carp for days — which kills iteration. This template replaces that with an instant localhost
stack: EVM (Hardhat) + Cardano (YACI DevKit, indexed by Dolos over UTxORPC), all from one `bun run dev`.
The clip shows the orchestrator booting the whole thing; the Explorer UI mints NFTs, sends ADA, and shows
a unified cross-chain event feed. The hook: multi-chain dev in minutes, not days.
</aside>
</section>
</section>
<!-- ===================== SEGMENT 4: LIVE LAUNCH ===================== -->
<section>
<section data-background-gradient="linear-gradient(135deg, #160f24 0%, #0d0d12 60%, #0b1f1a 100%)">
<span class="tag" style="color:var(--es-ok);border-color:var(--es-ok);background:rgba(74,222,128,0.12)">Part 4 · Live</span>
<h2>Launch an EVM + Midnight app</h2>
<p class="muted">From <span class="kbd">git clone</span> to a running cross-chain dApp</p>
<aside class="notes">
The main event. I'll launch the evm-midnight-v2 template. Heads up to me: I pre-booted this ~5 min
before going live so it's already synced — see RUNBOOK.md. If anything stalls, I drop to the screenshots
on the next slides and narrate.
</aside>
</section>
<section>
<h2>What we're building</h2>
<div class="grid2">
<div class="card">
<h3>On EVM</h3>
<p class="small">An <strong>ERC-721 NFT</strong>. Public ownership, standard <code>Transfer</code> events.</p>
</div>
<div class="card">
<h3>On Midnight</h3>
<p class="small">A <strong>private property</strong> added via a ZK circuit — only the owner can run it; the result is published for the app to read.</p>
</div>
</div>
<p style="margin-top:0.6em;">One database unifies both chains. <em>Public token + private metadata, no bridge.</em></p>
<aside class="notes">
The app: an NFT lives on EVM where ownership is public. A private property — think a hidden stat,
a sealed bid, hidden inventory — is set on Midnight through a zero-knowledge circuit only the owner can run.
EffectStream watches both and stitches them into one queryable record. This is the "use each chain's
strength" idea made concrete.
</aside>
</section>
<section>
<h2>The whole launch</h2>
<pre><code class="language-bash" data-trim data-line-numbers>git clone https://github.com/effectstream/effectstream.git
cd effectstream/templates/evm-midnight-v2
bun i # install dependencies
bun run dev # 🚀 compiles contracts AND boots the entire local stack
open http://localhost:10599 # 👀 the running dApp in your browser</code></pre>
<p class="small muted">One command — <code>bun run dev</code> — compiles the contracts and orchestrates everything below.</p>
<aside class="notes">
This is the entire thing — straight from the docs: clone, bun i, bun run dev, then open localhost:10599.
That dev command compiles the contracts and brings up the whole stack. Let me run it. (Switch to terminal.)
</aside>
</section>
<section>
<h2>It launches <em>everything</em> for you — in order</h2>
<p class="small muted" style="margin-bottom:0.5em;">The orchestrator runs the whole dependency graph from that one command:</p>
<div class="flow small">
<span class="node">🗄️ Database</span>
<span class="arrow">→</span>
<span class="node">⛓️ Chains<br/><span class="muted" style="font-size:0.8em">Hardhat · Midnight node, indexer, proof server</span></span>
<span class="arrow">→</span>
<span class="node">🔨 Compile<br/><span class="muted" style="font-size:0.8em">EVM + Compact</span></span>
<span class="arrow">→</span>
<span class="node">🚀 Deploy<br/><span class="muted" style="font-size:0.8em">contracts</span></span>
<span class="arrow">→</span>
<span class="node">📜 Your custom scripts</span>
<span class="arrow">→</span>
<span class="node">🖥️ Dev tools + frontend</span>
</div>
<p class="small" style="margin:0.4em 0;"><span style="color:var(--purple)">▼</span> waits until every dependency is ready <span style="color:var(--purple)">▼</span></p>
<div class="layer engine" style="width:80%; margin:0 auto; border-radius:14px; padding:0.6em 0.9em;">
<span class="lbl" style="font-size:0.5em; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-tertiary); display:block;">then starts the EffectStream node</span>
<strong>⚡ Sync service</strong> · State machine · API
</div>
<p class="small muted" style="margin-top:0.5em;">You declare this graph once in config — no manual wiring, no "start these 8 terminals."</p>
<aside class="notes">
This is the developer-experience payoff — say it out loud. Normally you'd hand-wire all this: start a
database, spin up each chain's node and indexer, run the proof server, compile your contracts, deploy
them, run any seed/custom scripts, launch dev tools and the frontend — and only THEN start your indexer,
in the right order, each waiting on the last. The orchestrator does the entire dependency graph for you
from one command, and once everything's ready it starts the EffectStream node — the sync service that
reads the chains, the state machine, and the API. You declare the graph once in config; the engine runs it.
</aside>
</section>
<section>
<h2>The demo flow</h2>
<ol class="small">
<li>Open <strong>localhost:10599</strong> → block heights from both chains ticking live.</li>
<li><strong>Mint</strong> an ERC-721 on the EVM side.</li>
<li>Watch the <strong>sync node index it</strong> in ~a second — it appears in the app.</li>
<li>Run the <strong>Midnight ZK circuit</strong> to attach a private property.</li>
<li>See the property show up — <strong>one record, two chains</strong>.</li>
<li>Optional: hit <code>localhost:9999/api/erc721</code> → raw JSON.</li>
</ol>
<aside class="notes">
The flow I'll click through: open the app, show both chains' blocks advancing. Mint an NFT on EVM —
show it landing. Then add the private property via Midnight, watch the batcher submit it and the record
update. Finally I'll hit the raw API endpoint so you can see it's real indexed state, not UI mockery.
</aside>
</section>
<!-- Screenshot fallback slides (filled after local boot) -->
<section>
<span class="tag" style="color:var(--es-warn);border-color:var(--es-warn);background:rgba(251,191,36,0.12)">Fallback</span>
<h2>The running app</h2>
<img src="assets/demo-frontend.png" alt="EVM + Midnight dApp running locally" style="max-height:470px; border-radius:10px;" />
<p class="small muted">Live local boot — block height ticking, EVM ↔ Midnight wallet demo, one merged token view.</p>
<aside class="notes">
This is the real app from a local boot — note the live block height top-right. If the live demo
misbehaves live, I narrate over this exactly as if it were live (mint → index → add property),
then move on. Rule: never debug live for more than ~30 seconds. See RUNBOOK.md for the click-path.
</aside>
</section>
</section>
<!-- ===================== SEGMENT 5: TOOLS / LINKS ===================== -->
<section>
<section data-background-color="#13131a">
<span class="tag">Part 5</span>
<h2>More tools & how to try it</h2>
<aside class="notes">Three minutes to land it: the tooling that makes the DX nice, then exactly how to start.</aside>
</section>
<section>
<h2>The toolbox · talking to chains</h2>
<div class="grid3 small">
<div class="card">
<h3>Chains</h3>
<p>Full sync for EVM, Midnight, Cardano, Bitcoin, Celestia, Avail, NEAR. Add one by declaring it in your config.</p>
</div>
<div class="card">
<h3>Primitives</h3>
<p>Typed event watchers — ERC-20 / ERC-721, Midnight contract state, Bitcoin & Cardano addresses. You declare <em>what</em> to track.</p>
</div>
<div class="card">
<h3>Batcher</h3>
<p>Users sign inputs → bundled into <strong>one on-chain tx</strong>. Storage-first & crash-safe — and it can <strong>pay gas for your users</strong> (next slide).</p>
</div>
</div>
<aside class="notes">
First half of the toolbox — getting data in and out of chains. You pick which chains by declaring them
in config. Primitives are the typed watchers that say "tell me about ERC-721 transfers" or "watch this
Midnight contract." And the batcher is how user inputs go back on-chain efficiently: sign, bundle into
one transaction, crash-safe because it persists first — and crucially it submits on the user's behalf,
which is what lets you sponsor their gas. More on that next.
</aside>
</section>
<section>
<h2>The toolbox · building your app</h2>
<div class="grid3 small">
<div class="card">
<h3>Sync</h3>
<p>Reads the chains <strong>automatically</strong> and orders inputs <em>deterministically</em> — every node computes the same state.</p>
</div>
<div class="card">
<h3>State machine</h3>
<p>A grammar of commands + one handler each, in TypeScript. SQL commits atomically per block.</p>
</div>
<div class="card">
<h3>API</h3>
<p>Your indexed application state, queryable over HTTP — power any frontend.</p>
</div>
<div class="card">
<h3>Orchestrator</h3>
<p>One command runs your whole local multi-chain stack. You declare the processes in your config — run only what your app needs.</p>
</div>
<div class="card">
<h3>@effectstream/wallets</h3>
<p>One <code>walletLogin()</code> across MetaMask, Cardano CIP-30, Midnight, Mina, Polkadot, Algorand… polling hidden.</p>
</div>
<div class="card" style="background:rgba(6,214,160,0.06); border-color:rgba(6,214,160,0.25);">
<h3 style="color:var(--cyan)">All TypeScript</h3>
<p>No build step in dev — write your logic, run it, iterate.</p>
</div>
</div>
<aside class="notes">
Second half — building the app itself. Sync is automatic and deterministic, that's the key word: same
inputs and code give the same state on every node. The state machine is what you actually write — a grammar
of commands and a handler each, committing SQL per block. The API exposes that state to any frontend. The
orchestrator runs the whole local stack from one command — and you control which processes run via config,
so you only spin up the chains your app uses. And the wallets package gives you one login call across every
ecosystem. It's all TypeScript, no build step in dev.
</aside>
</section>
<section>
<span class="tag">Batcher · gasless UX</span>
<h2>Sponsor your users' transactions</h2>
<p class="small muted" style="margin-bottom:0.5em;">The batcher submits inputs <em>on the user's behalf</em> — your app holds the key and pays the gas. So you can offer Web2-style, gas-free UX:</p>
<div class="grid3 small">
<div class="card">
<h3>Gasless onboarding</h3>
<p>Users sign, you pay — <strong>no gas, no faucet, no bridge</strong> to get started.</p>
</div>
<div class="card">
<h3>Metered & tiered</h3>
<p>Add a <strong>policy</strong>: subscription → <strong>5 free tx/day</strong>, rate-limits, allowlists — enforced before submit.</p>
</div>
<div class="card" style="background:rgba(6,214,160,0.06); border-color:rgba(6,214,160,0.25);">
<h3 style="color:var(--cyan)">Proven</h3>
<p>Tarochi subsidized <strong>1M+ tx</strong> this way — users never touched gas.</p>
</div>
</div>
<p class="small muted" style="margin-top:0.5em;">It's just custom batcher validation — see the <code>batcher-validations</code> template.</p>
<aside class="notes">
This is a big UX/business unlock, so call it out. Because the batcher submits the on-chain transaction on
the user's behalf — holding the app's key and paying the gas — you can give users a completely gas-free
experience: they sign, you sponsor. No faucet, no bridge, no "go buy ETH first." And it's easy to meter:
you add a validation policy on the batcher — e.g. a subscriber gets 5 free transactions a day, free users
get rate-limited, certain addresses get allowlisted — enforced before anything is submitted. That's just
custom batcher validation; there's a batcher-validations template for it. Tarochi did exactly this to
subsidize over a million transactions so players never thought about gas.
</aside>
</section>
<section>
<h2>Give it a try</h2>
<pre><code class="language-bash" data-trim>git clone https://github.com/effectstream/effectstream.git
cd effectstream/templates/evm-midnight-v2
bun i && bun run dev</code></pre>
<div class="grid2 small" style="margin-top:0.6em;">
<ul>
<li><strong>Code</strong> — github.com/effectstream/effectstream</li>
<li><strong>Docs</strong> — effectstream.github.io/docs</li>
</ul>
<ul>
<li><strong>Blog</strong> — /docs/blog</li>
<li><strong>Play</strong> — safesolver.midnight.fun</li>
</ul>
</div>
<p class="small muted" style="margin-top:0.6em;">Templates to start from: <code>minimal</code> · <code>evm-midnight-v2</code> · <code>evm-cardano</code> · <code>chess-v2</code> · <code>preorder</code></p>
<aside class="notes">
If you want to play with it after this: three commands and you have what I just demoed running locally.
Repo, docs, blog, and the live games are all linked. Pick a template close to what you want — minimal to
learn, evm-midnight for cross-chain + privacy, evm-cardano for an instant multi-chain dev env, chess for a full game.
</aside>
</section>
<section data-background-gradient="linear-gradient(135deg, #0d0d12 0%, #160f24 100%)">
<h1 style="font-size:1.3em; margin-bottom:0.1em;">Thank you 🙏</h1>
<p class="muted" style="margin-bottom:0.6em;">Questions?</p>
<img src="assets/contact-card.png" alt="EffectStream — contact card" style="max-height:330px; border-radius:16px;" />
<p class="small" style="margin-top:0.6em;">
<a href="https://github.com/effectstream/effectstream" target="_blank" rel="noopener">github.com/effectstream/effectstream</a>
·
<a href="https://effectstream.github.io/docs" target="_blank" rel="noopener">effectstream.github.io/docs</a>
</p>
<aside class="notes">
That's it — thank you. Happy to take questions — over to the host for the Q&A queue.
Backup Q&A talking points: determinism (same inputs+code → same state), how finality is handled
per chain, custody (funds never leave L1), and which chains are full-sync vs signature-only today.
</aside>
</section>
</section>
<!-- ===================== APPENDIX: Q&A BACKUP ===================== -->
<section>
<section data-background-color="#13131a">
<span class="tag">Q&A</span>
<h2>Topics we can dig into</h2>
<div class="grid3 small" style="margin-top:0.4em; text-align:left;">
<div class="card">
<h3 style="font-size:0.85em;">How it works</h3>
<p style="font-size:0.8em;">Indexer impact · finality & slow settlement · batcher mechanics · "just a metadata standard?"</p>
</div>
<div class="card">
<h3 style="font-size:0.85em;">The template</h3>
<p style="font-size:0.8em;">Ready to clone? · common cross-chain pitfalls · private data ↔ EVM linkage</p>
</div>
<div class="card">
<h3 style="font-size:0.85em;">Capabilities</h3>
<p style="font-size:0.8em;">>2-chain swaps · how ordering / time works · wallets for partners</p>
</div>
</div>
<aside class="notes">
Presenter backup — not part of the main flow. Jump to any of these via the overview (Esc / O). Each slide
has the short answer on it and the grounded detail here in the notes.
</aside>
</section>
<!-- ===== A. How it works ===== -->
<section>
<span class="tag">How it works</span>
<h3>Do recent indexer issues affect our indexer?</h3>
<ul class="small">
<li>The chain <strong>only moves forward when it can confirm blocks closed at or before the target time</strong> — otherwise it <strong>waits</strong>.</li>
<li>So a degraded upstream indexer makes sync <strong>pause and resume</strong>, never advance on bad data — state stays deterministic, nothing corrupts.</li>
<li class="muted">Sync reads from the chain's indexer/RPC and is decoupled from it; it catches up when the source recovers.</li>
</ul>
<aside class="notes">
Key point: EffectStream only advances when it can confirm blocks are closed at/before the target time —
if it can't, it waits rather than moving on. So an upstream indexer hiccup delays ingestion but never
corrupts state; it resumes from the last confirmed point when the source recovers.
</aside>
</section>
<section>
<span class="tag">How it works</span>
<h3>Sync times & slow finality (rollups settling for days)?</h3>
<ul class="small">
<li>Each chain has a <strong><code>confirmationDepth</code></strong> — sync waits N blocks before treating a block as final (EVM ~12, BTC ~6, instant-finality 0).</li>
<li>A per-chain <strong>delay</strong> ≈ <code>depth × blockTime + buffer</code> absorbs latency & reorg risk; slow/deep-settlement chains get a higher value.</li>
<li>The deterministic <strong>NTP "Main" clock</strong> ticks independently, so the app keeps a steady tick-rate regardless of any one chain's settlement speed.</li>
</ul>
<aside class="notes">
confirmationDepth (config/src/schema/sync-protocols/common.ts) sets reorg safety per chain; ordering is
provisional as blocks arrive and finalized at depth. As a based rollup, EffectStream inherits its DA
layer's finality. The NTP main protocol decouples app tick-rate from variable chain block/finality times.
</aside>
</section>
<section>
<span class="tag">How it works</span>
<h3>Batcher — per chain, then sync? Not cross-network at once?</h3>
<ul class="small">
<li><strong>Correct.</strong> The batcher batches <strong>per chain / per adapter</strong> — independent queues, one bundled tx per chain — and the sync layer picks each up.</li>
<li>It is <strong>not</strong> doing a single atomic operation across multiple networks simultaneously.</li>
<li>Batch triggers: time / size / value / hybrid / custom. Confirmation: <code>no-wait</code> · <code>wait-receipt</code> · <code>wait-effectstream-processed</code>.</li>
</ul>
<aside class="notes">
Per packages/batcher/README: each adapter target (e.g. "ethereum", "midnight") has its own queue and
polling loop, so a slow chain doesn't block others — but they're independent per-chain submissions, not
one cross-network atomic batch. Storage is the source of truth, so it survives restarts.
</aside>
</section>
<section>
<span class="tag">How it works</span>
<h3>Is this mostly a metadata standard for publishing across networks?</h3>
<ul class="small">
<li><strong>No</strong> — it's a full <strong>execution + state model</strong>: read many chains → deterministic state machine → queryable state.</li>
<li>The metadata/NFT-projection <strong>standards (PRCs) are an optional feature</strong>, not the core.</li>
</ul>
<aside class="notes">
Common misconception that conflates EffectStream with the PRC standards it publishes. Core = data
projections + deterministic computation + optional settlement/batching. PRC-3/5 (inverse projections,
serving live metadata to L1 marketplaces) are an optional layer on top, not the foundation.
</aside>
</section>
<!-- ===== B. The template ===== -->
<section>
<span class="tag">The template</span>
<h3>Ready for people to clone the Midnight-EVM work now?</h3>
<ul class="small">
<li><strong>Yes</strong> — early release but <strong>stable</strong>: only fixes are being applied, <strong>no API or structural changes</strong>.</li>
<li>You only need to <strong>clone if you want to add infra</strong> — otherwise just pull the <strong><code>@effectstream/*</code> npm packages</strong>.</li>
<li class="muted">Docs: <a href="https://effectstream.github.io/docs/" target="_blank" rel="noopener">effectstream.github.io/docs</a></li>
</ul>
<aside class="notes">
Yes — we're in early release but stable. We're only applying fixes, no API or structural changes, so it's
safe to build on. You don't have to clone the repo unless you want to add your own infrastructure — for
most apps you just install the @effectstream/* packages from npm. Docs live at effectstream.github.io/docs.
</aside>
</section>
<section>
<span class="tag">The template</span>
<h3>Common issues devs hit with cross-chain apps?</h3>
<ul class="small">
<li><strong>Finality differences & reorgs</strong> — handled by per-chain <code>confirmationDepth</code>.</li>
<li><strong>Different block times / ordering</strong> — handled by the NTP "Main" clock.</li>
<li><strong>RPC reliability & rate limits</strong> — you still need an RPC, but you can <strong>slow query frequency to match your RPC's limits</strong> (per chain).</li>
<li>Per-ecosystem <strong>wallet & address formats</strong> — abstracted by typed primitives + the wallets package.</li>
</ul>
<aside class="notes">
The framework absorbs most of the classic cross-chain pain: reorg/finality via confirmationDepth,
ordering via the deterministic clock, and wallet/format heterogeneity via @effectstream/wallets and typed
primitives. On RPCs: you still need one, but query frequency is tunable per chain, so you can dial it down
to stay under your provider's rate limits.
</aside>
</section>
<section>
<span class="tag">The template</span>
<h3>How is the private data tied to the metadata on Ethereum?</h3>
<ul class="small">
<li>Tied together <strong>in the L2 layer</strong> — a deterministic Postgres DB. Each EffectStream is a node; <strong>every node has the same deterministic database</strong>. No bridge.</li>
<li>You can <strong>mix & match any chain feature</strong>. Here we read the <strong>Midnight Ledger as if it were events</strong> — so you inherit Midnight's security.</li>
<li><strong>Selective disclosure:</strong> we reveal only "Token XXX ↔ YYY on Midnight" — you could keep that secret instead.</li>
</ul>
<aside class="notes">
The link isn't a bridge — it's the L2. Each EffectStream node runs a deterministic Postgres database, and
every node computes the same DB from the same chain events, so the association is consistent everywhere.
You can use any feature the underlying chains expose and mix them: in this example we read Midnight's
public ledger as if it emitted events, which means you lean on Midnight's security and choose what to
disclose — here, just that token XXX is associated with YYY; you could equally keep that private.
</aside>
</section>
<!-- ===== C. Capabilities ===== -->
<section>
<span class="tag">Capabilities</span>
<h3>Swaps across >2 chains at once (BTC ↔ ETH ↔ NIGHT)?</h3>
<ul class="small">
<li><strong>Yes.</strong> An action on A can trigger actions on <strong>both B and C</strong> — <strong>optimistically</strong> (parallel) or <strong>sequentially</strong>.</li>
<li><strong>You decide the sequence.</strong> It's <strong>not a bridge by nature</strong> — but you can build one on top.</li>
</ul>
<aside class="notes">
More capable than people expect. EffectStream is a multichain co-processor: an event on chain A can fan
out to actions on B and C — you can be optimistic and fire them in parallel, or make it strictly
sequential; you design the sequence. It isn't a bridge by default, but a bridge is one of the things you
can build with this. The legs still settle on the underlying chains, subject to their finality.
</aside>
</section>
<section>
<span class="tag">Capabilities</span>
<h3>What determines transaction order? How is time managed across networks?</h3>
<ul class="small">
<li><strong>Reads are ordered & deterministic</strong> — events map onto a deterministic <strong>"Main" NTP clock</strong> by source block height + time, so every node reconstructs the same order.</li>
<li><strong>Writes are best-effort</strong> — they happen whenever they're accepted; batchers <strong>retry</strong>, but still depend on external factors (gas prices, asset no longer available, bad tx).</li>
</ul>
<aside class="notes">
The clean split to give: reads vs writes. Reads/ingestion are deterministic — the Main/NTP protocol is the
official timeline, so same inputs + same code → same state on every node. Writes (posting back to chains
via the batcher) are best-effort: they land when accepted, the batcher retries, but they're still subject
to real-world factors like gas, an asset being gone, or a rejected tx. Deterministic in, best-effort out.
</aside>
</section>
<section>
<span class="tag">Capabilities</span>
<h3>What do the wallets mean for partners?</h3>
<ul class="small">
<li>One <strong><code>walletLogin()</code></strong> across MetaMask/EVM, Cardano CIP-30, Midnight (Lace), Mina, Polkadot, Algorand, Avail. Users keep <strong>L1 custody</strong> — partners never hold funds.</li>
<li>Full sync + batcher + wallet on <strong>EVM, Midnight, Cardano, Avail, Bitcoin</strong>; others are wallet-connect / sign-only today.</li>
<li>Plus <strong>JS testing wallets</strong> (Midnight · EVM · Cardano) to drive features <strong>end-to-end</strong> — great for automated & <strong>AI-assisted</strong> build/test.</li>
</ul>
<aside class="notes">
@effectstream/wallets covers 8+ wallet families with one login + sendTransaction abstraction; non-custodial,
so partners never take custody. Full-sync vs sign-only differs by chain. And we ship JS testing wallets for
Midnight, EVM, and Cardano — so you can exercise the whole flow end-to-end in code, which makes automated
testing (and using AI to build and test) much easier. Full-sync vs sign-only differs by chain per the matrix.
</aside>
</section>
</section>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/dist/reveal.js"></script>
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/plugin/notes/notes.js"></script>
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/plugin/highlight/highlight.js"></script>
<script src="https://cdn.jsdelivr.net/npm/reveal.js@5.1.0/plugin/markdown/markdown.js"></script>
<script>
Reveal.initialize({
hash: true,
slideNumber: "c/t",
transition: "slide",
backgroundTransition: "fade",
plugins: [RevealNotes, RevealHighlight, RevealMarkdown],
});
</script>
</body>
</html>