Fund Me Smart Contract using Typescript on Near (Non-EVM)
Tags: near, blockchain, web3, typescript
Published: 2023-10-29 11:17:10

Near is a Non-EVM blockchain with 100,000 TPS which is 3x faster, and cheaper than the Non-EVM blockchain Solana!!

<img width="100%" style="width:100%" src="https://media.giphy.com/media/d4blalI6x2oc4xAA/giphy.gif">

On top of that, You can deploy Typescript code as a smart contract on Near!!

Let's build and deploy a fully functional FundMe DApp with  Typescript smart contract on Near and learn some new concepts that make Near better than other EVM and non-EVM chains.

Here's a little preview of what we gonna build:

![App Preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c6alx34a7lyeazkxhslo.gif)

This DApp allows users to donate Near tokens, and allows admins to withdraw the donations from the contract from the blockchain explorer.

So let's go 🚀

## Setup the development environment
Setup the development environment using the below template. It's made using Vite React and Near CLI which is much faster than the official Near's `npx create-near-app` CLI.

```
https://github.com/Rishabhraghwendra18/react-near-template
```

Just click on the top green button to `Use this template` which will setup a new repository in your account using the template.

Clone the repository created in your account on your local machine and follow the below instructions:

```
cd frontend 
npm i
```

In another terminal:
```
cd contract
npm i
```

After installing dependencies in both folders, run `npm run deploy` in the contract folder to deploy the dummy `HelloNear` contract. 

On the successful deployment of the contract run, `npm run dev` in the frontend folder.

You will see a basic UI running at `http://localhost:5173/` in the browser.

## Create a testnet wallet on Near
Create a testnet wallet on Near on [MyNearWallet](https://testnet.mynearwallet.com/). Near will automatically deposit $200 worth of test Near in your wallet so no need for faucets.

_**Fun Fact: Near doesn't provide a random hash as your wallet account address. It has named account addresses like mine is `rishabh.testnet` which is very easy to remember.**_

Let's now see step by step to build the Typescript smart contract. I will explain to you the meaning of each and every line.

## Building the FundMe Contract
Navigate to `contract/src/contract.ts` file and remove all the code.

Import dependencies on first line:
```
import { NearBindgen, near, call, view,UnorderedMap } from 'near-sdk-js';
```
- **NearBindgen**: This decorator helps to convert typescript classes to Near smart contracts.
- **near**: It has lots of built-in near API like getting contract balances, caller account IDs, etc.
- **call**: Decorator for setter functions in contract.
- **view**: Decorator for getter functions in the contract.
- **UnorderedMap**: An iterable hashmap data structure to store the donation amount with the donor account.

Now create a class with NearBindgen decorator:
```
@NearBindgen({})
class FundMe {
  beneficiary: string ="<beneficary-account-id>";
  donations= new UnorderedMap<bigint>('d');
}
```
You may wonder what is `'d'` in the UnorderedMap. It's used as a prefix to store key-value pairs in the memory on the near blockchain. So whenever you have two unorderedmap make sure their prefixes are different.

Let's add `donate` function:
```
@call({payableFunction:true})
  donate(){
    let donor=near.predecessorAccountId();
    let donationAmount: bigint = near.attachedDeposit() as bigint;

    let donatedSoFar = this.donations.get(donor, {defaultValue: BigInt(0)})
    donatedSoFar+=donationAmount;
    this.donations.set(donor,donatedSoFar);
    near.log(`Thank you ${donor} for donating ${donationAmount}! You donated a total of ${donatedSoFar}`);
  }
```
Here, we have passed `payableFunction:true` to decorator because the caller will attach some near amount with function call. It's similar to `payable` in solidity.
- **near.predecessorAccountId()**: To get the caller account ID.
- **near.attachedDeposit()**: Near attached to the function call like `msg.value` in solidity.
- **this.donations.get()**: to get the donor account ID and previously donated amount (if any, otherwise default value is 0) from the hashmap.
- **this.donations.set**: updating new donation amount for the caller in hashmap.
- **near.log**: To log any message on blockchain explorer when transactions complete. Same as `console.log` in javascript.

Wow! Donation function is complete. Let's move to `withdraw_funds` function which will allow the admin (the contract deployer) to withdraw the donations from the contract.

```
@call({privateFunction:true})
  withdraw_funds(){
    let balance=near.accountBalance() - BigInt('10000000000000000000000000');
    const promise = near.promiseBatchCreate(this.beneficiary)
    near.promiseBatchActionTransfer(promise, balance);
  }
```
Notice the `privateFunction:true` we have passed to the decorator because we want this function to only be called by the admin.

Private functions work differently in Near than EVM chains. 

There's a concept of access keys in Near which is similar to Private keys but are shareable. By default, the user who deploys the contract at first has full access to smart contract functions including private functions. It enables the user to transfer tokens, cross-contract calls, and even update the contract after deployment (we will talk about this in the end).

_To know more about new concepts, and cool stuff on Near visit my recent blog for an easy explanation: https://dev.to/rishabhraghwendra/near-blockchain-development-guide-for-ethereum-developers-1f1n_

We are also leaving 10 Near in the contract for future use to pay for gas fees in the future:
```
near.accountBalance() - BigInt('10000000000000000000000000');
```
Since smart contract is built using Typescript it also has a Promises concept. Every token transfer and cross-contract call returns a promise. In this case, we have like this:
```
const promise = near.promiseBatchCreate(this.beneficiary)
```
Remember we can't use async/await in the contract. It's not supported. 

To resolve this promise we can do something like this:
```
near.promiseBatchActionTransfer(promise, balance);
```

Now let's create the function to get all the donations list

```
@view({})
  get_all_donations(){
    return this.donations.toArray();
  }
```
It's a simple view function call that converts the donations hashmap into an array and returns it to the caller.

To get the balance of the contract:
```
@view({})
  view_balance(){
    return near.accountBalance();
  }
```
Deploy the smart contract by running `npm run deploy` in the contract folder. It will also output the transaction ID link on the near test net where the contract has been deployed. 

Copy the transaction hash and search it on https://testnet.nearblocks.io/ for better UI.

Note: Whenever you deploy a contract using the above template it will automatically store the contract address in the `contract/neardev/dev-account.env` environment variable which will be automatically taken up by the frontend.

Now smart contract is done. Since it's not a frontend tutorial I will build a simple frontend and connect it with smart contract.

## Building the simple frontend
The frontend is made using Vite React. If your frontend is not running, run it by running `npm run dev` in the frontend folder. 

I am using Material UI to get some UI components. Install it in frontend folder using:
```
npm install @mui/material @emotion/react @emotion/styled
```

Go to `App.jsx` file and paste below code:
```
import * as nearAPI from "near-api-js";
import React, { useState } from "react";
import Card from "@mui/material/Card";
import CardContent from "@mui/material/CardContent";
import Button from "@mui/material/Button";
import Table from "@mui/material/Table";
import TableBody from "@mui/material/TableBody";
import TableCell from "@mui/material/TableCell";
import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import Paper from "@mui/material/Paper";
import "./App.css";

import {  SignInButton,SignOutButton } from "./components/SignInButton.jsx";

const contractId=process.env.CONTACT_NAME;

export default function App({contractId, wallet }) {
  const [donorsList, setDonorsList] = useState([]);
  const [depositValue, setDepositValue] = useState();
  const [totalFunds, setTotalFunds] = useState('');

  // Get blockchian state once on component load
  useEffect(() => {
    getDontations()
      .then((response) => {
        const { utils } = nearAPI;

        let donors = response.map((donor) => ({
          donor: donor[0],
          amount: utils.format.formatNearAmount(donor[1]),
        }));
        setDonorsList(donors);
        console.log("response: ", response);
      })
      .catch(alert)
      getTotalFunds().then((response)=>{
        let funds =nearAPI.utils.format.formatNearAmount(response);
        funds=funds.split(".");
        let wholeNumber = funds[0];
        let decimalPart='0';
        if(funds.length>=2){
          decimalPart=funds[1].padEnd(2,'0').slice(0,2);
        }
        setTotalFunds(wholeNumber+'.'+decimalPart);
      })
  }, []);

  const getTotalFunds = async()=>{
    return wallet.viewMethod({ method: "view_balance", contractId })
  }

  function donate(e) {
    e.preventDefault();
    const deposit = nearAPI.utils.format.parseNearAmount(depositValue);

    // use the wallet to send the funds to the contract
    wallet
      .callMethod({
        method: "donate",
        deposit,
        contractId,
      })
      .then(async () => {
        console.log("Deposited!!");
        return getDontations();
      })
      .then(setValueFromBlockchain)
  }

  function getDontations() {
    // use the wallet to query the all contract's donations
    return wallet.viewMethod({ method: "get_all_donations", contractId });
  }

  return (
    <>
      {wallet.accountId ?<SignOutButton accountId={wallet.accountId} onClick={()=>{wallet.signOut()}}/>:<SignInButton onClick={()=>{wallet.signIn()}}/>}
      <main>
        <div className="flex">
          <h1>Welcome To Fund Me Contract on Near</h1>
          <h2>(Total Funds Raised: {totalFunds} Near)</h2>
          <div className="container-div">
            <div className="txn-list">
              <h3>All Donors</h3>
              <TableContainer component={Paper}>
                <Table
                  sx={{ minWidth: 400 }}
                  size="small"
                  aria-label="a dense table"
                >
                  <TableHead>
                    <TableRow>
                      <TableCell>Donor</TableCell>
                      <TableCell align="right">Amount</TableCell>
                    </TableRow>
                  </TableHead>
                  <TableBody>
                    {donorsList.map((row) => (
                      <TableRow
                        key={row.name}
                        sx={{
                          "&:last-child td, &:last-child th": { border: 0 },
                        }}
                      >
                        <TableCell component="th" scope="row">
                          {row.donor}
                        </TableCell>
                        <TableCell align="right">{row.amount} Near</TableCell>
                      </TableRow>
                    ))}
                  </TableBody>
                </Table>
              </TableContainer>
            </div>
            <div className="fund-me">
              <h3>Fund Me</h3>
              <Card>
                <CardContent sx={{display:'flex',flexDirection:'column',gap:'1.3rem'}}>
                  <label>Enter Amount</label>
                  <input
                    autoComplete="off"
                    defaultValue={0}
                    className="amount"
                    type="number"
                    onChange={(e)=>setDepositValue(e.target.value)}
                  />
                  <Button variant="contained" size="small" sx={{width:'100%'}} onClick={donate}>Submit</Button>
                </CardContent>
              </Card>
            </div>
          </div>
        </div>
      </main>
    </>
  );
}
```

The template has the helper class called `Wallet` in `frontend/src/utils/near-wallet.js` which has the all methods to call the `call` function on the contract as well as the `view` functions.

Checkout `main.jsx` to know how the template setups the Class object at the app startup.

In `App.css` paste this:
```
.flex{
    display: flex;
    gap: 1rem;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
.container-div{
    display: flex;
    flex-direction: column-reverse;
    gap: 1rem;
    width: 100%;
    align-items: center;
    justify-content: center;
}
.txn-list{
    width: 100%;
    max-width: 600px;
}
.fund-me{
    width: 100%;
    max-width: 600px;
}
.amount{
    border: 2px solid grey;
    border-radius: 0.3rem;
}
```

And your first DApp on Near Blockchain is ready!! 🚀

<img width="100%" style="width:100%" src="https://media.giphy.com/media/StKiS6x698JAl9d6cx/giphy.gif">

I hope you liked the blog. If you have any doubts let me know in the comments.

To support me, you can donate me some Near on `rishabhrag.near` 🥳