Introduction to the Mina Javascript SDK

Since writing this post, Coda has rebranded to Mina Protocol. I’ve left the article in its original form but all references to Coda are equivalent to Mina.

This post provides an introduction to the current implementation of the Coda Javascript SDK. Using the SDK, it is possible with Javascript to generate keys, sign and verify messages, and sign transactions that can be broadcast to the network. From the package’s Readme:

This is a NodeJS client SDK that allows you to sign transactions and strings using Coda’s keypairs. The project contains Typescript and ReasonML typings but can be used from plain NodeJS as well.

The SDK will likely be incorporated into wallet software and browser-based services. It also enables the ability to generate and sign transactions on an air-gapped device that never exposes a private key to the internet.

The SDK is still in active development, so it is likely to change over time. Currently, the following methods are available with further expected to be added, such as to support the use of tokens.

  • genKeys - generates a public/private keypair
  • signMessage - signs an arbitrary message
  • verifyMessage - verifies that a signature matches a message
  • signPayment - signs a payment using a private key
  • signStakeDelegation - signs a stake delegation using a private key

The following code simply generates a new keypair using the SDK:

Node.js example to generate a new keypair

Currently, this will return a keypair, and while you can use these keys with the SDK, to import these keys into the Coda daemon, the keys need to be wrapped. You can achieve this using the CLI and the coda advanced wrap-key command. This command is interactive that will prompt for, and subsequently wrap, the provided private key to the file path specified.

$ coda advanced help wrap-key
Wrap a private key into a private key file

coda advanced wrap-key

=== flags ===

-privkey-path FILE File to write private key into (public key will be FILE.pub)

If you have an existing wrapped private key that you wish to use, for example, one generated by the CLI or using the Coda keygen utility, you can convert this to a simple keypair for use in the SDK by using the coda advanced dump-keypair command.

$ coda advanced help dump-keypair
Print out a keypair from a private key file

coda advanced dump-keypair

=== flags ===

-privkey-path FILE File to read private key from

You can use the keypair to sign a message with your private key to, for example, verify that you own an address. Anyone with your public key can subsequently verify this. In the following example, a message is signed by a newly generated keypair and subsequently verified.

Typescript example to sign and verify an arbitrary message

To demonstrate generating and signing a transaction on the Coda blockchain using the SDK, we need to fund our newly created account. The account will not appear in the ledger until it has been included in a transaction. There is a small fee for creating a new account and adding it to the ledger of 0.0001 Coda. This fee is automatically deducted from the amount received when the account is created. For example, if we send 1 Coda to the newly created account, the amount received will be 0.999 Coda.

To obtain funds, we can either send from a different account with funds that we own, make a request to the faucet, or if it is not operational, make a request in the Discord community chat.

Once the account has a balance, we can generate a transaction and sign it. To do so, first, we need to determine the account nonce. Typically when sending from the CLI, this is inferred and does not need to be specified. However, when signing “offline”, we need to determine this before constructing the transaction. For a newly created account, this would be 0.

The nonce for the account is available via the CLI, GraphQL API as well as being displayed on available block explorers. For example, via the CLI:

$ coda advanced get-nonce -address 4vsRCVGPNFDmHvuCctawApbyUg8J6iWzdHvJ2bNP8AL1GtMeaLwC6P8ZNTavKmAD4mMGbDq8H6DYramtiQP5fcf5xhdqAfYibt7Am9syUUU91oSf7HhLKrA26RPutsoyQSmZiMjPZoe1vDXW
11

Or via a GraphQL query:

query {
account(publicKey: "4vsRCVGPNFDmHvuCctawApbyUg8J6iWzdHvJ2bNP8AL1GtMeaLwC6P8ZNTavKmAD4mMGbDq8H6DYramtiQP5fcf5xhdqAfYibt7Am9syUUU91oSf7HhLKrA26RPutsoyQSmZiMjPZoe1vDXW") {
nonce
}
}

Amounts and fees are specified in nanocoda (there are 1 billion nanocoda in one Coda). The below transaction is for 1 Coda with a fee of 0.1 Coda. This sample code (the keys used have been truncated), simply outputs a formatted signed transaction to the console.

const CodaSDK = require("@o1labs/client-sdk");

let keys = {
"privateKey": "6Bn...",
"publicKey": "4vs..."
}

let recipient = "4vs..."

let signedPayment = CodaSDK.signPayment({
to: recipient,
from: keys.publicKey,
amount: 1000000000,
fee: 100000000,
nonce: 11,
memo: "Signed offline"
}, keys);

console.log(JSON.stringify(signedPayment, null, 2))

This results in the following response:

{
"publicKey": "4vsRCVGPNFDmHvuCctawApbyUg8J6iWzdHvJ2bNP8AL1GtMeaLwC6P8ZNTavKmAD4mMGbDq8H6DYramtiQP5fcf5xhdqAfYibt7Am9syUUU91oSf7HhLKrA26RPutsoyQSmZiMjPZoe1vDXW",
"signature": {
"field": "4095534492658964702093302946974471182296188502484380120926504384263504042032610580669486071343218811349585089608613942032855643518392809723352341936756681853799141236113411446072063867512380649660792574865803333232887390913090",
"scalar": "35403554010515316356568795584928624224609840002021165817923103399494288887666114389157076781767107373893931726328638542598055926963988193265351042590307015450026793718295883767841977811817570644316899178951192564007648210326202"
},
"payload": {
"to": "4vsRCVGPNFDmHvuCctawApbyUg8J6iWzdHvJ2bNP8AL1GtMeaLwC6P8ZNTavKmAD4mMGbDq8H6DYramtiQP5fcf5xhdqAfYibt7Am9syUUU91oSf7HhLKrA26RPutsoyQSmZiMjPZoe1vDXW",
"from": "4vsRCVGPNFDmHvuCctawApbyUg8J6iWzdHvJ2bNP8AL1GtMeaLwC6P8ZNTavKmAD4mMGbDq8H6DYramtiQP5fcf5xhdqAfYibt7Am9syUUU91oSf7HhLKrA26RPutsoyQSmZiMjPZoe1vDXW",
"fee": "100000000",
"amount": "1000000000",
"nonce": "11",
"memo": "Signed offline",
"validUntil": "4294967295"
}
}

The role of the nonce is to prevent anyone from replaying this transaction on the blockchain.

Now that we have a signed transaction, the next step is to broadcast it to the network so it can be gossipped to all nodes on the network and included in a future block. To achieve this, you can either broadcast the transaction via the GraphQL API or use a third party service.

If you have a running node, you can use the GraphQL API to submit the transaction directly via the API or using the GraphiQL GUI as illustrated below, by copying the values from the signed transaction output to the correct input fields. In the below query, you will be returned the payment id that you can subsequently query for the transaction status.

Using GraphiQL to broadcast a transaction

If you do not have access to a node but still wish to broadcast your transaction, you can use a third-party service to broadcast the transaction to the rest of the network. For example, I added a broadcast transaction form to the block explorer I developed. To use, simply copy the output from the SDK and paste it into the form. As the ecosystem grows, expect to see more services offering this functionality, such as outlined in the Figment Networks token grant to build a “Coda Gateway”.

Broadcast a transaction using a block explorer

Common issues with broadcasting transactions include specifying an incorrect nonce, using an account without a sufficient balance, or specifying amounts and fees in Coda instead of nanocoda (there is a minimum fee of 0.001 Coda or 1000000 nanocoda).

Technical writer, data wrangler and (former) full stack dev

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store