@toon-protocol/client 0.20.3 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,412 @@
1
+ // src/channel/mina-channel-deploy.ts
2
+ import { hexToMinaBase58PrivateKey as hexToMinaBase58PrivateKey2 } from "@toon-protocol/core";
3
+
4
+ // src/channel/mina-channel-open.ts
5
+ import { hexToMinaBase58PrivateKey } from "@toon-protocol/core";
6
+ var cachedO1js = null;
7
+ var cachedPaymentChannel = null;
8
+ var compiledContract = null;
9
+ var runtimeOverride = null;
10
+ async function loadMinaRuntime() {
11
+ if (cachedO1js && cachedPaymentChannel) {
12
+ return { o1js: cachedO1js, PaymentChannel: cachedPaymentChannel };
13
+ }
14
+ if (runtimeOverride) {
15
+ const injected = await runtimeOverride();
16
+ cachedO1js = injected.o1js;
17
+ cachedPaymentChannel = injected.PaymentChannel;
18
+ return injected;
19
+ }
20
+ const { createRequire } = await import("module");
21
+ const nodePath = await import("path");
22
+ const requireHere = createRequire(import.meta.url);
23
+ const mzkPkgPath = requireHere.resolve(
24
+ "@toon-protocol/mina-zkapp/package.json"
25
+ );
26
+ const requireFromMzk = createRequire(mzkPkgPath);
27
+ const o1js = requireFromMzk("o1js");
28
+ const mzkPkgJson = requireFromMzk(mzkPkgPath);
29
+ const mzkDir = nodePath.dirname(mzkPkgPath);
30
+ const mzkEntry = nodePath.join(mzkDir, mzkPkgJson.main ?? "dist/index.js");
31
+ const mzk = requireFromMzk(mzkEntry);
32
+ const PaymentChannel = mzk.PaymentChannel ?? mzk.default?.PaymentChannel;
33
+ if (!PaymentChannel) {
34
+ throw new Error(
35
+ "@toon-protocol/mina-zkapp does not export PaymentChannel \u2014 cannot open a Mina channel"
36
+ );
37
+ }
38
+ cachedO1js = o1js;
39
+ cachedPaymentChannel = PaymentChannel;
40
+ return { o1js, PaymentChannel };
41
+ }
42
+ async function getO1js() {
43
+ return (await loadMinaRuntime()).o1js;
44
+ }
45
+ var compiledVerificationKeyHash;
46
+ async function getCompiledPaymentChannel() {
47
+ const { PaymentChannel } = await loadMinaRuntime();
48
+ if (!compiledContract) {
49
+ const compiled = await PaymentChannel.compile();
50
+ compiledVerificationKeyHash = compiled?.verificationKey?.hash?.toString() ?? void 0;
51
+ compiledContract = PaymentChannel;
52
+ }
53
+ return compiledContract;
54
+ }
55
+ function getCompiledVerificationKeyHash() {
56
+ return compiledVerificationKeyHash;
57
+ }
58
+ function abandonLeakedMinaTransaction(o1js) {
59
+ const ctx = o1js.Mina.currentTransaction;
60
+ const c = ctx;
61
+ if (!c || typeof c.has !== "function" || typeof c.leave !== "function")
62
+ return;
63
+ for (let i = 0; i < 64 && c.has(); i += 1) {
64
+ try {
65
+ const id = typeof c.id === "function" ? c.id() : void 0;
66
+ c.leave(id);
67
+ } catch {
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ async function buildMinaTransaction(o1js, feePayer, f) {
73
+ try {
74
+ return await o1js.Mina.transaction(feePayer, f);
75
+ } catch (err) {
76
+ abandonLeakedMinaTransaction(o1js);
77
+ throw err;
78
+ }
79
+ }
80
+ var MINA_ACCOUNT_CREATION_FEE_NANOMINA = 1000000000n;
81
+ var MinaFeePayerUnfundedError = class extends Error {
82
+ constructor(address, requiredNanomina, haveNanomina, graphqlUrl) {
83
+ const mina = (n) => `${(Number(n) / 1e9).toFixed(3)} MINA`;
84
+ const state = haveNanomina === void 0 ? `does not exist on-chain (0 MINA)` : `holds only ${mina(haveNanomina)}`;
85
+ super(
86
+ `Mina fee-payer wallet ${address} ${state} but needs ~${mina(
87
+ requiredNanomina
88
+ )} to open a payment channel (\u22481 MINA account-creation fee + tx fees) on ${graphqlUrl}. Fund ${address} and retry \u2014 no circuit was compiled (this check runs before the ~1-3 min compile).`
89
+ );
90
+ this.address = address;
91
+ this.requiredNanomina = requiredNanomina;
92
+ this.haveNanomina = haveNanomina;
93
+ this.graphqlUrl = graphqlUrl;
94
+ this.name = "MinaFeePayerUnfundedError";
95
+ }
96
+ address;
97
+ requiredNanomina;
98
+ haveNanomina;
99
+ graphqlUrl;
100
+ };
101
+ async function assertMinaFeePayerFunded(params) {
102
+ const { o1js, payerPublicKey, requiredNanomina, graphqlUrl } = params;
103
+ const address = payerPublicKey.toBase58();
104
+ const res = await o1js.fetchAccount({ publicKey: payerPublicKey });
105
+ if (res.error || !res.account) {
106
+ throw new MinaFeePayerUnfundedError(
107
+ address,
108
+ requiredNanomina,
109
+ void 0,
110
+ graphqlUrl
111
+ );
112
+ }
113
+ const raw = res.account.balance?.toString();
114
+ const have = raw !== void 0 ? BigInt(raw) : 0n;
115
+ if (have < requiredNanomina) {
116
+ throw new MinaFeePayerUnfundedError(
117
+ address,
118
+ requiredNanomina,
119
+ have,
120
+ graphqlUrl
121
+ );
122
+ }
123
+ }
124
+ var MINA_CHANNEL_STATE_OPEN = 1n;
125
+ var MINA_CHANNEL_STATE_UNINITIALIZED = 0n;
126
+ async function openMinaChannelOnChain(params) {
127
+ const { Mina, PrivateKey, PublicKey, Field, AccountUpdate, fetchAccount } = await getO1js();
128
+ const network = Mina.Network(params.graphqlUrl);
129
+ Mina.setActiveInstance(network);
130
+ const txFee = params.feeNanomina ?? 100000000n;
131
+ const payerKeyBase58 = hexToMinaBase58PrivateKey(params.payerPrivateKey);
132
+ const payerPrivateKey = PrivateKey.fromBase58(payerKeyBase58);
133
+ const payerPublicKey = payerPrivateKey.toPublicKey();
134
+ const zkAppPublicKey = PublicKey.fromBase58(params.zkAppAddress);
135
+ const readChannelState = async () => {
136
+ const res = await fetchAccount({ publicKey: zkAppPublicKey });
137
+ if (res.error || !res.account) {
138
+ throw new Error(
139
+ `Mina zkApp account ${params.zkAppAddress} not found on-chain (${String(
140
+ res.error
141
+ )}) \u2014 deploy the PaymentChannel zkApp before opening a channel`
142
+ );
143
+ }
144
+ const appState = res.account.zkapp?.appState;
145
+ const raw = appState?.[3]?.toString() ?? "0";
146
+ return BigInt(raw);
147
+ };
148
+ const readDepositTotal = async () => {
149
+ const res = await fetchAccount({ publicKey: zkAppPublicKey });
150
+ if (res.error || !res.account) {
151
+ throw new Error(
152
+ `Mina zkApp account ${params.zkAppAddress} not found on-chain (${String(
153
+ res.error
154
+ )}) \u2014 deploy the PaymentChannel zkApp before opening a channel`
155
+ );
156
+ }
157
+ const appState = res.account.zkapp?.appState;
158
+ const raw = appState?.[4]?.toString() ?? "0";
159
+ return BigInt(raw);
160
+ };
161
+ const currentState = await readChannelState();
162
+ await fetchAccount({ publicKey: payerPublicKey });
163
+ let opened = false;
164
+ let initTxHash;
165
+ let zkApp;
166
+ const getZkApp = async () => {
167
+ if (!zkApp) {
168
+ const PaymentChannel = await getCompiledPaymentChannel();
169
+ zkApp = new PaymentChannel(zkAppPublicKey);
170
+ }
171
+ return zkApp;
172
+ };
173
+ if (currentState === MINA_CHANNEL_STATE_UNINITIALIZED) {
174
+ const channel = await getZkApp();
175
+ const participantA = payerPublicKey;
176
+ const participantB = params.peerPublicKey ? PublicKey.fromBase58(params.peerPublicKey) : payerPublicKey;
177
+ const nonce = Field(0);
178
+ const timeoutField = Field((params.timeout ?? 86400n).toString());
179
+ const tokenIdField = Field(params.tokenId ?? "1");
180
+ await fetchAccount({ publicKey: zkAppPublicKey });
181
+ await fetchAccount({ publicKey: payerPublicKey });
182
+ const initTx = await buildMinaTransaction(
183
+ await getO1js(),
184
+ { sender: payerPublicKey, fee: Number(txFee) },
185
+ async () => {
186
+ await channel.initializeChannel(
187
+ participantA,
188
+ participantB,
189
+ nonce,
190
+ timeoutField,
191
+ tokenIdField
192
+ );
193
+ }
194
+ );
195
+ await initTx.prove();
196
+ const sentInit = await initTx.sign([payerPrivateKey]).send();
197
+ initTxHash = sentInit.hash ?? void 0;
198
+ opened = true;
199
+ await sentInit.wait();
200
+ await fetchAccount({ publicKey: zkAppPublicKey });
201
+ await fetchAccount({ publicKey: payerPublicKey });
202
+ } else if (currentState !== MINA_CHANNEL_STATE_OPEN) {
203
+ throw new Error(
204
+ `Mina channel ${params.zkAppAddress} is in state ${currentState} (not UNINITIALIZED/OPEN) \u2014 cannot open`
205
+ );
206
+ }
207
+ let depositTxHash;
208
+ if (params.deposit && params.deposit.amount > 0n) {
209
+ const channel = await getZkApp();
210
+ await fetchAccount({ publicKey: zkAppPublicKey });
211
+ const amountField = Field(params.deposit.amount.toString());
212
+ const depositTx = await buildMinaTransaction(
213
+ await getO1js(),
214
+ { sender: payerPublicKey, fee: Number(txFee) },
215
+ async () => {
216
+ await channel.deposit(amountField, payerPublicKey);
217
+ }
218
+ );
219
+ await depositTx.prove();
220
+ const sentDeposit = await depositTx.sign([payerPrivateKey]).send();
221
+ depositTxHash = sentDeposit.hash ?? void 0;
222
+ await sentDeposit.wait();
223
+ await fetchAccount({ publicKey: zkAppPublicKey });
224
+ await fetchAccount({ publicKey: payerPublicKey });
225
+ }
226
+ let finalState;
227
+ try {
228
+ finalState = Number(await readChannelState());
229
+ } catch {
230
+ finalState = opened ? Number(MINA_CHANNEL_STATE_OPEN) : Number(currentState);
231
+ }
232
+ if (opened && finalState === Number(MINA_CHANNEL_STATE_UNINITIALIZED)) {
233
+ finalState = Number(MINA_CHANNEL_STATE_OPEN);
234
+ }
235
+ void AccountUpdate;
236
+ let depositTotal;
237
+ try {
238
+ depositTotal = await readDepositTotal();
239
+ } catch {
240
+ depositTotal = 0n;
241
+ }
242
+ return {
243
+ zkAppAddress: params.zkAppAddress,
244
+ opened,
245
+ initTxHash,
246
+ depositTxHash,
247
+ channelState: finalState,
248
+ depositTotal
249
+ };
250
+ }
251
+
252
+ // src/channel/mina-channel-deploy.ts
253
+ var APPSTATE_CHANNEL_HASH = 0;
254
+ var APPSTATE_CHANNEL_STATE = 3;
255
+ var STATE_UNINITIALIZED = 0n;
256
+ var STATE_OPEN = 1n;
257
+ function requiredDeployBalanceNanomina(feeNanomina) {
258
+ return MINA_ACCOUNT_CREATION_FEE_NANOMINA + feeNanomina * 2n;
259
+ }
260
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
261
+ async function deployMinaChannelZkApp(params) {
262
+ const { o1js } = await loadMinaRuntime();
263
+ const { Mina, PrivateKey, AccountUpdate, fetchAccount } = o1js;
264
+ const progress = params.onProgress ?? (() => void 0);
265
+ const network = Mina.Network(params.graphqlUrl);
266
+ Mina.setActiveInstance(network);
267
+ const payerKeyBase58 = hexToMinaBase58PrivateKey2(params.payerPrivateKey);
268
+ const payerPrivateKey = PrivateKey.fromBase58(payerKeyBase58);
269
+ const payerPublicKey = payerPrivateKey.toPublicKey();
270
+ const feeNanomina = params.feeNanomina ?? 100000000n;
271
+ await assertMinaFeePayerFunded({
272
+ o1js,
273
+ payerPublicKey,
274
+ requiredNanomina: requiredDeployBalanceNanomina(feeNanomina),
275
+ graphqlUrl: params.graphqlUrl
276
+ });
277
+ const zkAppPrivateKey = params.zkAppPrivateKey ? PrivateKey.fromBase58(params.zkAppPrivateKey) : PrivateKey.random();
278
+ const zkAppPublicKey = zkAppPrivateKey.toPublicKey();
279
+ const zkAppAddress = zkAppPublicKey.toBase58();
280
+ if (!params.zkAppPrivateKey) {
281
+ await params.onDeploying?.({
282
+ zkAppAddress,
283
+ zkAppPrivateKey: zkAppPrivateKey.toBase58(),
284
+ feePayer: payerPublicKey.toBase58()
285
+ });
286
+ }
287
+ progress(
288
+ "compiling the PaymentChannel circuit (one-time, can take 1-3 minutes)\u2026"
289
+ );
290
+ const PaymentChannel = await getCompiledPaymentChannel();
291
+ const zkApp = new PaymentChannel(zkAppPublicKey);
292
+ await fetchAccount({ publicKey: payerPublicKey });
293
+ progress(
294
+ `deploying a dedicated PaymentChannel zkApp ${zkAppAddress} (costs the 1 MINA account-creation fee + tx fee)\u2026`
295
+ );
296
+ const fee = Number(feeNanomina);
297
+ const deployTx = await buildMinaTransaction(
298
+ o1js,
299
+ { sender: payerPublicKey, fee },
300
+ async () => {
301
+ AccountUpdate.fundNewAccount(payerPublicKey);
302
+ await zkApp.deploy();
303
+ }
304
+ );
305
+ await deployTx.prove();
306
+ const sent = await deployTx.sign([payerPrivateKey, zkAppPrivateKey]).send();
307
+ const deployTxHash = sent.hash ?? void 0;
308
+ progress(
309
+ `deploy tx sent${deployTxHash ? ` (${deployTxHash})` : ""} \u2014 waiting for inclusion (devnet blocks are ~3 minutes)\u2026`
310
+ );
311
+ const interval = params.pollIntervalMs ?? 15e3;
312
+ const budget = params.pollTimeoutMs ?? 54e4;
313
+ const started = Date.now();
314
+ try {
315
+ await sent.wait();
316
+ } catch {
317
+ }
318
+ for (; ; ) {
319
+ const res = await fetchAccount({ publicKey: zkAppPublicKey });
320
+ if (!res.error && res.account) break;
321
+ if (Date.now() - started > budget) {
322
+ throw new Error(
323
+ `Mina zkApp ${zkAppAddress} did not appear on-chain within ${Math.round(budget / 6e4)} minutes of the deploy tx${deployTxHash ? ` (${deployTxHash})` : ""} \u2014 check the tx on minascan before retrying (the zkApp key has been recorded)`
324
+ );
325
+ }
326
+ await sleep(interval);
327
+ }
328
+ progress(`zkApp ${zkAppAddress} is on-chain (bare \u2014 initialize follows).`);
329
+ return {
330
+ zkAppAddress,
331
+ zkAppPrivateKey: zkAppPrivateKey.toBase58(),
332
+ feePayer: payerPublicKey.toBase58(),
333
+ ...deployTxHash !== void 0 ? { deployTxHash } : {},
334
+ ...getCompiledVerificationKeyHash() !== void 0 ? { vkHash: getCompiledVerificationKeyHash() } : {}
335
+ };
336
+ }
337
+ async function ensureOwnedMinaZkApp(params) {
338
+ const { o1js } = await loadMinaRuntime();
339
+ const { Mina, PrivateKey, PublicKey, Field, Poseidon, fetchAccount } = o1js;
340
+ const progress = params.onProgress ?? (() => void 0);
341
+ const network = Mina.Network(params.graphqlUrl);
342
+ Mina.setActiveInstance(network);
343
+ const payerKeyBase58 = hexToMinaBase58PrivateKey2(params.payerPrivateKey);
344
+ const clientPublicKey = PrivateKey.fromBase58(payerKeyBase58).toPublicKey();
345
+ const peerPublicKey = PublicKey.fromBase58(params.peerPublicKey);
346
+ if (!Poseidon) {
347
+ throw new Error(
348
+ "the loaded o1js runtime does not expose Poseidon \u2014 cannot derive the pair hash"
349
+ );
350
+ }
351
+ const expectedChannelHash = Poseidon.hash([
352
+ clientPublicKey.x,
353
+ peerPublicKey.x,
354
+ Field(0)
355
+ ]).toString();
356
+ const candidates = [];
357
+ if (params.deployed?.zkAppAddress) {
358
+ candidates.push({ address: params.deployed.zkAppAddress, ownRecord: true });
359
+ }
360
+ if (params.candidateZkAppAddress && params.candidateZkAppAddress !== params.deployed?.zkAppAddress) {
361
+ candidates.push({
362
+ address: params.candidateZkAppAddress,
363
+ ownRecord: false
364
+ });
365
+ }
366
+ let pendingOwnKey;
367
+ for (const candidate of candidates) {
368
+ let appState;
369
+ try {
370
+ const res = await fetchAccount({
371
+ publicKey: PublicKey.fromBase58(candidate.address)
372
+ });
373
+ if (res.error || !res.account) {
374
+ if (candidate.ownRecord && params.deployed?.zkAppPrivateKey) {
375
+ pendingOwnKey = params.deployed.zkAppPrivateKey;
376
+ }
377
+ continue;
378
+ }
379
+ appState = res.account.zkapp?.appState;
380
+ } catch {
381
+ continue;
382
+ }
383
+ const channelHash = appState?.[APPSTATE_CHANNEL_HASH]?.toString() ?? "";
384
+ const channelState = BigInt(
385
+ appState?.[APPSTATE_CHANNEL_STATE]?.toString() ?? "0"
386
+ );
387
+ if (channelState === STATE_OPEN && channelHash === expectedChannelHash) {
388
+ progress(`reusing our open Mina channel zkApp ${candidate.address}.`);
389
+ return { zkAppAddress: candidate.address, deployed: false };
390
+ }
391
+ if (candidate.ownRecord && channelState === STATE_UNINITIALIZED) {
392
+ progress(
393
+ `reusing our recorded (uninitialized) Mina zkApp ${candidate.address}.`
394
+ );
395
+ return { zkAppAddress: candidate.address, deployed: false };
396
+ }
397
+ }
398
+ const record = await deployMinaChannelZkApp({
399
+ ...params,
400
+ ...pendingOwnKey ? { zkAppPrivateKey: pendingOwnKey } : {},
401
+ ...params.onDeploying ? { onDeploying: params.onDeploying } : {}
402
+ });
403
+ await params.onDeployed?.(record);
404
+ return { zkAppAddress: record.zkAppAddress, deployed: true, record };
405
+ }
406
+
407
+ export {
408
+ openMinaChannelOnChain,
409
+ deployMinaChannelZkApp,
410
+ ensureOwnedMinaZkApp
411
+ };
412
+ //# sourceMappingURL=chunk-7TDAU43L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/channel/mina-channel-deploy.ts","../src/channel/mina-channel-open.ts"],"sourcesContent":["/**\n * Per-pair Mina `PaymentChannel` zkApp deployment — the missing zero-config\n * piece of the Mina channel-open path.\n *\n * The `PaymentChannel` zkApp is SINGLE-PAIR: `initializeChannel` bakes\n * `channelHash = Poseidon([participantA.x, participantB.x, nonce])` into the\n * zkApp's on-chain state, and the zkApp address IS the channel id. One\n * deployment therefore serves exactly one client↔connector pair — a fresh\n * client cannot reuse the pair-bound zkApp another identity opened (its claim\n * would fail `mina_claim_verification_failed`: wrong on-chain channelHash).\n *\n * {@link ensureOwnedMinaZkApp} makes the open path self-sufficient:\n *\n * 1. Check the candidates (a previously recorded own deployment first, then\n * the announce/preset-resolved address) for a zkApp that is ALREADY OURS\n * — on-chain OPEN with channelHash == Poseidon([client.x, peer.x, 0]),\n * or our own recorded deployment still awaiting initialization.\n * 2. Otherwise deploy a FRESH zkApp (new random zkApp key) from the same\n * npm `@toon-protocol/mina-zkapp` + o1js build the claim verifier uses —\n * the deployed verification key is the locally compiled one BY\n * CONSTRUCTION, so the \"vk drift\" class of failure cannot occur.\n *\n * The connector accepts claims against ANY zkApp it can read on-chain (its\n * inbound verify resolves the claim's `zkAppAddress` on-chain and registers\n * unknown channels dynamically), so no connector-side registration step is\n * needed.\n *\n * Deploy and initialize are SEPARATE transactions (connector Issue #128:\n * `initializeChannel`'s `getAndRequireEquals` precondition fails while the\n * account does not exist yet) — this module only DEPLOYS; the normal\n * {@link openMinaChannelOnChain} initialize+deposit flow runs afterwards.\n *\n * Reuses the `createRequire`-anchored o1js loader from `mina-channel-open.ts`\n * (one shared CJS o1js instance — the ESM/CJS split instance bug) and stays\n * lazy so npm consumers who never touch Mina never load the WASM runtime.\n *\n * @module\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { hexToMinaBase58PrivateKey } from '@toon-protocol/core';\nimport {\n MINA_ACCOUNT_CREATION_FEE_NANOMINA,\n assertMinaFeePayerFunded,\n buildMinaTransaction,\n getCompiledPaymentChannel,\n getCompiledVerificationKeyHash,\n loadMinaRuntime,\n} from './mina-channel-open.js';\n\n/** PaymentChannel appState indices (see mina-channel-open.ts). */\nconst APPSTATE_CHANNEL_HASH = 0;\nconst APPSTATE_CHANNEL_STATE = 3;\n/** CHANNEL_STATE values. */\nconst STATE_UNINITIALIZED = 0n;\nconst STATE_OPEN = 1n;\n\n/** Everything worth persisting about an auto-deployed zkApp. */\nexport interface MinaZkAppDeployRecord {\n /** The deployed zkApp B62 address (== the channel id). */\n zkAppAddress: string;\n /** The zkApp's `EK…` base58 private key (needed to co-sign future txs). */\n zkAppPrivateKey: string;\n /** The fee payer (client Mina B62) that funded the deployment. */\n feePayer: string;\n /** Deploy tx hash, when the send surfaced one. */\n deployTxHash?: string;\n /** Verification-key hash of the compiled contract that was deployed. */\n vkHash?: string;\n}\n\nexport interface DeployMinaZkAppParams {\n /** Mina GraphQL endpoint to deploy through. */\n graphqlUrl: string;\n /** Fee payer private key (hex scalar or `EK…` base58 — same as the opener). */\n payerPrivateKey: string;\n /**\n * Deploy fee in nanomina. Default 100_000_000 (0.1 MINA); the new zkApp\n * account additionally costs the protocol's 1 MINA account-creation fee,\n * charged to the payer via `AccountUpdate.fundNewAccount`.\n */\n feeNanomina?: bigint;\n /**\n * Reuse THIS zkApp key (`EK…` base58) instead of generating a fresh one.\n * Used to re-attempt a deployment whose key was already recorded (so a\n * crashed/pending first attempt does not orphan a NEW ~1.1-MINA zkApp on\n * every retry — the SAME address is redeployed).\n */\n zkAppPrivateKey?: string;\n /**\n * Called with the zkApp address + key IMMEDIATELY after the key is known and\n * the fee-payer preflight passes — BEFORE the circuit compiles or the deploy\n * tx is sent. The rig store persists this so a crash between send and\n * confirmation reuses the SAME zkApp next run rather than deploying (and\n * paying for) a second one. Fired only for a freshly-generated key (a\n * redeploy of a recorded key is already persisted).\n */\n onDeploying?: (record: {\n zkAppAddress: string;\n zkAppPrivateKey: string;\n feePayer: string;\n }) => void | Promise<void>;\n /** Progress lines (compile/deploy/inclusion phases take minutes). */\n onProgress?: (line: string) => void;\n /** Inclusion poll interval ms (default 15_000; tests shrink it). */\n pollIntervalMs?: number;\n /** Inclusion poll budget ms (default 540_000 ≈ 9 min). */\n pollTimeoutMs?: number;\n}\n\nexport interface EnsureOwnedMinaZkAppParams extends DeployMinaZkAppParams {\n /** The connector peer's Mina settlement B62 (participantB of the pair). */\n peerPublicKey: string;\n /** A previously recorded own deployment for this identity/pair, if any. */\n deployed?: { zkAppAddress: string; zkAppPrivateKey: string };\n /** The announce/preset-resolved zkApp address (checked after `deployed`). */\n candidateZkAppAddress?: string;\n /**\n * Called with the record IMMEDIATELY after a fresh deployment is confirmed\n * on-chain — BEFORE this function returns — so the zkApp key is persisted\n * even if the caller's subsequent initialize fails.\n */\n onDeployed?: (record: MinaZkAppDeployRecord) => void | Promise<void>;\n}\n\n/** Required fee-payer balance to deploy: 1 MINA account creation + tx fees. */\nfunction requiredDeployBalanceNanomina(feeNanomina: bigint): bigint {\n // account-creation (1 MINA) + the deploy tx fee + a buffer for the follow-up\n // initializeChannel tx (same fee) the opener submits right after.\n return MINA_ACCOUNT_CREATION_FEE_NANOMINA + feeNanomina * 2n;\n}\n\nexport interface EnsureOwnedMinaZkAppResult {\n /** The zkApp to open the channel on (ours — reused or freshly deployed). */\n zkAppAddress: string;\n /** True when this call deployed a fresh zkApp. */\n deployed: boolean;\n /** The deploy record (fresh deploys only). */\n record?: MinaZkAppDeployRecord;\n}\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Deploy a fresh `PaymentChannel` zkApp (BARE — no initialization) and wait\n * for the account to exist on-chain. Returns the full deploy record; the\n * caller persists it (losing the zkApp key would strand the ~1.1 MINA the\n * deployment cost).\n */\nexport async function deployMinaChannelZkApp(\n params: DeployMinaZkAppParams\n): Promise<MinaZkAppDeployRecord> {\n const { o1js } = await loadMinaRuntime();\n const { Mina, PrivateKey, AccountUpdate, fetchAccount } = o1js;\n const progress = params.onProgress ?? ((): void => undefined);\n\n const network = Mina.Network(params.graphqlUrl);\n Mina.setActiveInstance(network);\n\n const payerKeyBase58 = hexToMinaBase58PrivateKey(params.payerPrivateKey);\n const payerPrivateKey = PrivateKey.fromBase58(payerKeyBase58);\n const payerPublicKey = payerPrivateKey.toPublicKey();\n\n const feeNanomina = params.feeNanomina ?? 100_000_000n;\n\n // ── Preflight (bug #1) ───────────────────────────────────────────────────\n // Fail fast if the fee payer cannot fund the deploy — BEFORE the ~1-3 min\n // circuit compile. Without this the compile runs first and only then does\n // Mina.transaction throw `getAccount: Could not find account …`, wasting the\n // compile AND leaking the o1js transaction context (see buildMinaTransaction).\n await assertMinaFeePayerFunded({\n o1js,\n payerPublicKey,\n requiredNanomina: requiredDeployBalanceNanomina(feeNanomina),\n graphqlUrl: params.graphqlUrl,\n });\n\n // Reuse a recorded key (redeploy of a pending/crashed attempt) or mint a\n // fresh one. A fresh key is PERSISTED via onDeploying BEFORE any spend so a\n // crash between send and confirmation never orphans a new zkApp (bug #3).\n const zkAppPrivateKey = params.zkAppPrivateKey\n ? PrivateKey.fromBase58(params.zkAppPrivateKey)\n : PrivateKey.random();\n const zkAppPublicKey = zkAppPrivateKey.toPublicKey();\n const zkAppAddress: string = zkAppPublicKey.toBase58();\n if (!params.zkAppPrivateKey) {\n await params.onDeploying?.({\n zkAppAddress,\n zkAppPrivateKey: zkAppPrivateKey.toBase58(),\n feePayer: payerPublicKey.toBase58(),\n });\n }\n\n progress(\n 'compiling the PaymentChannel circuit (one-time, can take 1-3 minutes)…'\n );\n const PaymentChannel = await getCompiledPaymentChannel();\n\n const zkApp = new PaymentChannel(zkAppPublicKey);\n\n // The payer account must be in the active-instance cache for the fee/nonce.\n await fetchAccount({ publicKey: payerPublicKey });\n\n progress(\n `deploying a dedicated PaymentChannel zkApp ${zkAppAddress} ` +\n '(costs the 1 MINA account-creation fee + tx fee)…'\n );\n const fee = Number(feeNanomina);\n // Deploy ONLY — initialize is a separate tx (Issue #128: the initialize\n // precondition needs the account to already exist). Built through\n // buildMinaTransaction so a failure cannot leak the o1js transaction context\n // and poison a retry (bug #2).\n const deployTx = await buildMinaTransaction(\n o1js,\n { sender: payerPublicKey, fee },\n async () => {\n AccountUpdate.fundNewAccount(payerPublicKey);\n await zkApp.deploy();\n }\n );\n await deployTx.prove();\n const sent = await deployTx.sign([payerPrivateKey, zkAppPrivateKey]).send();\n const deployTxHash: string | undefined = sent.hash ?? undefined;\n\n progress(\n `deploy tx sent${deployTxHash ? ` (${deployTxHash})` : ''} — waiting for ` +\n 'inclusion (devnet blocks are ~3 minutes)…'\n );\n // Poll fetchAccount rather than trusting `.wait()` alone: what the opener\n // needs is the ACCOUNT resolvable through this GraphQL endpoint.\n const interval = params.pollIntervalMs ?? 15_000;\n const budget = params.pollTimeoutMs ?? 540_000;\n const started = Date.now();\n // Best-effort `.wait()` first — on some o1js versions it returns promptly\n // after inclusion, which shortcuts the polling below.\n try {\n await sent.wait();\n } catch {\n // fall through to polling — some endpoints reject the wait subscription\n }\n for (;;) {\n const res = await fetchAccount({ publicKey: zkAppPublicKey });\n if (!res.error && res.account) break;\n if (Date.now() - started > budget) {\n throw new Error(\n `Mina zkApp ${zkAppAddress} did not appear on-chain within ` +\n `${Math.round(budget / 60_000)} minutes of the deploy tx` +\n `${deployTxHash ? ` (${deployTxHash})` : ''} — check the tx on ` +\n 'minascan before retrying (the zkApp key has been recorded)'\n );\n }\n await sleep(interval);\n }\n progress(`zkApp ${zkAppAddress} is on-chain (bare — initialize follows).`);\n\n return {\n zkAppAddress,\n zkAppPrivateKey: zkAppPrivateKey.toBase58(),\n feePayer: payerPublicKey.toBase58(),\n ...(deployTxHash !== undefined ? { deployTxHash } : {}),\n ...(getCompiledVerificationKeyHash() !== undefined\n ? { vkHash: getCompiledVerificationKeyHash() }\n : {}),\n };\n}\n\n/**\n * Resolve the zkApp this (client, peer) pair should open its channel on:\n * reuse one that is provably ours, else deploy fresh. See the module doc for\n * the decision table.\n */\nexport async function ensureOwnedMinaZkApp(\n params: EnsureOwnedMinaZkAppParams\n): Promise<EnsureOwnedMinaZkAppResult> {\n const { o1js } = await loadMinaRuntime();\n const { Mina, PrivateKey, PublicKey, Field, Poseidon, fetchAccount } = o1js;\n const progress = params.onProgress ?? ((): void => undefined);\n\n const network = Mina.Network(params.graphqlUrl);\n Mina.setActiveInstance(network);\n\n const payerKeyBase58 = hexToMinaBase58PrivateKey(params.payerPrivateKey);\n const clientPublicKey = PrivateKey.fromBase58(payerKeyBase58).toPublicKey();\n const peerPublicKey = PublicKey.fromBase58(params.peerPublicKey);\n\n // The participant-form pair hash `initializeChannel` bakes on-chain (and the\n // off-chain claim signer binds to): Poseidon([A.x, B.x, nonce=0]).\n if (!Poseidon) {\n throw new Error(\n 'the loaded o1js runtime does not expose Poseidon — cannot derive the pair hash'\n );\n }\n const expectedChannelHash: string = Poseidon.hash([\n clientPublicKey.x,\n peerPublicKey.x,\n Field(0),\n ]).toString();\n\n interface Candidate {\n address: string;\n ownRecord: boolean;\n }\n const candidates: Candidate[] = [];\n if (params.deployed?.zkAppAddress) {\n candidates.push({ address: params.deployed.zkAppAddress, ownRecord: true });\n }\n if (\n params.candidateZkAppAddress &&\n params.candidateZkAppAddress !== params.deployed?.zkAppAddress\n ) {\n candidates.push({\n address: params.candidateZkAppAddress,\n ownRecord: false,\n });\n }\n\n // A recorded own key whose account is not (yet) on-chain: the previous\n // deploy attempt was persisted BEFORE its tx confirmed (bug #3), then\n // crashed/never landed. Redeploy the SAME key rather than minting a new\n // zkApp — otherwise every retry orphans another ~1.1-MINA deployment.\n let pendingOwnKey: string | undefined;\n\n for (const candidate of candidates) {\n let appState: { toString(): string }[] | undefined;\n try {\n const res = await fetchAccount({\n publicKey: PublicKey.fromBase58(candidate.address),\n });\n if (res.error || !res.account) {\n // Not on-chain. If it is OUR recorded deployment, remember its key so\n // the deploy below reuses the SAME address instead of orphaning a new\n // zkApp on every retry.\n if (candidate.ownRecord && params.deployed?.zkAppPrivateKey) {\n pendingOwnKey = params.deployed.zkAppPrivateKey;\n }\n continue;\n }\n appState = res.account.zkapp?.appState;\n } catch {\n continue;\n }\n const channelHash = appState?.[APPSTATE_CHANNEL_HASH]?.toString() ?? '';\n const channelState = BigInt(\n appState?.[APPSTATE_CHANNEL_STATE]?.toString() ?? '0'\n );\n if (channelState === STATE_OPEN && channelHash === expectedChannelHash) {\n // OPEN for exactly our pair — ours, reuse (open is idempotent on it).\n progress(`reusing our open Mina channel zkApp ${candidate.address}.`);\n return { zkAppAddress: candidate.address, deployed: false };\n }\n if (candidate.ownRecord && channelState === STATE_UNINITIALIZED) {\n // Our own recorded deployment that never got initialized (crash between\n // deploy and init) — reuse it; the normal open initializes it.\n progress(\n `reusing our recorded (uninitialized) Mina zkApp ${candidate.address}.`\n );\n return { zkAppAddress: candidate.address, deployed: false };\n }\n // Anything else — a foreign pair's channel, the shared announce zkApp\n // someone already initialized, CLOSING/SETTLED remnants — is not ours.\n }\n\n // No usable candidate: deploy a dedicated zkApp for this pair. Reuse the\n // recorded pending key when present (redeploy the same address, no orphan).\n const record = await deployMinaChannelZkApp({\n ...params,\n ...(pendingOwnKey ? { zkAppPrivateKey: pendingOwnKey } : {}),\n ...(params.onDeploying ? { onDeploying: params.onDeploying } : {}),\n });\n // Persist BEFORE returning: if the caller's initialize fails, the zkApp key\n // (and the ~1.1 MINA it cost) must not be lost.\n await params.onDeployed?.(record);\n return { zkAppAddress: record.zkAppAddress, deployed: true, record };\n}\n","/**\n * On-chain Mina payment-channel open — connector-parity.\n *\n * Opens (initializes) — and optionally deposits into — a REAL on-chain Mina\n * payment channel on the deployed `PaymentChannel` zkApp, so the connector's\n * `MinaPaymentChannelSDK.getChannelState(zkAppAddress)` finds a channel whose\n * on-chain `channelState == OPEN` (status `'opened'`) and the Mina claim\n * verifies + stores. This is the Mina analog of `openSolanaChannel`\n * (`solana-payment-channel.ts`, connector#105): the client opens its own\n * per-channel on-chain state rather than relying on a pre-initialized channel.\n *\n * ## Why this is separate from `mina-payment-channel.ts`\n *\n * `mina-payment-channel.ts` builds the OFF-chain balance-proof claim with\n * `mina-signer` (no o1js — keeps the client lightweight). But INITIALIZING a\n * zkApp channel requires producing a zkApp method proof, which is heavyweight\n * o1js WASM circuit work. So this module lazily imports `o1js` +\n * `@toon-protocol/mina-zkapp` ONLY when an on-chain open is actually requested\n * (the e2e client / Node settlement path), mirroring the connector's own\n * `getO1js()` lazy-require. Both are OPTIONAL dependencies and are kept out of\n * the bundle via `tsup` `external` so npm consumers that never open a Mina\n * channel don't pay the o1js cost.\n *\n * ## Contract call (must match the connector's `MinaPaymentChannelSDK.openChannel`)\n *\n * `PaymentChannel.initializeChannel(participantA, participantB, nonce, timeout, tokenId)`\n * sets `channelState = OPEN` (1). The deployed zkApp address IS the channel id\n * (`MinaClaimMessage.zkAppAddress`), identical to the claim's channel-hash\n * preimage in `mina-payment-channel.ts`. `deposit(amount, depositor)` then\n * bumps `depositTotal` (only valid while `channelState == OPEN`).\n *\n * The zkApp is deployed out-of-band (the operator/e2e harness deploys it\n * deterministically and advertises its B62 address); this module assumes the\n * account exists and only INITIALIZES the channel on it (idempotent — if the\n * channel is already `OPEN`, it returns without re-initializing).\n *\n * @module\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { hexToMinaBase58PrivateKey } from '@toon-protocol/core';\n\n/** Result shape of o1js `fetchAccount` (the bits we read). */\ninterface FetchAccountResult {\n error?: unknown;\n account?: {\n zkapp?: { appState?: { toString(): string }[] };\n /** Account MINA balance (o1js `UInt64`) — the preflight reads its total. */\n balance?: { toString(): string };\n };\n}\n\n/** Minimal o1js surface this module uses (lazy-loaded). */\nexport interface O1jsLike {\n Mina: any;\n PrivateKey: any;\n PublicKey: any;\n Field: any;\n AccountUpdate: any;\n /** Present on the real o1js; the deploy/ownership path uses it. */\n Poseidon?: any;\n fetchAccount: (args: { publicKey: any }) => Promise<FetchAccountResult>;\n}\n\nlet cachedO1js: O1jsLike | null = null;\nlet cachedPaymentChannel: any | null = null;\nlet compiledContract: any | null = null;\n\n/**\n * Test-only override for the o1js + contract loader. When set, `loadMinaRuntime`\n * returns this instead of doing the `createRequire` resolution — so unit tests\n * can inject fakes WITHOUT pulling the real o1js WASM runtime (vitest's\n * `vi.mock` cannot intercept the CJS `require` path the production loader uses).\n */\nlet runtimeOverride:\n | (() => Promise<{ o1js: O1jsLike; PaymentChannel: any }>)\n | null = null;\n\n/** Test hook: inject a fake o1js + PaymentChannel runtime. */\nexport function _setMinaRuntimeForTests(\n loader: (() => Promise<{ o1js: O1jsLike; PaymentChannel: any }>) | null\n): void {\n runtimeOverride = loader;\n}\n\n/**\n * Resolve `o1js` AND the `PaymentChannel` contract through ONE shared module\n * instance.\n *\n * ⚠️ o1js keeps its \"active Mina instance\" in a module-level closure\n * (`mina-instance.js`). `@toon-protocol/mina-zkapp` ships as CommonJS, so its\n * internal `import {Mina}` is emitted as `require('o1js')` → o1js's CJS build\n * (`dist/node/index.cjs`). A bare ESM `import('o1js')` from this module resolves\n * o1js's DIFFERENT ESM build (`dist/node/index.js`) — a SEPARATE module instance\n * with a SEPARATE `activeInstance` closure. Calling `setActiveInstance` on the\n * ESM instance while `PaymentChannel.initializeChannel` reads the CJS instance\n * throws `channelState.get() failed … Must call Mina.setActiveInstance first`\n * (observed in the local-HS Mina e2e on the FIRST publish). The connector's own\n * settlement path and `scripts/deploy-mina-zkapp.ts` both work around this by\n * requiring o1js through the same anchor the zkApp uses.\n *\n * Fix: anchor a `createRequire` at the `@toon-protocol/mina-zkapp` package and\n * `require('o1js')` + the contract from there, so both share the CJS o1js\n * instance and `setActiveInstance` is visible inside the contract method. Kept\n * lazy (and `external` in tsup) so the multi-hundred-MB WASM runtime is only\n * loaded when a Mina channel is actually opened.\n */\nexport async function loadMinaRuntime(): Promise<{\n o1js: O1jsLike;\n PaymentChannel: any;\n}> {\n if (cachedO1js && cachedPaymentChannel) {\n return { o1js: cachedO1js, PaymentChannel: cachedPaymentChannel };\n }\n if (runtimeOverride) {\n const injected = await runtimeOverride();\n cachedO1js = injected.o1js;\n cachedPaymentChannel = injected.PaymentChannel;\n return injected;\n }\n const { createRequire } = await import('node:module');\n const nodePath = await import('node:path');\n // Anchor resolution at this module so the consumer's node_modules graph (where\n // both o1js and @toon-protocol/mina-zkapp are installed) resolves them, then\n // re-anchor at the mina-zkapp package so its CJS `require('o1js')` and ours are\n // the SAME physical module instance (shared active-instance closure).\n const requireHere = createRequire(import.meta.url);\n const mzkPkgPath = requireHere.resolve(\n '@toon-protocol/mina-zkapp/package.json'\n );\n const requireFromMzk = createRequire(mzkPkgPath);\n // o1js resolved from the mina-zkapp anchor → the SAME (CJS) instance the\n // contract's `require('o1js')` uses.\n const o1js = requireFromMzk('o1js') as O1jsLike;\n // ⚠️ A pnpm workspace package has NO self-referential symlink, so\n // `requireFromMzk('@toon-protocol/mina-zkapp')` fails with MODULE_NOT_FOUND.\n // Load the contract by PATH from the package's own `main` entry instead (the\n // same approach scripts/deploy-mina-zkapp.ts uses). This works in both the\n // workspace (no self-symlink) and the flat consumer node_modules layouts.\n const mzkPkgJson: { main?: string } = requireFromMzk(mzkPkgPath);\n const mzkDir = nodePath.dirname(mzkPkgPath);\n const mzkEntry = nodePath.join(mzkDir, mzkPkgJson.main ?? 'dist/index.js');\n const mzk: any = requireFromMzk(mzkEntry);\n const PaymentChannel = mzk.PaymentChannel ?? mzk.default?.PaymentChannel;\n if (!PaymentChannel) {\n throw new Error(\n '@toon-protocol/mina-zkapp does not export PaymentChannel — cannot open a Mina channel'\n );\n }\n cachedO1js = o1js;\n cachedPaymentChannel = PaymentChannel;\n return { o1js, PaymentChannel };\n}\n\n/** Lazily resolve `o1js` (shared CJS instance with the contract). */\nasync function getO1js(): Promise<O1jsLike> {\n return (await loadMinaRuntime()).o1js;\n}\n\nlet compiledVerificationKeyHash: string | undefined;\n\n/**\n * Lazily resolve + compile the `PaymentChannel` contract. Compilation is the\n * expensive o1js step; cache the compiled artifact so repeated opens in the\n * same process don't recompile.\n */\nexport async function getCompiledPaymentChannel(): Promise<any> {\n const { PaymentChannel } = await loadMinaRuntime();\n if (!compiledContract) {\n const compiled = await PaymentChannel.compile();\n // Record the vk hash for the deploy path's provenance record (drift\n // debugging: the deployed vk IS this locally compiled one by\n // construction, but keeping the hash makes that checkable later).\n compiledVerificationKeyHash =\n compiled?.verificationKey?.hash?.toString() ?? undefined;\n compiledContract = PaymentChannel;\n }\n return compiledContract;\n}\n\n/** The vk hash of the last {@link getCompiledPaymentChannel} compile, if any. */\nexport function getCompiledVerificationKeyHash(): string | undefined {\n return compiledVerificationKeyHash;\n}\n\n/** Test hook: reset the cached o1js + compiled-contract state. */\nexport function _resetMinaChannelOpenCache(): void {\n cachedO1js = null;\n cachedPaymentChannel = null;\n compiledContract = null;\n compiledVerificationKeyHash = undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Transaction-nesting safety (o1js currentTransaction leak)\n// ---------------------------------------------------------------------------\n\n/**\n * o1js tracks the \"current transaction\" in a module-level stack\n * (`Mina.currentTransaction`). `Mina.transaction(feePayer, f)` ENTERS that\n * context, runs the circuit `f`, then LEAVES it — but the fee-payer nonce read\n * (`getAccount(sender)`) happens AFTER the enter and OUTSIDE the try/finally\n * that would leave it. So when the fee payer's account is not in the cache\n * (e.g. an unfunded / nonexistent wallet — `getAccount: Could not find account\n * for public key …`), that read THROWS with the context still entered: the\n * transaction is leaked. The very next `Mina.transaction` anywhere in the\n * SAME process then hits `if (currentTransaction.has()) throw 'Cannot start\n * new transaction within another transaction'` — which is exactly what the\n * cache-invalidation retry path re-triggered after a first failed deploy.\n *\n * This pops any leaked contexts so a subsequent transaction (a retry, or the\n * next zkApp tx) starts clean. Best-effort and defensive: unknown/absent\n * context shapes are ignored.\n */\nexport function abandonLeakedMinaTransaction(o1js: O1jsLike): void {\n const ctx = (o1js.Mina as { currentTransaction?: unknown })\n .currentTransaction;\n const c = ctx as\n | { has?: () => boolean; id?: () => unknown; leave?: (id: unknown) => void }\n | undefined;\n if (!c || typeof c.has !== 'function' || typeof c.leave !== 'function')\n return;\n // Bounded loop — never spin if leave() does not shrink the stack.\n for (let i = 0; i < 64 && c.has(); i += 1) {\n try {\n const id = typeof c.id === 'function' ? c.id() : undefined;\n c.leave(id);\n } catch {\n break; // inconsistent context — stop rather than throw over the caller's error\n }\n }\n}\n\n/**\n * `Mina.transaction(feePayer, f)` that cannot leak the o1js currentTransaction\n * context on failure (see {@link abandonLeakedMinaTransaction}). Every Mina tx\n * this package builds goes through here so ONE failed build (unfunded fee\n * payer, prove error, …) can never poison the next attempt in the process.\n */\nexport async function buildMinaTransaction(\n o1js: O1jsLike,\n feePayer: unknown,\n f: () => Promise<void>\n): Promise<any> {\n try {\n return await o1js.Mina.transaction(feePayer, f);\n } catch (err) {\n abandonLeakedMinaTransaction(o1js);\n throw err;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Fee-payer preflight (fail fast BEFORE the multi-minute circuit compile)\n// ---------------------------------------------------------------------------\n\n/** The protocol's account-creation fee for a brand-new Mina account (1 MINA). */\nexport const MINA_ACCOUNT_CREATION_FEE_NANOMINA = 1_000_000_000n;\n\n/**\n * The Mina fee payer cannot fund an on-chain operation: either the account\n * does not exist on-chain yet (never received MINA) or its balance is below\n * the required minimum. Carries the address, the shortfall and the network so\n * the message is directly actionable (\"send N MINA to <addr> on <network>\").\n */\nexport class MinaFeePayerUnfundedError extends Error {\n constructor(\n readonly address: string,\n readonly requiredNanomina: bigint,\n readonly haveNanomina: bigint | undefined,\n readonly graphqlUrl: string\n ) {\n const mina = (n: bigint) => `${(Number(n) / 1e9).toFixed(3)} MINA`;\n const state =\n haveNanomina === undefined\n ? `does not exist on-chain (0 MINA)`\n : `holds only ${mina(haveNanomina)}`;\n super(\n `Mina fee-payer wallet ${address} ${state} but needs ~${mina(\n requiredNanomina\n )} to open a payment channel (≈1 MINA account-creation fee + tx fees) ` +\n `on ${graphqlUrl}. Fund ${address} and retry — no circuit was ` +\n `compiled (this check runs before the ~1-3 min compile).`\n );\n this.name = 'MinaFeePayerUnfundedError';\n }\n}\n\n/**\n * Read the fee payer's on-chain MINA balance and throw\n * {@link MinaFeePayerUnfundedError} when the account is missing or under\n * `requiredNanomina`. Runs BEFORE any circuit compile / zkApp deploy so an\n * unfunded wallet fails in seconds, not after minutes of wasted compilation.\n * The active o1js Mina instance must already be set to `graphqlUrl`.\n */\nexport async function assertMinaFeePayerFunded(params: {\n o1js: O1jsLike;\n payerPublicKey: { toBase58(): string };\n requiredNanomina: bigint;\n graphqlUrl: string;\n}): Promise<void> {\n const { o1js, payerPublicKey, requiredNanomina, graphqlUrl } = params;\n const address = payerPublicKey.toBase58();\n const res = await o1js.fetchAccount({ publicKey: payerPublicKey });\n if (res.error || !res.account) {\n // No account on-chain → 0 MINA, cannot even pay the account-creation fee.\n throw new MinaFeePayerUnfundedError(\n address,\n requiredNanomina,\n undefined,\n graphqlUrl\n );\n }\n const raw = res.account.balance?.toString();\n const have = raw !== undefined ? BigInt(raw) : 0n;\n if (have < requiredNanomina) {\n throw new MinaFeePayerUnfundedError(\n address,\n requiredNanomina,\n have,\n graphqlUrl\n );\n }\n}\n\n/** CHANNEL_STATE.OPEN from `@toon-protocol/mina-zkapp` constants. */\nconst MINA_CHANNEL_STATE_OPEN = 1n;\n/** CHANNEL_STATE.UNINITIALIZED. */\nconst MINA_CHANNEL_STATE_UNINITIALIZED = 0n;\n\nexport interface OpenMinaChannelParams {\n /** Mina GraphQL endpoint of the network the zkApp is deployed on. */\n graphqlUrl: string;\n /** Deployed payment-channel zkApp B62 address (the channel id). */\n zkAppAddress: string;\n /**\n * Fee-payer / participantA `EK…` base58 private key — the client's Mina key\n * (same key the off-chain claim is signed with). Pays the Mina tx fee and\n * authorizes the `initializeChannel` (+ `deposit`) transaction.\n */\n payerPrivateKey: string;\n /**\n * participantB B62 public key — the apex's Mina settlement address. When\n * omitted, the payer is used for both participants (single-party dev channel).\n */\n peerPublicKey?: string;\n /** Channel settlement timeout in slots. Default 86400. */\n timeout?: bigint;\n /** Mina token id field (decimal string). Default '1' (native MINA). */\n tokenId?: string;\n /** Optional on-chain deposit amount (base units) after initialization. */\n deposit?: { amount: bigint };\n /** Per-call network id for the Schnorr/account prefix. Default 'devnet'. */\n networkId?: 'devnet' | 'mainnet';\n /**\n * Transaction fee in nanomina for the `initializeChannel` + `deposit` zkApp\n * method calls. Lightnet/devnet REJECTS fee-less zkApp commands with\n * \"Insufficient fee\", so a non-zero fee is REQUIRED. Default 100_000_000\n * (0.1 MINA), matching scripts/deploy-mina-zkapp.ts.\n */\n feeNanomina?: bigint;\n}\n\nexport interface OpenMinaChannelResult {\n /** The zkApp address (channel id) — echoed for parity with the Solana opener. */\n zkAppAddress: string;\n /** True when a fresh `initializeChannel` tx was submitted this call. */\n opened: boolean;\n /** `initializeChannel` tx hash (absent when the channel was already OPEN). */\n initTxHash?: string;\n /** `deposit` tx hash, when a deposit was requested + submitted. */\n depositTxHash?: string;\n /** On-chain channelState after the call (0=UNINIT,1=OPEN,2=CLOSING,3=SETTLED). */\n channelState: number;\n /**\n * On-chain `depositTotal` (base units), read from the zkApp appState after the\n * open/deposit settled. The Mina balance-proof signer needs this so it can bind\n * `balanceB = depositTotal − balanceA` (toon-protocol/connector#133). A channel\n * can be re-deposited, so this is the CURRENT on-chain value, not a config one.\n */\n depositTotal: bigint;\n}\n\n/**\n * Open (initialize) — and optionally deposit into — a real on-chain Mina\n * payment channel on the already-deployed `PaymentChannel` zkApp.\n *\n * Idempotent: if the on-chain `channelState` is already `OPEN`, returns without\n * re-initializing (mirrors `openSolanaChannel`'s \"channel already exists\" path).\n * Throws if the zkApp account does not exist on-chain.\n */\nexport async function openMinaChannelOnChain(\n params: OpenMinaChannelParams\n): Promise<OpenMinaChannelResult> {\n const { Mina, PrivateKey, PublicKey, Field, AccountUpdate, fetchAccount } =\n await getO1js();\n\n // Use the plain-string `Mina.Network(graphqlUrl)` form (matching the\n // connector's MinaPaymentChannelSDK._setNetwork). The object form\n // (`{ networkId, mina }`) behaves inconsistently across o1js versions and left\n // the active-instance ledger unable to resolve fetched accounts\n // (`channelState.get()` → \"we can't find this zkapp account\"). The Schnorr\n // network prefix is governed by the off-chain signer (`networkId`), not this\n // on-chain endpoint binding.\n const network = Mina.Network(params.graphqlUrl);\n Mina.setActiveInstance(network);\n\n // zkApp method txs MUST carry a fee on lightnet/devnet (\"Insufficient fee\"\n // otherwise). 0.1 MINA matches scripts/deploy-mina-zkapp.ts.\n const txFee = params.feeNanomina ?? 100_000_000n;\n\n // The client's mnemonic-derived Mina key is a big-endian hex scalar (the form\n // `deriveFullIdentity()` emits); o1js `PrivateKey.fromBase58` needs the Mina\n // `EK…` base58check form. Convert (idempotent — passes an already-`EK…` key\n // through unchanged), matching what the off-chain MinaSigner does.\n const payerKeyBase58 = hexToMinaBase58PrivateKey(params.payerPrivateKey);\n const payerPrivateKey = PrivateKey.fromBase58(payerKeyBase58);\n const payerPublicKey = payerPrivateKey.toPublicKey();\n const zkAppPublicKey = PublicKey.fromBase58(params.zkAppAddress);\n\n // Read channelState (appState index 3) straight from the `fetchAccount`\n // result rather than `zkApp.channelState.get()`. `.get()` outside a\n // transaction is fragile here (it can throw \"Must call Mina.setActiveInstance\n // first\" / \"can't find this zkapp account\" even right after a successful\n // fetch); the network-fetched appState array is the reliable source. A\n // missing account is a hard error — the zkApp must be deployed out-of-band.\n const readChannelState = async (): Promise<bigint> => {\n const res = await fetchAccount({ publicKey: zkAppPublicKey });\n if (res.error || !res.account) {\n throw new Error(\n `Mina zkApp account ${params.zkAppAddress} not found on-chain (${String(\n res.error\n )}) — deploy the PaymentChannel zkApp before opening a channel`\n );\n }\n // PaymentChannel state field order: [channelHash, balanceCommitment,\n // nonceField, channelState, depositTotal, ...] → channelState is index 3.\n const appState = res.account.zkapp?.appState;\n const raw = appState?.[3]?.toString() ?? '0';\n return BigInt(raw);\n };\n\n // Read the on-chain `depositTotal` (appState index 4). The signer must bind\n // `balanceB = depositTotal − balanceA` against this CURRENT on-chain value\n // (connector#133); a channel can be re-deposited, so a stale config value\n // would fail the signatureA verification on settle. A missing account is a\n // hard error (same as readChannelState).\n const readDepositTotal = async (): Promise<bigint> => {\n const res = await fetchAccount({ publicKey: zkAppPublicKey });\n if (res.error || !res.account) {\n throw new Error(\n `Mina zkApp account ${params.zkAppAddress} not found on-chain (${String(\n res.error\n )}) — deploy the PaymentChannel zkApp before opening a channel`\n );\n }\n const appState = res.account.zkapp?.appState;\n const raw = appState?.[4]?.toString() ?? '0';\n return BigInt(raw);\n };\n\n const currentState = await readChannelState();\n await fetchAccount({ publicKey: payerPublicKey });\n let opened = false;\n let initTxHash: string | undefined;\n let zkApp: any;\n const getZkApp = async () => {\n if (!zkApp) {\n const PaymentChannel = await getCompiledPaymentChannel();\n zkApp = new PaymentChannel(zkAppPublicKey);\n }\n return zkApp;\n };\n\n if (currentState === MINA_CHANNEL_STATE_UNINITIALIZED) {\n const channel = await getZkApp();\n const participantA = payerPublicKey;\n const participantB = params.peerPublicKey\n ? PublicKey.fromBase58(params.peerPublicKey)\n : payerPublicKey;\n const nonce = Field(0);\n const timeoutField = Field((params.timeout ?? 86400n).toString());\n const tokenIdField = Field(params.tokenId ?? '1');\n\n // `initializeChannel` reads `this.channelState.getAndRequireEquals()` as a\n // precondition, which needs the zkApp account in o1js's active-instance\n // cache. Re-fetch BOTH the zkApp and the fee-payer immediately before\n // building the transaction so the precondition read resolves (a stale or\n // missing cache surfaces as \"channelState.get() failed / Must call\n // setActiveInstance first\" — even though the network IS set).\n await fetchAccount({ publicKey: zkAppPublicKey });\n await fetchAccount({ publicKey: payerPublicKey });\n\n const initTx = await buildMinaTransaction(\n await getO1js(),\n { sender: payerPublicKey, fee: Number(txFee) },\n async () => {\n await channel.initializeChannel(\n participantA,\n participantB,\n nonce,\n timeoutField,\n tokenIdField\n );\n }\n );\n await initTx.prove();\n const sentInit = await initTx.sign([payerPrivateKey]).send();\n initTxHash = sentInit.hash ?? undefined;\n opened = true;\n // ALWAYS wait for the init tx to be INCLUDED in a block (and re-fetch the\n // account) before returning — NOT only when a deposit follows.\n //\n // Why this matters (issue #158): the two-party `channelHash =\n // Poseidon([client.x, apex.x, 0])` is only written to the zkApp's on-chain\n // state once `initializeChannel` is included in a block. If we fire-and-forget\n // the init tx, the publish proceeds immediately and the connector reads the\n // STILL-BARE zkApp (channelState=0, channelHash empty → `participants:[\"\",\"\"]`)\n // before the init lands, so its participant-form balance-proof reconstruction\n // mismatches → `mina_claim_verification_failed: \"Invalid balance proof\n // signature\"`. The EVM (`waitForTransactionReceipt`) and Solana\n // (`waitForConfirmation`) openers both confirm their open tx before returning;\n // Mina must do the same for parity. `.wait()` blocks until inclusion (lightnet\n // block time can be a few minutes).\n await sentInit.wait();\n await fetchAccount({ publicKey: zkAppPublicKey });\n await fetchAccount({ publicKey: payerPublicKey });\n } else if (currentState !== MINA_CHANNEL_STATE_OPEN) {\n // CLOSING (2) or SETTLED (3): cannot (re)open. Surface clearly.\n throw new Error(\n `Mina channel ${params.zkAppAddress} is in state ${currentState} (not UNINITIALIZED/OPEN) — cannot open`\n );\n }\n\n // Optional deposit (only valid while OPEN — which it now is).\n let depositTxHash: string | undefined;\n if (params.deposit && params.deposit.amount > 0n) {\n const channel = await getZkApp();\n // Re-fetch so the deposit tx sees the post-init state.\n await fetchAccount({ publicKey: zkAppPublicKey });\n const amountField = Field(params.deposit.amount.toString());\n const depositTx = await buildMinaTransaction(\n await getO1js(),\n { sender: payerPublicKey, fee: Number(txFee) },\n async () => {\n await channel.deposit(amountField, payerPublicKey);\n }\n );\n await depositTx.prove();\n const sentDeposit = await depositTx.sign([payerPrivateKey]).send();\n depositTxHash = sentDeposit.hash ?? undefined;\n // ALWAYS wait for the deposit tx to be INCLUDED before returning — same\n // confirmation discipline as initializeChannel above (issue #158). The\n // connector's claimFromChannel runs a #126 balance-conservation gate that\n // reads the on-chain `depositTotal`; if we fire-and-forget the deposit, the\n // publish + claim race ahead and the connector reads depositTotal=0 (deposit\n // not yet in a block) → `PROOF_GENERATION_FAILED: Claim violates balance\n // conservation` and the settle aborts non-retryably. Blocking on inclusion\n // (and re-fetching) guarantees the funded depositTotal is on-chain before any\n // claim settles against it.\n await sentDeposit.wait();\n await fetchAccount({ publicKey: zkAppPublicKey });\n await fetchAccount({ publicKey: payerPublicKey });\n }\n\n // Read the resulting state from the network-fetched appState (best-effort —\n // may still reflect the pre-confirmation value on a slow node; the connector\n // re-reads at verification time). If we just opened, optimistically report\n // OPEN even if the node hasn't surfaced the new state yet.\n let finalState: number;\n try {\n finalState = Number(await readChannelState());\n } catch {\n finalState = opened\n ? Number(MINA_CHANNEL_STATE_OPEN)\n : Number(currentState);\n }\n if (opened && finalState === Number(MINA_CHANNEL_STATE_UNINITIALIZED)) {\n finalState = Number(MINA_CHANNEL_STATE_OPEN);\n }\n\n // Touch AccountUpdate so the (lazy) import is retained even if a future\n // refactor stops referencing it directly above; harmless no-op.\n void AccountUpdate;\n\n // Read the resulting on-chain depositTotal (post init+deposit confirmation, so\n // a fresh re-deposit is reflected). Best-effort: fall back to 0n if the read\n // throws on a slow node — the connector re-reads at verification time.\n let depositTotal: bigint;\n try {\n depositTotal = await readDepositTotal();\n } catch {\n depositTotal = 0n;\n }\n\n return {\n zkAppAddress: params.zkAppAddress,\n opened,\n initTxHash,\n depositTxHash,\n channelState: finalState,\n depositTotal,\n };\n}\n"],"mappings":"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","names":["hexToMinaBase58PrivateKey","hexToMinaBase58PrivateKey"]}