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! 🚀