Skip to main content

Mina Signer

Mina Signer is a NodeJS/Browser compatible JavaScript library built for the Mina Protocol. It empowers developers to sign transactions and generate keys seamlessly. One of its standout features is its capability to sign transactions offline, which can then be broadcasted to the network as and when needed. There are additional features such as signing zkApp transactions, verifying zkApp transactions, generating nullifiers, and more.

Installation

To install the library, run the following command:

npm install mina-signer

Mina Protocol Usage

The Mina Signer offers a wide range of features for the Mina Protocol. It can be used to generate keys, sign transactions, verify transactions, and more. Mina Signer also supports compatability between different networks, such as mainnet and testnet.

Specifying the network

When initializing the Mina Signer, the network must be specified. This is done by passing the network parameter to the constructor. The network can be either mainnet or testnet. If no network is specified, the default network is mainnet.

tip

If you wish to specify the current Berkeley network, the value of testnet should be used

import Client from 'mina-signer';
const MainnetClient = new Client({ network: 'mainnet' }); // Specify mainnet
const TestnetClient = new Client({ network: 'testnet' }); // Specify testnet (Berkeley)

Generating keys

With Mina Signer, generating keys is straightforward.

import Client from 'mina-signer';
const client = new Client({ network: 'mainnet' }); // Specify mainnet
const keypair = client.genKeys(); // Generates a public and private keypair

Signing/Verifying transactions

The Mina Signer supports signing and verifying both transactions and stake delegations. To sign a transaction or delegation, the private key of the sender must be provided. To verify a transaction or delegation, the public key of the sender must be provided.

Payments

Payments are transactions that transfer funds from one account to another. To sign a payment, the following parameters must be provided:

import Client from 'mina-signer';
const client = new Client({ network: 'mainnet' });
const keypair = client.genKeys();

const payment = client.signPayment(
{
to: keypair.publicKey, // Public key of the recipient
from: keypair.publicKey, // Public key of the sender
amount: '1', // Amount to be sent (in nano MINA)
fee: '1', // Fee to be paid (in nano MINA)
nonce: '0', // Nonce of the sender
},
keypair.privateKey
);

const verifiedPayment = client.verifyPayment(payment);

Delegations

Stake delegations are a way for users to delegate their stake to a validator. This allows the validator to produce blocks on behalf of the delegator. To sign a stake delegation, the following parameters must be provided:

import Client from 'mina-signer';
const client = new Client({ network: 'mainnet' });
const keypair = client.genKeys();

const delegation = client.signStakeDelegation(
{
to: keypair.publicKey, // Public key of the validator
from: keypair.publicKey, // Public key of the delegator
fee: '1', // Fee to be paid (in nano MINA)
nonce: '0', // Nonce of the delegator
},
keypair.privateKey
);

const verifiedDelegation = client.verifyStakeDelegation(delegation);

Generic Signing

Mina Signer can take a generic payload for signing and decide which signing method it should use by using signTransaction(). This is useful for applications that want to support both different types of transaction payloads in an easy and maintanable way.

import Client from 'mina-signer';
const client = new Client({ network: 'mainnet' });

// Sign a payment
client.signTransaction(
{
to: keypair.publicKey,
from: keypair.publicKey,
amount: '1',
fee: '1',
nonce: '0',
},
keypair.privateKey
);

// Sign a delegation
client.signTransaction(
{
to: keypair.publicKey,
from: keypair.publicKey,
fee: '1',
nonce: '0',
},
keypair.privateKey
);


// Sign a zkApp transaction
client.signTransaction(
{
zkappCommand: ...,
feePayer: keypair.privateKey
},
keypair.privateKey
);

// Sign a simple string payload
client.signTransaction('Hello World', keypair.privateKey);

Rosetta

If you are developing for Rosetta, Mina Signer lets you take a signed Rosetta transaction and transform it into a Mina compatible transaction that is ready to be broadcasted via the Mina Daemon.

import Client from 'mina-signer';
const client = new Client({ network: 'mainnet' });

const signedRosettaTx = '...';
const signedGraphQLCommand =
client.signedRosettaTransactionToSignedCommand(signedRosettaTx);

Payment/Delegation Transaction Hashes

In addition to signing/verifying payments/delegations for the Mina Protocol, Mina Signer allows you to compute the hash that will be used to identify the transaction on the blockchain. This is useful for applications that require the transaction hash before the transaction is broadcasted to the network.

import Client from 'mina-signer';
const client = new Client({ network: 'mainnet' });
const keypair = client.genKeys();

const payment = client.signTransaction(
{
to: keypair.publicKey,
from: keypair.publicKey,
amount: '1',
fee: '1',
nonce: '0',
},
keypair.privateKey
);
const hashedPayment = client.hashPayment(payment);

const delegation = client.signTransaction(
{
to: keypair.publicKey,
from: keypair.publicKey,
fee: '1',
nonce: '0',
},
keypair.privateKey
);
const hashedDelegation = client.hashStakeDelegation(delegation);

o1js Usage

Mina Signer can seamlessly integrate with o1js, offering a wide range of features for zkApps. It can be used to sign zkApp transactions, verify zkApp transactions, generate nullifiers, and more.

Signing/Verifying zkApp transactions

Mina Signer supports signing and verifying zkApp transactions that have been previously signed with Mina Signer. o1js itself can be used to sign zkApp transactions, but Mina Signer offers the ability to create a transaction that is easily broadcasted via a running Mina Daemon. This can be very useful for wallet applications that want to support zkApps.

import Client from 'mina-signer';
import { Mina } from 'o1js';
const client = new Client({ network: 'testnet' });
const keypair = client.genKeys();

const zkAppTransaction = await Mina.transaction(feePayerAddress, () => {
// ... Interact with a zkApp inside this block to produce a zkApp transaction
});

// Sign the zkApp transaction with Mina Signer
const signedZkAppTransaction = client.signZkappCommand(
{
zkappCommand: JSON.parse(JSON.stringify(txn.transaction)),
feePayer: {
feePayer: keypair.publicKey,
fee: '1',
nonce: '0',
memo: 'memo',
},
},
keypair.privateKey
);

// Verify the zkApp transaction with Mina Signer
const verifiedZkAppTransaction = client.verifyZkappCommand(
signedZkAppTransaction
);

Firstly, when supplying the input parameters for signZkappCommand(), we must first parse the zkApp transaction into a string and then into a JSON object. This is because the types generated from Mina.transaction() are not compatible with the types used by Mina Signer. Secondly, we specify the feePayer object which contains the public key of the fee payer, the fee to be paid, the nonce of the fee payer, and the memo of the transaction. The feePayer object is used to sign the zkApp transaction.

tip

Use o1js to sign zkApp transactions if you can, as it's more ergonomic and easier to use. Only use Mina Signer if you need to sign zkApp transactions offline and broadcast at a later time (e.g. wallet software).

Signing/Verifying Field payloads

Mina Signer supports signing and verifying Field payloads. This can be helpful if you want to sign a Field payload with a specific keypair and verify the signature as to make sure that it has not been altered by a third party.

import Client from 'mina-signer';
const client = new Client({ network: 'testnet' });
const keypair = client.genKeys();

const fields = [10n, 20n, 30n, 340817401n, 2091283n, 1n, 0n];
const signedFields = client.signFields(fields, keypair.privateKey);
const verifiedFields = client.verifyFields(signedFields);

If you are using o1js to generate Field payloads, you must convert the Fields to BigInts before signing/verifying them. In Mina Signer, the Field type is a BigInt (while in o1js they are a seperate data structure), so you must convert the fields from o1js to BigInts before signing/verifying them.

import Client from 'mina-signer';
import { Field } from 'o1js';
const client = new Client({ network: 'testnet' });
const keypair = client.genKeys();

const fields = [Field(10), Field(20)].map((f) => f.toBigInt());
const signedFields = client.signFields(fields, keypair.privateKey);
const verifiedFields = client.verifyFields(signedFields);

Nullifiers

Examples