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โ
- Clone the transaction-examples repository by running:
git clone https://github.com/near-examples/transaction-examples.git
- Follow setup instructions
Importsโ
In send-tokens-easy.js
we use two dependencies:
- NEAR API JavaScript library
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 variablesKeyPair
- creates a keyPair from the private key you'll provide in an.env
filekeyStores
- stores the keyPair that you will create from the private key and used to sign Transactionsutils
- 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).
- To perform this you will use the
near-api-js
methodparseNearAmount()
(located inutils/format
)
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 rannear login
in your terminal, your private key can be found in the your machine's keychain. - 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
- In your browser's dev tools...
// 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 prefix = (networkId === "testnet") ? "testnet" : "www";
const config = {
networkId,
keyStore,
nodeUrl: `https://rpc.${networkId}.near.org`,
walletUrl: `https://wallet.${networkId}.near.org`,
helperUrl: `https://helper.${networkId}.near.org`,
explorerUrl: `https://${prefix}.nearblocks.io`,
};
// 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 no need to create a result
variable aside from inspecting the response details from your transaction and even create a link to NearBlocks Explorer to view a GUI version of the transaction details.
LOW LEVEL -- Create a Transactionโ
Setupโ
- Clone the transaction-examples repository by running:
git clone https://github.com/near-examples/transaction-examples.git
- Follow setup instructions
Importsโ
In send-tokens-deconstructed.js
we use three dependencies:
- NEAR API JavaScript library
js-sha256
(cryptographic hashing algorithm)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).
- To perform this you will use the
near-api-js
methodparseNearAmount()
(located inutils/format
)
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 rannear login
in your terminal, your private key can be found in your machine's keychain. - 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
- In your browser's dev tools...
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 akeyPair
object to sign transactions.
const privateKey = process.env.SENDER_PRIVATE_KEY;
const keyPair = nearAPI.KeyPair.fromString(privateKey);
Transaction Requirementsโ
As stated before, all transactions require six parts:
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
anddata
.
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 thekeyPair
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 thereceiverId
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
โ
- There are currently eight supported
Action
types. [see here] - For this example, we are using
Transfer
- This transfer action can be created using the imported
nearAPI
object and the formatted โ amount created earlier.
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 innearAPI
.
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 oncreateTransaction()
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.
const serializedTx = nearAPI.utils.serialize.serialize(
nearAPI.transactions.SCHEMA.Transaction,
transaction
);
- Hash the serialized transaction using a
sha256
cryptographic hashing algorithm.
const serializedTxHash = new Uint8Array(sha256.sha256.array(serializedTx));
- Create a signature with the
keyPair
.
const signature = keyPair.sign(serializedTxHash);
- 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 insidenear
.
// 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 NearBlocks Explorer, enter the
hash
located undertransaction
/Transaction Results
. - In addition, you can create a link in JS using the
networkId
andresult.transaction.hash
.
const prefix = (networkId === "testnet") ? "testnet." : "";
const transactionLink = `https://${prefix}nearblocks.io/txns/${result.transaction.hash}`;
Happy Coding! ๐