Pre-requisites:
- Basic knowledge of React, Tailwind CSS, and related web technologies
- Basic understanding of the Stellar blockchain
- Node.js and npm installed
- Web browser with Varial Wallet extension installed
1. Introduction
The role of frontend in Stellar dapps
Frontend development plays a crucial role in decentralized applications (dapps) built on the Stellar network. It serves as the primary interface between users and the underlying blockchain technology. A well-designed frontend not only makes your dapp accessible and user-friendly but also helps users interact seamlessly with complex blockchain operations. In Stellar dapps, the frontend is responsible for:- Presenting blockchain data in a human-readable format
- Facilitating user interactions with smart contracts and Stellar operations
- Managing user accounts and keys securely
- Providing real-time updates on transaction status and account balances
- Guiding users through complex processes like multi-signature transactions or claimable balance operations
Importance of user interface and user experience
The importance of a good user interface (UI) and user experience (UX) in Stellar dapps cannot be overstated. Blockchain technology can be intimidating for many users, and a well-designed UI/UX can make the difference between a successful dapp and one that users find frustrating or confusing. Key aspects of UI/UX in Stellar dapps include:- Simplicity: Presenting complex blockchain concepts in an easy-to-understand manner
- Transparency: Providing clear information about transaction fees, network status, and operation outcomes
- Feedback: Offering immediate and clear feedback on user actions and transaction progress
- Error Handling: Gracefully managing and explaining errors in a user-friendly way
- Performance: Ensuring quick load times and responsive interactions, even when dealing with blockchain operations
2. Setting up the development environment
Before we start building our Stellar dapp, we need to set up our development environment. We’ll be using React with Next.js for our frontend framework, Tailwind CSS for styling, and the Stellar SDK for interacting with the Stellar network.Installing Node.js and npm
First, make sure you have Node.js and npm (Node Package Manager) installed on your system. You can download and install them from the official Node.js website: https://nodejs.org/ To verify your installation, open a terminal and run:Setting up a Next.js project
Next.js is a React framework that provides features such as server-side rendering and routing out of the box. To create a new Next.js project, run the following commands in your terminal:- Would you like to use TypeScript? Yes
- Would you like to use ESLint? Yes
- Would you like to use Tailwind CSS? Yes
- Would you like to use
src/directory? No - Would you like to use App Router? Yes
- Would you like to customize the default import alias? No
Setup HTTPS on Localhost
Varial wallet requires a secure connection to interact with your dapp. Browsers treathttp://localhost and http://127.0.0.1 as secure contexts per the W3C Secure Contexts specification, so local development over plain HTTP works without extra setup. To enable HTTPS on localhost anyway, you can use a tool like mkcert. Fortunately, Next.js provides built-in support for HTTPS.
To enable HTTPS in your Next.js project, open the package.json file and edit the scripts section as follows:
Installing Stellar SDK and other dependencies
To interact with the Stellar network, we’ll need to install the Stellar SDK and some additional dependencies:@stellar/stellar-sdk: The official Stellar SDK for interacting with the Stellar network@stellar/varial-sdk: A library for integrating with the Varial wallet (a popular Stellar wallet browser extension)bignumber.js: A library for arbitrary-precision decimal and non-decimal arithmetic
3. Building basic interface elements
In this section, we’ll create reusable components for our Stellar dapp and implement forms and inputs using React and Tailwind CSS.Creating reusable components
Let’s start by creating a button component that we’ll use throughout our application. Create a new filecomponents/Button.tsx:
components/ConnectButton.tsx:
setAllowed function from the @stellar/varial-sdk library to connect the Varial wallet when clicked.
Implementing forms and inputs
Next, let’s create a reusable input component. Create a new filecomponents/Input.tsx:
components/SendPaymentForm.tsx:
Designing responsive layouts with Tailwind CSS
To create a responsive layout for our dapp, we’ll use Tailwind CSS utility classes. Let’s create a layout component that we can use across our pages. Edit the fileapp/layout.tsx:
app/page.tsx file to use this layout and our new components:
4. Basic Concepts of Soroban Contracts
Before we start integrating smart contract functionality into our Stellar dapp, let’s understand the basic concepts of Soroban contracts. Soroban is a smart contract platform built on the Stellar network. It allows developers to write and deploy smart contracts that run on the Stellar blockchain. Soroban contracts are written in Rust and compiled to XDR (External Data Representation) for execution on the Stellar network.Data Types
Stellar supports a few data types that can be used in Soroban contracts and we need to do conversions from time to time between those types and the types we have in Javascript. The full list of primitive data types are explained here.XDR
Values that are passed to contracts and returned from contracts are serialized using XDR. XDR is a standard data serialization format used in Stellar to represent complex data structures. It is used to encode and decode data for transmission over the Stellar network. XDR is mostly represented in Javascript as string and can be converted to other types using the Stellar SDK.Fees
Gas fees like they are called in the ethereum network are charged differently here. When submitting different types of transactions, the type of fees paid are different. When submitting a transaction to the network that interacts with the network, a Base fee is paid together with the resource fee for the operation. The base fee is a fixed fee that is paid for every transaction on the network. The resource fee is a fee that is paid for the resources used by the operation and is calculated based on the resources used by the operation and the network’s current resource price. Calculating these fees can be cumbersome but the Stellar SDK provides a way to calculate these fees using theserver.prepareTransaction method which simulates the transaction, gets the appropriate fees and appends the correct fee settings to the transaction.
ABI or Spec
The ABI or spec is a json file that contains the contract’s interface. It defines the functions that can be called on the contract, their parameters, and return types. The ABI is used by clients to interact with the contract and execute its functions. The ABI is generated from the contract’s source code and is used to compile the contract into XDR for execution on the Stellar network. ABI can be genrated for a contract using thestellar contract bindings command and can be used to interact with the contract.
This ABI can also be generated as a typescript library to ease development in your DApp and we’ll be looking it later in this guide
5. Integrating with Stellar blockchain
Now that we have our basic UI components in place, let’s integrate them with the Stellar blockchain using the Stellar SDK. It is imperative to know that the Stellar blockchain has three major networks:- Public network (also called Mainnet): This is the main Stellar network where real transactions take place.
- Test network: This is a test environment for developers to test their applications without using real lumens.
- Futurenet network: This is a network for testing new features before they are deployed to the public network.
Setting up the Stellar SDK
Below is a snippet that shows how to set up the Stellar SDK in your project. We will be using parts of it a lot in this guide so lets try to understand it.BASE_FEE is the minimum fee required for a transaction on the Stellar network. while the Networks.TESTNET is the network passphrase for the Stellar Testnet. When using the public network, you can replace TESTNET with PUBLIC or FUTURENET for the Futurenet network.
Interacting with the Stellar network
If you are coding along this guide at this point, you should have a minimal web application with a form to send payments. Now, let’s integrate this form with the Stellar SDK to send actual payments on the Stellar network. Before we proceed, please ensure that you have setup the Varial wallet extension on your browser. You can download it here. Also, ensure that you have lumens on the Testnet account you are using. You can get free lumens from the Stellar Friendbot. Now, let’s update ourapp/page component to interact with the Stellar network:
app/page component, we added a useEffect hook to check if the Varial wallet is connected and retrieve the public key. We also added a handleConnectWallet function to connect the wallet if it’s not already connected.
The handleSendPayment function now interacts with the Stellar network to send a payment. It retrieves the source account details, creates a payment transaction, signs the transaction using the Varial wallet, and sends the transaction to the Stellar network. If the transaction is successful, it displays an alert to the user.
Notice how we imported the isConnected, setAllowed, getAddress, and signTransaction functions from the @stellar/varial-sdk library. These functions are used to interact with the Varial wallet extension and sign transactions securely.
Interacting with smart contracts
In the above example, we sent a simple payment transaction using theStellarSdk.Operation.payment operation. However, Stellar also supports many other operations, one of which is invokeHostFunction operation which is used to interact with smart contracts on the Stellar network.
We will be working with a deployed version of the counter smart contract on the Stellar Testnet. The smart contract has a single function increment which increments a counter value stored on the Stellar network.
Create a new file app/counter/page.tsx:
CounterPage that interacts with the counter smart contract on the Stellar network. The handleIncrement function sends a transaction to the smart contract to increment the counter value. It then retrieves the updated counter value from the transaction result and displays it to the user.
Notice how we used a different operation contract.call("increment") to interact with the smart contract. This operation is a facade for the invokeHostFunction operation on the Stellar network. We also used the prepareTransaction and sendTransaction methods to send the transaction to the network and retrieve the transaction result.
prepareTransaction call is used to prepare a transaction for submission to the network. It simulates the transaction, then uses the appropriate resource fees and other params to create a transaction that is ready to be submitted to the network.
<ConnectButton> is a helper button that connects the Varial wallet when clicked like we described earlier.
After calling the transaction to increment successfully, we need to poll and then call another method server.getTransaction to get the transaction result. The transaction result contains the new counter value which we extract and display to the user.
The value gotten from the transaction result is stored in a form called scval due to the limited number of types that exist in soroban today. This value is transmitted about in the form of xdr which can be converted to an scVal using the sdk. The value is then parsed as u32 which is a 32-bit unsigned integer. We will learn more about converting these types in this collection of guides.
Reading events from the Stellar network
Events on Stellar are values emitted from contracts grouped by topics. These events are emitted when a contract is called and can be read by any client that is interested in the contract. Events are stored in the Stellar ledger and can be read by any client that is interested in the contract. In addition to sending transactions and interacting with smart contracts, you can also read events from the Stellar network using the Stellar SDK. This allows you to monitor account changes, transaction status, and other network events in real-time. This is made possible by using theserver.getEvents method which allows you to query events based on topics. We will consider a simple example where we read events from the counter smart contract we interacted with earlier.
We will be editing the CounterPage component to read events from the counter smart contract immediately the page loads to get the initial counter value and update instead of using “Unknown”. Before we continue, please take a look at the contract code. In the contract code, an event named increment is emitted whenever the increment function is called. It is published over 2 topics, increment and COUNTER and we need to listen to these topics to get the events.
The topics are stored in a data type called symbol and we will need to convert both increment and COUNTER to symbol before we can use them in the server.getEvents method. At maximum, stellar RPCs keep track of events for 7 days and you can query events that happened within the last 7 days, so if you need to store events for longer, you may need to make use of an indexer.
To use events,we edit our counter page and add the following code:
getEvents to query events from the Stellar network. We pass in the contract ID, topics, and other filters like the ledger to start searching from to 2000 ledgers away to get the events we are interested in. We then extract the counter value from the events and update the state accordingly.
However, we had to convert the topics COUNTER and increment to symbol using the xdr.ScVal.scvSymbol and then convert them to XDR before we can use them in the getEvents method. This is because the topics are stored in the form of symbol and transmitted over XDR format to the network like we discussed earlier. Similarly, the value of the new count is sent over the network as XDR but the SDK helped us convert it to an scVal and then we parsed it as a u32 to get the actual value.
Source code for this program is available here
Now, when the page loads, it will query the events from the Stellar network and update the counter value accordingly.
6. Using Typescript Bindings
The Stellar CLI comes with support for exporting a contract spec to a typescript library. This library is optimized for importing as an npm based module to your application. The library generated contains helper methods for calling each contract and also does automatic type conversion of the types for any contract method.How to create the bindings library
To achieve this, you need to- Have the Stellar CLI installed.
- Have either source code or deployed contract ID of the contract.
- Know the network it was deployed to.
Scenario 1: I have the Contract ID but no code
In this scenario, we need to use the commandstellar contract fetch to fetch the wasm code of the contract
Scenario 2: I have the code
The next step here is to build it to a wasm file using thestellar contract build command.
Generating the Library
After getting the wasm, we can now runstellar contract bindings typescript to generate the library which is ready to be published to NPM. The library generated is suited for working with complex contracts.
7. Common Pitfalls
Below are a few common pitfalls that soroban DApp developers might run intoData Type Conversions
The data types in Javascript are different from the data types in soroban. When working with soroban contracts, you need to be aware of the data types and how to convert them to the types you are familiar with in Javascript. The Stellar SDK provides helper methods for converting between XDR and Javascript types in thexdr namespace.
Fees
Calculating fees for transactions can be tricky. The Stellar SDK provides a way to calculate fees using theserver.prepareTransaction method, which simulates the transaction and returns the appropriate fees. Fees are calculated in stroops
SendTransaction and GetTransaction
Results from a smart contract execution or any of the valid transactions on soroban are not immediate. They are kept in aPENDING state until they are confirmed. You need to poll the getTransaction method to get the final result of the transaction.