@nori-zk/mina-token-bridge
Version:
A Mina zk-program contract allowing users to mint tokens on Nori Bridge.
297 lines (210 loc) • 8.98 kB
Markdown
# Mina zkApp: Token Bridge
A Mina zk-program contract allowing users to mint tokens on Nori Bridge.
## How to build
In the repositories root directory run:
```sh
npm run build
```
## Usage
### Import workers
You have several options depending on your environment and bundler setup:
---
#### 1. **Use Node.js workers directly**
> Only valid in a Node.js environment (e.g., backend services, CLI tools)
```typescript
import { getTokenMintWorker } from '@nori-zk/mina-token-bridge/node/workers/tokenMint';
import { getCredentialAttestationWorker } from '@nori-zk/mina-token-bridge/node/workers/credentialAttestation';
```
---
#### 2. **For the browser, import pure logic worker classes and lift them into workers manually, using a spec**
> Use this if you're building your own worker pipeline (custom setup, unsupported bundler, etc.)
```typescript
import {
CredentialAttestationWorker,
TokenMintWorker,
} from '-zk/mina-token-bridge/workers/defs';
```
- You must lift these pure classes into actual workers.
- You can use either:
- **(a) Provided worker tooling**
> Uses `WorkerParent`, `WorkerChild`, and `createParentFromSpec/createWorker` from the mina-token-bridge repo
```typescript
//workers/credentialAttestation/browser/parent.ts
import { CredentialAttestationWorkerSpec } from '@nori-zk/mina-token-bridge/workers/specs';
import { WorkerParent } from '@nori-zk/mina-token-bridge/browser/worker/parent';
import { createProxyFromSpec } from '@nori-zk/mina-token-bridge/worker';
const worker = new Worker(new URL('./child.ts', import.meta.url), {
type: 'module',
});
// workerParent has a ready function which returns a promise, that you can await, as
// it can take a long time, for the worker to load.
const workerParent = new WorkerParent(worker);
export const getCredentialAttestationWorker = () =>
createProxyFromSpec(workerParent, CredentialAttestationWorkerSpec);
```
```typescript
//workers/credentialAttestation/browser/child.ts
import { CredentialAttestationWorker } from '@nori-zk/mina-token-bridge/workers/defs';
import { WorkerChild } from '@nori-zk/mina-token-bridge/browser/worker/child';
import { createWorker } from '@nori-zk/mina-token-bridge/worker';
const credentialAttestationWorker = createWorker(
new WorkerChild(),
CredentialAttestationWorker
);
```
---
- **(b) Your own tooling (e.g., using Comlink)**
> This example shows how to wire up a pure worker using [Comlink](https://github.com/GoogleChromeLabs/comlink)
```typescript
//workers/credentialAttestation/child.ts
import * as Comlink from 'comlink';
import { CredentialAttestationWorker } from '-zk/mina-token-bridge/workers/defs';
Comlink.expose(new CredentialAttestationWorker());
```
```typescript
//workers/credentialAttestation/parent.ts
import * as Comlink from 'comlink';
import type { CredentialAttestationWorker } from '-zk/mina-token-bridge/workers/defs';
const worker = new Worker(new URL('./child.ts', import.meta.url), { type: 'module' });
type CredentialAttestationWorkerInstance = InstanceType<typeof CredentialAttestationWorker>;
const workerApi = Comlink.wrap<CredentialAttestationWorkerInstance>(worker);
export const getCredentialAttestationWorker = () => workerApi;
```
---
### Main imports
```typescript
import { TransitionNoticeMessageType } from '-zk/pts-types';
import { getReconnectingBridgeSocket$ } from '-zk/mina-token-bridge/rx/socket';
import {
getBridgeStateTopic$,
getBridgeTimingsTopic$,
getEthStateTopic$,
} from '-zk/mina-token-bridge/rx/topics';
import {
BridgeDepositProcessingStatus,
getDepositProcessingStatus$,
bridgeStatusesKnownEnoughToLockUnsafe,
bridgeStatusesKnownEnoughToLockSafe,
getDepositProcessingStatus$,
// Promise triggers resolve when key event occurs and reject after opportunity has been missed:
canMint,
readyToComputeMintProof,
// Observable triggers:
// CanMint observable:
getCanMint$, // cycles through states of 'Waiting', 'ReadyToMint' and 'MissedMintingOpportunity'
CanMintStatus,
// CanComputeEthProof observable:
getCanComputeEthProof$, // cycles through states of 'Waiting', 'CanCompute', or 'MissedMintingOpportunity'
CanComputEthProof,
} from '-zk/mina-token-bridge/rx/deposit';
import { FungibleToken, NoriStorageInterface, NoriTokenController, signSecretWithEthWallet } from '-zk/mina-token-bridge';
// Import your worker getter function here to support minting, e.g.:
// import { getCredentialAttestationWorker } from './workers/credentialAttestation/parent';
// Do other logic such as retrieving user balance etc.
```
### Example of mint flow
See the [E2E test](src/e2e.workers3.spec.ts) for a comprehensive example.
### Example of token contracts usage
#### Get user balance
```typescript
const tokenBase = new FungibleToken(tokenAddress);
await fetchAccount({
publicKey: userPublicKey,
tokenId: tokenBase.deriveTokenId(),
});
const balance = await tokenBase.getBalanceOf(userPublicKey);
```
#### Get user storage info
```typescript
const noriTokenController = new NoriTokenController(noriAddress);
const storage = new NoriStorageInterface(
userPublicKey,
noriTokenController.deriveTokenId()
);
await fetchAccount({
publicKey: userPublicKey,
tokenId: noriTokenController.deriveTokenId(),
});
const userKeyHash = await storage.userKeyHash.fetch();
const mintedSoFar = await storage.mintedSoFar.fetch();
```
## Token Contract Deployment
In order to deploy the contract, a script has been provided as an npm command:
```sh
npm run deploy
```
This command builds the project and runs the deployment script with Node.js using the necessary experimental flags for VM and WASM modules.
### Environment Variables
To configure the deployment, set the following environment variables:
- `MINA_RPC_NETWORK_URL`
The Mina network RPC endpoint URL.
Defaults to `http://localhost:8080/graphql` if unset.
- `SENDER_PRIVATE_KEY`
Private key of the deploying sender account.
Required for non-local deployments. Auto-generated for localhost.
- `NORI_CONTROLLER_PRIVATE_KEY`
Private key for the Nori Token Controller contract.
Randomly generated if not provided.
- `TOKEN_BASE_PRIVATE_KEY`
Private key for the Token Base contract.
Randomly generated if not provided.
- `ADMIN_PUBLIC_KEY`
Public key of the admin.
Defaults to the public key derived from `SENDER_PRIVATE_KEY`.
- `ETH_PROCESSOR_ADDRESS`
Optional Ethereum processor contract address.
- `TX_FEE`
Transaction fee to use. Defaults to `0.1`.
- `MOCK`
Optional flag to indicate mock mode ('true' or omit for false [the default]).
### Example `.env` file
```
MINA_RPC_NETWORK_URL=http://localhost:8080/graphql
SENDER_PRIVATE_KEY=your_sender_private_key_here
NORI_CONTROLLER_PRIVATE_KEY=your_nori_controller_key_here
TOKEN_BASE_PRIVATE_KEY=your_token_base_private_key_here
ADMIN_PUBLIC_KEY=your_admin_public_key_here
ETH_PROCESSOR_ADDRESS=optional_eth_processor_address
TX_FEE=0.1
MOCK=true
```
## How to run the tests
### Install Mina lightnet
1. `npm install -g zkapp-cli`
2. `zk lightnet start`
### Configure the .env file in the contracts/ethereum workspace
contracts/ethereum/.env
```
ETH_PRIVATE_KEY=<Your ETH private key>
ETH_RPC_URL=<Ethereum execution RPC e.g. 'https://ethereum-holesky.core.chainstack.com/<apiKey>'>
ETH_NETWORK=holesky (for example)
NORI_TOKEN_BRIDGE_TEST_MODE=true
NORI_TOKEN_BRIDGE_ADDRESS=<Extract this from contracts/ethereum/.env.nori-token-bridge, after running npm run deploy (within the contracts/ethereum workspace), or use an already deployed test contract>
```
### Then run the tests
```sh
npm run test # all tests (hangs due to multiple instances of o1js deps)
npm run test -- -t 'e2e_complete' # run a specific test (e.g. the litenet e2e test)
npm run test -- src/e2e.workers2.spec.ts # run a specific test file (e.g. the litenet e2e test)
npm run testw # watch mode
```
### How to run E2E test on DevNet
Firstly configure your `contracts/mina/token-bridge/.env` file:
```
ETH_PRIVATE_KEY=<Holesky ETH private key which will send the Holesky ETH>
ETH_RPC_URL=https://ethereum-holesky.core.chainstack.com/<apiKey>
NORI_TOKEN_BRIDGE_ADDRESS=<Holesky ETH Nori Token Bridge address>
NORI_CONTROLLER_PUBLIC_KEY=<Nori Mina TestNet Token controller base58 address>
MINA_RPC_NETWORK_URL=https://devnet.minaprotocol.network/graphql
SENDER_PRIVATE_KEY=<Nori Mina TestNet private key which will receive the deposit>
NORI_TOKEN_PUBLIC_KEY=<Nori Mina TestNet Token base base58 address>
```
**Note a real Mina TestNet contract must already be deployed.**
#### After configuration, run the E2E test procedure:
`npm run test -- src/e2e.workers3.spec.ts`
## How to run coverage
```sh
npm run coverage
```
## License
[Apache-2.0](LICENSE)