Skip to main content

Create Transactions

To construct & process transactions you will need our API JavaScript library: near-api-js. There are many ways to create transactions but for this example we'll show you two ways to create a simple token transfer transaction.

  • HIGH LEVEL - easiest way to create a transaction
  • LOW LEVEL - performs the exact same transaction as above, but deconstructs the entire process for those curious about each step

At the core, all transactions require the following:

  • signerId (account ID of the transaction originator)
  • signerPublicKey
  • receiverId (account ID of the transaction recipient)
  • nonceForPublicKey (each time a key is used the nonce value should be incremented by 1)
  • actions ( [click here] for supported arguments)
  • blockHash (a current block hash (within 24hrs) to prove the transaction was recently created)

See Transaction Class for a more in depth outline.


HIGH LEVEL -- Create a transaction

Setup

  1. Clone the transaction-examples repository by running:
git clone https://github.com/near-examples/transaction-examples.git
  1. Follow setup instructions

Imports

In send-tokens-easy.js we use two dependencies:

  1. NEAR API JavaScript library
  2. dotenv (used to load environment variables for private key)
const nearAPI = require("near-api-js");
const { connect, KeyPair, keyStores, utils } = nearAPI;
require("dotenv").config();

The second line above deconstructs several utilities from nearAPI that you will use to interact with the blockchain.

  • connect - create a connection to NEAR passing configuration variables
  • KeyPair - creates a keyPair from the private key you'll provide in an .env file
  • keyStores - stores the keyPair that you will create from the private key and used to sign Transactions
  • utils - used to format NEAR amounts

Accounts & Network

Next, you'll need to enter the accountId of the sender and receiver, as well as the networkId (betanet, testnet, or mainnet).

const sender = "sender.testnet";
const receiver = "receiver.testnet";
const networkId = "testnet";

Formatting Token Amounts

When sending NEAR tokens (Ⓝ) during a transaction, the amount needs to be converted into Yocto Ⓝ or (10^-24).

const amount = nearAPI.utils.format.parseNearAmount("1.5");

Create a Key Store

In order to sign transactions you will need to create a "Key Store" that will hold a full access key to sign your transactions. There are several ways to accomplish this, but for this example we will use a private key stored in either an .env file in your project or an environment variable exported globally.

  • If you created the account using near-cli or ran near login in your terminal, your private key can be found in a .json file located in /HOME/.near-credentials.
  • If you created an account using NEAR Wallet, your key will be found in your browser's Local Storage.
    • In your browser's dev tools... Application >> Storage >> Local Storage
// sets up an empty keyStore object in memory using near-api-js
const keyStore = new keyStores.InMemoryKeyStore();
// creates a keyPair from the private key provided in your .env file
const keyPair = KeyPair.fromString(process.env.SENDER_PRIVATE_KEY);
// adds the key you just created to your keyStore which can hold multiple keys (must be inside an async function)
await keyStore.setKey(networkId, sender, keyPair);

Setting up a connection to NEAR

Now create a connection to NEAR using a configuration object that will contain your networkId setup earlier as well as your keyStore.

// configuration used to connect to NEAR
const config = {
networkId,
keyStore,
nodeUrl: `https://rpc.${networkId}.near.org`,
walletUrl: `https://wallet.${networkId}.near.org`,
helperUrl: `https://helper.${networkId}.near.org`,
explorerUrl: `https://explorer.${networkId}.near.org`,
};

// connect to NEAR! :)
const near = await connect(config);
// create a NEAR account object
const senderAccount = await near.account(sender);

You'll notice the last line uses your NEAR connection to create a senderAccount object that you'll use to perform the transaction.

Create, Sign, & Send Transaction

Now that everything is setup, creating the transaction is a single line of code.

const result = await senderAccount.sendMoney(receiver, amount);

This simple command constructs, signs, and sends a token transfer transaction on the NEAR blockchain. There is not a need to create a result variable aside from inspecting the response details from your transaction and even create a link to NEAR Explorer to view a GUI version of the transaction details.


LOW LEVEL -- Create a Transaction

Setup

  1. Clone the transaction-examples repository by running:
git clone https://github.com/near-examples/transaction-examples.git
  1. Follow setup instructions

Imports

In send-tokens-deconstructed.js we use three dependencies:

  1. NEAR API JavaScript library
  2. js-sha256 (cryptographic hashing algorithm)
  3. dotenv (used to load environment variables)
const nearAPI = require("near-api-js");
const sha256 = require("js-sha256");
require("dotenv").config();

Accounts & Network

Next, you'll need to enter the accountId of the sender and receiver, as well as the networkId (betanet, testnet, or mainnet).

const sender = "sender.testnet";
const receiver = "receiver.testnet";
const networkId = "testnet";

Formatting Token Amounts

When sending NEAR tokens (Ⓝ) during a transaction, the amount needs to be converted into Yocto Ⓝ or (10^-24).

const amount = nearAPI.utils.format.parseNearAmount("1.5");

Setting up a connection to NEAR

In this example, we will create a NEAR RPC provider that allows us to interact with the chain via RPC endpoints.

const provider = new nearAPI.providers.JsonRpcProvider(
`https://rpc.${networkId}.near.org`
);

Access Keys

To sign a transaction to send NEAR Ⓝ, we will need a FullAccess key to the sender's account.

  • If you created the account using near-cli or ran near login in your terminal, your private key can be found in a .json file located in /HOME/.near-credentials.
  • If you created an account using NEAR Wallet, your key will be found in your browser's Local Storage.
    • In your browser's dev tools... Application >> Storage >> Local Storage

Once you have access to the private key of the sender's account, create an environment variable SENDER_PRIVATE_KEY or hard code it as a string on line 18 of send-tokens.js.

  • With this privateKey, we can now construct a keyPair object to sign transactions.
const privateKey = process.env.SENDER_PRIVATE_KEY;
const keyPair = nearAPI.utils.key_pair.KeyPairEd25519.fromString(privateKey);

Transaction Requirements

As stated before, all transactions require six parts:

  1. signerId
  2. signerPublicKey
  3. receiverId
  4. nonceForPublicKey
  5. actions
  6. blockHash

1 signerId

  • The signerId is the account ID of the transaction originator.
  • This value is passed as a string (ex. 'example.testnet' or 'bob.near')

2 signerPublicKey

  • The signerPublicKey is required to be an object with two key value pairs: keyType and data.
PublicKey = {
keyType: 0,
data: Uint8Array(32)[
(190,
150,
152,
145,
232,
248,
128,
151,
167,
165,
128,
46,
20,
231,
103,
142,
39,
56,
152,
46,
135,
1,
161,
180,
94,
212,
195,
201,
73,
190,
70,
242)
],
};
  • This can be constructed by calling getPublicKey() using the keyPair we setup earlier.
const publicKey = keyPair.getPublicKey();

3 receiverId

  • The receiverId is the account ID of the transaction recipient.
  • This value is passed as a string (ex. 'example.testnet' or 'bob.near')
  • The certain cases, the signerId and the receiverId can be the same account.

4 nonceForPublicKey

  • A unique number or nonce is required for each transaction signed with an access key.
  • To ensure a unique number is created for each transaction, the current nonce should be queried and then incremented by 1.
  • Current nonce can be retrieved using the provider we created earlier.
const accessKey = await provider.query(
`access_key/${sender}/${publicKey.toString()}`,
""
);
  • now we can create a unique number for our transaction by incrementing the current nonce.
const nonce = ++accessKey.nonce;

5 actions

const actions = [nearAPI.transactions.transfer(amount)];

[click here] to view source for transfer().

6 blockHash

  • Each transaction requires a current block hash (within 24hrs) to prove that the transaction was created recently.
  • Hash must be converted to an array of bytes using the base_decode method found in nearAPI.
const recentBlockHash = nearAPI.utils.serialize.base_decode(
accessKey.block_hash
);

[click here] to view source for base_decode().


Constructing the Transaction

With all of our required arguments, we can construct the transaction.

  • Using nearAPI, we call on createTransaction() to perform this task.
const transaction = nearAPI.transactions.createTransaction(
sender,
publicKey,
receiver,
nonce,
actions,
recentBlockHash
);

[click here] to view source code for the Transaction class


Sign Transaction

Now that the transaction is created, we sign it before sending it to the NEAR blockchain. At the lowest level, there are four steps to this process.

  1. Using nearAPI, we call on serialize() to serialize the transaction in Borsh.
const serializedTx = nearAPI.utils.serialize.serialize(
nearAPI.transactions.SCHEMA,
transaction
);
  1. Hash the serialized transaction using a sha256 cryptographic hashing algorithm.
const serializedTxHash = new Uint8Array(sha256.sha256.array(serializedTx));
  1. Create a signature with the keyPair.
const signature = keyPair.sign(serializedTxHash);
  1. Construct the signed transaction using near-api-js SignedTransaction class.
const signedTransaction = new nearAPI.transactions.SignedTransaction({
transaction,
signature: new nearAPI.transactions.Signature({
keyType: transaction.publicKey.keyType,
data: signature.signature,
}),
});

Send Transaction

Final step is to encode and send the transaction.

  • First we serialize transaction into Borsh, and store the result as signedSerializedTx. (required for all transactions)
  • Then we send the transaction via RPC call using the sendJsonRpc() method nested inside near.
// encodes transaction to serialized Borsh (required for all transactions)
const signedSerializedTx = signedTransaction.encode();
// sends transaction to NEAR blockchain via JSON RPC call and records the result
const result = await provider.sendJsonRpc("broadcast_tx_commit", [
Buffer.from(signedSerializedTx).toString("base64"),
]);

Transaction Results

Detailed transaction results of the transaction are returned in the following format:

{
status: { SuccessValue: '' },
transaction: {
signer_id: 'sender.testnet',
public_key: 'ed25519:8RazSLHvzj4TBSKGUo5appP7wVeqZNQYjP9hvhF4ZKS2',
nonce: 57,
receiver_id: 'receiver.testnet',
actions: [ [Object] ],
signature: 'ed25519:2sK53w6hybSxX7qWShXz6xKnjnYRUW7Co3evEaaggNW6pGSCNPvx7urY4akwnzAbxZGwsKjx8dcVm73qbitntJjz',
hash: 'EgGzB73eFxCwZRGcEyCKedLjvvgxhDXcUtq21SqAh79j'
},
transaction_outcome: {
proof: [ [Object] ],
block_hash: 'J6cFDzAFkuknHMCEYW2uPQXDvCfSndkJmADVEWJbtTwV',
id: 'EgGzB73eFxCwZRGcEyCKedLjvvgxhDXcUtq21SqAh79j',
outcome: {
logs: [],
receipt_ids: [Array],
gas_burnt: 223182562500,
tokens_burnt: '22318256250000000000',
executor_id: 'sender.testnet',
status: [Object]
}
},
receipts_outcome: [
{
proof: [Array],
block_hash: 'FSS7UzTpMr4mUm6aw8MmzP6Q7wnQs35VS8vYm1R461dM',
id: '3LjBxe2jq1s7XEPrYxihp4rPVdyHAbYfkcdJjUEVijhJ',
outcome: [Object]
},
{
proof: [Array],
block_hash: '4XBio5dM5UGYjJgzZjgckfVgMZ9uKGbTkt8zZi5webxw',
id: 'AXFA4kwiYfruKQ4LkD1qZA8P7HoAvtFwGqwQYdWtWNaW',
outcome: [Object]
}
]
}
Transaction Results: {
signer_id: 'sender.testnet',
public_key: 'ed25519:8RazSLHvzj4TBSKGUo5appP7wVeqZNQYjP9hvhF4ZKS2',
nonce: 57,
receiver_id: 'receiver.testnet',
actions: [ { Transfer: [Object] } ],
signature: 'ed25519:2sK53w6hybSxX7qWShXz6xKnjnYRUW7Co3evEaaggNW6pGSCNPvx7urY4akwnzAbxZGwsKjx8dcVm73qbitntJjz',
hash: 'EgGzB73eFxCwZRGcEyCKedLjvvgxhDXcUtq21SqAh79j'
}

For detailed information on transaction receipts [click here]

  • To view the transaction in NEAR Explorer, enter the hash located under transaction / Transaction Results.
  • In addition, you can create a link in JS using the networkId and result.transaction.hash.
const transactionLink = `https://explorer.${networkId}.near.org/transactions/${result.transaction.hash}`;

Got a question?

> Ask it on StackOverflow! >

Happy Coding! 🚀