Create Transactions
To construct & process transactions you will need our API JavaScript library: near-api-js
.
- 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.
Note: This is a very low level approach for creating transactions on NEAR. Our JavaScript library,
near-api-js
, provides much higher level ways to create transactions. See working examples of this at NEAR.dev.
Create a "token transfer" 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 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
- 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.utils.key_pair.KeyPairEd25519.fromString(privateKey)
Transaction Requirements
As stated before, all transactions require six parts:
signerId
1 - The
signerId
is the account ID of the transaction originator. - This value is passed as a string (ex.
'example.testnet'
or'bob.near'
)
signerPublicKey
2 - 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();
receiverId
3 - 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.
nonceForPublicKey
4 - 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;
actions
5 - 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()
.
blockHash
6 - 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
);
- 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 NEAR Explorer, enter the
hash
located undertransaction
/Transaction Results
. - In addition, you can create a link in JS using the
networkId
andresult.transaction.hash
.
const transactionLink = `https://explorer.${networkId}.near.org/transactions/${result.transaction.hash}`
Got a question?
Ask it on StackOverflow!
Happy Coding! 🚀