Select Page

How to build a dApp on the NEAR blockchain?

HOW TO BUILD A DAPP ON THE NEAR BLOCKCHAIN

One of the most important features of web 3.0 is its power to run decentralized finance. DeFi, with the help of Web3, provides the corporate industry with open access to the global digital finance network. However, decentralized finance is still evolving to eliminate high transaction costs and low network speed. NEAR has surfaced as one of the most user-friendly blockchains in the DeFi space to address these limitations.

NEAR has a distinctive sharding feature and provides a robust ecosystem for decentralized application development. It has low transaction cost, high speed and is environment friendly. NEAR blockchain focuses on the limitations of older generation blockchain through its mechanism based on the sharding technique. It runs on Proof-of-Stake consensus that offers stable gas fees and on-chain scalability.

As mentioned earlier, NEAR functions are based on the sharding technique. This technique breaks down the computational data load into smaller parts, increasing scalability. Sharding, therefore, results in high throughput of transactions and smooth functioning of dApps and networks. Thus, NEAR is one of the most suited blockchain platforms for building a decentralized application.

Let us now unfold the dApp development on the NEAR platform in this article.

About NEAR Protocol

NEAR is a smart contract-capable blockchain platform designed to develop highly-scalable decentralized applications. It can process up to 100,000 transactions per second. NEAR blockchain provides a community-operated cloud infrastructure to deploy and run the dApps. It combines the attributes of a decentralized database with the database of a serverless computing platform. The token that powers the NEAR platform to function also enables the apps built on NEAR’s ecosystem to interact with each other in different ways.

All these features of the NEAR blockchain help developers build censorship-resistant back-ends for the smart contracts that handle high-stake data like identity, assets and money. The NEAR platform consists of some fundamental elements. Some of these fundamental elements are native to the platform, like its native token:

NEAR token

The NEAR token is a native element of the NEAR platform, and each token is a distinctive NEAR asset. These tokens can be used for the following purposes:

  • Payment for processing transactions and storing data on the NEAR blockchain.
  • It helps run a node for validating as a part of the NEAR network by participating in the process of staking.
  • It helps analyze the network resource allocation by participating in the process of governance.

NEAR, using its native token focuses on incentivizing the network contributors so that they can build and launch the dApps on the decentralized finance platform; and earn profit by staking and sharding. It secures and is permissionless in nature, and it also functions based on Doomslug for new block generation.

As mentioned earlier, NEAR’s scalability is high as its data computational load is managed via sharding. In sharding, the network’s computational load is split up into different segments, also known as nodes. These different nodes handle a fraction of the network’s transactions and processes. This fractional split up of network load also helps in easy retrieval of data and simultaneously reduces network congestion.

Now that we are familiar with NEAR let us learn more about its development suite in the next section.

What does NEAR’s development suite consist of?

NEAR blockchain is designed to be utilized independently in a permissionless manner but with a set of implementations and tools. These tools are as the following:

NEAR SDKs

NEAR uses the JavaScript, Rust and AssemblyScript languages to write the NEAR smart contracts. NEAR has a complete SDK with standard data structures and testing tools for Rust and AssemblyScript.

Gitpod for NEAR

NEAR utilizes the existing Gitpod technology to give a zero-time onboarding experience for the developers. This technology gives an online ‘Integrated Development Environment (IDE). Using the IDE, developers can smoothly write, test and deploy smart contracts from the browser.

NEAR also has other templates, that can be deployed with a single click, making the development process simple for new and old developers.

NEAR wallet

NEAR wallet includes a built-in functionality to smoothly enable holders’ participation in the staking and governance processes on the NEAR network. It helps developers and end-users to store the digital assets on the NEAR blockchain. NEAR wallet is a user-friendly wallet for processing transactions and payments on the blockchain.

NEAR explorer

To help users understand the performance of the NEAR network, as well as facilitate debugging of the contracts, NEAR explorer provides all the real-time network information in an easily understandable web-based format.

NEAR command-line tools

NEAR has its set command-line tools that allow the developers to create, test and deploy the apps from their local environments.

Now that we are familiar with the NEAR blockchain development suite, let us understand the process of developing a CRUD dApp on NEAR in the next two sections.

What is a CRUD dApp?

A CRUD application is a particular type of software application that has four fundamental operations like the following:

C-Create

Create helps add new records or rows in a database or table. For instance, adding or creating a new table for a data management application.

R-Read

Read operation allows viewing the records that were created. It does change or alter the data; and simply displays it on the data management application. Read operation contains the command to fetch data or to make an inquiry. Using the Read operation, single data can also be entered if there is a unique identifier (UID).

U-Update

The update operation is used for modifying the existing records and data within a table. A single field can be easily modified within a record or multiple fields using the Update. It is also allowed to alter and change multiple records in a single go.

D-Delete

Delete operation is used to delete tables or remove records from multiple fields in a data management application.

  • Update
  • Delete

The CRUD app is divided into three parts: database, user interface, and APIs. Now, as mentioned above, CRUD applications have four fundamental operations; these operations have four relevant functions as mentioned below:

  • Insert
  • Select
  • Update
  • Delete

All the letters in CRUD can also be matched with an HTTP protocol method:

CRUD OPERATIONS HTTP PROTOCOL

  1. Create – Post
  2. Read – Get
  3. Update – Put
  4. Delete – Delete

There are various use cases of a CRUD application like event management apps, student portal apps, book club apps, the content marketing calendar, applicant tracking systems, etc.

A CRUD application development requires low-code platforms, frameworks, and tech stacks that provide an efficient application workflow. Following tech stacks are used to create various CRUD applications:

  • Python and Django
  • Budibase
  • Linux
  • Apache
  • MySQL
  • PHP
  • Supabase
  • Next.js
  • Mongo
  • Express
  • Angular
  • Node.js

Now that we are familiar with the CRUD application let us learn how to create one on the NEAR blockchain in the next section.

Modular blockchain solutions for new-age enterprise software

Build a dapp on NEAR protocol with LeewayHertz.

How to build a dApp on NEAR?

Here, we will build a CRUD dApp on the NEAR ecosystem. This dApp will have two distinct layers of smart contract and a web application. While building the CRUD application, add the smart contract methods to invoke the CRUD operations. For instance, write an express endpoint for building a REST application that will take an incoming POST request. That endpoint will be utilized as a model to insert a ‘todo’ into the database.

Follow the below-mentioned command for the same:

app.post('/todos', async(req, res) => {
const todo = await Todo.insert(req.body.task);
res.send(todo);
});

Instead of HTTP endpoints, the NEAR application takes a smart contract method to store information on the blockchain. The development of the CRUD application is a test-driven development concept. TDD, or the Test-Driven Development, is a software development process that is based on the repetition of a short development cycle as given below:

  • In the initial failure, the developer writes an automated test case defining the required improvement or new function to be integrated.
  • Then the minimum amount of code to pass the test is produced.
  • Then the developer finally refactors the new code to match acceptable standards.

Step 1

Setup

To set up the development base for the NEAR CRUD application, install dependencies by inserting the following command:

cd todos-crud-contract && yarn

Then run the tests:

yarn test

Then compile and deploy the contract for the CRUD operations:

yarn build 
yarn deploy

After this, for the app tests, install the dependencies using the following command:

cd todos-crud-web && yarn

Then start the server by inserting the below-mentioned command:

yarn start

For the pre-requisites, install the npm and the near-cli.

Step 2

Smart contract

Write a smart contract in AssemblyScript, which is quite similar to TypeScript. It also complies with WebAssembly. Additionally, use the near-sdk-as library to write a contract that can easily interact with the blockchain.

Create a new directory for the smart contract and then initialize the newly created directory with an AssemblyScript application by running the following command:

mkdir todos-crud-contract && cd todos-crud-contract
npm init -y
npm i @assemblyscript/loader@latest assemblyscript@latest asbuild near-cli near-sdk-as
npx asinit .
For the near-sdk-as, replace the asconfig.json file using the following command:
{
"extends": "near-sdk-as/asconfig.json"
}

Then create an assembly/as_types.d.ts file using the following command:

/// 

Step 3

Aspect testing

Create the as-pect.config.js file by running the below-mentioned command:

module.exports = require('near-sdk-as/imports')

and the create the assembly/__test__/as-pect.d.ts file using the following code:

module.exports = require('near-sdk-as/imports')

Then, set up the tests in assembly/__tests__/index.spec.ts by inserting the following code:

/// 

Step 4

Web Application

Use the creat-react-app to scaffold the web application and the near-api-js library for NEAR integration in React. Run the following code:

npx create-react-app todos-crud-web
cd todos-crud-web
npm i near-api-js

Then replace the src/index.js code with the below-mentioned code:

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import getConfig from './config.js';
import * as nearAPI from 'near-api-js';
// Initializing contract
async function initContract() {
const nearConfig = getConfig(process.env.NODE_ENV || 'testnet');
// Initializing connection to the NEAR TestNet
const near = await nearAPI.connect({
deps: {
keyStore: new nearAPI.keyStores.BrowserLocalStorageKeyStore()
},
...nearConfig
});
// Needed to access wallet
const walletConnection = new nearAPI.WalletConnection(near);
// Load in account data
let currentUser;
if(walletConnection.getAccountId()) {
currentUser = {
accountId: walletConnection.getAccountId(),
balance: (await walletConnection.account().state()).amount
};
}
// Initializing our contract APIs by contract name and configuration
const contract = await new nearAPI.Contract(walletConnection.account(), nearConfig.contractName, {
// View methods are read-only – they don't modify the state, but usually return some value
viewMethods: ["get"],
// Change methods can modify the state, but you don't receive the returned value when called
changeMethods: ["create", "update", "del"],
// Sender is the account ID to initialize transactions.
// getAccountId() will return empty string if user is still unauthorized
sender: walletConnection.getAccountId()
});
return { contract, currentUser, nearConfig, walletConnection };
}
window.nearInitPromise = initContract()
.then(({ contract, currentUser, nearConfig, walletConnection }) => {
ReactDOM.render(
,
document.getElementById('root')
);
});
And then replace the src/App.js code with the following:
import 'regenerator-runtime/runtime';
import React from 'react';
import PropTypes from 'prop-types';
const App = ({ contract, currentUser, nearConfig, wallet }) => {
const signIn = () => {
wallet.requestSignIn(
nearConfig.contractName,
'NEAR ToDo List'
);
};
const signOut = () => {
wallet.signOut();
window.location.replace(window.location.origin + window.location.pathname);
};
return (
(
<>
< h1 >NEAR Todos CRUD App< h1 >
{ currentUser
? < div>
< h2>
Account ID: {currentUser.accountId}
{""}
< button onClick={signOut}>Log out</ button>
< /h2>
< /div>
:
< div>
Sign In To Use The App:
{""}
< button onClick={signIn}>Log in</ button>
< /div>
}
</>
);
};
App.propTypes = {
contract: PropTypes.shape({
create: PropTypes.func.isRequired,
get: PropTypes.func.isRequired,
update: PropTypes.func.isRequired,
del: PropTypes.func.isRequired,
}).isRequired,
currentUser: PropTypes.shape({
accountId: PropTypes.string.isRequired,
balance: PropTypes.string.isRequired
}),
nearConfig: PropTypes.shape({
contractName: PropTypes.string.isRequired
}).isRequired,
wallet: PropTypes.shape({
requestSignIn: PropTypes.func.isRequired,
signOut: PropTypes.func.isRequired
}).isRequired
};
export default App;

After replacing the codes, create src/config.js by running the following command:

const CONTRACT_NAME = process.env.CONTRACT_NAME || 'dev-1631631317655-25327263281645';
function getConfig(env) {
switch(env) {
case 'mainnet':
return {
networkId: 'mainnet',
nodeUrl: 'https://rpc.mainnet.near.org',
contractName: CONTRACT_NAME,
walletUrl: 'https://wallet.near.org',
helperUrl: 'https://helper.mainnet.near.org'
};
// This is an example app so production is set to testnet.
// You can move production to mainnet if that is applicable.
case 'production':
case 'development':
case 'testnet':
return {
networkId: 'testnet',
nodeUrl: 'https://rpc.testnet.near.org',
contractName: CONTRACT_NAME,
walletUrl: 'https://wallet.testnet.near.org',
helperUrl: 'https://helper.testnet.near.org'
};
default:
throw Error(Unconfigured environment '${env}'. Can be configured in src/config.js.);
}
}
module.exports = getConfig;

Step 5

Data storage

Create a model.ts file to manage all the data persistence. Then, create the PersistentUnorderedMap in the model.ts file and a ToDo model class by using the following code:

// contract/assembly/model.ts
import { PersistentUnorderedMap, math } from "near-sdk-as";
// Think of this PersistentUnorderedMap like a database table.
// We'll use this to persist and retrieve data.
export const todos = new PersistentUnorderedMap<u32, Todo>("todos");
// Think of this like a model class in something like mongoose or
// sequelize. It defines the shape or schema for our data. It will
// also contain static methods to read and write data from and to
// the todos PersistentUnorderedMap.
@nearBindgen
export class Todo {
id: u32;
task: string;
done: bool;
constructor(task: string) {
Home | This.ID  = math.hash32(task);
this.task = task;
this.done = false;
}
}

Step 6

C-Create

To begin with, create new todos and save these todos on the NEAR blockchain. Now test the contract todos by running the following code:

// contract/assembly/tests/index.spec.ts
import { create } from "../index";
import { Todo, todos } from "../model";
describe("contract methods", () => {
it("creates a todo", () => {
// call the create method
const todo = create("Drink water");
// lookup in the PersistentUnorderedMap for our todo
// expect the persisted todo to equal the todo returned
// by the create method above.
expect(todos.getSome(todo.id)).toStrictEqual(todo);
});

});

Model

To save the todo in the todos PersistentUnorderedMap, add a static insert method for the Todo class. This method is responsible for pushing a todo into the todos PersistentUnordered Map. Run the following code:

// contract/assembly/model.ts
import { PersistentUnorderedMap, math } from "near-sdk-as";
export const todos = new PersistentUnorderedMap<u32, Todo>("todos");
@nearBindgen
export class Todo {
id: u32;
task: string;
done: bool;
constructor(task: string) {
Home | This.ID  = math.hash32(task);
this.task = task;
this.done = false;
}
static insert(task: string): Todo {
// create a new Todo
const todo = new Todo(task);
// add the todo to the PersistentUnorderedMap
// where the key is the todo's id and the value
// is the todo itself. Think of this like an
// INSERT statement in SQL.
todos.set(todo.id, todo);
return todo;
}
}

Smart contract method

Smart contract methods define the public interface for smart contracts. Define the create method which utilizes the Todo model to push a new todo on the NEAR blockchain by using the following code:

// contract/assembly/index.ts
import { Todo } from "./model";
// export the create method. This acts like an endpoint
// that we'll be able to call from our web app.
export function create(task: string): Todo {
// use the Todo class to persist the todo data
return Todo.insert(task);
}

Modular blockchain solutions for new-age enterprise software

Build a dapp on NEAR protocol with LeewayHertz.

Step 7

Deploy and test

Run the aspect tests by inserting the following code:

npm run test

After passing all the tests, build the smart contract and deploy it to the development account. Then add some scripts to the package.json by running the following code:

"scripts": {
"build": "asb",
"deploy": "near dev-deploy build/release/todos-crud-contract.wasm",
"dev": "npm run build && npm run deploy",
"test": "asp",
}

The contract building step will compile the AssemblyScript code with the WebAssembly. The deploy step will then send and store the WebAssembly file to the NEAR blockchain with the following codes:

npm run build
npm run deploy

Finally, test the deployed smart contract:

npx near call $(cat neardev/dev-account) create '{"task":"Drink water"}' --accountId YOUR_ACCOUNT_ID.testnet

Step 8

Web Application

To interact with the smart contract create method, call a smart contract function using the following command:

const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
// invoke the smart contract's create method
const todo = await contract.create({ task });
// print the todo to the console
console.log('my todo', todo)
};

Then, write a form component using the following code:

// src/components/CreateTodo.js
// src/components/CreateTodo.js
import { useState } from "react";
const CreateTodo = ({ contract }) => {
const [task, setTask] = useState("");
const [loading, setLoading] = useState(false);
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
// invoke the smart contract's create method
const todo = await contract.create({ task });
setTask("");
setLoading(false);

// print the todo to the console
console.log('my todo', todo);
};
return (
< form onSubmit={handleSubmit}>
< input type="text" placeholder="Buy Groceries" value={task} onChange={({ target }) => setTask(target.value)}
/>
< button disabled={loading}>Create Task< /button>
< /form>
);
}
export default CreateTodo;

Add this form component to the App.js file by running the following code:

import 'regenerator-runtime/runtime';
import React from 'react';
import PropTypes from 'prop-types';
import CreateTodo from './components/CreateTodo';
const App = ({ contract, currentUser, nearConfig, wallet }) => {
const signIn = () => {
wallet.requestSignIn(
nearConfig.contractName,
'NEAR ToDo List'
);
};
const signOut = () => {
wallet.signOut();
window.location.replace(window.location.origin + window.location.pathname);
};
return (
<>
< h1>NEAR Todos CRUD App</ h1>
{ currentUser
? < div>
< h2>
Account ID: {currentUser.accountId}
{""}
< button onClick={signOut}>Log out< /button>
< /h2>
          
        < /div>
      : 
      < div>
        Sign In To Use The App: 
        {""}
        < button onClick={signIn}>Log in< /button>
      < /div>
    }
</>
);
};
App.propTypes = {
contract: PropTypes.shape({
create: PropTypes.func.isRequired,
}).isRequired,
currentUser: PropTypes.shape({
accountId: PropTypes.string.isRequired,
balance: PropTypes.string.isRequired
}),
nearConfig: PropTypes.shape({
contractName: PropTypes.string.isRequired
}).isRequired,
wallet: PropTypes.shape({
requestSignIn: PropTypes.func.isRequired,
signOut: PropTypes.func.isRequired
}).isRequired
};
export default App;

Lastly, run the web application with npm start. Fill out the form and look at the todo log to the console. Save the todo id required for the next step.

Step 9

R-Read by id

Retrieve the todo by using a getById method. To test the getById method create some test todos. Then get each todo using its id by. Run the following codes:

// contract/assembly/tests/index.spec.ts
import { create, getById } from "../index";
import { Todo, todos } from "../model";
describe("contract methods", () => {
it("creates a todo", () => {...});
it("gets a todo by id", () => {
// create three todos
const a = Todo.insert("Drink water");
const b = Todo.insert("Get sleep");
const c = Todo.insert("Exercise");
// get each todo by its it
expect(getById(a.id)).toStrictEqual(a);
expect(getById(b.id)).toStrictEqual(b);
expect(getById(c.id)).toStrictEqual(c);
});
})

Model

To get the todos, add a static findById method that will get a todo from the todos and PersistentUnoderedMap through the getSome method. Run the following command:

// contract/assembly/model.ts
import { PersistentUnorderedMap, math } from "near-sdk-as";
export const todos = new PersistentUnorderedMap<u32, Todo>("todos");
@nearBindgen
export class Todo {
id: u32;
task: string;
done: bool;
constructor(task: string) {
Home | This.ID  = math.hash32(task);
this.task = task;
this.done = false;
}
static insert(task: string): Todo {...}
static findById(id: u32): Todo {
// Lookup a todo in the PersistentUnorderedMap by its id.
// This is like a SELECT * FROM todos WHERE id=?
return todos.getSome(id);
}
}

Step 10

Smart contract method

Find the todo by id and continue to define the smart contracts public interface by defining and exporting the getById method by running the following command:

// contract/assembly/index.ts
import { Todo } from "./model";
export function create(task: string): Todo {...}
export function getById(id: u32): Todo {
return Todo.findById(id);
}

Step 11

Deploy and test

After finishing the getById method test it by running the following command:

npm run test

After passing all the tests, build the smart contract and deploy it to the development account by running the following command:

npm run dev

Now test the deployed smart contracts and replace SOME_ID_HERE with the id using which the web app was logged:

npx near view $(cat neardev/dev-account) getById '{"id":SOME_ID_HERE}' --accountId YOUR_ACCOUNT_ID.testnet

Step 12

R-Read list

Use the offset and limit pattern to get the paged list of results back from the smart contract. Then test it by running the following code:

// contract/assembly/tests/index.spec.ts
import { create, getById, get } from "../index";
import { Todo, todos } from "../model";
describe("contract methods", () => {
it("creates a todo", () => {...});
it("gets a todo by id", () => {...});
it('gets a list of todos', () => {
const todos = new Array(100)
.fill(0)
.map((_, i) => Todo.insert('todo' + i.toString()))
expect(get(20)).toStrictEqual(todos.slice(20, 30));
expect(get(0, 10)).toStrictEqual(todos.slice(0, 10));
expect(get(10, 10)).toStrictEqual(todos.slice(10, 20));
expect(get(50, 50)).toStrictEqual(todos.slice(50, 100));
});
});

Test the get method after finishing it by running the following code:

npm run test

After passing all the tests, deploy the smart contract to the development account by running the following command:

npm run dev

And then test the deployed smart contract:

npx near view $(cat neardev/dev-account) get '{"offset":0}' --accountId YOUR_ACCOUNT_ID.testnet

Step 13

WebApp

Create TodoList component to get smart contract method to fetch a list of the todos. Then iterate over those todos and make a list item for each todo:

// src/components/Todo.js
// src/components/Todo.js
import { useState } from "react";
export function Todo({ id, task, done }) {
return (
<>
< p>{task}< /p>
</>
);
}
// src/components/TodoList.js
import { useEffect, useState } from "react";
import { Todo } from "./Todo";
const PER_PAGE_LIMIT = 3;
const TodoList = ({ contract }) => {
const [todos, setTodos] = useState([]);
const [page, setPage] = useState(1);
useEffect(() => {
let offset;
if(page < 1) { setPage(1); offset = 0; } else { offset = (page - 1) * PER_PAGE_LIMIT; } // every second after the component first mounts // update the list of todos by invoking the get // method on the smart contract const id = setInterval(() => {
  contract
    .get({ offset, limit: PER_PAGE_LIMIT })
    .then((todos) => setTodos(todos));
}, 1000);
return () => clearInterval(id);
}, [page, contract]);
return (
< ul>
< div className="flex">
Current Page: {page}
< /div>
< button onClick={() => setPage((page) => page - 1)}><< /button>
{""}
< button onClick={() => setPage((page) => page + 1)}>></ button>
{todos.map((todo) => (
< li key={todo.id}>

< /li>
))}
< /ul>
);
}
export default TodoList;
And then insert the following command:
import 'regenerator-runtime/runtime';
import React from 'react';
import PropTypes from 'prop-types';
import CreateTodo from './components/CreateTodo';
import TodoList from './components/TodoList';
const App = ({ contract, currentUser, nearConfig, wallet }) => {
const signIn = () => {
wallet.requestSignIn(
nearConfig.contractName,
'NEAR ToDo List'
);
};
const signOut = () => {
wallet.signOut();
window.location.replace(window.location.origin + window.location.pathname);
};
return (
<>
< h1>NEAR Todos CRUD App< /h1>
{ currentUser
? < div>
< h2>
Account ID: {currentUser.accountId}
{""}
< button onClick={signOut}>Log out< /button>
< /h2>
          
          
        < /div>
      : 
      < div>
        Sign In To Use The App: 
        {""}
        < button onClick={signIn}>Log in< /button>
      < /div>
    }
</>
);
};
App.propTypes = {
contract: PropTypes.shape({
create: PropTypes.func.isRequired,
get: PropTypes.func.isRequired,
}).isRequired,
currentUser: PropTypes.shape({
accountId: PropTypes.string.isRequired,
balance: PropTypes.string.isRequired
}),
nearConfig: PropTypes.shape({
contractName: PropTypes.string.isRequired
}).isRequired,
wallet: PropTypes.shape({
requestSignIn: PropTypes.func.isRequired,
signOut: PropTypes.func.isRequired
}).isRequired
};
export default App;

Step 14

U-Update

Update the todo with an update method and run the following command to test it:

// contract/assembly/tests/index.spec.ts
import { create, getById, get, update } from "../index";
import { Todo, todos } from "../model";
describe("contract methods", () => {
it("creates a todo", () => {...});
it("gets a todo by id", () => {...});
it('gets a list of todos', () => {...});
it('updates a todo', () => {
const todo = Todo.insert('Water drink');
update(todo.id, { task: 'Drink water', done: true });
const todoAfterUpdate = Todo.findById(todo.id);
expect(todoAfterUpdate.id).toStrictEqual(todo.id);
expect(todoAfterUpdate.task).toStrictEqual('Drink water');
expect(todoAfterUpdate.done).toStrictEqual(true);
});
});

Model

Add static findByIdAndUpdate method to update the todos:

// contract/assembly/model.ts
import { PersistentUnorderedMap, math } from "near-sdk-as";
export const todos = new PersistentUnorderedMap<u32, Todo>("todos");
@nearBindgen
export class PartialTodo {
task: string;
done: bool;
}
@nearBindgen
export class Todo {
id: u32;
task: string;
done: bool;
constructor(task: string) {
Home | This.ID  = math.hash32(task);
this.task = task;
this.done = false;
}
static insert(task: string): Todo {...}
static findById(id: u32): Todo {...}
static find(offset: u32, limit: u32): Todo[] {...}
static findByIdAndUpdate(id: u32, partial: PartialTodo): Todo {
// find a todo by its id
const todo = this.findById(id);
// update the todo in-memory
todo.task = partial.task;
todo.done = partial.done;
// persist the updated todo
todos.set(id, todo);
return todo;
}
}

Smart contract method

Define the smart contract’s public interface by defining the update function:

// contract/assembly/index.ts
import { Todo, PartialTodo } from "./model";
export function create(task: string): Todo {...}
export function getById(id: u32): Todo {...}
export function get(offset: u32, limit: u32 = 10): Todo[] {...}
export function update(id: u32, updates: PartialTodo): Todo {
return Todo.findByIdAndUpdate(id, updates);
}

Then test the update method by running the following command:

npm run test

Build and deploy the smart contract to the development account:

npm run dev
npx near call $(cat neardev/dev-account) update '{"id":SOME_ID_HERE, "updates":{"done":true, "task":"Drink nothing"} }' --accountId YOUR_ACCOUNT_ID.testnet

WebApp

Now, refractor the Todo.js to complete the tasks by running the following command:

// src/components/Todo.js
import { useState } from "react";
export function Todo({ contract, id, task, done }) {
const [checked, setChecked] = useState(done);
const complete = ({ target }) => {
setChecked(target.checked);
contract.update({ id, updates: { task, done: target.checked } });
};
return (
<>


{task}

</>
);
}

Step 15

D-Delete

Delete a todo using the del method.

Test

// contract/assembly/tests/index.spec.ts
import { create } from "../index";
import { Todo, todos } from "../model";
describe("contract methods", () => {
it("creates a todo", () => {...});
it("gets a todo by id", () => {...});
it('gets a list of todos', () => {...});
it('updates a todo', () => {...});
itThrows('deletes a todo', () => {
const todo = Todo.insert('Drink water');
del(todo.id)
Todo.findById(todo.id)
});
});

Model

Add a static findByIdAndDelete method to delete the todos:

// contract/assembly/model.ts
import { PersistentUnorderedMap, math } from "near-sdk-as";
export const todos = new PersistentUnorderedMap<u32, Todo>("todos");
@nearBindgen
export class Todo {
id: u32;
task: string;
done: bool;
constructor(task: string) {
Home | This.ID  = math.hash32(task);
this.task = task;
this.done = false;
}
static insert(task: string): Todo {...}
static findById(id: u32): Todo {...}
static find(offset: u32, limit: u32): Todo[] {...}
static findByIdAndUpdate(id: u32, partial: PartialTodo): Todo {...}
static findByIdAndDelete(id: u32): void {
todos.delete(id);
}
}

Smart contract method

Define the smart contract’s public interface by defining the del function:

// contract/assembly/index.ts
import { Todo } from "./model";
export function create(task: string): Todo {...}
export function getById(id: u32): Todo {...}
export function get(offset: u32, limit: u32 = 10): Todo[] {...}
export function update(id: u32, updates: PartialTodo): Todo {...}
export function del(id: u32): void {
Todo.findByIdAndDelete(id);
}

Test the del method by running it using the following code:

npm run test

Then, build the smart contract and deploy it to the development account by inserting the following command:

npm run dev

Finally, test the deployed smart contract by using the following command:

npx near call $(cat neardev/dev-account) del '{"id":SOME_ID_HERE }' --accountId YOUR_ACCOUNT_ID.testnet

WebApp

Refractor the todo.js component to delete a todo by using the following code:

// src/components/Todo.js
import { useState } from "react";
export function Todo({ contract, id, task, done }) {
const [checked, setChecked] = useState(done);
const complete = ({ target }) => {
setChecked(target.checked);
contract.update({ id, updates: { task, done: target.checked } });
};
const del = () => {
// on clicking the delete button invoke the del method on
// the smart contract
contract.del({ id });
};
return (
<>

<  input checked="checked" type="checkbox" / >
{task}

Now that we are familiar with the process of CRUD dApp development on the NEAR blockchain, let us understand why one should build on the NEAR network in the next section.

Modular blockchain solutions for new-age enterprise software

Build a dapp on NEAR protocol with LeewayHertz.

Why should you build on NEAR?

NEAR blockchain is built with a progressive User Experience(UX) for the dApp users of the decentralized finance platform. It is environment-friendly as the NEAR blockchain is carbon neutral and minimizes carbon emissions during transactions. NEAR protocol uses the Proof-of-Stake election mechanism, also known as the Threshold Proof-of-Stake or the TPoS. With the help of TPoS, NEAR develops a fair reward distribution system for its dApp users on-chain.

NEAR protocol have various benefits to its credibility, as mentioned below:

  • Sustainability
  • Cost-effective
  • User-friendly
  • Secure
  • Interoperable
  • Pragmatic

What NEAR dApp development services does LeewayHertz offer?

Scalable dApps

We build highly scalable, secure and robust dApps on the NEAR blockchain as per your business requirement. Our experts also examine the technical aspects of various businesses to increase the utility of the dApps.

Smart contracts

We develop smart contracts that help businesses onboard their processes, especially Defi-related, processes on the NEAR blockchain. We cover every aspect of the smart contract, from architectural designing to auditing and optimization.

Node and wallets

We help businesses build and run nodes on the NEAR network. Our wallet experts also develop and integrate wallets to the dApps and DEX to support transactions on the NEAR network.

Maintenance and upgrade

We provide constant support and maintenance services to our clients. From dApp porting to checking on updates, we ensure that our clients are using optimized dApps running with negligible downtime.

Closing note

NEAR blockchain is one of the unique blockchains which is environment-friendly and carbon-neutral protocol with minimal carbon footprint emissions. NEAR is a highly scalable network because it functions as per the sharding technique.

Along with scalability, it also has other interesting features like interoperability and human-readable account names instead of cryptographically-coded wallet addresses, making it easier for the users to access the wallets and dApps. NEAR is, therefore, one of the most compatible blockchains meeting the dApp development needs of various industries.

If you are looking to develop decentralized applications for your businesses on the NEAR protocol, contact our blockchain experts for further in-depth guidance.

Author’s Bio

 

Akash Takyar

Akash Takyar LinkedIn
CEO LeewayHertz
Akash Takyar is the founder and CEO at LeewayHertz. The experience of building over 100+ platforms for startups and enterprises allows Akash to rapidly architect and design solutions that are scalable and beautiful.
Akash's ability to build enterprise-grade technology solutions has attracted over 30 Fortune 500 companies, including Siemens, 3M, P&G and Hershey’s.
Akash is an early adopter of new technology, a passionate technology enthusiast, and an investor in AI and IoT startups.

Start a conversation by filling the form

Once you let us know your requirement, our technical expert will schedule a call and discuss your idea in detail post sign of an NDA.
All information will be kept confidential.

Insights

Follow Us