My NEAR learning experience

Silas
7 min readNov 21, 2020

--

I wrote this guide to help anyone interested in ramping up on what the NEAR team and the NEAR Protocol actually do. I want to help anyone to learn what makes NEAR unique and build your first NEAR smart contract by sharing my learning experience.

Get Started with NEAR Pathway Tutorials! There are five missions for us.

  1. Connect to a NEAR node using DataHub

In order to benefit from the full potential of the NEAR blockchain, you will first need to connect to a NEAR node. In this tutorial, we are going to use the official NEAR Javascript API to connect to a NEAR node hosted by DataHub. We will be building a simple Nodejs application, which we will develop further during the NEAR Pathway.

First, Install the Near packages:

npm install --save near-api-js dotenv js-sha256

Second, open .env file and add a few environment variables that we're going to use in all tutorials moving forward:

  • NEAR_NODE_URL - URL for NEAR DataHub node.
  • NEAR_NETWORK - Name of the NEAR network, should be testnet by default.
  • NEAR_ACCOUNT - Name of your NEAR account, create a new unique name and end in .testnet suffix

Now that we have completed all the necessary configuration steps, we can finally start building our application. We will start by configuring the NEAR JavaScript API and then checking if everything works by fetching the node status.

Create the connect.js file with the following content:

// Load environment variablesrequire("dotenv").config();// Load NEAR Javascript API componentsconst near = require("near-api-js");// Setup default client optionsconst options = {networkId:   process.env.NEAR_NETWORK,nodeUrl:     process.env.NEAR_NODE_URL,walletUrl:   `https://wallet.${process.env.NEAR_NETWORK}.near.org`,helperUrl:   `https://helper.${process.env.NEAR_NETWORK}.near.org`,explorerUrl: `https://explorer.${process.env.NEAR_NETWORK}.near.org`,keyStore:    {} // we will configure this later}async function main() {// Configure the client with optionsconst client = await near.connect(options);const provider = client.connection.provider;console.log("Client config:", client.config);// Get current node statusconst status = await provider.status();console.log("Status:", status);}main();

Let’s run the code:

node connect.js

2. Create your first NEAR account

Creating an Account

Time to start building! We are finally ready to jump in and create our NEAR account on the Testnet.

Make a new file create_account.js with the snippet below:

// Load environment variables
require("dotenv").config();
// Load Near Javascript API components
const near = require("near-api-js");
const fs = require("fs");
// Configure the directory where NEAR credentials are going to be stored
const credentialsPath = "./credentials";
// Configure the keyStore to be used with the NEAR Javascript API
const UnencryptedFileSystemKeyStore = near.keyStores.UnencryptedFileSystemKeyStore;
const keyStore = new UnencryptedFileSystemKeyStore(credentialsPath);
// Setup default client options
const options = {
networkId: process.env.NEAR_NETWORK,
nodeUrl: process.env.NEAR_NODE_URL,
walletUrl: `https://wallet.${process.env.NEAR_NETWORK}.near.org`,
helperUrl: `https://helper.${process.env.NEAR_NETWORK}.near.org`,
explorerUrl: `https://explorer.${process.env.NEAR_NETWORK}.near.org`,
accountId: process.env.NEAR_ACCOUNT,
keyStore: keyStore
}
async function main() {
let keyPair;
// Configure the client with options and our local key store
const client = await near.connect(options);
// Configure the key pair file location
const keyRootPath = client.connection.signer.keyStore.keyDir;
const keyFilePath = `${keyRootPath}/${options.networkId}/${options.accountId}.json`;
// Check if the key pair exists, and create a new one if it does not
if (!fs.existsSync(keyFilePath)) {
console.log("Generating a new key pair")
keyPair = near.KeyPair.fromRandom("ed25519");
} else {
let content = JSON.parse(fs.readFileSync(keyFilePath).toString());
keyPair = near.KeyPair.fromString(content.private_key);
console.log(`Key pair for account ${options.accountId} already exists, skipping creation`);
}
// Create a key pair in credentials directory
await client.connection.signer.keyStore.setKey(options.networkId, options.accountId, keyPair);
// Determine if account already exists
try {
await client.account(options.accountId);
return console.log(`Sorry, account '${options.accountId}' already exists.`);
}
catch (e) {
if (!e.message.includes("does not exist while viewing")) {
throw e;
}
}
// Generate a public key for account creation step
const publicKey = keyPair.getPublicKey()
// Create the account
try {
const response = await client.createAccount(options.accountId, publicKey);
console.log(`Account ${response.accountId} for network "${options.networkId}" was created.`);
console.log("----------------------------------------------------------------");
console.log("OPEN LINK BELOW to see account in NEAR Explorer!");
console.log(`${options.explorerUrl}/accounts/${response.accountId}`);
console.log("----------------------------------------------------------------");
}
catch(error) {
console.log("ERROR:", error);
}
}
main();

3. Query the NEAR blockchain

Often times you will need to pull information about the blockchain and its state such as:

  • Node information
  • Most recent synced block
  • Validator set status
  • Account balances
  • Transaction details

We can start by creating a new file query.js in our project root directory. Use the following code:

// Load environment variables
require("dotenv").config();
// Load NEAR Javascript API components
const near = require("near-api-js");
// Setup default client options
const options = {
networkId: process.env.NEAR_NETWORK,
nodeUrl: process.env.NEAR_NODE_URL,
walletUrl: `https://wallet.${process.env.NEAR_NETWORK}.near.org`,
helperUrl: `https://helper.${process.env.NEAR_NETWORK}.near.org`,
explorerUrl: `https://explorer.${process.env.NEAR_NETWORK}.near.org`,
accountId: process.env.NEAR_ACCOUNT,
keyStore: {}
}
async function main() {
// Configure the client with options and our local key store
const client = await near.connect(options);
const provider = client.connection.provider;

// Make queries to the blockchain here:
// 1) get status
// 2) get latest block
// 3) get block by number
// 4) get current validators
// 5) get account details
// 6) get gas price
}
main()

4. Submit your first transactions

In the previous tutorials, we created our account and we used the Testnet faucet to request test tokens. Now that we have some tokens to spend, let’s send some of them to another account.

Start by creating a new file transfer.js in the project directory and add the code below:

// Load environment variables
require("dotenv").config();
// Load NEAR Javascript API components
const near = require("near-api-js");
const { sha256 } = require("js-sha256");
const fs = require("fs");
// Formatter helper for NEAR amounts
function formatAmount(amount) {
return BigInt(near.utils.format.parseNearAmount(amount.toString()));
};
// Directory where NEAR credentials are going to be stored
const credentialsPath = "./credentials";
// Configure the keyStore to be used with the SDK
const UnencryptedFileSystemKeyStore = near.keyStores.UnencryptedFileSystemKeyStore;
const keyStore = new UnencryptedFileSystemKeyStore(credentialsPath)
// Setup default client options
const options = {
networkId: process.env.NEAR_NETWORK,
nodeUrl: process.env.NEAR_NODE_URL,
walletUrl: `https://wallet.${process.env.NEAR_NETWORK}.near.org`,
helperUrl: `https://helper.${process.env.NEAR_NETWORK}.near.org`,
explorerUrl: `https://explorer.${process.env.NEAR_NETWORK}.near.org`,
accountId: process.env.NEAR_ACCOUNT,
deps: {
keyStore: keyStore
}
}
// Configure transaction details
const txSender = options.accountId;
const txReceiver = "pizza.testnet"; // CHANGE THE RECEIVER
const txAmount = formatAmount(1);
async function main() {
// Configure the client with options and our local key store
const client = await near.connect(options);
const provider = client.connection.provider;
// Private key configuration
const keyRootPath = client.connection.signer.keyStore.keyDir;
const keyFilePath = `${keyRootPath}/${options.networkId}/${options.accountId}.json`;
// Load key pair from the file
const content = JSON.parse(fs.readFileSync(keyFilePath).toString());
const keyPair = near.KeyPair.fromString(content.private_key);
// Get the sender public key
const publicKey = keyPair.getPublicKey();
console.log("Sender public key:", publicKey.toString())
// Get the public key information from the node
const accessKey = await provider.query(
`access_key/${txSender}/${publicKey.toString()}`, ""
);
console.log("Sender access key:", accessKey);
// Check to make sure provided key is a full access key
if (accessKey.permission !== "FullAccess") {
return console.log(`Account [${txSender}] does not have permission to send tokens using key: [${publicKey}]`);
};
// Each transaction requires a unique number or nonce
// This is created by taking the current nonce and incrementing it
const nonce = ++accessKey.nonce;
console.log("Calculated nonce:", nonce);
// Construct actions that will be passed to the createTransaction method below
const actions = [near.transactions.transfer(txAmount)];
// Convert a recent block hash into an array of bytes.
// This is required to prove the tx was recently constructed (within 24hrs)
const recentBlockHash = near.utils.serialize.base_decode(accessKey.block_hash);
// Create a new transaction object
const transaction = near.transactions.createTransaction(
txSender,
publicKey,
txReceiver,
nonce,
actions,
recentBlockHash
);
// Before we can sign the transaction we must perform three steps
// 1) Serialize the transaction in Borsh
const serializedTx = near.utils.serialize.serialize(
near.transactions.SCHEMA,
transaction
);
// 2) Hash the serialized transaction using sha256
const serializedTxHash = new Uint8Array(sha256.array(serializedTx));
// 3) Create a signature using the hashed transaction
const signature = keyPair.sign(serializedTxHash);
// Sign the transaction
const signedTransaction = new near.transactions.SignedTransaction({
transaction,
signature: new near.transactions.Signature({
keyType: transaction.publicKey.keyType,
data: signature.signature
})
});
// Send the transaction
try {
const result = await provider.sendTransaction(signedTransaction);
console.log("Creation result:", result.transaction);
console.log("----------------------------------------------------------------");
console.log("OPEN LINK BELOW to see transaction in NEAR Explorer!");
console.log(`${options.explorerUrl}/transactions/${result.transaction.hash}`);
console.log("----------------------------------------------------------------");
setTimeout(async function() {
console.log("Checking transaction status:", result.transaction.hash);
const status = await provider.sendJsonRpc("tx", [result.transaction.hash, options.accountId]);
console.log("Transaction status:", status);
}, 5000);
}
catch(error) {
console.log("ERROR:", error);
}
};
main()

5. Write and deploy your first NEAR smart contract

At some point, while building on the NEAR blockchain, you will want to create smart contracts in order to unlock the full power of NEAR.

In this tutorial, we will learn how to create a simple contract using the NEAR SDK for AssemblyScript Contracts, compile it to WASM binary and deploy it to NEAR testnet using DataHub.

Before we start working on the contract code itself, we need to install the necessary tooling to support compilation of AssemblyScript codebase into WASM. Compilation is handled by the NEAR SDK for AssemblyScript Contracts and can be found on Github.

In the terminal run the following command:

npm install --save near-sdk-as

Let’s start by creating a new file contract.ts with the following content:

import { context, storage, logging } from "near-sdk-as";export function getValue(): string | null {return storage.getString("state");}export function setValue(value: string): void {logging.log("Setting value to " + value);storage.setString("state", value);}

Compiling to WebAssembly

Now that we have the contract code ready, we can focus on the compilation step. Before we can deploy the contract onto the NEAR network it needs to be converted from the AssemblyScript into a WASM binary file. Luckily it is extremely easy to do with the NEAR SDK for AssemblyScript Contracts.

First, create a new file in the current project directory, named asconfig.json with the content below. The config file is necessary for the correct compilation of the contract code.

{"extends": "near-sdk-as/asconfig.json"}

Next, create a new file asconfig.js with the compilation steps from the following code:

const { compile } = require("near-sdk-as/compiler");compile("./contract.ts", "",["--runPasses", "inlining-optimizing,dce","--binaryFile", "./contract.wasm","--measure",],{ verbose: false });

If you have not installed the CLI yet, run the following command:

npm install -g near-cli

Once you run near --help you should see the general usage and list of available commands

To deploy your smart contract using the CLI run the command:

near deploy \--contractName=figment-learn.testnet \--keyPath=./credentials/testnet/figment-learn.testnet.json \--wasmFile=./contract.wasm

Thanks!

--

--

No responses yet