Skip to content

Commit 363f34c

Browse files
committed
feat(connect): add test zkapp
1 parent 7fe60e8 commit 363f34c

File tree

18 files changed

+435
-49
lines changed

18 files changed

+435
-49
lines changed

apps/docs/package.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,19 @@
99
"cleanup": "rimraf dist .turbo node_modules"
1010
},
1111
"dependencies": {
12-
"@theguild/remark-mermaid": "^0.1.2",
12+
"@theguild/remark-mermaid": "0.1.3",
1313
"@types/react": "latest",
14-
"lucide-react": "^0.438.0",
15-
"react": "latest",
16-
"react-dom": "latest",
17-
"typescript": "latest",
18-
"vocs": "latest"
14+
"@uidotdev/usehooks": "^2.4.1",
15+
"clsx": "^2.1.1",
16+
"lucide-react": "0.454.0",
17+
"react": "18.3.1",
18+
"react-dom": "18.3.1",
19+
"typescript": "5.6.3",
20+
"vocs": "1.0.0-alpha.62"
1921
},
2022
"devDependencies": {
2123
"@mina-js/klesia-sdk": "workspace:*",
2224
"@mina-js/connect": "workspace:*",
23-
"daisyui": "^4.12.10"
25+
"daisyui": "4.12.14"
2426
}
2527
}
Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
import { createStore } from "@mina-js/connect";
2+
import { useLocalStorage, useObjectState } from "@uidotdev/usehooks";
3+
import { clsx } from "clsx";
4+
import { useState, useSyncExternalStore } from "react";
5+
6+
const store = createStore();
7+
8+
export const TestZkApp = () => {
9+
const [currentProvider, setCurrentProvider] = useLocalStorage(
10+
"minajs:provider",
11+
"",
12+
);
13+
const [message, setMessage] = useState("A message to sign");
14+
const [fields, setFields] = useState('["1", "2", "3"]');
15+
const [transactionBody, setTransactionBody] = useObjectState({
16+
to: "B62qnVUL6A53E4ZaGd3qbTr6RCtEZYTu3kTijVrrquNpPo4d3MuJ3nb",
17+
amount: "3000000000",
18+
fee: "100000000",
19+
memo: "Hello from MinaJS!",
20+
nonce: "0",
21+
});
22+
const [results, setResults] = useObjectState({
23+
mina_accounts: "",
24+
mina_chainId: "",
25+
mina_getBalance: "",
26+
mina_sign: "",
27+
mina_signFields: "",
28+
mina_signTransaction: "",
29+
});
30+
const providers = useSyncExternalStore(store.subscribe, store.getProviders);
31+
const provider = providers.find(
32+
(p) => p.info.slug === currentProvider,
33+
)?.provider;
34+
const fetchAccounts = async () => {
35+
if (!provider) return;
36+
const { result } = await provider.request({
37+
method: "mina_accounts",
38+
});
39+
setResults(() => ({ mina_accounts: JSON.stringify(result) }));
40+
};
41+
const fetchRequestAccounts = async () => {
42+
if (!provider) return;
43+
const { result } = await provider.request({
44+
method: "mina_requestAccounts",
45+
});
46+
setResults(() => ({ mina_accounts: JSON.stringify(result) }));
47+
};
48+
const fetchChainId = async () => {
49+
if (!provider) return;
50+
const { result } = await provider.request({
51+
method: "mina_chainId",
52+
});
53+
setResults(() => ({ mina_chainId: result }));
54+
};
55+
const fetchBalance = async () => {
56+
if (!provider) return;
57+
const { result } = await provider.request({
58+
method: "mina_getBalance",
59+
});
60+
setResults(() => ({ mina_getBalance: result }));
61+
};
62+
const signMessage = async () => {
63+
if (!provider) return;
64+
const { result } = await provider.request({
65+
method: "mina_sign",
66+
params: [message],
67+
});
68+
setResults(() => ({ mina_sign: JSON.stringify(result, undefined, "\t") }));
69+
};
70+
const signFields = async () => {
71+
if (!provider) return;
72+
const parsedFields = JSON.parse(fields);
73+
const { result } = await provider.request({
74+
method: "mina_signFields",
75+
params: [parsedFields],
76+
});
77+
setResults(() => ({
78+
mina_signFields: JSON.stringify(result, undefined, "\t"),
79+
}));
80+
};
81+
const createNullifier = async () => {
82+
if (!provider) return;
83+
const parsedFields = JSON.parse(fields);
84+
const { result } = await provider.request({
85+
method: "mina_createNullifier",
86+
params: [parsedFields],
87+
});
88+
setResults(() => ({
89+
mina_signFields: JSON.stringify(result, undefined, "\t"),
90+
}));
91+
};
92+
const signTransaction = async () => {
93+
if (!provider) return;
94+
const { result: accounts } = await provider.request({
95+
method: "mina_accounts",
96+
});
97+
const transaction = {
98+
...transactionBody,
99+
from: accounts[0],
100+
};
101+
const { result } = await provider.request({
102+
method: "mina_signTransaction",
103+
params: [{ transaction }],
104+
});
105+
setResults(() => ({
106+
mina_signTransaction: JSON.stringify(result, undefined, "\t"),
107+
}));
108+
};
109+
const signZkAppCommand = async () => {
110+
if (!provider) return;
111+
const { result: accounts } = await provider.request({
112+
method: "mina_accounts",
113+
});
114+
const command = {
115+
zkappCommand: {
116+
accountUpdates: [],
117+
memo: "E4YM2vTHhWEg66xpj52JErHUBU4pZ1yageL4TVDDpTTSsv8mK6YaH",
118+
feePayer: {
119+
body: {
120+
publicKey: accounts[0],
121+
fee: "100000000",
122+
validUntil: "100000",
123+
nonce: "1",
124+
},
125+
authorization: "",
126+
},
127+
},
128+
feePayer: {
129+
feePayer: accounts[0],
130+
fee: transactionBody.fee,
131+
nonce: transactionBody.nonce,
132+
memo: transactionBody.memo,
133+
},
134+
};
135+
const { result } = await provider.request({
136+
method: "mina_signTransaction",
137+
params: [{ command }],
138+
});
139+
setResults(() => ({
140+
mina_signTransaction: JSON.stringify(result, undefined, "\t"),
141+
}));
142+
};
143+
return (
144+
<main className="flex flex-col gap-8">
145+
<h1 className="text-3xl font-bold">Test zkApp</h1>
146+
<section className="card bg-neutral">
147+
<div className="card-body gap-4">
148+
<h2 className="card-title">Available Wallets</h2>
149+
<ul className="list-none">
150+
{providers.map((provider) => {
151+
const active = currentProvider === provider.info.slug;
152+
return (
153+
<li
154+
key={provider.info.slug}
155+
className="flex items-center gap-8"
156+
>
157+
<img
158+
src={provider.info.icon}
159+
alt={provider.info.name}
160+
className="w-8 h-8"
161+
/>
162+
<h3 className="flex-1 text-lg">{provider.info.name}</h3>
163+
<button
164+
type="button"
165+
className={clsx(
166+
"btn",
167+
active ? "btn-neutral" : "btn-secondary",
168+
)}
169+
onClick={() => setCurrentProvider(provider.info.slug)}
170+
>
171+
{active ? "Connected" : "Connect"}
172+
</button>
173+
</li>
174+
);
175+
})}
176+
</ul>
177+
</div>
178+
</section>
179+
<section className="card bg-neutral">
180+
<div className="card-body gap-4">
181+
<h2 className="card-title">Queries</h2>
182+
<div className="flex flex-col gap-2">
183+
<label>mina_accounts / mina_requestAccounts</label>
184+
<div className="flex justify-between items-center gap-4">
185+
<input
186+
value={results.mina_accounts}
187+
className="input input-bordered flex-1"
188+
/>
189+
<button
190+
type="button"
191+
className="btn btn-primary"
192+
onClick={fetchAccounts}
193+
>
194+
Get Accounts
195+
</button>
196+
<button
197+
type="button"
198+
className="btn btn-primary"
199+
onClick={fetchRequestAccounts}
200+
>
201+
Request Accounts
202+
</button>
203+
</div>
204+
<label>mina_chainId</label>
205+
<div className="flex justify-between items-center gap-4">
206+
<input
207+
value={results.mina_chainId}
208+
className="input input-bordered flex-1"
209+
/>
210+
<button
211+
type="button"
212+
className="btn btn-primary"
213+
onClick={fetchChainId}
214+
>
215+
Get Chain ID
216+
</button>
217+
</div>
218+
<label>mina_getBalance</label>
219+
<div className="flex justify-between items-center gap-4">
220+
<input
221+
value={results.mina_getBalance}
222+
className="input input-bordered flex-1"
223+
/>
224+
<button
225+
type="button"
226+
className="btn btn-primary"
227+
onClick={fetchBalance}
228+
>
229+
Get Balance
230+
</button>
231+
</div>
232+
</div>
233+
</div>
234+
</section>
235+
<section className="card bg-neutral">
236+
<div className="card-body gap-4">
237+
<h2 className="card-title">Sign Message</h2>
238+
<div className="flex flex-col gap-2">
239+
<label>mina_sign</label>
240+
<div className="flex justify-between items-center gap-4">
241+
<input
242+
value={message}
243+
onChange={(event) => setMessage(event.target.value)}
244+
className="input input-bordered flex-1"
245+
/>
246+
<button
247+
type="button"
248+
className="btn btn-primary"
249+
onClick={signMessage}
250+
>
251+
Sign Message
252+
</button>
253+
</div>
254+
<label>Result</label>
255+
<textarea
256+
value={results.mina_sign}
257+
className="textarea textarea-bordered h-48 resize-none"
258+
/>
259+
</div>
260+
</div>
261+
</section>
262+
<section className="card bg-neutral">
263+
<div className="card-body gap-4">
264+
<h2 className="card-title">Sign Fields / Create Nullifier</h2>
265+
<p>mina_signFields / mina_createNullifier</p>
266+
<div className="flex flex-col gap-2">
267+
<div className="flex justify-between items-center gap-4">
268+
<input
269+
value={fields}
270+
onChange={(event) => setFields(event.target.value)}
271+
className="input input-bordered flex-1"
272+
/>
273+
<button
274+
type="button"
275+
className="btn btn-primary"
276+
onClick={signFields}
277+
>
278+
Sign Fields
279+
</button>
280+
<button
281+
type="button"
282+
className="btn btn-primary"
283+
onClick={createNullifier}
284+
>
285+
Create Nullifier
286+
</button>
287+
</div>
288+
<label>Result</label>
289+
<textarea
290+
value={results.mina_signFields}
291+
className="textarea textarea-bordered h-48 resize-none"
292+
/>
293+
</div>
294+
</div>
295+
</section>
296+
<section className="card bg-neutral">
297+
<div className="card-body gap-4">
298+
<h2 className="card-title">Sign Transaction</h2>
299+
<p>mina_signTransaction</p>
300+
<div className="flex flex-col gap-2">
301+
<label>To</label>
302+
<input
303+
value={transactionBody.to}
304+
onChange={(event) =>
305+
setTransactionBody(() => ({ to: event.target.value }))
306+
}
307+
className="input input-bordered"
308+
/>
309+
<label>Amount</label>
310+
<input
311+
value={transactionBody.amount}
312+
onChange={(event) =>
313+
setTransactionBody(() => ({ amount: event.target.value }))
314+
}
315+
className="input input-bordered"
316+
/>
317+
<label>Fee</label>
318+
<input
319+
value={transactionBody.fee}
320+
onChange={(event) =>
321+
setTransactionBody(() => ({ fee: event.target.value }))
322+
}
323+
className="input input-bordered"
324+
/>
325+
<label>Memo</label>
326+
<input
327+
value={transactionBody.memo}
328+
onChange={(event) =>
329+
setTransactionBody(() => ({ memo: event.target.value }))
330+
}
331+
className="input input-bordered"
332+
/>
333+
<label>Nonce</label>
334+
<input
335+
value={transactionBody.nonce}
336+
onChange={(event) =>
337+
setTransactionBody(() => ({ nonce: event.target.value }))
338+
}
339+
className="input input-bordered"
340+
/>
341+
</div>
342+
<div className="flex gap-4">
343+
<button
344+
type="button"
345+
className="btn btn-primary flex-1"
346+
onClick={signTransaction}
347+
>
348+
Sign Transaction
349+
</button>
350+
<button
351+
type="button"
352+
className="btn btn-primary flex-1"
353+
onClick={signZkAppCommand}
354+
>
355+
Sign ZK App Command
356+
</button>
357+
</div>
358+
<div className="flex flex-col gap-2">
359+
<label>Result</label>
360+
<textarea
361+
value={results.mina_signTransaction}
362+
className="textarea textarea-bordered h-48 resize-none"
363+
/>
364+
</div>
365+
</div>
366+
</section>
367+
</main>
368+
);
369+
};

0 commit comments

Comments
 (0)