UNPKG

@nori-zk/mina-token-bridge

Version:

Nori ethereum state settelment and nETH token bridge zkApp

807 lines (806 loc) 39.5 kB
import type { ProofDataOutput, SP1ProofWithPublicValuesPlonkNoTee } from '@nori-zk/proof-conversion/min'; import { type NetworkId } from 'o1js'; import { type MerkleTreeContractDepositAttestorInputJson } from '../../depositAttestation.js'; /** * Detects whether this module is currently executing inside a browser * runtime, covering both the main thread and a Web Worker context. * Returns `false` in Node (including Node worker threads). * * Used by `compileMinterDeps` to gate browser-only network cache * fetches (that cached path is currently disabled -- see the * `TokenBridgeWorker` class docstring), and by the bootstrap trace * below to record which runtime the worker was instantiated in. * * @returns `true` when running in a browser main thread or Web * Worker, `false` otherwise (e.g. Node, or any environment that * lacks both a DOM and Web Worker-style `importScripts`). */ export declare function isBrowser(): boolean; /** * # TokenBridgeWorker * * Off-main-thread o1js bridge worker. Runs inside a browser Web Worker * (or a Node worker thread) behind the RPC bridge in `workers/defs.ts`, * wired through `workers/tokenBridgeWorker/{parent,child}.ts`. * * ## RPC surface and serialisation * * Every public (non-`#private`) method on this class is reachable by * the client over the RPC bridge. Because every call crosses a worker * boundary, every parameter and every return value on every public * method must be serialisation-safe: base58 / hex / decimal strings, * plain numbers and booleans, and plain JSON objects composed of the * same. No class instances cross the boundary -- not `Field`, not * `PublicKey`, not `Signature`, not `Mina.Transaction`, not * `VerificationKey`. Methods reconstruct those types from their * serialised form at call entry and re-serialise on return. If you add * a new public method, its signature and return type must honour this * invariant. * * There is no framework-level gate on which methods a client can reach. * The method-name prefixes below are a developer-facing convention that * says when each method is appropriate to call. Each method's docstring * states explicitly when it must not be called. Renaming any public * method is a client-visible API change. * * ## Production flow vs test flow * * Each stateful tx-producing operation (storage setup, mint) exists * under a production flow and one or more test flows. The test flows * stand in for the parts of the production flow that live outside * the worker (most importantly, the user's real wallet). * * ### Production flow (target public API) * * A real frontend calls a target-API method (`setupStorage`, `mint`) * and receives the proved transaction as a JSON string. The frontend * then hands that JSON to the user's real wallet (e.g. Auro), which * signs and submits it. The wallet lives entirely outside this file; * the worker has no counterpart method for the sign-and-send half of * this flow. The target-API method is the last worker call in the * production flow for that operation. * * ### Test flow: single-call mock * * Integration specs inject a throwaway private key into the worker via * `WALLET_setMinaPrivateKey`, then call `MOCK_setupStorage` / * `MOCK_mint`. Those collapse build, prove, sign and send into one * in-worker call using the throwaway key. Used when the spec does not * need to mirror the production call shape. * * ### Test flow: split-stage mock * * Integration specs that do want to mirror the production call shape * at the spec level use the pair * `MOCK_computeMintProofAndCache` (build-and-prove half, caches the * in-memory `Mina.Transaction` on the instance) followed by * `WALLET_MOCK_signAndSendMintProofCache` (sign-and-send half, using * the throwaway key). The handoff between the two is via an * in-instance field rather than JSON, which sidesteps the o1js blocker * described below. When that blocker is fixed, those specs migrate to * `mint` + `WALLET_signAndSend` as a structurally identical pair. * * ### Wallet emulation (`WALLET_signAndSend`) * * `WALLET_signAndSend` is the aspirational wallet-emulation companion * to the target-API methods: it takes the returned JSON tx, signs it * with the worker-held throwaway key, and submits it. Test-only -- in * tests it replaces the real wallet that a production frontend would * use. A production frontend never calls `WALLET_signAndSend`, because * in production the real wallet performs that step. Currently blocked * by the `Transaction.fromJSON` issue described below. * * ## Upstream o1js blockers * * Two o1js limitations shape the test-flow scaffolding: * * - `Transaction.fromJSON` does not round-trip a proved transaction * with its `lazyAuthorization` intact, so `WALLET_signAndSend` * cannot reconstruct a tx that was proved in-worker and then * serialised. The in-progress attempt is preserved in the commented * block near `deserializeTransaction` as a reference for the next * attempt. Until this is fixed, split-stage test flows pass the * proved tx between worker calls via an in-instance field * (`#mintProofCache`) rather than JSON. * * - The o1js browser compilation cache does not function reliably, so * `compileMinterDeps` force-delegates to `compileMinterDepsNoCache`. * The cache-enabled body is retained in full so it can be * reactivated with a single edit once the upstream issue is resolved. * * ## Method-name prefix key * * Safe in production code: * * - no prefix: target public API. Builds and proves a transaction and * returns it as a JSON string; the user's real wallet signs and * submits it. * - `SCRAM_`: pure SCRAM helpers (code-challenge creation, * field-to-hex conversion). No wallet state required. * * Test-only -- must not be called from production frontend code: * * - `WALLET_setMinaPrivateKey`: injects a throwaway private key into * the worker so subsequent `WALLET_`-prefixed methods can stand in * for a real wallet. * - `WALLET_signAndSend`: wallet-emulation sign-and-send for a JSON tx * returned by a target-API method. Replaces the real wallet in * tests; never called from production. Currently blocked by the * o1js `Transaction.fromJSON` issue above. * - `MOCK_`: single-call test variant of a target-API method * (`MOCK_setupStorage`, `MOCK_mint`) that collapses the full build, * prove, sign-and-send pipeline into one in-worker call. * - `WALLET_MOCK_`: wallet-emulation half of a split-stage mock, * paired with a `MOCK_` proof-compute step that caches the proved * tx in-instance. * - `MOCK_SCRAM_`: SCRAM helper that signs using the worker-held key. * Production code must use the real wallet for SCRAM signing. */ export declare class TokenBridgeWorker { #private; /** * Inject a throwaway Mina private key into the worker so that * subsequent `WALLET_`-prefixed and `MOCK_`-prefixed methods can * stand in for a real wallet during tests. The key is held in a * private class field and never leaves the worker. * * Must be called exactly once per worker instance. A second call * throws rather than silently replacing the key, to avoid * test-harness bugs where one spec's key bleeds into another. * * Not appropriate to call from production frontend code: a real * frontend never hands a private key to the worker, because the * user's real wallet performs all signing outside this file. * * @param minaPrivateKeyBase58 Base58-encoded Mina `PrivateKey` of * the account that will pay fees for and authorise transactions * produced by the `MOCK_` and `WALLET_MOCK_` methods. * @returns Resolves once the key has been installed. * @throws `Error` if a private key has already been set on this * worker instance. */ WALLET_setMinaPrivateKey(minaPrivateKeyBase58: string): Promise<void>; /** * Work-in-progress reconstruction of a proved transaction from its * serialised JSON form, retained as reference material for the * next attempt once o1js supports round-tripping * `lazyAuthorization`. * * The approach: deserialise the JSON with `Mina.Transaction.fromJSON` * to recover the structural transaction, then reattach the * `lazyAuthorization` (and, where present, its `blindingValue`) * from a freshly-built sibling transaction `txNew` that still * carries that state in memory. This is needed because * `Transaction.fromJSON` drops `lazyAuthorization`, which the * signer needs to complete account-update authorisation. * * Disabled until the upstream o1js blocker is resolved. * * @param serializedTransaction JSON string carrying `{ tx, * blindingValues, length }`: `tx` is the stringified proved * transaction, `blindingValues` is the per-account-update * blinding-value array (empty strings where absent), and * `length` is the expected account-update count used as a * sanity check against both the fresh sibling and the * deserialised transaction. * @returns The reconstructed `Mina.Transaction` with * `lazyAuthorization` restored on every account update. * @throws `Error` if the serialised transaction's account-update * count disagrees with either the freshly-built sibling or * itself. */ /** * Minimal reconstruction of a transaction from its serialised JSON * form, using the stock `Transaction.fromJSON` with no * `lazyAuthorization` reattachment. Called by * `WALLET_signAndSend` as the deserialisation step prior to * signing. * * This form does not round-trip a proved transaction's * `lazyAuthorization` and is the direct cause of * `WALLET_signAndSend` being non-functional today; see the * class-level docstring for the o1js blocker. The commented-out * `payload` block below sketches an alternative shape (a signer * payload with `onlySign: true`, `feePayer.fee` and * `feePayer.memo`) kept for reference. * * Private helper: not reachable over the RPC surface, used only * by `WALLET_signAndSend`. * * @param serializedTransaction JSON string produced by * `provedTx.toJSON()` on a target-API method's return value. * @returns The `Transaction` reconstructed by * `Transaction.fromJSON`, suitable (in principle) for signing * and submission. */ private deserializeTransaction; /** * Wallet-emulation sign-and-send for a proved transaction returned * by a target-API method (`setupStorage`, `mint`). Reconstructs * the transaction from JSON, signs it with the throwaway key * installed by `WALLET_setMinaPrivateKey`, submits it to the * network, and waits for inclusion. * * Exists so integration tests can mirror the exact shape of the * production call site, where the frontend hands the JSON to the * user's real wallet and the wallet performs sign-and-send out of * process. In production the real wallet replaces this call * entirely; a production frontend never invokes * `WALLET_signAndSend`. * * Currently non-functional because `Transaction.fromJSON` does * not round-trip a proved transaction's `lazyAuthorization` (see * the class-level docstring). While this is blocked, split-stage * tests use `MOCK_computeMintProofAndCache` + * `WALLET_MOCK_signAndSendMintProofCache` instead, which pass the * proved transaction via an in-instance field rather than JSON. * * Not appropriate to call from production frontend code. * * @param provedTxJsonStr JSON string produced by * `provedTx.toJSON()` inside a target-API method, transported * back into the worker as a serialisation-safe string. * @returns An object carrying the submitted transaction's hash as * `{ txHash }`. * @throws `Error` if `WALLET_setMinaPrivateKey` has not yet been * called on this worker instance. */ WALLET_signAndSend(provedTxJsonStr: string): Promise<{ txHash: string; }>; /** * Configure and activate the Mina network that this worker will * target for every subsequent account fetch, transaction build, * and transaction submission. The new network becomes the * process-wide active instance via `Mina.setActiveInstance`. * * Must be called before any method that reads from or writes to * the chain (`needsToSetupStorage`, `setupStorage`, `mint`, * `getBalanceOf`, etc.). Safe to re-invoke to switch networks; * the latest call wins. * * @param options Plain-object configuration passed straight to * `Mina.Network`: * - `networkId`: optional Mina network id; defaults to the o1js * default when omitted. * - `mina`: single node URL or array of node URLs for the JSON * RPC endpoint. * - `archive`: single archive URL or array of archive URLs for * historical action and event queries. * - `lightnetAccountManager`: optional lightnet account-manager * URL for local development networks. * - `bypassTransactionLimits`: optional flag that relaxes o1js * transaction-size limits, intended for local testing. * - `minaDefaultHeaders`, `archiveDefaultHeaders`: optional * default headers applied to every node / archive request * (e.g. auth tokens). * @returns Resolves once the active instance has been installed. */ minaSetup(options: { networkId?: NetworkId; mina: string | string[]; archive: string | string[]; lightnetAccountManager?: string; bypassTransactionLimits?: boolean; minaDefaultHeaders?: HeadersInit; archiveDefaultHeaders?: HeadersInit; }): Promise<void>; /** * Fetch the current on-chain state for a batch of accounts in * parallel, hydrating the o1js account cache so subsequent * `Mina.transaction` builds can read their state synchronously. * Tx-producing methods call this at entry against the accounts * they will read from or modify (typically the sender and the * `NoriTokenBridge` address, optionally under a specific tokenId * via direct `fetchAccount` calls elsewhere). * * Private helper: not reachable over the RPC surface. * * @param accounts Array of `PublicKey` instances to fetch. Each * is fetched under the default tokenId; callers needing a * non-default tokenId invoke `fetchAccount` directly rather * than going through this helper. * @returns Resolves once every fetch has settled. Individual * fetch failures propagate because `Promise.all` short-circuits * on the first rejection. */ private fetchAccounts; /** * Compute the deposit-attestation witness for a given SCRAM code * challenge and deposit block, ready to be fed into the mint * circuit. Converts the serialisable code-challenge form into the * big-endian hex form that the attestor expects, then delegates * to `computeDepositAttestationWitness` from the depositAttestation * module. * * Safe to call from production frontend code. * * @param codeChallengeSCRAM Decimal string form of the SCRAM code * challenge `Field`, as produced by `SCRAM_createCodeChallenge`. * @param depositBlockNumber Ethereum block number at which the * deposit event was emitted. Used by the attestor to locate the * deposit in the chain's action history. * @param domain Attestation-service domain used to look up the * deposit proof. Defaults to the Nori production PCS endpoint; * override for staging, local development, or test harnesses. * @returns The `MerkleTreeContractDepositAttestorInputJson` that * serialises the witness for transport back over the RPC * surface. Re-hydrated by `mint` / `MOCK_mint` via * `buildMerkleTreeContractDepositAttestorInput`. */ computeDepositAttestationWitness(codeChallengeSCRAM: string, depositBlockNumber: number, domain?: string): Promise<{ path: string[]; depositIndex: number; despositSlotRaw: { slot_key_code_challenge: string; value: string; }; }>; /** * Probe whether the given Mina account needs its * `NoriStorageInterface` subtree set up for the given * `NoriTokenBridge`. Storage setup only needs to be done once per * account, so clients call this before `setupStorage` to decide * whether the setup step can be skipped. Fetches the storage * account under the * bridge's derived tokenId and checks for the presence of * `userKeyHash`; if fetch or read fails for any reason the probe * errs on the side of "setup is needed" so the caller will not * silently skip a required step. Also logs the current * `mintedSoFar` value when setup is already in place. * * Safe to call from production frontend code. * A `true` return means a subsequent `setupStorage` call is * required before the account can mint. * * @param noriTokenBridgeAddressBase58 Base58 address of the * `NoriTokenBridge` zkApp whose storage subtree is being * probed. * @param minaSenderPublicKeyBase58 Base58 public key of the * Mina account being probed. * @returns `false` when the storage interface is already set up * (userKeyHash present and readable); `true` when setup is * needed or could not be confirmed. */ needsToSetupStorage(noriTokenBridgeAddressBase58: string, minaSenderPublicKeyBase58: string): Promise<boolean>; /** * Target public API for storage setup. Builds and proves the * setup transaction that funds and initialises the user's * `NoriStorageInterface` subtree under the given * `NoriTokenBridge`, then returns the proved transaction as a * JSON string for the user's real wallet (e.g. Auro) to sign and * submit. Sign-and-send happens outside this worker; this method * is the last worker call in the production storage-setup flow. * * Hydrates the user and bridge accounts via `fetchAccounts` * before building the transaction, funds a new on-chain account * for the storage subtree (`AccountUpdate.fundNewAccount`), and * invokes `NoriTokenBridge.setUpStorage` with the supplied * verification key. * * Safe to call from production frontend code. * * @param userPublicKeyBase58 Base58 public key of the user whose * storage subtree is being set up. This account pays the fee * and owns the new storage account update. * @param noriTokenBridgeAddressBase58 Base58 address of the * `NoriTokenBridge` zkApp whose storage subtree is being * initialised. * @param txFee Fee in nanomina for the setup transaction. * @param storageInterfaceVerificationKeySafe Serialisation-safe * `NoriStorageInterface` verification key as returned by * `compileMinterDeps` / `compileMinterDepsNoCache` * (`{ data, hashStr }`). The `hashStr` decimal string is * rehydrated back into a `Field` internally. * @returns JSON string produced by `provedTx.toJSON()` on the * proved setup transaction. Fed to the user's real wallet (or, * in tests, to `WALLET_signAndSend` once that is unblocked). */ setupStorage(userPublicKeyBase58: string, noriTokenBridgeAddressBase58: string, txFee: number, storageInterfaceVerificationKeySafe: { data: string; hashStr: string; }): Promise<string>; /** * End-to-end test substitute for `setupStorage`. Builds, proves, * signs and submits the setup transaction in one worker call * using the throwaway key installed by `WALLET_setMinaPrivateKey`, * then waits for inclusion and returns the tx hash. * * Exists only because `WALLET_signAndSend` is currently blocked by * the o1js `Transaction.fromJSON` issue; without it, specs have * no way to drive `setupStorage` + wallet-side sign-and-send as * two worker calls. Goes away once `WALLET_signAndSend` is * unblocked and tests migrate to `setupStorage` + * `WALLET_signAndSend`. * * Not appropriate to call from production frontend code. * * @param userPublicKeyBase58 Base58 public key of the user whose * storage subtree is being set up. The throwaway key installed * via `WALLET_setMinaPrivateKey` must correspond to this public * key, since the mock signs with it. * @param noriTokenBridgeAddressBase58 Base58 address of the * `NoriTokenBridge` zkApp whose storage subtree is being * initialised. * @param txFee Fee in nanomina for the setup transaction. * @param storageInterfaceVerificationKeySafe * `NoriStorageInterface` verification key in the * `{ data, hashStr }` form returned by `compileMinterDeps` / * `compileMinterDepsNoCache`. * @returns Object carrying the submitted transaction's hash as * `{ txHash }` once the network has accepted and included it. */ MOCK_setupStorage(userPublicKeyBase58: string, noriTokenBridgeAddressBase58: string, txFee: number, storageInterfaceVerificationKeySafe: { data: string; hashStr: string; }): Promise<{ txHash: string; }>; /** * Fetch the user's current balance of the wrapped fungible token * issued by the given `FungibleToken` zkApp. Hydrates the * balance-holding account under the token's derived tokenId, then * calls `FungibleToken.getBalanceOf` and returns the balance as a * decimal string of the raw on-chain value (nanounits of the * fungible token, equivalently the minted amount in the base * unit). * * Safe to call from production frontend code. * * @param noriTokenBaseBase58 Base58 address of the `FungibleToken` * zkApp that issues the wrapped token being queried. * @param minaSenderPublicKeyBase58 Base58 public key of the * account whose balance is being queried. * @returns Decimal string form of the balance in the token's raw * on-chain units. Caller formats this for display. */ getBalanceOf(noriTokenBaseBase58: string, minaSenderPublicKeyBase58: string): Promise<string>; /** * Fetch the cumulative amount a given user has already minted * against the given `NoriTokenBridge`, as tracked by the user's * `NoriStorageInterface` subtree. Requires the storage subtree * to have been set up for the user (see `needsToSetupStorage` / * `setupStorage`); errors if it has not been. * * Safe to call from production frontend code. * * @param noriTokenBridgeAddressBase58 Base58 address of the * `NoriTokenBridge` zkApp whose storage subtree is being read. * @param minaSenderPublicKeyBase58 Base58 public key of the user * whose minted-so-far value is being queried. * @returns Decimal string form of the `mintedSoFar` field on the * user's storage subtree. * @throws `Error` if the storage account's `userKeyHash` cannot * be read, which typically indicates the account has not yet * been set up. */ mintedSoFar(noriTokenBridgeAddressBase58: string, minaSenderPublicKeyBase58: string): Promise<string>; /** * @deprecated Deprecated in favour of tokenBridgeTester.update */ update(noriTokenBridgeAddressBase58: string, sp1PlonkProof: SP1ProofWithPublicValuesPlonkNoTee, proofData: ProofDataOutput, txFee: number): Promise<string>; /** * @deprecated Deprecated in favour of tokenBridgeTester.update */ MOCK_update(noriTokenBridgeAddressBase58: string, sp1PlonkProof: SP1ProofWithPublicValuesPlonkNoTee, proofData: ProofDataOutput, txFee: number): Promise<{ txHash: string; }>; /** * Decide whether the mint transaction should fund a new token * account for the sender via `AccountUpdate.fundNewAccount`. The * caller passes the resulting boolean straight into `mint` / * `MOCK_mint` as the `fundNewAccount` flag. * * Probes the sender's account under the given `FungibleToken`'s * derived tokenId. A missing account, or any error while * fetching, is taken as "needs funding" so the mint transaction * will create the account rather than fail on a missing * destination. * * Safe to call from production frontend code. * * @param noriTokenBaseBase58 Base58 address of the `FungibleToken` * zkApp whose tokenId the sender will receive wrapped tokens * under. * @param minaSenderPublicKeyBase58 Base58 public key of the * account that will receive the minted tokens. * @returns `true` when the sender does not yet have an account * under the token's tokenId (or when the probe failed); `false` * when an existing account was found. */ needsToFundAccount(noriTokenBaseBase58: string, minaSenderPublicKeyBase58: string): Promise<boolean>; /** * Convert an o1js `VerificationKey` (carrying a `Field` hash) into * the `{ data, hashStr }` form that crosses the worker boundary. * `hashStr` is the hash's decimal-string representation, which * consumers rehydrate back into a `Field` via * `new Field(BigInt(hashStr))`. * * Private helper used by `compileMinterDeps` and * `compileMinterDepsNoCache` when packaging return values. * * @param vk o1js `VerificationKey` produced by contract * compilation. * @returns `{ hashStr, data }` with `hashStr` as the decimal * string form of `vk.hash` and `data` taken verbatim from the * compiled key. */ private vkToVkSafe; /** * Cache-aware entry point for compiling the three minter * dependencies. When the browser cache is functional this fetches * prebuilt artefacts for each circuit from the given * `cacheServer` and compiles against them; when not, it * delegates to `compileMinterDepsNoCache`. * * Currently force-delegates to `compileMinterDepsNoCache` on * every call regardless of `cacheServer`, because the o1js * browser compilation cache does not function reliably. The * cache-enabled body below the early return is retained in full * so it can be reactivated once the upstream issue is resolved; * do not remove it. * * Safe to call from production frontend code. * * @param cacheServer Optional base URL of a network cache server * that exposes the prebuilt circuit artefacts laid out per * `NoriStorageInterfaceCacheLayout`, `FungibleTokenCacheLayout`, * and `NoriTokenBridgeCacheLayout`. Ignored while the cache * path is disabled. * @returns `{ noriStorageInterfaceVerificationKeySafe, * fungibleTokenVerificationKeySafe, * noriTokenBridgeVerificationKeySafe }`, each in the * `{ data, hashStr }` form produced by `vkToVkSafe`. Fed into * `setupStorage` / `mint` at their verification-key parameter. */ compileMinterDeps(cacheServer?: string): Promise<{ noriStorageInterfaceVerificationKeySafe: { hashStr: string; data: string; }; fungibleTokenVerificationKeySafe: { hashStr: string; data: string; }; noriTokenBridgeVerificationKeySafe: { hashStr: string; data: string; }; }>; /** * Cache-free fallback that compiles every minter dependency from * source on every call. Sequentially compiles * `NoriStorageInterface`, `FungibleToken`, and `NoriTokenBridge` * via `compileAndOptionallyVerifyContracts` and returns their * verification keys in serialisable form. * * Currently the only compilation path actually exercised because * `compileMinterDeps` force-delegates here. Deprecate this once * the o1js browser compilation cache is reliable. * * Safe to call from production frontend code. * * @returns `{ noriStorageInterfaceVerificationKeySafe, * fungibleTokenVerificationKeySafe, * noriTokenBridgeVerificationKeySafe }` in the * `{ data, hashStr }` form produced by `vkToVkSafe`. */ compileMinterDepsNoCache(disableCache?: boolean): Promise<{ noriStorageInterfaceVerificationKeySafe: { hashStr: string; data: string; }; fungibleTokenVerificationKeySafe: { hashStr: string; data: string; }; noriTokenBridgeVerificationKeySafe: { hashStr: string; data: string; }; }>; /** * Target public API for mint. Builds and proves the mint * transaction that credits the user with wrapped tokens * corresponding to a previously-attested Ethereum deposit, then * returns the proved transaction as a JSON string for the user's * real wallet (e.g. Auro) to sign and submit. Sign-and-send * happens outside this worker; this method is the last worker * call in the production mint flow. * * Rehydrates the deposit-attestation witness and the SCRAM * message / signature from their serialised forms, hydrates the * user and bridge accounts via `fetchAccounts`, conditionally * funds a new token account for the user when `fundNewAccount` * is true, and invokes `NoriTokenBridge.noriMint` with the * witnesses. * * Safe to call from production frontend code. * * @param userPublicKeyBase58 Base58 public key of the user * receiving the minted tokens. Pays the fee. * @param noriTokenBridgeAddressBase58 Base58 address of the * `NoriTokenBridge` zkApp performing the mint. * @param merkleTreeContractDepositAttestorInputJson * Serialised deposit-attestation witness as produced by * `computeDepositAttestationWitness`. * @param messageSCRAMStr SCRAM message as a string, rehydrated * internally into a `CircuitString` and then field array. * @param signatureSCRAMBase58 Base58-encoded Mina `Signature` * over the SCRAM message by the user's wallet key. * @param txFee Fee in nanomina for the mint transaction. * @param fundNewAccount When true, the transaction includes an * `AccountUpdate.fundNewAccount` for the user under the token's * derived tokenId. Driven by `needsToFundAccount`. * @returns JSON string produced by `provedTx.toJSON()` on the * proved mint transaction. Fed to the user's real wallet (or, * in tests, to `WALLET_signAndSend` once that is unblocked). */ mint(userPublicKeyBase58: string, noriTokenBridgeAddressBase58: string, merkleTreeContractDepositAttestorInputJson: MerkleTreeContractDepositAttestorInputJson, messageSCRAMStr: string, signatureSCRAMBase58: string, txFee: number, fundNewAccount: boolean): Promise<string>; /** * End-to-end test substitute for `mint`. Builds, proves, signs * and submits the mint transaction in one worker call using the * throwaway key installed by `WALLET_setMinaPrivateKey`, then * waits for inclusion and returns the tx hash. * * Exists only because `WALLET_signAndSend` is currently blocked * by the o1js `Transaction.fromJSON` issue; without it, specs * have no way to drive `mint` + wallet-side sign-and-send as two * worker calls. Goes away once `WALLET_signAndSend` is unblocked * and tests migrate to `mint` + `WALLET_signAndSend`. * * Not appropriate to call from production frontend code. * * @param userPublicKeyBase58 Base58 public key of the user * receiving the minted tokens. The throwaway key installed via * `WALLET_setMinaPrivateKey` must correspond to this public * key, since the mock signs with it. * @param noriTokenBridgeAddressBase58 Base58 address of the * `NoriTokenBridge` zkApp performing the mint. * @param merkleTreeContractDepositAttestorInputJson * Serialised deposit-attestation witness as produced by * `computeDepositAttestationWitness`. * @param messageSCRAMStr SCRAM message as a string. * @param signatureSCRAMBase58 Base58-encoded SCRAM signature. * @param txFee Fee in nanomina for the mint transaction. * @param fundNewAccount When true, the transaction includes an * `AccountUpdate.fundNewAccount` for the user under the * token's derived tokenId. * @returns Object carrying the submitted transaction's hash as * `{ txHash }` once the network has accepted and included it. */ MOCK_mint(userPublicKeyBase58: string, noriTokenBridgeAddressBase58: string, merkleTreeContractDepositAttestorInputJson: MerkleTreeContractDepositAttestorInputJson, messageSCRAMStr: string, signatureSCRAMBase58: string, txFee: number, fundNewAccount: boolean): Promise<{ txHash: string; }>; /** * Convenience alias for `compileMinterDeps`. Kept alongside the * mint methods because the mint flow is the compilation's only * consumer; client code that wants the whole worker ready to go * calls `compileAll` rather than threading through the more * specific `compileMinterDeps` name. * * Safe to call from production frontend code. * * @param cacheServer Optional base URL of a network cache server; * forwarded verbatim to `compileMinterDeps`. Ignored while the * browser cache path is disabled. * @returns The same `{ noriStorageInterfaceVerificationKeySafe, * fungibleTokenVerificationKeySafe, * noriTokenBridgeVerificationKeySafe }` shape returned by * `compileMinterDeps`. */ compileAll(cacheServer?: string): Promise<{ noriStorageInterfaceVerificationKeySafe: { hashStr: string; data: string; }; fungibleTokenVerificationKeySafe: { hashStr: string; data: string; }; noriTokenBridgeVerificationKeySafe: { hashStr: string; data: string; }; }>; /** * Prove half of the split-stage mint mock. Builds and proves the * mint transaction identically to `mint`, then stores the proved * `Mina.Transaction` on the instance in `#mintProofCache` for a * subsequent `WALLET_MOCK_signAndSendMintProofCache` call to sign * and submit. Returns nothing over the RPC surface; the proved * transaction never crosses the worker boundary because * `Mina.Transaction` is not serialisation-safe (this is the same * reason `WALLET_signAndSend` is blocked). * * Overwrites any previously cached proof on the same worker * instance, so pairs one-to-one with a following * `WALLET_MOCK_signAndSendMintProofCache` call. * * Not appropriate to call from production frontend code. * * @param userPublicKeyBase58 Base58 public key of the user * receiving the minted tokens. * @param noriTokenBridgeAddressBase58 Base58 address of the * `NoriTokenBridge` zkApp performing the mint. * @param merkleTreeContractDepositAttestorInputJson * Serialised deposit-attestation witness as produced by * `computeDepositAttestationWitness`. * @param messageSCRAMStr SCRAM message as a string. * @param signatureSCRAMBase58 Base58-encoded SCRAM signature. * @param txFee Fee in nanomina for the mint transaction. * @param fundNewAccount When true, the transaction includes an * `AccountUpdate.fundNewAccount` for the user under the * token's derived tokenId. * @returns Resolves once the proof has been computed and cached. */ MOCK_computeMintProofAndCache(userPublicKeyBase58: string, noriTokenBridgeAddressBase58: string, merkleTreeContractDepositAttestorInputJson: MerkleTreeContractDepositAttestorInputJson, messageSCRAMStr: string, signatureSCRAMBase58: string, txFee: number, fundNewAccount: boolean): Promise<void>; /** * Sign-and-send half of the split-stage mint mock. Signs the * `Mina.Transaction` that `MOCK_computeMintProofAndCache` left in * `#mintProofCache` using the throwaway key installed by * `WALLET_setMinaPrivateKey`, submits it, and waits for * inclusion. * * Mirrors what `WALLET_signAndSend` will do once the o1js * `Transaction.fromJSON` blocker is resolved: the two-call shape * at the spec level is the same, only the handoff between calls * differs (in-instance field here, JSON there). * * Not appropriate to call from production frontend code. Must be * preceded by a `MOCK_computeMintProofAndCache` call on the same * worker instance; calling without a prior prove will throw a * null-dereference inside o1js. * * @returns Object carrying the submitted transaction's hash as * `{ txHash }` once the network has accepted and included it. */ WALLET_MOCK_signAndSendMintProofCache(): Promise<{ txHash: string; }>; /** * Create the SCRAM code challenge from a base58-encoded Mina * signature (the "code verifier" in SCRAM parlance). Thin * worker-side wrapper around `createCodeChallenge`. * * Safe to call from production frontend code. * * @param signatureSCRAMBase58 Base58-encoded Mina `Signature` * that plays the role of the code verifier. * @returns Decimal string form of the resulting `Field`, suitable * as input to `computeDepositAttestationWitness` / * `SCRAM_codeChallengeToBEHex`. */ SCRAM_createCodeChallenge(signatureSCRAMBase58: string): Promise<string>; /** * Convert a SCRAM code-challenge `Field` into its big-endian hex * string representation, as required by the * deposit-attestation service. Thin worker-side wrapper around * `codeChallengeFieldToBEHex`. * * Safe to call from production frontend code. * * @param codeChallengeStr Decimal string form of the code * challenge `Field`, as produced by `SCRAM_createCodeChallenge`. * @returns `0x`-prefixed big-endian hex string form of the code * challenge, ready to be fed to the attestation service. */ SCRAM_codeChallengeToBEHex(codeChallengeStr: string): Promise<string>; /** * Sign a message with the throwaway key installed by * `WALLET_setMinaPrivateKey`, producing a SCRAM signature * suitable as the `signatureSCRAMBase58` input to `mint` / * `MOCK_mint` / `MOCK_computeMintProofAndCache`. * * Exists so tests can produce SCRAM signatures without a real * wallet. Not appropriate to call from production frontend code: * production SCRAM signing happens in the user's real wallet, * never in the worker. * * @param messageToSign Plain string to sign. Converted to a * `CircuitString` and then to a field array before signing. * @returns Base58-encoded Mina `Signature` over the message * fields, ready to be passed back into mint-flow methods. */ MOCK_SCRAM_signMessage(messageToSign: string): Promise<string>; }