Skip to main content

Examples Using near-api-js

Overview

This page includes several examples of using near-api-js presented as a collection of code snippets.

near-api-js is a JavaScript/TypeScript library for development of DApps on the NEAR platform that can be used from any client or server-side JavaScript environment.

Every code snippet on this page will work as expected if you copy and paste it into a "working context" by which we mean something like the Playground presented in the introduction to near-api-js.

Most of these examples either come directly from, or were heavily inspired by, the near-api-js test suite. You can see the code on GitHub if you're into that sort of thing.

Keep in mind the near-api-js source has two main folders with hand-crafted code.

  • /src
  • /test

All other folders in this repository are generated by automated scripts (see package.json in the repo for details)

So, when looking for a feature of near-api-js in the source code, know that the src folder is the authority for functionality and test contains the code that exercises all of this functionality. The rest of the repo can (should?) be safely ignored.

heads up

This document assumes you:

  • already have a basic understanding of near-api-js which includes
    • how to include it in your client or server-side JavaScript project
    • how to connect to the NEAR network
  • already know how to work with async / await syntax in JavaScript

For a refresher on these topics, please check out the introduction to near-api-js.

It's also worth mentioning now in case these examples seem overly simplistic that, for expediency in learning, the code below doesn't follow best practices. We add various properties to the window object, for example. While this is clearly not good practice in general (because it pollutes the global namespace) it's very useful for quick prototyping from the JavaScript Developer Console because it's easier for you to move through these examples while testing and validating your understanding along the way.

In your own code, consider best practices as you normally would. The examples on this page are intended to minimize the time and effort required to become productive while learning. Exercise your own best judgement when building applications for your users in the wild.

For our take on best practices with near-api-js and the rest of the NEAR platform, take a look at our GitHub org and star a few of our internal tools like NEAR CLI, NEAR Wallet and NEAR Explorer, all of which use near-api-js under the hood. You can also subscribe to our newsletter for updates about our starter kits, beta products and new tools as we release them the community.

Happy hacking!

near-api-js At 1000ft

This section introduces near-api-js at a very high level in an attempt to keep the "map" in full view. If at any point while working with near-api-js you find something doesn't make sense, this section should help you put it back in context. Either that or we need to make some adjustments! So don't hold back with those issues and pull requests -- we remain very receptive to developer feedback.

To begin, the playground presented in the introduction to near-api-js surfaces 3 objects including:

  • near-api-js (the library, the SDK, the big enchilada tostada)
  • near (the connection object returned by a call to nearApi.connect())
  • wallet (convenience methods for authentication and credential management)

The near-api-js Interface

This is the reference to the top level near-api-js SDK (view source)

classes / constructors

  • nearApi.Account represents an account on the NEAR platform
  • nearApi.Connection represents a connection to the NEAR platform
  • nearApi.Contract represents a Smart Contract on the NEAR platform
  • nearApi.InMemorySigner is used for signing transactions before they're sent to the network
  • nearApi.KeyPair is used to generate new key pairs
  • nearApi.WalletAccount is used to instantiate a wallet (as per the big 3 above)

utility collections

  • nearApi.keyStores surfaces various key stores including
    • BrowserLocalStorageKeyStore for client-side development (stores keys in LocalStorage)
    • UnencryptedFileSystemKeyStore for server-side development (stores keys in ./neardev/${network}/${account}.json)
    • InMemoryKeyStore for testing
    • and MergeKeyStore to support any combination of the above
  • nearApi.transactions surfaces transaction factory methods (ie. nearApi.transactions.addKey() produces a hydrated AddKey transaction ready for serialization and signing before being sent to the network) and their parameters (ie. FunctionCallPermission is a type of AccessKey that may be used as a parameter to the addKey() method)
  • nearApi.utils surfaces various utility classes and functions including
    • KeyPairEd25519 for key pair generation using the ed25519 algorithm
    • and serialization utilities base_encode, base_decode, serialize and deserialize
  • nearApi.providers surfaces access to the JsonRpcProvider which wraps the RPC interface

methods

  • nearApi.connect(config) returns a near connection object given a configuration object as per the introduction to near-api-js
  • nearApi.providers.getTransactionLastResult(fxo) returns the result of the last transaction given a FinalExecutionOutcome object (the value returned after sending a signed transaction to the network)

The near Connection Interface

This is an instance of the nearApi.Connection class (view source)

objects

  • near.config exposes the original configuration data passed in at connection time
  • near.connection.provider exposes the JsonRpcProvider
  • near.connection.signer exposes the InMemorySigner

methods

  • near.connect(account) returns a reference to a validated and fully hydrated account on the network
  • near.loadContract(contract, options) loads a previously deployed Smart Contract from the NEAR network for access in your client or server-side JavaScript.
  • near.sendTokens(amount, sender, receiver) (deprecated, use account.sendMoney() instead)

The wallet Interface

This is an instance of the nearApi.WalletAccount class (view source)

methods

  • wallet.requestSignIn(contract, appTitle, successUrl, failureUrl) directs the current window through the NEAR Wallet authorization flow and back
  • wallet.isSignedIn() returns a boolean indicator of signed-in status
  • wallet.getAccountId() returns the name of the wallet account currently signed (as a string)
  • wallet.signOut() removes the LocalStorage entry that represents an authorized WalletAccount but leaves private keys intact
did you know?

NEAR examples includes several starter projects where you can see near-api-js, the near connection and the wallet in action by looking at main.js in the src folder. If you happen to notice a main.ts in the assembly folder, well, that's a contract.

near-api-js in Pictures

near-api-js surfaces NEAR primitives as first class objects and facilitates communicate with the NEAR platform through our JSON-RPC interface.

                                        o ----------------------- o
| |
o --------------- o | +-------------------+ |
use | | ------> | | | |
-------> | near-api-js | RPC | | NEAR platform | |
| | <------ | | | |
o --------------- o | +-------------------+ |
| |
o ----------------------- o

If we zoom in on near-api-js a little we'll notice a few key components that we can explore more deeply.

   near-api-js (zoomed in)
o ------------------------------------------------------------------------ o
| |
| o -------- o ---> o ------- o | |
| | Contract | | Account | | |
| o -------- o <--- o ------- o | |
| | |
| o ------------- o | |
| | WalletAccount | | |
| o ------------- o | |
| | |
| o ----------- o | |
| | Transaction | | |
| o ----------- o | |
| | ---> o --------------- o | ------> |
| | | JsonRpcProvider | | RPC |
| | o ------- o | | <--- o --------------- o | <------ |
| | | KeyPair | | |
| o -------------- o ---> | o ------- o | |
| | InMemorySigner | | | |
| o -------------- o <--- | o --------- o | |
| | | KeyStores | | |
| | o --------- o | |
| | |
| | |
| o --------- o | |
| | Utilities | | |
| o --------- o | |
| |
o ------------------------------------------------------------------------ o

All communications with the NEAR platform pass through JsonRpcProvider. Anything else that's happening in near-api-js is intended to make our lives as developers easier.

And of course the fact that this is JavaScript is equally arbitrary -- we could do the same with Java, C#, Ruby, Elixir, or any other language binding you prefer. The key is the JSON RPC interface with the NEAR platform.

Zooming In

Some parts of near-api-js are better seen first because it will help you make sense of the library as a whole.

The following short list of code snippets and examples should quickly give you a sense of how near-api-js works under ths hood.

If you feel like any of this could be improved, please share your thoughts by submitting an issue to the documentation repo.

JsonRpcProvider

This class provides connectivity to the NEAR platform. It is used by all other parts of near-api-js when interacting with the NEAR network to send transactions. JsonRpcProvider can also be used as a standalone utility for querying the network via RPC calls directly for network status, processed transactions, blocks and chunks on the network.

Here we've included the Typescript interface for JsonRpcProvider to make it clear which types are accepted as parameters and returned by the method calls.

// DO NOT TRY TO RUN THIS (it's incomplete TypeScript)
class JsonRpcProvider extends Provider {
async status(): Promise<NodeStatusResult> {}
async block(blockId: BlockId): Promise<BlockResult> {}
async chunk(chunkId: ChunkId): Promise<ChunkResult> {}
async txStatus(
txHash: Uint8Array,
accountId: string
): Promise<FinalExecutionOutcome> {}
async query(path: string, data: string): Promise<any> {}
async sendTransaction(
signedTransaction: SignedTransaction
): Promise<FinalExecutionOutcome> {}
}

view source on Github

Moving through this interface one method at a time ...

near.connection.provider.status()

This method returns a NodeStatusResult which is good for getting the latest block hash or height, a list of validators, and a few other high level network details

await near.connection.provider.status();

near.connection.provider.block

This method returns a BlockView, one of NEAR platform's primitives, which itself is made up of a BlockHeaderView and a collection of ChunkHeaderViews

// using the previous snippet to pull the latest block hash
let chain = (await near.connection.provider.status()).sync_info;
let hash = chain.latest_block_hash; // <-- note hash vs height

// we can pass the results to the provider.block method
await near.connection.provider.block(hash);
// using the previous snippet to pull the latest block hash
let chain = (await near.connection.provider.status()).sync_info;
let number = chain.latest_block_height; // <-- note height vs hash

// we can pass the results to the provider.block method
await near.connection.provider.block(number);

near.connection.provider.chunk

This method returns a ChunkView, one of NEAR platform's primitives, which itself is made up of a ChunkHeaderView and a collection of SignedTransactionViews and a collection of ReceiptViews

The code snippet below is too short to be useful except as an illustration. If you actually want to see results from the live network then the chunk hash should be queried using the provider.block snippets above. Chunks are returned as a collection attached to a block.

To avoid a longer example, a chunk hash was taken from the live network at time of writing but this may be invalid when you run it since it's a Testnet artifact which may be restarted. With Mainnet we could assume this would exist as long as the network survives.

// best to fetch this chunk hash using the code above
// if this call fails it's because this chunk hash is no longer valid
let hash = "6SrBkhwVLAZC2YQJG3YUSef2UGf7UxwpsLZEY3dsZMKb";
await near.connection.provider.chunk(hash);
// to see a list of transactions processed on a block we have to map the
// collection of [chunk_hash]es to chunks
let block = await near.connection.provider.block(
"A2AbjnAx9gbZNBE8URjWJB6gQy7wZEWuanZaQhkeE7pw"
);
let chunkFromChunkHash = async (c) => {
return await near.connection.provider.chunk(c.chunk_hash);
};
await Promise.all(block.chunks.map(chunkFromChunkHash));

// and there we will find the transaction that was available at the time of writing
// in the 4th chunk of this block, along with some chunk header info, this transaction:
// {
// transactions: [
// {
// actions: [{…}]
// hash: "3L37n7LowxfpFEJcQvYmoQPo4NpgV2ZUmr4vXSBYhxPL"
// nonce: 14
// public_key: "ed25519:91G4Y3jJk5hwYWQm4BZLVDLWh52axtjmc42P2VtyKh4"
// receiver_id: "cruz"
// signature: "ed25519:44mZ1CSMm1Ybku51413QTPS43gN7iSHkRBt1oFd2hdtu8Z2HFgsKr6CLGaCTKY3rwTcJnB3AWtGu1zZdb2DYbGqA"
// signer_id: "cruz"
// }
// ]
// }

near.connection.provider.txstatus

This method returns a [FinalExecutionOutcome].

The code snippet below is too short to be useful except as an illustration. A better example would use a hash from a recent transaction that you sent to the network.

To avoid a longer example, a transaction hash was taken from the live network at time of writing but this may be invalid when you run it since it's a Testnet artifact which may be restarted. With Mainnet we could assume this would exist as long as the network survives.

// best to fetch this transaction hash using a call to the live network
// if this call fails it's because this transaction hash is no longer valid
let txHash = "3L37n7LowxfpFEJcQvYmoQPo4NpgV2ZUmr4vXSBYhxPL";
let decodedTxHash = nearAPI.utils.serialize.base_decode(
"3L37n7LowxfpFEJcQvYmoQPo4NpgV2ZUmr4vXSBYhxPL"
);
await near.connection.provider.txStatus(decodedTxHash, "accountid.testnet");

near.connection.provider.query

This method accepts RPC paths and returns their results as JSON.

// this will return an AccountState for any account
let account = "test.near";
let state = await near.connection.provider.query(`account/${account}`, "");
console.table(state);
// this will return all access keys for any account
let account = "test.near";
let keys = await near.connection.provider.query(`access_key/${account}`, "");
console.dir(keys);

Account

This class represents an account on the NEAR platform. It has a number of convenience methods that wrap valid transactions on the network and, if a call to account.state() does not throw an error then you can be sure that you have (a) a valid account ID that (b) exists on the networkId which you passed via the connection configuration and (c) you have at least one private key for which the account ID has a matching public access key (either FullAccess permissions or FunctionCall access permissions)

did you know?

Calling await account.state() on an account object validates and hydrates the object at the same time if you have control of at least one private key associated with the account already available in your LocalStorage (or ./neardev/${networkId} folder for server-side development)

There are 3 possible outcomes of a call to await account.state():

(1) If the call succeeds you will see a hydrated account object returned to the console

{
"amount": "20000001999372696601",
"code_hash": "11111111111111111111111111111111",
"locked": "0",
"storage_paid_at": 1798245,
"storage_usage": 1366
}

/**
Note the above is actually an AccountState struct as JSON but it's the most
visible part of a valid Account object in the JavaScript Developer Console.
The same object also includes a reference to the Account object through its
prototype
*/

(2) If you call await account.state() on an Account object using an account that does not exist on the configured network account.connection.networkId then you will see the following error:

Server error: account ${accountId} does not exist while viewing

(3) If you call await to instantiate a new Account object without having the proper credentials you will see the following message emitted via console.log (client-side) or to stdout (server-side).

Missing public key for ${accountId} in default

Here we've included the Typescript interface for Account to make it clear which types are accepted as parameters and returned by the method calls.

// account hydration and validation
async fetchState(): Promise<void> {}
async state(): Promise<AccountState> {}

// account management
async createAccount(newAccountId: string, publicKey: string | PublicKey, amount: BN): Promise<FinalExecutionOutcome> {}
async deleteAccount(beneficiaryId: string): Promise<FinalExecutionOutcome> {}

// access key management
async addKey(publicKey: string | PublicKey, contractId?: string, methodName?: string, amount?: BN): Promise<FinalExecutionOutcome> {}
async deleteKey(publicKey: string | PublicKey): Promise<FinalExecutionOutcome> {}
async getAccessKeys(): Promise<any> {}
async getAccountDetails(): Promise<any> {}

// contract management
async deployContract(data: Uint8Array): Promise<FinalExecutionOutcome> {}
async createAndDeployContract(contractId: string, publicKey: string | PublicKey, data: Uint8Array, amount: BN): Promise<Account> {}

// function invocation on Smart Contracts
async viewFunction(contractId: string, methodName: string, args: any): Promise<any> {}
async functionCall(contractId: string, methodName: string, args: any, gas: number, amount?: BN): Promise<FinalExecutionOutcome> {}

// sending money to other accounts and staking (also sending money that you get back if you behave)
async sendMoney(receiverId: string, amount: BN): Promise<FinalExecutionOutcome> {}
async stake(publicKey: string | PublicKey, amount: BN): Promise<FinalExecutionOutcome> {}

// the method below could arguably be made public
private async signAndSendTransaction(receiverId: string, actions: Action[]): Promise<FinalExecutionOutcome> {}

view source on GitHub


work in progress expect the following content soon

  • zooming in

    • JsonRpcProvider
      • near.connection.provider.sendTransaction
    • Account
      • interface examples
    • Transaction
      • interface examples
  • cookbook

    • create an account
    • create a transaction
    • sign a transaction
    • send a transaction

Cookbook Recipes

Offline transaction signing in 3 steps

Fetch latest block hash (requires online)

let status = await connection.provider.status();
const blockHash = status.sync_status.last_block_hash;

Create transaction (offline):

const transaction = nearApi.transactions.createTransaction(
fromAccount,
publicKey,
receiverAccount,
nonce_for_publicKey,
actions,
blockHash
);
const bytes = transaction.encode();

Sign transaction (offline with access to the key):

const message = new Uint8Array(sha256.sha256.array(bytes));
const signature = await signer.signMessage(message, accountId, networkId);
const signedTx = new SignedTransaction({
transaction,
signature: new Signature(signature.signature),
});

Got a question?

Ask it on StackOverflow!