Connect a React app to the NEAR Blockchain
Tags: react, blockchain, webdev, javascript
Published: 2022-07-04 20:42:21

by [Melvin Kosisochukwu](https://blog.openreplay.com/authors/melvin-kosisochukwu)


You must've heard of [blockchain](https://en.wikipedia.org/wiki/Blockchain), [decentralized applications](https://en.wikipedia.org/wiki/Decentralized_application), [web3](https://en.wikipedia.org/wiki/Web3), and the hype surrounding them. As a software developer, you're expected to be flexible, evolve with the time, and learn new and exciting techs. Web3 development involves building applications on a distributed blockchain or decentralized network over single servers; an example of this network is the NEAR blockchain. For additional information on web3, you can check out this [article](https://www.freecodecamp.org/news/what-is-web3/).

[NEAR](https://near.org/) is a development platform built on a new [layer-1](https://academy.binance.com/en/articles/what-is-layer-1-in-blockchain#:~:text=Layer%201%20refers%20to%20a,we've%20seen%20with%20Bitcoin.), [Proof-of-stake](https://en.wikipedia.org/wiki/Proof_of_stake) blockchain. You can interact with the NEAR blockchains with a set of instructions/programs called [smart contracts](https://docs.near.org/docs/develop/contracts/overview); smart contracts for NEAR blockchain are written in AssemblyScript or Rust; for additional information on writing smart contracts on the NEAR blockchain, you can look over the official documentation [here](https://docs.near.org/docs/develop/contracts/overview).

For this article, we will focus on how to connect to the NEAR blockchain and make smart contract calls. We will be working with a dice game smart contract deployed on the blockchain. You can find additional information on the smart contract [here](https://graate.org/projects/01). To continue with this article, you need knowledge of Reactjs and Javascript; You will also need access to the smart-contract address deployed on the near blockchain: dev-1641682453576-30872819216475 and a NEAR wallet. The NEAR wallet is an in-browser wallet for creating and interacting with NEAR accounts. The wallet exists on the web and stores account keys in your browser's `localStorage`. You can find information on creating a wallet on the NEAR blockchain [here](https://docs.near.org/docs/develop/basics/create-account). 

> Near tokens are the native cryptocurrency used on the NEAR blockchain, with units in yocto. 1 NEAR token is equivalent to 10^24 yocto.

## Project Resources

We will work with a Smart Contract Address/Name [dev-1641682453576-30872819216475](https://explorer.testnet.near.org/accounts/dev-1641682453576-30872819216475). The game rules are very simple:
- Users are allowed to create a new game with a 0.5 NEAR token attached as the game fee; a user creating a game is automatically entered into the game.
- Other users can join a created game with 0.5 NEAR attached as the game fee.
- Game fees are added to a price pool for game-winner(s).
- Each game has a 30 minutes countdown from when it starts.
- Players in a game can roll the dice once; When a player rolls the first dice, the game countdown starts.
- Once a game ends, the player(s) with the highest dice count will claim the winnings from the game.

## Getting Started

The first order will be to create a React Application and set up the configuration file for interacting with the NEAR blockchain; you can find the repository with the complete project [here](https://github.com/Graate-Org/NEAR-Block-Dice-UI). The NEAR blockchain has different development environments, the most popular two being the testnet and the mainnet; for this article, we will be working with the testnet. The testnet is a development network that mocks the behaviors of the main network; this allows you to test your smart contract functions and verify that everything works correctly without incurring any cost.

Once you have successfully cloned the repository navigate to [src/config.js](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/config.js) file:

```javascript
const CONTRACT_NAME = process.env.CONTRACT_NAME || "dev-1641682453576-30872819216475";

export default function getConfig(env) {
  switch (env) {
    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.`);
  }
}
```

We have the smart contract address on line 1 in the code block above. This file holds the configuration file for the environment the smart contract is deployed on (testnet). The `getConfig` function accepts an environment and returns the configuration that matches that environment. You can find the configuration for other environments in the [code repository](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/config.js). The next step will be to hook up the configuration to your React application -- see [src/index.js](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/index.js).

```javascript
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";
import "./index.css";

// 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: ["getWinners", "getGameDetails", "getPlayersDetails", "getProfileDetails", "getActiveGames", "getCreatedGames", "getCompletedGames"],
    // Change methods can modify the state, but you don't receive the returned value when called
    changeMethods: ["createNewGame", "joinGame", "rollDice", "claimWinnings"],
    // 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(
    <React.StrictMode>
      <App contract={contract} currentUser={currentUser} nearConfig={nearConfig} wallet={walletConnection} />
    </React.StrictMode>,
    document.getElementById("root")
  );
});
```

On line 9, we have the `initContract` function. On line 13, we assign the connection configuration to the near variable and follow that up by calling the `WalletConnection` method on the `near-js-api` library accepts the connection configuration as params. The `WalletConnection` method allows connection to the near wallet; this allows you to sign/authenticate transactions/requests.

The next step will be to set up the contract connection; this is accomplished on lines 33 through 41 by calling the `Contract` method from the `near-js-api`. It accepts three arguments: the wallet account, smart contract address, and contract methods.

## Wallet Connection

The NEAR wallet is a browser-based wallet. You will be redirected to the website to authenticate your wallet/account - see [src/components/Navbar/Navbar.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/components/Navbar/Navbar.jsx).

```javascript
...  
const handleUser = (e) => {
    if (currentUser && e.target.textContent === "Sign Out") {
      (function signOut() {
        wallet.signOut();
        window.location.replace(
          window.location.origin + window.location.pathname
        );
      })();
    } else if (!currentUser && e.target.textContent === "Login") {
      (function signIn() {
        wallet.requestSignIn(nearConfig.contractName, "NEAR Block Dice");
      })();
    }
  };
...
```

On line 12, the `wallet.requestSignIn` function accepts two arguments: the contract address and the application name. The `wallet.requestSignIn` function will trigger a wallet connection request, which, once approved, will redirect back our application from the NEAR browser wallet;

![Wallet connection request](https://blog.openreplay.com/static/a502c5543f5989f16e9b768b8b9d9117/1a152/image01.png)

The wallet connection can be revoked by calling `wallet.signOut()` function.

## Open Source Session Replay

_[OpenReplay](https://github.com/openreplay/openreplay) is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data._

![replayer.png](https://static.openreplay.com/images/banner-blog.png)

_Start enjoying your debugging experience - [start using OpenReplay for free](https://github.com/openreplay/openreplay)._



## Smart Contract Interaction

NEAR smart contracts have view and change methods.

- **View Methods** are smart contract methods that do not mutate the contract storage; they are used to read data stored on the smart contract storage. 
- **Change Methods** are the opposite of the View methods in the sense that they alter smart contract storage. Change methods must be signed by a wallet to execute successfully; Signing contract calls usually requires gas fees. Gas fees are the number of NEAR tokens needed to perform a transaction on the NEAR Blockchain.

On lines 35 and 37, we can see the contract methods defined. You can find additional information on the smart contract method interface [here](https://github.com/Graate-Org/NEAR-Block-Dice--AssemblyScript-II/blob/master/src/block-dice/README.md). 

We have successfully connected to the NEAR blockchain and the smart contract provided. The next action order will be to make smart contract calls using the defined contract methods.

### createNewGame

This change function allows you to create a new game with a 0.5 NEAR token fee - see [src/pages/home/index.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/pages/home/index.jsx).

```javascript
...
const txFee = Big(0.5)
  .times(10 ** 24)
  .toFixed()

const GAS = Big(3)
  .times(10 ** 13)
  .toFixed()
...
const query = useQuery()
const hash = query.get('transactionHashes')
const createNewGame = async () => {
    try {
      await contract.createNewGame({}, GAS, txFee)
    } catch (error) {
      console.log(error)
    }
  }

  useEffect(() => {
    if (hash) {
      txReturnArgsFromHash({ hash, accountId: currentUser.accountId }).then(
        (res) => {
          handleShowGameId(decodeArgs(res))
        },
      )
    }
    // eslint-disable-next-line
  }, [])
```

On line 2 above, we calculate the transaction fee required to create a game(0.5 NEAR tokens), and on line 6, the gas fee required for the transaction: 30000000000000 yocto. 
 
![Create a new game](https://blog.openreplay.com/87b6444a5b891102129d14d0313bbf18/image02.gif)

On line 12, we are making the smart contract call for creating a new game by calling the `createNewGame` method on the contract, which accepts three arguments: the arguments required to execute `createNewGame` on the smart contract, the gas fee, and the transaction fee. The `createNewGame` method returns the `gameId` of newly created games; this value can be read by querying the transaction hash appended to url query after the transaction is redirected from the NEAR wallet and decoding the value, as seen on line 22. See [src/utils/near.js](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/utils/near.js).

```javascript
import Big from "big.js";
import { connect, keyStores } from "near-api-js";
import { hexy } from "hexy";
...
export const txReturnArgsFromHash = async ({ hash, accountId }) => {
  const config = {
    keyStore: new keyStores.BrowserLocalStorageKeyStore(),
    networkId: "testnet",
    nodeUrl: "https://rpc.testnet.near.org",
    walletUrl: "https://wallet.testnet.near.org",
    helperUrl: "https://helper.testnet.near.org",
    explorerUrl: "https://explorer.testnet.near.org",
  };
  const near = await connect(config);

  const {
    receipts_outcome: [
      {
        outcome: { status },
      },
    ],
  } = await near.connection.provider.txStatus(hash, accountId);

  return status.SuccessValue;
};

export const decodeArgs = (args) => {
  const decodedArgs = Buffer.from(args, "base64");
  let prettyArgs;
  try {
    prettyArgs = JSON.parse(decodedArgs.toString());
    // prettyArgs = JSON.stringify(parsedJSONArgs, null, 2);
  } catch {
    prettyArgs = hexy(decodedArgs, { format: "twos" });
  }

  return prettyArgs;
};
...
```

On line 22 above, we are querying the transaction hash and destructuring the status value from the query. On line 24, we return the `SuccessValue` value from the transaction `status` in base64 format; this is the value returned when the transaction is successful. The `decodeArgs` function takes the `SuccessValue` returned and decodes the value to JSON.

### joinGame

Like createNewGame, this is also a change method that allows other players to join an existing game. The joinGame method accepts the `gameId` as an execution argument. See [src/pages/game/index.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/pages/game/index.jsx).

[](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/pages/game/index.jsx)

```javascript
await contract.joinGame({ gameId: id }, GAS, txFee)
```

### rollDice

This accepts a `gameId` as an argument and rolls the dice for the wallet/sender signing the contract call; the dice roll will only execute successfully if the sender is a player in the game with the `gameId` specified. The `rollDice` method returns the values of the dice rolled. See [src/pages/game/index.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/pages/game/index.jsx).

```javascript
await contract.rollDice({ gameId: id })
```

![Roll A Dice](https://blog.openreplay.com/50680073741431a04153d2517ea93aeb/image03.gif)

### claimWinnings

The `claimWinnings` is a change method that allows players that won a game to redeem their share of the winning pool in their wallet. See `[src/pages/game/index.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/pages/game/index.jsx).

```javascript
await contract?.claimWinnings({ gameId: id })
```

![Claim Winnings](https://blog.openreplay.com/6955695d1b1f908d8f3f6b2c4b8bcc29/image04.gif)

### getGameDetails

This is a view method that returns the details of the `gameId` passed as an argument. See [src/pages/game/index.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/pages/game/index.jsx).

```javascript
const [game] = await contract?.getGameDetails({ gameId: id })

console.log(game)
//output
// {
//   createdAt: "1655411037283823319"
//     createdBy: "james1.testnet"
//   ended: "0"
//   id: "BD-4077698424"
//   players: 1
//   pool: null
//   prize: "500000000000000000000000"
//   started: "0"
//   status: 0
// }
```    

### getPlayersDetails

This method returns every player's details for the `gameId` passed as an argument. See [src/pages/game/index.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/34b52f31959bc50d0cf69a14856fe99cc87a0040/src/pages/game/index.jsx#L39).

```javascript
const players = await contract?.getPlayersDetails({ gameId: id })

console.log(players)
// output
// [
//   {
//     claimedWin: 0,
//     gameId: 'BD-2085016183',
//     playerId: 'melvinmanni09.testnet',
//     roll1: 5,
//     roll2: 3,
//     timeJoined: '1655409404439293761',
//     timeRolled: '1655409445158884992',
//   },
// ]
```

### getProfileDetails

`getProfileDetails` returns the profile details for the account passed as an argument to the method. This will return an array of all games that an account joined. See [src/pages/profile/](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/34b52f31959bc50d0cf69a14856fe99cc87a0040/src/pages/profile/index.jsx#L10).

```javascript
...
const getProfile = async () => {
    setGames([])
    const profile = await contract?.getProfileDetails({
      account: currentUser?.accountId,
    })
    console.log(profile) //['BD-3568281945', 'BD-4077698424', 'BD-3306428487']
    profile.forEach(async (el) => {
      const data = await contract?.getGameDetails({ gameId: el })
      setGames((prev) => [data[0], ...prev])
    })
  }
...
```

### getWinners

This view method returns an array with the account id of the winners for the `gameId` passed in as an execution argument. See [src/pages/game/index.jsx](https://github.com/Graate-Org/NEAR-Block-Dice-UI/blob/main/src/pages/game/index.jsx).

```javascript
const resWinners = await contract?.getWinners({ gameId: id })

//resWinners will return an array of account(s) that won the game id passed
```

### getActiveGames, getCompletedGames and getCreatedGames

These are change methods because they update games before returning the queried game state based on the game status.

![Active games](https://blog.openreplay.com/static/23d31d55128cf2d55822adc2aa7b4fc7/1a152/image05.png)

## Conclusion

We have covered initializing a connection to the NEAR blockchain, connecting to a wallet on the NEAR blockchain, and making smart contract calls. There is no doubt that at this point, you should be able to make smart contract calls to the NEAR blockchain. If you are interested in previewing the live project, you will find that [here](https://block-dice-game.graate.org/) and the link to the [smart contract repository](https://github.com/Graate-Org/NEAR-Block-Dice--AssemblyScript-II).

[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)