Open source Dapp template for the Elrond blockchain.

Overview

NextJS Dapp Template

Simple alternative to the dapp-core with React.

The Dapp is built using Nextjs and a couple of helpful tools. More docs soon! It has straightforward and complete functionality.

Main assumption for the dapp:

  • it works on Nextjs
  • it uses erdjs 10.* without the dapp-core library.
  • it uses backed side redirections to hide the API endpoint. The only exposed one is /api
  • it uses the .env file - there is an example in the repo
  • it uses a couple of config files in the 'config' directory (it will be simplified in the future)
  • it uses chakra-ui

How to start it locally:

  1. clone or download the repo
  2. cd nextjs-dapp-template
  3. npm install
  4. configure .env.local (you can copy the contents of the .env.example)
  5. npm run dev -> for development
  6. npm run build -> npm start for production

Howto

For simplicity, the template uses the main index page with demo components built using the core building blocks. Below you will find the list of most essential utilities, hooks, and components with examples that are actual code from the template. You can search them in the code to better understand how they work.

There are much more hooks and tools, but most of them are already used in the ones listed below.

The code samples are not ready to copy and paste. Please search them in the code.

useElrondNetworkSync()

The hook is responsible for synchronizing the network on each refresh. It should be used in the root component. Here is the _app.tsx.

Why not the context wrapper? Because context wrappers with auth state data checks will break Next ASO.

This way, you can check the auth state in chosen places. You are not forced to do this constantly for the whole document tree.

import { useElrondNetworkSync } from '../hooks/auth/useElrondNetworkSync';

const NextJSDappTemplate = ({ Component, pageProps }: AppProps) => {
  useElrondNetworkSync();
  return (
    <ChakraProvider theme={theme}>
      <Component {...pageProps} />
    </ChakraProvider>
  );
};

LoginModalButton

The component provides the Connect button with the modal, which will contain another three buttons for three different authentication possibilities (Maiar Mobile App, Maiar Defi Wallet - browser extension, Elrond Web Wallet). You should be able to use it in any place on the website.

import { LoginModalButton } from '../tools/LoginModalButton';

<LoginModalButton />

Authenticated

The component is used as a small wrapper where we need to be in the authenticated context, for example, for all transactions.

It can display the spinner and also the fallback React element.

Important Do not wrap it in big sections of the code. Its purpose is to be used multiple times on as small blocks as possible.

<Authenticated
  spinnerCentered
  fallback={
    <>
      <Text fontWeight="bold" fontSize="2xl" textAlign="center" mt={8}>
        Connect your wallet!
      </Text>
      <Flex mt={4} justifyContent="center">
        <LoginModalButton />
      </Flex>
    </>
  }
>
  <Box>Do something here in the auth context...</Box>
</Authenticated>

useTransaction()

The hook provides all that is required for triggering transactions. useTransaction can also take a callback function as an argument.

const { pending, triggerTx, transaction, error } = useTransaction({ cb });

const handleSendTx = useCallback(() => {
  const demoMessage = 'Transaction demo!';
  triggerTx({
    address: egldTransferAddress,
    gasLimit: 50000 + 1500 * demoMessage.length,
    data: new TransactionPayload(demoMessage),
    value: 0.001,
  });
}, [triggerTx]);

useScTransaction()

The hook provides all that is required for triggering smart contract transactions. useScTransaction can also take a callback function as an argument.

 const { pending, triggerTx, transaction, error } = useScTransaction({ cb });

const handleSendTx = useCallback(() => {
  triggerTx({
    smartContractAddress: mintSmartContractAddress,
    func: mintFunctionName,
    gasLimit: 14000000,
    args: [new U32Value(1)],
    value: 0.001,
  });
}, [triggerTx]);

useScQuery()

The hook uses useSWR under the hood and can be triggered on a component mount or remotely on some action. It has two different states for the pending action. For initial load and on revalidate. It also takes one of two return data types: 'int' and 'string'. You can still use the string type for boolean and check if you will get 01, which is true. For now, it assumes that you know what data type will be returned by a smart contract.

const {
  data: queryResult,
  fetch, // you can always trigger the query manually if 'autoInit' is set to false
  isLoading, // pending state for initial load
  isValidating, // pending state for each revalidation of the data, for example using the mutate
  error,
} = useScQuery({
  type: SCQueryType.INT, // can be int or string
  payload: {
    scAddress: mintSmartContractAddress,
    funcName: queryFunctionName,
    args: [],
  },
  autoInit: false, // you can enable or disable the trigger of the query on the component mount
});

useLoggingIn()

The hook will provide information about the authentication flow state. It will tell if the user is already logged in or is logging in.

const { isLoggingIn, error, isLoggedIn } = useLoggingIn();

useAccount()

The hook will provide information about the user's account data state. The data: address, nonce, balance.

const { address, nonce, balance } = useAccount();

useLoginInfo()

The hook will provide the information about the user's auth data state. The data: loginMethod, expires, loginToken, signature. Login token and signature won't always be there. It depends if you'll use the token. Check Elven Tools Dapp backend integration article for more info.

const { loginMethod, expires, loginToken, signature } = useLoginInfo();

Working with the API

The API endpoint is proxied on the backend side. The only public API endpoint is /api. This is useful when you don't want to show the API endpoint because, for example, you use the paid ones. Also, there is an option to block the /api endpoint to be used only within the Dapp, even previewing it in the browser won't be possible.

You can use API_ALLOWED_DAPP_HOST in the .env file to enable /api restrictions. If you don't want to restrict it, you can remove that variable.

In the pages/api/_middleware.ts, you'll find the logic for the API restrictions. And in the next.config.js, you'll find the configuration for rewrites of the API.

In this demo, the Dapp uses a public API endpoint, so it isn't essential, but it is beneficial when you need to use paid 3rd party service.

Working with the .env and config files

There is an env.example file that you can copy and rename into .env.local to run the app locally. You would need to configure these variables for your production-ready dapp.

Here are all variables:

# =============================================
# Public variables (exposed on the frontend)
# =============================================

# Elrond chain (can be devnet, testnet, mainnet)
NEXT_PUBLIC_ELROND_CHAIN = devnet

# This is the masked/proxied public API endpoint
# only the current instance of the Dapp can use it if only API_ALLOWED_DAPP_HOST is set
NEXT_PUBLIC_ELROND_API = /api

# This is the main domain of your dapp
NEXT_PUBLIC_DAPP_HOST = http://localhost:3000

# =============================================
# Private variables (used on the backend)
# =============================================

# Your main Elrond API can be a custom one. There won't be a possibility
# to reveal this endpoint. NEXT_PUBLIC_ELROND_API will mask it
ELROND_CUSTOM_API = https://devnet-api.elrond.com

# Only this host will be allowed to consume the API (optional)
API_ALLOWED_DAPP_HOST = http://localhost:3000

All variables which start with NEXT_PUBLIC_ will be readable on the frontend side of the dapp. So please don't use them for any secret keys and data. If you need something to be available only on the backend side, don't use the NEXT_PUBLIC_ prefix.

You can set up the chain type here. Use NEXT_PUBLIC_ELROND_CHAIN to set devnet, testnet or mainnet.

Each hosting provider will have a different way of doing that. We will take a look at how Netlify is doing that below.

Deployment

For deployment, we recommend the Netlify. Why Netlify? Because it is the simplest way to deploy the Nextjs app for free. Of course, the most recommended is the Vercel which you could also try.

As for Netlify, the only what you need to do there is to go to the settings and configure from which repository the app should be deployed. Check out how: Netlify getting started.

Then fill up the env variables. See how here: Netlify env vars setup.

On each repository code push, the Netlify services will redeploy the app.

Here are other deployment solutions: NextJS Deployment.

Missing for now:

  • Ledger auth
  • More docs and examples
  • More tooling and components
  • tests

Other solutions

If you would like to test other templates:

Also, check the implementation of the NFT Minter Dapp based on almost the same codebase:

Contact

Comments
  • Support for calling smart contract endpoints, which expect MultiESDT payment

    Support for calling smart contract endpoints, which expect MultiESDT payment

    Hi @juliancwirko,

    First of all thank you for this extra ordinary work. This lightweight template is very impressive.

    I would like to ask whether it has also support for calling smart contract endpoints, which expect MultiESDT payment.

    Thanks!

    opened by Alannek1992 10
  • Sending transaction after signing with Maiar wallet fails on some devices

    Sending transaction after signing with Maiar wallet fails on some devices

    Hi Julian,

    This problem seems to be very difficult to replicate. But in general I am using your template and it works perfectly fine for me.

    Recently I got a lot of reports that the sending transaction fails after signin with maiiar wallet. They have the same problem on your template website -> https://elrond-nextjs-dapp.netlify.app/.

    So Far I managed to find that the line which is failing is await apiNetworkProvider.sendTransaction(tx); within sendTxOperation.ts.

    This is then very general exception: Error: Request error on url [transactions]: [{"statusCode":400,"message":""}] at ApiNetworkProvider.handleApiError (apiNetworkProvider.js?5989:213:1) at ApiNetworkProvider.eval (apiNetworkProvider.js?5989:202:1) at Generator.throw (<anonymous>) at rejected (apiNetworkProvider.js?5989:6:42)

    This really happening only for some users, at the moment I have no idea why.

    Do you have any hints, what might be wrong?

    Thanks a lot!

    opened by Alannek1992 8
  • After signing the transaction using Elrond Wallet the information about transaction is somehow gone.

    After signing the transaction using Elrond Wallet the information about transaction is somehow gone.

    Hi Julian,

    I m struggling with tracing the transaction when it is signed by elrond wallet. After signing the transaction I am redirected back to the page and although transaction is still pending my state is not updated correctly and CustomTransactionTrigger component is displayed again in this example even though I should still see Pending. When I click on the btn again then it seems the transaction starts to be processed again.

    Please excuse me for this very brief code example. But it should demonstrate whats happening. Probably I m doing something wrong. For e.g. maiar wallet this approach works totally fine.

    const TransactionModel: React.FC = () => {
      const [result, setResult] = useState<{ type: string; content: string }>();
      const [pending, setPending] = useState(false);
      const [error, setError] = useState<string>();
    
      const handleTxCb = useCallback(
        ({ transaction, pending, error }: TransactionCb) => {
          if (transaction) {
            setResult({ type: "tx", content: transaction.getHash().hex() });
            setPending(false);
            setError(undefined);
          }
          if (pending) {
            setPending(true);
            setError(undefined);
            setResult(undefined);
          }
          if (error) {
            setError(error);
            setPending(false);
            setResult(undefined);
          }
        },
        []
      );
    
      if (pending) {
        return <h1>TRANSACTION PENDING</h1>;
    }
    
       if (result) {
    return <h1>TRANSACTION RESULT</h1>
    }
    
    return <CustomTransactionTrigger />;
    };
    
    
    const CustomTransactionTrigger : React.FC<> = ({
      cb,
    }) => {
      const { pending, triggerTx, transaction, error } = useScMultiESDTTransaction({
        cb
      });
      const handleSendTx = useCallback(
        (membership: SaleMembershipIf) => {
          const payload = TransactionPayload.contractCall()
            .setFunction(new ContractFunction("MultiESDTNFTTransfer"))
            .setArgs([
              new AddressValue(
                new Address(process.env.NEXT_PUBLIC_SC_ADDRESS as string)
              ),
              new U32Value(2),
              BytesValue.fromUTF8(process.env.NEXT_PUBLIC_WEGLD_TOKEN_ID as string),
              new U64Value(0),
              new BigUIntValue(new BigNumber(egldPrice)),
              BytesValue.fromUTF8(process.env.NEXT_PUBLIC_ELRD_TOKEN_ID as string),
              new U64Value(0),
              new BigUIntValue(new BigNumber(elrdPrice)),
              BytesValue.fromUTF8(BUY_CARD_ENDPOINT),
            ])
            .build();
    
          triggerTx({
            receiver: address,
            gasLimit: 14000000,
            data: payload,
          });
        },
        [triggerTx]
      );
    
      return <button onClick={handleSendTx}>trigger</button>;
    }
    
    opened by Alannek1992 6
  • Rough TODO

    Rough TODO

    • ~~migrate the elven-tools-dapp to erdjs 10~~ [done]
    • ~~clone and clean the template to be more generic~~ [done]
    • ~~add docs (readme)~~ [done]
    • then more improvements
      • ~~Ledger auth~~ [done]
      • more React hooks
      • more tooling
      • rethink the backend integration and server-side auth checks, research: https://github.com/ElrondNetwork/native-auth
      • languages/translations support
    • then maybe split into separate npm packages, but I am not sure if this is required
    opened by juliancwirko 6
  • Focus lost on every keystroke when placing a NumberInput inside a CardWrapper

    Focus lost on every keystroke when placing a NumberInput inside a CardWrapper

    It seems like something is wrong with the CardWrapper. I'm a very fresh web development beginner (coming from the .NET World), so it probably is just me, but I found a very strange behavior:

    This is working. It only rerenders the updated value-attribute to the DOM every time I enter a character into the NumberInput.

    const Test= () => {
      const [limit, setLimit] = useState('0.0');
      return (
        <NumberInput
          minW="40%"
          maxW="40%"
          ml={5}
          clampValueOnBlur={false}
          defaultValue={0}
          precision={2}
          onChange={(enteredLimit) => setLimit(enteredLimit)}
          value={limit}
          step={0.2}
        >
          <NumberInputField />
        </NumberInput>
      );
    };
    

    This is NOT working. It rerenders the whole outer div every time I type a character into the number input, thus loosing the keyboardfocus every time I type a character into the NumberInput:

    const Test = () => {
      const [limit, setLimit] = useState('0.0');
      return (
        <CardWrapper>
          <NumberInput
            minW="40%"
            maxW="40%"
            ml={5}
            clampValueOnBlur={false}
            defaultValue={0}
            precision={2}
            onChange={(enteredLimit) => setLimit(enteredLimit)}
            value={limit}
            step={0.2}
          >
            <NumberInputField />
          </NumberInput>
        </CardWrapper>
      );
    };
    

    I must be doing something terribly wrong. Do you have any idea, what could be wrong?

    opened by janniksam 3
  • Ledger Auth problem

    Ledger Auth problem

    Hi,

    I'm having an issue to connect with my ledger while developing a DApp. When I click on the Ledger button to connect it always goes into the first if statement meaning the await dappProvider.init(); hasn't returned a successful response, so nothing happens except for the console.warn message in the console.

    image

    When I connect myself with the Extansion (or any other login method), logout and then try to reconnect with ledger, it is working fine. Not sure how it really works there ?

    opened by MartySalade 3
  • Render child of MainLayout when state is changed

    Render child of MainLayout when state is changed

    First of all, thanks a lot for the template!

    Child of MainLayout not re-rendered

    The root component of the child of MainLayout is not re-rendered after changing the state. It affects just to the root component, the tree is re-rendered upon the state changes.

    This behaviour is confusing when using hooks on nextjs pages because changes are not reflected in the page but in the children components.

    How to reproduce

    1. Create a nextjs page as below
    const Home: NextPage = () => {
      const { address } = useAccount()
      return (
        <MainLayout>
          <HeaderMenu>
            <HeaderMenuButtons enabled={['auth']} />
          </HeaderMenu>
          <h1>### {address} ###</h1>
          <GetUserDataDemo />
       <MainLayout>
    )
    
    1. Connect your Elrond account
    2. The value of address inside <h1> is not updated but the same value in component GetUserDataDemo is updated
    opened by joaquin-alfaro 2
  • Make public API masking optional

    Make public API masking optional

    By removing / commenting out the enviroment variable NEXT_PUBLIC_MULTIVERSX_API the maskin of the public MultiversX api can be disabled. When masking is disabled all api requests done by the dapp will go directly to the public api endpoint.

    Further more I've simplified the access to the active network configuration. We can use activeNetworkConfiguration-method to get the active network configuration, if we don't want to retrieve a specific configuration for a given chainType.

    Fixes #18

    opened by janniksam 1
  • Make the API proxy optional. For example, when someone uses public API, it isn't needed at all

    Make the API proxy optional. For example, when someone uses public API, it isn't needed at all

    First solution:

    1. make the NEXT_PUBLIC_MULTIVERSX_API actual public API endpoint (not proxied)
    2. use a different name for the proxy endpoint, for example, NEXT_PUBLIC_MULTIVERSX_API_PROXY.
    3. allow passing the NEXT_PUBLIC_MULTIVERSX_API_PROXY as optional env var. If not present, then use NEXT_PUBLIC_MULTIVERSX_API as Elrond's public API endpoint (visible for all)

    Second solution, probably better:

    1. make the private env var MULTIVERSX_CUSTOM_API optional and check if it exists
    2. keep the public env var NEXT_PUBLIC_MULTIVERSX_API as is, but use it for absolute public API endpoint or for relative proxy API endpoint when MULTIVERSX_CUSTOM_API is configured

    Changes will require a new major version and improvements in docs, more info about it (README.md).

    enhancement 
    opened by juliancwirko 0
  • updates

    updates

    • Nextjs update
    • React update
    • erdjs libraries update
    • other dependencies updates
    • improvements for useScQuery hook
    • config moved to env variables - see .env.example
    • minor fixes
    • ts types improvements
    • switched to MIT license (erdjs libs are now MIT too)

    I'll be yolo merging it to test the Netlify deployment with new env vars.

    opened by juliancwirko 0
  • Login methods Maiar + Ledger in Web Wallet don't allow to send transactions

    Login methods Maiar + Ledger in Web Wallet don't allow to send transactions

    If you log into your wallet via WEB WALLET and use either the method Maiar or Ledger, you can't send transactions with that session.

    Maiar: Does say "rejected by user" Ledger: Does give something like BAD REQUEST signature invalid

    bug 
    opened by janniksam 5
  • Expose result of await transactionWatcher.awaitCompleted(tx); to callback

    Expose result of await transactionWatcher.awaitCompleted(tx); to callback

    In many cases its necessary to know, if a transaction is failing or not.

    We already call it here: sendTxOperations#L27

    const transactionWatcher = new TransactionWatcher(apiNetworkProvider);
    await transactionWatcher.awaitCompleted(tx);
    setTransaction(tx);
    cb?.({ transaction: tx, pending: false });
    

    To be able to deal with the transactions status at a higher level I would suggest something like this:

    const transactionWatcher = new TransactionWatcher(apiNetworkProvider);
    const txResult = await transactionWatcher.awaitCompleted(tx);
    setTransaction(tx);
    cb?.({ transaction: tx, pending: false; result: txResult });
    
    enhancement 
    opened by janniksam 1
  • Handle vm query in useScQuery

    Handle vm query in useScQuery

    For now it is an abstraction for /vm-values/string and /vm-values/int the /vm-values/query is also needed

    This will be probably simplified and it will use ABI + result parser

    enhancement 
    opened by juliancwirko 0
Releases(v4.1.0)
Owner
Elrond's Dev Guild
All about developing on the Elrond blockchain - docs, open-source tools, community, learning materials. Not official. Community-based.
Elrond's Dev Guild
This template can be used as a starting point for any minting dApp on the Elrond Network.

Minting dApp Template Made by Giants & NF-Tim by Creative Tim Live Demo This is a dApp template based on erd-next-starter by Giants & soft-ui-dashboar

Giants Labs 11 Dec 23, 2022
Elrond blockchain CLI helper tools - interaction with APIs, smart contracts and protocol

Buildo Begins ?? Meet Buildo. He is here to help you start creating in the Elrond blockchain ecosystem. Here is where everything begins. I'm going on

Elrond's Dev Guild 22 Dec 30, 2022
Grupprojekt för kurserna 'Javascript med Ramverk' och 'Agil Utveckling'

JavaScript-med-Ramverk-Laboration-3 Grupprojektet för kurserna Javascript med Ramverk och Agil Utveckling. Utvecklingsguide För information om hur utv

Svante Jonsson IT-Högskolan 3 May 18, 2022
Hemsida för personer i Sverige som kan och vill erbjuda boende till människor på flykt

Getting Started with Create React App This project was bootstrapped with Create React App. Available Scripts In the project directory, you can run: np

null 4 May 3, 2022
Kurs-repo för kursen Webbserver och Databaser

Webbserver och databaser This repository is meant for CME students to access exercises and codealongs that happen throughout the course. I hope you wi

null 14 Jan 3, 2023
Using a Decentralized Application (DApp) to Sell artwork on the Ethereum blockchain with smart contracts written in Solidity.

Decentralized Applications For Selling Limited Time Artwork This repository houses the Solidity, JavaScript, and HTML code for a Decentralized Applica

Keyan Ahmadi 4 Mar 20, 2023
Complete Open Source Front End Candy Machine V2 Minter dAPP Built For The Frog Nation NFT Solana Project. Built With React, Candy Machine V2, Typescript

Complete Open Source Front End Candy Machine V2 Minter dAPP Built For The Frog Nation NFT Solana Project. Built With React, Candy Machine V2, Typescript

null 17 Sep 24, 2022
SW DAOs open-source Polygon dApp frontend.

SW DAOs open-source Polygon dApp frontend. At SW DAO, we're committed to being the industry-leading provider of automated investing solutions and cryp

SW DAO 5 Jul 27, 2022
It is a very basic implementation of how blockchain works, mainly how the bitcoin blockchain.

How to run this program npm install node core/blockchain.js What is this It is a very basic implementation of how blockchain works, mainly how the bit

Anish Jain 12 May 9, 2022
HackMIT 2022. 2nd Place in Blockchain for Society sponsored by Jump Crypto. A revolutionary web application that leverages machine learning and blockchain technology to improve the crowdsourcing experience!

?? Wikisafe ?? Wikisafe is a revolutionary new crowdsourcing web application that innovates the process of crowdsourcing information. This application

Benson Liu 5 Dec 8, 2022
Open-source NFID SDK for Internet Identity, a blockchain authentication system for the Internet Computer.

NFID-SDK is an open source software development kit that contains examples and packages for developers to integrate NFID into your application

Internet Identity Labs 15 Dec 23, 2022
Minimal Typescript / NextJS dApp template bootstrapped with wagmi Ethereum react hooks library.

Welcome to the NextJS wagmi starter template ?? Looking to get up and running with a Typescript / NextJS dApp as quickly as possible? You're in the ri

Seth 78 Jan 4, 2023
Modern, Flexible Starknet Dapp Template

cairopal • Modern, Flexible Starknet Dapp Template. Developing Clone the repository git clone [email protected]:a5f9t4/cairopal.git cd cairopal Install D

andreas 38 Sep 28, 2022
Reference for How to Write an Open Source JavaScript Library - https://egghead.io/series/how-to-write-an-open-source-javascript-library

Reference for How to Write an Open Source JavaScript Library The purpose of this document is to serve as a reference for: How to Write an Open Source

Sarbbottam Bandyopadhyay 175 Dec 24, 2022
An Open-Source Platform to certify open-source projects.

OC-Frontend This includes the frontend for Open-Certs. ?? After seeing so many open-source projects being monetized ?? without giving any recognition

Open Certs 15 Oct 23, 2022
Shikhar 4 Oct 9, 2022
This is a project for open source enthusiast who want to contribute to open source in this hacktoberfest 2022. 💻 🎯🚀

HACKTOBERFEST-2022-GDSC-IET-LUCKNOW Beginner-Hacktoberfest Need Your first pr for hacktoberfest 2k22 ? come on in About Participate in Hacktoberfest b

null 8 Oct 29, 2022
Boilerplate starter template for a new TON blockchain project - FunC contracts, JS tests, compilation and deployment scripts

TON Starter Template - Contracts Starter template for a new TON project - FunC contracts, JS tests, compilation and deployment scripts Overview This p

TON DeFi Ecosystem 44 Dec 17, 2022
An Open Source Remix template that integrates Stripe Subscriptions, Social Authentication, Testing and a few more features. PostgreSQL version. Deploys to Fly.io

Live Demo · Twitter An open source Remix Stack that integrates Stripe Subscriptions, Social Authentication, Testing and a few more features. PostgreSQ

xo 25 Dec 7, 2022