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](https://javascript.info/async-await) syntax in JavaScriptFor a refresher on these topics, please check out the [introduction to `near-api-js`](/docs/develop/front-end/introduction).
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 tonearApi.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 platformnearApi.Connection
represents a connection to the NEAR platformnearApi.Contract
represents a Smart Contract on the NEAR platformnearApi.InMemorySigner
is used for signing transactions before they’re sent to the networknearApi.KeyPair
is used to generate new key pairsnearApi.WalletAccount
is used to instantiate awallet
(as per the big 3 above)
utility collections
nearApi.keyStores
surfaces various key stores includingBrowserLocalStorageKeyStore
for client-side development (stores keys inLocalStorage
)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 hydratedAddKey
transaction ready for serialization and signing before being sent to the network) and their parameters (ie.FunctionCallPermission
is a type ofAccessKey
that may be used as a parameter to theaddKey()
method)nearApi.utils
surfaces various utility classes and functions includingKeyPairEd25519
for key pair generation using the ed25519 algorithm- and serialization utilities
base_encode
,base_decode
,serialize
anddeserialize
nearApi.providers
surfaces access to theJsonRpcProvider
which wraps the RPC interface
methods
nearApi.connect(config)
returns anear
connection object given a configuration object as per the introduction tonear-api-js
nearApi.providers.getTransactionLastResult(fxo)
returns the result of the last transaction given aFinalExecutionOutcome
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 timenear.connection.provider
exposes theJsonRpcProvider
near.connection.signer
exposes theInMemorySigner
methods
near.connect(account)
returns a reference to a validated and fully hydrated account on the networknear.loadContract(contract, options)
loads a previously deployed Smart Contract from the NEAR network for access in your client or server-side JavaScript.(deprecated, usenear.sendTokens(amount, sender, receiver)
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 backwallet.isSignedIn()
returns a boolean indicator of signed-in statuswallet.getAccountId()
returns the name of the wallet account currently signed (as a string)wallet.signOut()
removes theLocalStorage
entry that represents an authorizedWalletAccount
but leaves private keys intact
did you know?
[NEAR examples](http://near.dev/) 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> {}
}
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 ChunkHeaderView
s
// 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 SignedTransactionView
s and a collection of ReceiptView
s
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
“`json
{
“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> {}
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),
});
Ask it on StackOverflow!