---
title: 7 Industries ripe for disruption with Solana integration and where to start!
tags: Solana, DeFi, DePin, Blockchain, Music, NFTs
---
# 7 Industries ripe for Solana integration and where to start!
## Table of Contents
- **What is this article about?**
- **Why build on Solana?**
- **What industries are poised to be disrupted?**
- **Current projects within these areas**
- **What tools are available to begin building in these areas?**
- **What avenues are best to find PMF and users?**
- **Conclusion**
- **Further Resources**
---
## What is this article about?
In this article we will cover the industries that are ready for disruption and why Solana has the perfect technology to be the solution. From gaming and music, to real-estate investing and infrastructure, Solana's capabilites provide the tools necessary to make it happen.
After taking a deepdive into what industries have problems, we will take a closer look into the current projects aiming to provide the solution with Solana integration.
From there, we will dive right into code examples to help get you started on the next million dollar idea! Whether you are looking for an idea or you are looking for a place to start, we'll walk you through it.
---
## Why build on Solana?
Blockchains solve a lot of issues that are existent in current industries. A lot of these issues we've come to accept as "a cost of doing business" because it's all we know. Whether it's transaction fees with Visa, frivoulous fees with Ticketmaster, deprecating services with higher prices from Verizon, or Google harvesting your data with no compensation, we've come to accept that as the norm. But, it doesn't have to be this way. Transparency, fee-control, and rewards for contributions are just a few of the benefits for blockchain implementation.
If you have kept an eye on Solana over the past year, you've likely noticed the phrase "Only Possible on Solana." Before we dive into what industries Solana can relieve pain points in, let's address what benefits it offers.
The three primary reasons are:
- High throughput
- Low fees
- Scalability
Let's take a closer look at each.
**High throughput -** A key component of [Solana's Virtual Machine (SVM)](https://squads.so/blog/solana-svm-sealevel-virtual-machine) is the parallel processing capability acheived by [Sealevel](https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192), a parallized transaction processing engine. Meaning, when a transaction is submitted it contains a set of instructions that inform Sealevel which states the transaction plans to read and write, allowing the SVM to process multiple transactions in a concurrent manner. This differs from other blockchains, like Ethereum, that are single threaded and only allows one contract to modify the blockchain state at a time. And, with the new validator [Firedancer](https://solana.com/ecosystem/firedancer) going live within the next year, things will only become faster.
**Low fees -** Another enticing aspect of Solana, in comparison to other blockchains, is [transaction fee calculation](https://solana.com/docs/core/transactions/fees#transaction-fee-calculation). Currently, the fee calculation is based on the number of signatures that need to be verified in the transaction with the average fee being less than [$0.001 USD](https://solanacompass.com/statistics/fees). This method differs from other chains where fees are dynamically adjusted in correlation with network activity, often leading to higher/less predicatble fees during periods of high-activity.
**Scalability -** [Solana's design](https://solana.com/news/8-innovations-that-make-solana-the-first-web-scale-blockchain) optimizes data propogation and leverages parallel GPUs for high-throughput transaction processing, allowing the hardware to operate at capacity. By being designed in such manner means, "Every time Nvidia doubles the number of SIMD lanes available, our network will double in computational capacity." according to Solana Co-Founder [Anatoly Yakovenko](https://twitter.com/aeyakovenko?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor). With scalability being a feature from the start, this will allow developers to not worry about dApp usage being a constraint in terms of growth. Whether it's 100 user or 10 million, Solana can handle your dApps usage.
---
## What industries are poised to be disrupted?
The old addage of "if you are not paying for a product, then you are the product" has become a standard model of business operations for over a decade now. In some cases, we accept higher prices for the ease of paying with a credit card or transferring money. In video games, the standard model has succumbed to enticing users to pay for "in-game currency" with micro-transactions that are not usable in the next year's iteration (looking at you FIFA). Even in the music industry, from ticketing to actual sales, what we pay and how much the artist receives is never incoherently clear.
These are all just a few pain points we've learned to accept, but are solvable through blockchain integration. With the transparency provided by the public ledger and the benefits of Solana's high throughput low cost and scalable nature can help alleviate these issues.
Next, we'll take an in-depth look at industries prime to be disrupted with the integration of Solana.
### Payments
In 2023 alone, e-commerce was expected to account for 21.2% of total retail sales, totaling [$6.3 trillion USD](https://www.shopify.com/enterprise/global-ecommerce-statistics#:~:text=How%20big%20is%20the%20global,21.2%25%20of%20total%20retail%20sales.). With current payment processing fees on credit cards ranging from 1.15-3.3% and online payment processor fees ranging from 2.9-3.49%, according to [Forbes](https://www.forbes.com/advisor/business/credit-card-processing-fees/), it's obvious why payments are a no-brainer for an area that can be improved.
Looking back to a primary reason of [Satoshi](https://medium.com/bitcoin-blockchain-explained/why-was-bitcoin-created-20ab3a65d952) creating Bitcoin, a key idea was to eliminate intermediaries involved in the transfer of currency. As that vision has expanded to include daily activities, from regular commerce, e-commerce, splitting a bill with friends, and even reconciling payroll, Solana has been working on tech to help. By integrating a tool like [Solana Pay](https://solana.com/solutions/payments-and-commerce) developers can make it simple for businesses, both online and in-person, to accept payment in the form of cryptocurrency where instantaneous transfers of funds happen at the fraction of a cent.
This not only means no wait times on when you get your money as a merchant or user, but also at, ideally, lower prices since transaction fees do not have to be baked into a merchant's bottom line. To take it a step further, businesses can even offer rewards or receipts in the form of SPL-Tokens (more on this later) making it easier to engage with their consumers outside of the standard method of spam email.
### Gaming
When you consider what a NFT (Non-Fungible Token) actually is, you realize the concept has been around longer than most blockchains. By definition, a [NFT](https://www.investopedia.com/non-fungible-tokens-nft-5115211#toc-what-is-a-non-fungible-token-nft) is a non-fungible asset tokenized on a blockchain. The value of a NFT is non-existant outside of the ecosystem in which it resides. Meaning, I can not buy groceries in exchange for my [MadLad](https://www.madlads.com/gallery) NFT because the only place to exchange this NFT is within the Solana ecosystem.
Now, take this concept of NFTs and let's compare it to [micro transactions](https://en.wikipedia.org/wiki/Microtransaction) inside video games. In 2009 FIFA, the globally renowned Soccer Game by EA Sports, introduced it's Ultimate Team game mode where users could buy in-game currency to purchase packs of players to assign to their team. By doing so, users were essentially purchasing non-fungible assets because outside of FIFA they had no value. Fast-forward nearly a decade later and micro-transactions have become a primary revenue tool for games, with Fortnite reporting [$1 billion](https://www.sciencedirect.com/science/article/abs/pii/S0306460319310585#:~:text=Fortnite%20has%20generated%20more%20than,2017%20(Henry%2C%202018)) USD in micro transactions within the first 3 years of it's release (2017-2020), and in 2023 alone, FIFA had reported a peak of [$4.3 billion USD](https://www.tbsnews.net/features/play-pay-how-microtransactions-took-over-gaming-712234) in revenue from the same method.
Now, how can this be improved with Solana? Well, this answer is multi-faceted, but we'll focus on two primary areas:
- in-game assets
- in-game currency
Currently, if you purchase player in a game like FIFA23 or a character skin in Fortnite, that is the only place you can use that item. Even if you purchase FIFA24 the following year, that player no longer resides on your Ultimate Team unless you repurchase them. With Blockchain gaming it is possible to extend an item's use case beyond just the current game's setting. In [Star Atlas](https://chainplay.gg/games/star-atlas/) (more on them later) an in-game item like a Ship is stored as an NFT unique to the player's wallet. This means the asset is theirs to keep or trade for cryptocurrency. The asset could even be used outside of the Star Atlas setting, for example, if someone wanted to build a Solana rendition of Flappy Bird they could use an existing NFT in the player's wallet as the character (i.e. the Star Atlas ship).
Same thing goes for in-game currency. If I spend $10 on FIFA23 it is a sunk cost. It is locked in on that game edition, anything unspent does not carry over to FIFA24 and there is no way to turn those items purchased back into cash. With Solana you can implement (or create) any SPL-Token as a form of in-game currency. This further extends the capabilities for game makers on how to use them. For example, a game could not only utilize $BONK as a form of in-game currency, but as reward for level completion as well. Doing so would allow the player to earn a reward that has use beyond the game to do things like [purchase NFTs](https://bravenewcoin.com/insights/bonk-coin#:~:text=The%20coin%20is%20accepted%20as,as%20Coinbase%2C%20Binance%20and%20Serum).
### Music & Art Distribution
Music and Art have always faced an issue of needing an intermediary to distribute their product to consumers. Typically these intermediaries are in the form of auction houses or streaming platforms, and often result in them taking a cut of the profits. To twist the knife further, when a piece of art is resold the artist, generally, does not see any profits from the secondary sale.
With Solana, artists and muscians have the power to distribute their art/music to the masses at a minimal costs. From compressed NFTs, enforced royalties, and even crowd funding, control is put back into the hands of the creator. By utilizing compression on Solana, an artist can create [1 million NFTs for ~$110](https://solana.com/news/state-compression-compressed-nfts-solana), then distribute them at a fraction of a cent. This is one of the many reasons Solana is emerging as the [top chains](https://cryptonews.com/news/solana-surpasses-ethereum-in-7-day-stablecoin-trading-volume-for-first-time-ever.htm) for art.
### DeFi
Financial systems have typically been limited to standard business hours, geopolitical locations, and control by a central entity. Thus, giving decentralized blockchains a [large advantage](https://www.theblock.co/learn/245713/why-is-defi-so-important) by implementing their own versions of financial systems (referred to as DeFi) that operate 24/7 for anyone with internet access. Originally, Ethereum was the go to layer for DeFi, but with low fees and high throughput Solana is beginnning to see a large up-tick in usage, seeing north of [200k unique addresses trading](https://dune.com/summit/solana-defi) in the last 30-days alone.
### Restaurant Operations
Restaurants rely on a variety of systems to manage food orders, inventory, employees, and payroll. Most small and medium sized restaurants rely on services like Toast or Square to manage their business. These services can quickly eat away at the bottom line. According to [Toast](https://pos.toasttab.com/blog/on-the-line/restaurant-management-statistics), restauranteurs regard payment processing, inventory management, and accounting as the most significant integrations in their POS (point of sale system).
With Solana a restaurant could not only lower the cost on the aforementioned areas, but they could also enhance their digital reach to customers through loyalty programs like [Boba Guys](https://www.cryptotimes.io/boba-guys-introduces-innovative-loyalty-program-using-solana/) is currently doing.
### Real Estate Investing
Real Estate has been a way to generate wealth for decades, however it has always required a large share of capital to begin. Another issue is the data surrounding Real Estate markets is proprietary and limited in access, making it hard to make smart decisions for someone looking to invest.
Solana is changing this. With companies like [HomebaseDAO](https://www.homebasedao.io/) tokenizing Real Estate properties, and [Parcl](https://www.parcl.co/) giving users a DeFi approach to investing, the landscape of Real Estate is changing giving the average person the ability to become involved.
### Physical Infrastructure Networks (DePin)
Physical infrastructure has long been a top-down model with decisions being made by a board of appointees who often don't resemble the average user. From telecom companies, ridesharing apps, ticketing platforms, to even mapping companies like Google and Waze, the user's best interest has never been the ultimate priority.
By building physical infrastructure on Solana (referred to as DePin) [decision making is spread out in a decentralized manner](https://medium.com/@rkmonarch/how-depin-projects-are-leveraging-solana-to-build-the-decentralized-physical-network-c129db29db48), thus increasing adaptability. This also allows projects to grow their user base through customer-first decisions and token incentives with out worrying about scalability issues. Ultimately this leads to win-win scenarios for DePin projects by increasing their data gathering and network availabilty, but also rewarding their users for it.
---
## Current projects within these areas
### Payments
Between the rise of e-commerce and consumer's [increasing preference](https://www.frbsf.org/cash/publications/fed-notes/2023/may/2023-findings-from-the-diary-of-consumer-payment-choice/) for digital payments coupled with Solana's high-throughput and low-fees, digital payment projects have been seeing tremendous success within the space. Two notable projects causing disruption are:
**[Helio](https://www.hel.io/) -** makes it simple for anyone looking to integrate Solana as a crypto payment option. With their platform you can create pre-built checkouts or embeddable payment options for games and content. They have also made it possible to quickly set up payment subscriptions for SaaS products and paywalls for video content. Even for those already operating a Shopify store, Helio has made it simple to [integrate crypto payments](https://docs.hel.io/product-guides/e-commerce) as a plugin.
**[TipLink](https://tiplink.io/) -** has made sending crypto and digital assets, like NFTs, as simple as sending a link. By design the TipLink is a lightweight wallet, making crypto accessible to everyone. Their process is simple and similar to buying and sending a digital giftcard. When the user receives a TipLink they are able to access the funds by simply logging in with their email, from there they are able to offload the funds/assets to a stand-alone wallet if they choose to.
### Gaming
High transaction through-put, low-fees, and scalability are essential in building Web3 games, anything other than that can hinder a game's performance and decrease the ability for users to play. By creating games with blockchain mechanics players have the ability to own their in-game assets and utilize them outside of just the game itself. Web3 gaming is becoming larger year over year with play-to-earn being an attractive model, here are a few games on Solana that are pushing this trend:
**[Star Atlas](https://staratlas.com/) -** a massively multiplayer online (MMO) play-to-earn game built with Solana mechanics to track everything on-chain. Star Atlas allows users to buy/sell in-game assets on their [NFT marketplace](https://play.staratlas.com/market/) for USDC as well as trade their in-game currencies (ATLAS & POLIS) on [decentralized exchanges](https://jup.ag/swap/USDC-ATLAS). A large reason Star Atlas chose to build on Solana is for it's scalability [stating](https://solana.com/developers/gaming), "We just needed a scalable tech because we anticipate having millions to potentially billions of users interacting with the metaverse at some point. High transaction throughput, low transaction costs, the sub-second finality which translates to low latency for our players on speed changes, all important."
**[Genopets](https://www.genopets.me/) -** a [move-to-earn game](https://whitepaper.genopets.me/overview/rewarding-an-active-lifestyle) that aims to reward players for an active lifestyle. Each Genopet is a NFT that can be traded on [NFT marketplaces](https://magiceden.io/marketplace/genopets). Over time, your Genopet can evolve by converting the steps you take in real life into in-game Energy and XP. Players also have the ability to earn tokens as well (KI & GENE) that can be traded on [decentralized exchanges](https://jup.ag/swap/USDC-GENE) for USDC.
**[Aurory](https://aurory.io/) -** a [Web3 gaming studio](https://docs.aurory.io/aurory-whitepaper/) with two games currently live: [Seekers of Tokane](https://app.aurory.io/seekers-of-tokane), an immersive RPG, and [Aurory Tactics](https://app.aurory.io/), a PvP battle arena. Aurory aims to take a unique approach to in-game currency by using a single token system ([AURY](https://jup.ag/swap/USDC-AURY)) to manage in-game currency and rewards. Aurory has also created a [marketplace](https://app.aurory.io/marketplace) that allows users to buy and sell their NFT collectibles that are used in-game.
### Music & Art Distribution
The music industry has seen a significant rise in the digital world with all-time highs being reached in the first six-months of 2023. Streaming alone accounted for [84% of the $8.4 billion pie](https://www.theverge.com/2023/9/18/23878916/riaa-music-industry-revenue-all-time-high-first-half-2023). In 2022 the [global art market grew 3%](https://www.artbasel.com/stories/key-findings-art-market-report-2023?lang=en) to ~$67.8 billion, reaching it's second highest level to date, with ~$11 billion being from online sales. Musicians and artists often losing a percent of profits to the platforms distributing their creations to the masses and sometimes all of it on secondary markets. Here are a few of the projects aiming to give control back to creators with the help of Solana:
**[Audius](https://audius.org/) -** is a music platform owned and operated by it's users. Acting similarly to a DAO, decisions are voted on and governed by stakers of their platforms token ([AUDIO](https://jup.ag/swap/USDC-AUDIO)). Artists are able to generate immutable, time-stamped records of their work and create gated content for their audience.
**[Nina](https://www.ninaprotocol.com/) -** a self-publishing protocol for Musicians where artists receive 100% of the profit from their music. With the goal of providing maximum value to artists and their fans, Nina utilizes decentralized file-distribution, tokenized ownership, and transparent royalty payments as a way to [circumvent the current status-quo of the Music industry](https://solana.com/ecosystem/nina).
**[Drip Haus](https://drip.haus/) -** a digital art platform that aims to make it affordable to mint and distribute art to large audiences using compression on Solana. By enabling artists to reach the masses at an affordable price, Drip Haus aims to foster a community of both appreciation and collaboration. In just 6 months, the top creator received [$80,000](https://twitter.com/drip/status/1724475756284182623) in SOL-denominated tips, proving that ease of access and vast distribution is beneficial to all digital artists.
**[Exchange Art](https://exchange.art/) -** is aiming to be the leading fine art marketplace where independent creators can auction 1/1 art and increase their status on the platform through sales. Their goal is to expand beyond the Solana ecosystem and onto other chains, allowing artists a central hub to provide access to enthusiasts.
### DeFi
**[Kamino](https://app.kamino.finance/) -** allows users to earn yield by contributing to vaults that provide concentrated liquidity. These vaults use [quantitative models to set and rebalance ranges](https://solana.com/ecosystem/kaminofinance), while auto-compounding fees and rewards for users.
**[Jito](https://www.jito.wtf/) -** is a project focused on MEV infrastructure and minimizing the negative effects of MEV on users while maximizing MEV returned to stakers. Their goal is to enhance performance and efficiency on Solana while maximizing validator rewards.
**[Jupiter](https://jup.ag/) -** is a liquidity aggregrator that allows users to swap the widest range of tokens on Solana. By identifying the best route for swaps with one of the cleanest user interfaces, Jupiter aims to be the go to aggregrator for users and developers looking to intergrate swaps on their applications or on-chain programs.
### Restaurant Operations
**[Boba Guys](https://www.bobaguys.com/passport) -** the popular bubble tea brand has partnered with [Hang](https://www.hang.com/), a company focused on reshaping customer loyalty. Their loyalty passport provides a way for customers to earn points that can upgrade and unlock [specialized mystery boxes](https://medium.com/hang-xyz/introducing-boba-guys-passport-a-new-loyalty-program-powered-by-hang-2fe5f9ef923e).
**[Devour](https://devour.io/) -** is gamifying the hospitality industry by utilizing Solana's compressed NFTs to create an overlap with the food and gaming industry. When a user orders food using their [DevourGO](https://apps.apple.com/us/app/devourgo-food-delivery/id6448696874) app they can earn in-game rewards or virtual assets.
### Real Estate Investing
**[Parcl](https://app.parcl.co/) -** is a platform that allows user to invest in the digital square footage of physical real-estate across different markets. They are utilizing live-data to create a perpetuals market where users can long or short the value of real-estate. One of the primary goals of their [ParclLabs](https://www.parcllabs.com/) side is to generate the single source truth for real-estate data and allow developers to build off their API's.
**[HomebaseDAO](https://www.homebasedao.io/) -** allows users to invest in real-estate through tokenization and fractional ownership. By creating fractional shares of real-estate on Solana users and investors have transparent and traceable sources of data for all available homes, allowing them to make better informed decisons around investing.
### Physical Infrastructure Networks (DePIN)
**[Helium](https://www.helium.com/) -** is changing the mobile telecom industry by allowing users to host hotspots, enhancing their 5g service in areas, in exchange for their token ([MOBILE](https://jup.ag/swap/USDC-MOBILE)). By incentivizing hotspot holders, Helium's COO [Scott Sigel](https://solana.com/news/case-study-helium) believes users can "create the coverage you want to see in the world."
**[Hivemapper](https://hivemapper.com/explorer) -** uses data gathered from user's dashcams to create maps while rewarding them with the company's token ([HONEY](https://solana.com/news/case-study-helium)). With this fundamental shift in how mapping occurs, the company is creating real-time up to date maps where the users are the builders.
**[Teleport](https://www.teleport.xyz/) -** has the goal of giving control back to drivers and users in the ridesharing industry. By creating a protocol where the [governance is controlled by TRIP holders](https://www.trip.dev/), Teleport is aiming to keep decisions in the hand of the most active users.
**[Render Network](https://rendernetwork.com/) -** is a decentralized GPU rendering platform where users can contribute unused GPU power to render motion graphics in exchange for their token ([RENDER](https://jup.ag/swap/USDC-RENDER)). By offering idle GPU on the Solana blockchain, artists have the ability to [create next generation rendering](https://rendernetwork.com/#about) at the fraction of the cost compared to a centralized GPU cloud.
---
# For Developers
## What tools are available to begin building in these areas?
Now that we have taken a look at each industry and some of the current projects having success, let's dive into where you can get started as a developer. Here we will look at a variety of things from starter code to tools to help you get building on the next greatest dapp on Solana.
---
### Payments
The `@solana/pay` sdk makes it easy to set up your build to accept payment, all you need to know is Typescript or Javascript. This walkthrough will show you how to set up a simple Next.JS/React app that will allow you to accept payment in SOL via QR Code. This build can be implemented for IRL stores or for your online store, allowing customers to pay without having to connect their wallet.
The flow will be simple:
1. Create UI with Next.JS/React
2. Set up Front-End to display products
3. Render QR code on Front-End
#### Create UI with Next.JS/React
First thing we will want to do is open up our terminal and run the command:
```
npx create-next-app@latest helius-solana-pay
```
This will prompt you with the following 6 questions, I recommend just answering with the default answer provided:
```
Need to install the following packages:
create-next-app@14.0.4
Ok to proceed? (y) y
✔ Would you like to use TypeScript? … No / Yes
✔ Would you like to use ESLint? … No / Yes
✔ Would you like to use Tailwind CSS? … No / Yes
✔ Would you like to use `src/` directory? … No / Yes
✔ Would you like to use App Router? (recommended) … No / Yes
✔ Would you like to customize the default import alias (@/*)? … No / Yes
```
After you see the `Success!` in your terminal, go ahead and `cd helius-solana-pay` and open up the code in your IDL.
Before we get building, let's add a couple more dependencies that will make the magic happen.
```
npm install @solana/web3.js @solana/pay
```
When you run `npm run dev` this will run your code on localhost, open up your browser and head to: `http://localhost:3000/`, you should see this:

Awesome, let's get building.
#### Set up Front-End to display products
Open up your `page.tsx` in `src/app` directory and lets delete everything, then replace it with:
```typescript=
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<div className="flex flex-col">
<h1 className="text-4xl font-mono font-bold text-center text-orange-600">
Helius 'R' Us
</h1>
<h3 className="text-2xl font-mono text-center text-orange-600">
Toy Store
</h3>
<div className="mt-32 mb-32 flex flex-row text-center gap-4 justify-center">
<div>
Product A
</div>
<div>
Product B
</div>
</div>
<button className="border-2 border-black">
Checkout
</button>
</div>
</main>
)
}
```
What we are doing here is creating a generic layout for our store where we will map out our products and display a checkout button that redirects our user to a checkout page. When you run `npm run dev` you should see this:

Before we get started, let's also edit the `next.config.js` to accomodate our product images that we will be using. You can replace what is in there with:
```javascript=
module.exports = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'black-historic-harrier-585.mypinata.cloud',
port: '',
pathname: '/ipfs/**',
},
],
},
}
```
Don't worry too much about the hostname, it's the IPFS gateway where I uploaded images for this build.
Cool, now let's actually make some products to display here. At the root of your app lets create a directory called `components` and `lib`.
Inside the `components` directory create a file named `Products.tsx` and a file named `NumberInput.tsx`. Then, inside of your `lib` directory create a file named `products.ts` and one named `calculatePrice.ts`.
Your file structure should now look like this:

Next, let's open up your `products.ts` and create some products that we'll map out in the `Products` component. We'll be giving each product an id, name, description, imageUrl, and a price in SOL.
```typescript=
export const products = [
{
id: 'bear',
name: 'Mert Bear',
description: 'A stuffed and snuggly Mert Bear',
imageUrl: 'https://black-historic-harrier-585.mypinata.cloud/ipfs/QmZbv3bmXUYiqmf4k85t9H1sr2roedzLN4sLxPrqkgZgwU?_gl=1*1dsyikz*_ga*OTY0MTU1NTY1LjE3MDQ4ODA5NTA.*_ga_5RMPXG14TE*MTcwNDg4MDk0OS4xLjEuMTcwNDg4MTA3NC4yNC4wLjA.',
priceSol: 0.05,
},
{
id: 'crab',
name: 'Mert Crab',
description: 'A glass chewing Mert Crab',
imageUrl: 'https://black-historic-harrier-585.mypinata.cloud/ipfs/QmWQeDAT8RDVH4JbaZMngMEZb5rMAEbhtH9hAZHGzEJiwE?_gl=1*1giwok2*_ga*OTY0MTU1NTY1LjE3MDQ4ODA5NTA.*_ga_5RMPXG14TE*MTcwNDg4MDk0OS4xLjEuMTcwNDg4MTA4NC4xNC4wLjA.',
priceSol: 0.1,
}
]
```
Next, let's open up `calculatePrice.ts` and insert the following:
```typescript=
import BigNumber from "bignumber.js";
import { ParsedUrlQuery } from "querystring";
import { products } from "./products";
export default function calculatePrice(query: ParsedUrlQuery): BigNumber {
let amount = new BigNumber(0);
for (let [id, quantity] of Object.entries(query)) {
const product = products.find(p => p.id === id)
if (!product) continue;
const price = product.priceSol
const productQuantity = new BigNumber(quantity as string)
amount = amount.plus(productQuantity.multipliedBy(price))
}
return amount
}
```
This is simply going to calculate our price and return the total amount that we will then pass into our checkout.
Once we have these to files filled, let's head over to `/components/NumberInput.tsx` and inject the following code:
```typescript=
import { useState } from "react"
interface Props {
name: string,
formRef: React.RefObject<HTMLFormElement>,
}
export default function NumberInput({ name, formRef }: Props) {
const [number, setNumber] = useState(0)
function decrement() {
setNumber(n => n > 0 ? n - 1 : 0)
}
function increment() {
setNumber(n => n + 1)
}
function handleKeyboard(e: React.KeyboardEvent<HTMLButtonElement>) {
if (e.key === "ArrowDown") {
e.preventDefault();
decrement();
}
if (e.key === "ArrowUp") {
e.preventDefault();
increment();
}
if (e.key === "Enter") {
e.preventDefault();
formRef.current?.submit();
}
}
return (
<div
className="w-36 border-2 border-gray-200 rounded-md flex flex-row items-center"
>
<button
type="button"
tabIndex={-1}
className="basis-1/3 focus:outline-none"
onClick={decrement}
onKeyDown={handleKeyboard}
>
<span className="m-auto text-2xl font-thin">−</span>
</button>
<input
type="number"
name={name}
value={number}
onChange={e => setNumber(Number(e.target.value))}
min={0}
className="w-12 border-none focus:ring-0 text-center bg-gray-200"
/>
<button
type="button"
tabIndex={-1}
className="basis-1/3 focus:outline-none"
onClick={increment}
onKeyDown={handleKeyboard}
>
<span className="m-auto text-2xl font-thin">+</span>
</button>
</div>
)
}
```
This component is responsible for increasing and decreasing the quantity on our product selection.
Now, let's complete our `Products.tsx` component with the following code:
```typescript=
import Image from "next/image";
import { useRef } from "react";
import { products } from "../lib/products"
import NumberInput from "./NumberInput";
interface Props {
submitTarget: string;
enabled: boolean;
}
export default function Products({ submitTarget, enabled }: Props) {
const formRef = useRef<HTMLFormElement>(null);
return (
<form method='get' action={submitTarget} ref={formRef}>
<div className='flex flex-col' style={{ alignItems: 'center' }}>
<div className='flex flex-row' style={{ marginBottom: '60px', gap: '40px' }}>
{products.map(product => {
return (
<div key={product.id}>
<Image src={product.imageUrl} width={200} height={200} alt={product.name}/>
<h3 className="text-2xl font-bold">{product.name}</h3>
<p className="text-sm text-gray-800">{product.description}</p>
<p className="my-4">
<span className="mt-4 text-xl font-bold">{product.priceSol} SOL</span>
</p>
<div className="mt-1">
<NumberInput name={product.id} formRef={formRef} />
</div>
</div>
)
})}
</div>
<button
style={{backgroundColor: '#FCA310', width: 'fit-content', padding: '10px', borderRadius: '10px', color: 'white'}}
disabled={!enabled}
>
Checkout
</button>
</div>
</form>
)
}
```
What we are doing here is mapping out our products along with a number input for each. All of this is taking place within a form that we will then attach to a `HTMLFormElement` which will allow us to pass the info of our selected items to the checkout where our QR code will be generated.
When you run `npm run dev` you should now see the following:

Nice! Now we have our store displaying our [0xMert](https://twitter.com/0xMert_) stuffed animals. At this point, you will see the number input works, but when you click checkout it brings you to a page with a `404 | This page could not be found.`, don't worry that is to be expected since we have not built that page yet.
#### Render QR code on Front-End
Now let's set up the checkout page. Create a `pages` directory at the root level and inside of it create a file and name it `checkout.tsx`. Once created, let's input the following code (make sure to read the comments):
```typescript=
import Link from "next/link";
import { createQR, encodeURL, TransferRequestURLFields, findReference, validateTransfer, FindReferenceError, ValidateTransferError } from "@solana/pay";
import { Connection, Keypair, PublicKey, clusterApiUrl } from "@solana/web3.js";
import { useRouter } from "next/router";
import { useEffect, useMemo, useRef } from "react";
import calculatePrice from "../lib/calculatePrice";
export default function Checkout() {
const router = useRouter()
const shopAddress = new PublicKey('7wK3jPMYjpZHZAghjersW6hBNMgi9VAGr75AhYRqR2n')
// ref to a div where we'll show the QR code
const qrRef = useRef<HTMLDivElement>(null)
const amount = useMemo(() => calculatePrice(router.query), [router.query])
// Read the URL query (which includes our chosen products)
const searchParams = new URLSearchParams();
for (const [key, value] of Object.entries(router.query)) {
if (value) {
if (Array.isArray(value)) {
for (const v of value) {
searchParams.append(key, v);
}
} else {
searchParams.append(key, value);
}
}
}
// Generate the unique reference which will be used for this transaction
const reference = useMemo(() => Keypair.generate().publicKey, []);
// Add it to the params we'll pass to the API
searchParams.append('reference', reference.toString());
// Get a connection to Solana devnet
const endpoint = clusterApiUrl('devnet')
const connection = new Connection(endpoint, 'confirmed')
// Solana Pay transfer params
const urlParams: TransferRequestURLFields = {
recipient: shopAddress,
amount,
reference,
label: "Helius Toys R Us",
message: "🦀 Thanks for your order! 🧸",
}
// Show the QR code
useEffect(() => {
const solanaUrl = encodeURL(urlParams)
const qr = createQR(solanaUrl, 512, 'transparent')
if (qrRef.current && amount.isGreaterThan(0)) {
qrRef.current.innerHTML = ''
qr.append(qrRef.current)
}
})
// Check every 0.5s if the transaction is completed
useEffect(() => {
const interval = setInterval(async () => {
try {
// Check if there is any transaction for the reference
const signatureInfo = await findReference(connection, reference, { finality: 'confirmed' })
// Validate that the transaction has the expected recipient, amount and SPL token
await validateTransfer(
connection,
signatureInfo.signature,
{
recipient: shopAddress,
amount,
reference,
},
{ commitment: 'confirmed' }
)
alert(`Transaction confirmed! Link: https://solscan.io/tx/${signatureInfo.signature}?cluster=devnet`)
router.push('/')
} catch (e) {
if (e instanceof FindReferenceError) {
// No transaction found yet, ignore this error
return;
}
if (e instanceof ValidateTransferError) {
// Transaction is invalid
console.error('Transaction is invalid', e)
return;
}
console.error('Unknown error', e)
}
}, 500)
return () => {
clearInterval(interval)
}
}, [amount])
return (
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px', height: '100vh', justifyContent: 'center', background: 'linear-gradient(180deg, #FFFFFF 0%, #E5E5E5 100%)'}}>
<Link href='/'style={{ color: 'blue', textDecoration: 'underline', cursor: 'pointer', fontSize: '24px' }}>
Cancel
</Link>
<h1 className="text-2xl font-bold">Checkout {amount.toString()} SOL</h1>
{/* div added to display the QR code */}
<div ref={qrRef} />
</div>
)
}
```
What we are doing here is taking the checkout info from the URL and attaching the: recipient, total amount, reference (unique publickey generated for this txn), label, and message. We then use `@solana/pay` SDK's to `encodeURL`, `createQR`, and append it URL to it.
Because we are just sending SOL we can do all of this on our front-end. However, if you wanted to send another SPL-Token like USDC or BONK then you would need to send the data to the back end and locate the proper Associated Token Addresses for the buyer/recipient, serialize the data, and then send it to the wallet for approval. If this is something you are interested in doing, check the end of this section for more walkthroughs.
Once we generate the transaction we begin searching for the reference with a 'confirmed' finality, this tells us that the transaction has been sent and we can then grab the signature to verify it has been completed.

If done correctly, you should have a scannable QR code on your checkout page that generates a transaction in your mobile wallet that appears as:

Congrats, you've just covered the basics of Solana Pay and can create your own IRL lemonade stand! Big shoutout to [Callum McIntyre](https://twitter.com/callum_codes), a lot of the code snippets are based on his open-source work.
If you need to reference the completed code you can find it [here](https://github.com/maweiche/helius_solana_pay).
#### Other Tutorials and Docs
- [Create a Store to send USDC](https://www.pointer.gg/tutorials/solana-pay-irl-payments/944eba7e-82c6-4527-b55c-5411cdf63b23)
- [Issue Receipts and Loyalty cNFTs using Solana Pay](https://hackmd.io/@maweiche/solana-pay-cnft)
- [Generate and Send TipLinks](https://docs.tiplink.io/docs/getting-started/overview)
- [Integrate Helio Payment into your App](https://docs.hel.io/developers/helio-api-key)
---
### Gaming
[0xIchigo](https://twitter.com/0xIchigo) has written a great in-depth blog post for developers looking to start building games on Solana [here](https://www.helius.dev/blog/gaming-how-solana-is-changing-the-playfield). I recommend checking it out for a complete overview on where to start.
Here, we will demonstrate how to build [Tiny Adventure 2](https://beta.solpg.io/tutorials/tiny-adventure-two) locally and execute tests to verify it's functionality. This Solana program was designed by [Jonas Hahn](https://twitter.com/SolPlay_jonas) and demonstrates a key component of Web3 gaming and that's earning tokens from Loot Boxes.
We'll be using Anchor to build and test this program, it works in conjuction with Cargo to bundle and deploy Rust programs on Solana.
First lets create an achor program locally, head to your terminal and execute:
```
anchor init game
cd game
```
Once created you should have a file structure like this:

If you navigate to `/programs/game` this is where our program logic is. First let's edit the `Cargo.toml` within this directory. You will want to update the `[dependencies]` to the following:
```
[dependencies]
solana-program = "1.16.1"
anchor-lang = {version = "0.26.0", features = ["init-if-needed"]}
```
Now let's open up `/src/lib.rs` you can see the generic program that anchor has created for you. This is where we will start, go ahead and remove all of the contents and replace it with the following:
```rust=
use anchor_lang::prelude::*;
use anchor_lang::solana_program::native_token::LAMPORTS_PER_SOL;
use anchor_lang::system_program;
// This is your program's public key and it will update
// automatically when you build the project.
declare_id!("");
#[program]
mod game {
use super::*;
#[error_code]
pub enum MyError {
#[msg("Password was wrong")]
WrongPassword,
}
// The amount of lamports that will be put into chests and given out as rewards.
const CHEST_REWARD: u64 = LAMPORTS_PER_SOL / 10; // 0.1 SOL
// This is the entry point for the initialize level one instruction
pub fn initialize_level_one(_ctx: Context<InitializeLevelOne>) -> Result<()> {
// Usually in your production code you would not print lots of text because it cost compute units.
msg!("A Journey Begins!");
msg!("o.......💎");
Ok(())
}
// this will the the player position of the given level back to 0 and fill up the chest with sol
pub fn reset_level_and_spawn_chest(ctx: Context<SpawnChest>) -> Result<()> {
ctx.accounts.game_data_account.player_position = 0;
let cpi_context = CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.payer.to_account_info().clone(),
to: ctx.accounts.chest_vault.to_account_info().clone(),
},
);
system_program::transfer(cpi_context, CHEST_REWARD)?;
msg!("Level Reset and Chest Spawned at position 3");
Ok(())
}
// this will move the player to the right and check if the player has reached the chest,
// it uses a password as a safety measure to make sure the signer has authority to move the player
pub fn move_right(ctx: Context<MoveRight>, password: String ) -> Result<()> {
let game_data_account = &mut ctx.accounts.game_data_account;
if game_data_account.player_position == 3 {
msg!("You have reached the end! Super!");
} else if game_data_account.player_position == 2 {
game_data_account.player_position = game_data_account.player_position + 1;
// Check if the password is correct
if password != "gib" {
return err!(MyError::WrongPassword);
}
msg!(
"You made it! Here is your reward {0} lamports",
CHEST_REWARD
);
// Transfer the reward to the player
**ctx
.accounts
.chest_vault
.to_account_info()
.try_borrow_mut_lamports()? -= CHEST_REWARD;
**ctx
.accounts
.player
.to_account_info()
.try_borrow_mut_lamports()? += CHEST_REWARD;
} else {
game_data_account.player_position = game_data_account.player_position + 1;
print_player(game_data_account.player_position);
}
Ok(())
}
}
// This is a helper function to print the player position within the on-chain transaction
fn print_player(player_position: u8) {
if player_position == 0 {
msg!("A Journey Begins!");
msg!("o.........💎");
} else if player_position == 1 {
msg!("..o.......💎");
} else if player_position == 2 {
msg!("....o.....💎");
} else if player_position == 3 {
msg!("........\\o/💎");
msg!("..........\\o/");
msg!("You have reached the end! Super!");
}
}
#[derive(Accounts)]
pub struct InitializeLevelOne<'info> {
// We must specify the space in order to initialize an account.
// First 8 bytes are default account discriminator,
// next 1 byte come from NewAccount.data being type u8.
// (u8 = 8 bits unsigned integer = 1 byte)
// You can also use the signer as seed [signer.key().as_ref()],
#[account(
init_if_needed,
seeds = [b"level1"],
bump,
payer = signer,
space = 8 + 1
)]
pub new_game_data_account: Account<'info, GameDataAccount>,
// This is the PDA in which we will deposit the reward SOl and
// from where we send it back to the first player reaching the chest.
#[account(
init_if_needed,
seeds = [b"chestVault"],
bump,
payer = signer,
space = 8 + 8
)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct SpawnChest<'info> {
#[account(mut)]
pub payer: Signer<'info>,
#[account(mut, seeds = [b"chestVault"], bump)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub game_data_account: Account<'info, GameDataAccount>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct MoveRight<'info> {
#[account(mut, seeds = [b"chestVault"], bump)]
pub chest_vault: Account<'info, ChestVaultAccount>,
#[account(mut)]
pub game_data_account: Account<'info, GameDataAccount>,
#[account(mut)]
pub player: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct GameDataAccount {
player_position: u8,
}
#[account]
pub struct ChestVaultAccount {
pub chest_reward: u64,
}
```
Without diving deep into the nuances of Rust, this is what is taking place.
First we have the function `initialize_level_one`, this is creating 2 PDA's: `GameDataAccount` and `ChestVaultAccount`. The program is using a `password` as a security measure to ensure the signer has the authority to call the `move_right` function. When the player executes `move_right` three times they reach the lootbox and win the funds from the `ChestVaultAccount`. The function `reset_level_and_spawn_chest` resets the user position back to 0 and requires them to refill the lootbox.
The next thing we want to do is build the program with the following command (this will take a few minutes the first time):
```
anchor build
```
Once that completes, you should see a `Finished` message, if not then check the erorr messages provided and resolve them. Now, open a second tab inside of your terminal and let's set our solana cluster to localhost:
```
solana config set --url localhost
```
This is where we want to first deploy and test our program to make sure everything works without worrying about Devnet funds. Typically, you want to start with Localnet and verify everything works. Then, you should deploy to Devnet to begin building your Front-End before finally paying real money to deploy on Solana Mainnet.
Now, back in the other terminal window lets run:
```
anchor deploy
```
Once complete you will see the deployed program id, we need to copy this and update it in 2 spots: `/src/lib.rs` & `Anchor.toml`

```rust=7
declare_id!("3L9CnAEGvcy3zhToKb3W3frSgRJrnzfEftczEX7AUtMQ");
```
```rust=4
[programs.localnet]
game = "3L9CnAEGvcy3zhToKb3W3frSgRJrnzfEftczEX7AUtMQ"
```
Now, run `anchor build` and `anchor deploy` for the necessary files to update with the deployed program id. Any time you make changes to your program, you will need to run those 2 commands for the edits to take place within the `/target/idl/game.json` (used for front-end integration) and on-chain.
Congrats, you just created a game in Rust. Now let's test it. Open up the `game.ts` located in the `tests` directory. Anchor installs [mocha](https://www.npmjs.com/package/mocha) for testing and provides a base structure to work from. Let's go ahead and replace everything in their with the following
```typescript=
import * as anchor from "@project-serum/anchor";
import * as assert from "assert";
import { Program } from "@project-serum/anchor";
import { Game } from "../target/types/game";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
describe("game", () => {
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Game as Program<Game>;
const wallet = anchor.workspace.Game.provider.wallet.payer;
const [gameDataAccount] = anchor.web3.PublicKey.findProgramAddressSync(
[
Buffer.from("level1")
],
program.programId
)
console.log("gameDataAccount", gameDataAccount.toBase58())
const [chestVaultAccount] = anchor.web3.PublicKey.findProgramAddressSync(
[
Buffer.from("chestVault")
],
program.programId
)
console.log("chestVaultAccount", chestVaultAccount.toBase58())
it("Is initialized!", async () => {
await program.methods.initializeLevelOne()
.accounts({
newGameDataAccount: gameDataAccount,
chestVault: chestVaultAccount,
signer: wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// get the balance of the chest vault
const chestVaultAccountInfo = await program.provider.connection.getAccountInfo(chestVaultAccount);
const chestVaultAccountBalance = chestVaultAccountInfo.lamports;
console.log("chestVaultAccountBalance", chestVaultAccountBalance)
const expected_balance = 1002240;
// Chest Vault Balance should just be rent
assert.strictEqual(chestVaultAccountBalance, expected_balance);
});
it("Level Reset and Chest Vault filled", async () => {
await program.methods.resetLevelAndSpawnChest()
.accounts({
payer: wallet.publicKey,
chestVault: chestVaultAccount,
gameDataAccount: gameDataAccount,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// get the balance of the chest vault
const chestVaultAccountInfo = await program.provider.connection.getAccountInfo(chestVaultAccount);
const chestVaultAccountBalance = chestVaultAccountInfo.lamports;
// get the playerPosition from the gameDataAccount
const gameDataAccountInfo = await program.provider.connection.getAccountInfo(gameDataAccount);
const gameDataAccountData = gameDataAccountInfo.data;
const info = program.coder.accounts.decode("GameDataAccount", gameDataAccountData);
const playerPosition = info.playerPosition;
const expected_reward = 1002240 + (LAMPORTS_PER_SOL / 10);
// Chest Vault Balance should be rent + reward, and player should be at position 0
assert.strictEqual(chestVaultAccountBalance, expected_reward);
assert.strictEqual(playerPosition, 0);
});
it("Player Moves Right Once", async () => {
await program.methods.moveRight("gib")
.accounts({
chestVault: chestVaultAccount,
gameDataAccount: gameDataAccount,
player: wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// get the balance of the chest vault
const chestVaultAccountInfo = await program.provider.connection.getAccountInfo(chestVaultAccount);
const chestVaultAccountBalance = chestVaultAccountInfo.lamports;
// get the playerPosition from the gameDataAccount
const gameDataAccountInfo = await program.provider.connection.getAccountInfo(gameDataAccount);
const gameDataAccountData = gameDataAccountInfo.data;
const info = program.coder.accounts.decode("GameDataAccount", gameDataAccountData);
const playerPosition = info.playerPosition;
const expected_reward = 1002240 + ((LAMPORTS_PER_SOL / 10));
// Chest Vault Balance should be rent + reward, and player should be at position 1
assert.strictEqual(chestVaultAccountBalance, expected_reward);
assert.strictEqual(playerPosition, 1);
});
it("Player Moves Right to 2nd Spot", async () => {
await program.methods.moveRight("gib")
.accounts({
chestVault: chestVaultAccount,
gameDataAccount: gameDataAccount,
player: wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// get the balance of the chest vault
const chestVaultAccountInfo = await program.provider.connection.getAccountInfo(chestVaultAccount);
const chestVaultAccountBalance = chestVaultAccountInfo.lamports;
// get the playerPosition from the gameDataAccount
const gameDataAccountInfo = await program.provider.connection.getAccountInfo(gameDataAccount);
const gameDataAccountData = gameDataAccountInfo.data;
const info = program.coder.accounts.decode("GameDataAccount", gameDataAccountData);
const playerPosition = info.playerPosition;
const expected_reward = 1002240 + ((LAMPORTS_PER_SOL / 10));
// Chest Vault Balance should be rent + reward, and player should be at position 2
assert.strictEqual(chestVaultAccountBalance, expected_reward);
assert.strictEqual(playerPosition, 2);
});
it("Player Moves Right to 3rd spot and wins", async () => {
await program.methods.moveRight("gib")
.accounts({
chestVault: chestVaultAccount,
gameDataAccount: gameDataAccount,
player: wallet.publicKey,
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// get the balance of the chest vault
const chestVaultAccountInfo = await program.provider.connection.getAccountInfo(chestVaultAccount);
const chestVaultAccountBalance = chestVaultAccountInfo.lamports;
// get the playerPosition from the gameDataAccount
const gameDataAccountInfo = await program.provider.connection.getAccountInfo(gameDataAccount);
const gameDataAccountData = gameDataAccountInfo.data;
const info = program.coder.accounts.decode("GameDataAccount", gameDataAccountData);
const playerPosition = info.playerPosition;
const expected_reward = 1002240;
// Chest Vault Balance should just be rent, and player should be at position 3
assert.strictEqual(chestVaultAccountBalance, expected_reward);
assert.strictEqual(playerPosition, 3);
});
});
```
There are 5 total tests we are running:
1) GameDataAccount and ChestVaultAccout initializes - verified by there being a rent balance paid on the accounts.
2) Reset Level and Spawn Chest - verified by the chest reward being paid to the ChestVaultAccount
3) Move right to position 1 - verified by player position
4) Move right to position 2 - verified by player position
5) Move right to position 3 - verified by player position and ChestVaultAccount being left with just rent balance (since it was paid out)
Now, with `solana-test-validator` still running in the other window, execute the following command:
```
anchor test --skip-local-validator
```
If there are no errors within your program or tests, you should see:

Nice job! You just learned how to create a basic game and test the logic locally.
You can reference the complete code [here](https://github.com/maweiche/helius_game).
Here are some more resources and tutorials to help build from here:
-[Gaming: How Solana is Changing the Playfield](https://www.helius.dev/blog/gaming-how-solana-is-changing-the-playfield)
-[Battle Coins: Learn to create a token mint with metadata, mint tokens, and burn tokens.](https://www.helius.dev/blog/gaming-how-solana-is-changing-the-playfield)
-[Gum Session Keys - allow your users to play without constant wallet approvals](https://crates.io/crates/session-keys)
-[Build an RPG Game on Solana with Python](https://dev.to/aeither/build-an-rpg-game-on-solana-45he)
---
### Music and Art Distribution
If you are still minting regular NFT's then you are not capitalizing on potential savings by using the compression technology available on Solana. Let's take a look at how you can quickly build an API that will mint and airdrop Music as a cNFT.
For this, we will be using [Irys](https://irys.xyz/) to upload our Image + Audio along with the Helius' `mintCompressedNft` endpoint, which is the easiest way to mint a compressed NFT.
Let's get started! We'll be using a Next.js app for this so, in your terminal, go ahead and run `npx create-next-app music_minter`, then answer the default answer to all of the questions.

After that let's install the Irys sdk:
```
npm install @irys/sdk
```
Once complete, open it up in your IDL and head to the `page.tsx` file in your `src/app` directory.
```typescript=
"use client";
const Page: React.FC = () => {
async function convertAndSubmit() {
try {
console.log("minting business card");
const res = await fetch("/api/mint", {
method: "POST"
});
console.log("res", res);
const response_status = res.status;
if (response_status === 200) {
console.log("business card minted, check terminal");
} else if (response_status === 500) {
console.log("error minting business card");
}
} catch (error) {
console.log(error);
}
}
return (
<div className="flex flex-col gap-4 items-center justify-center h-screen w-screen bg-gradient-to-r from-orange-400 to-orange-700">
<h1 className="text-4xl font-bold text-white">
Helius Music Minter
</h1>
<button
className="bg-red-700 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-fit"
onClick={() => convertAndSubmit()}
>
Create cNFT
</button>
</div>
);
};
export default Page;
```
If you run `npm run dev` and head over to `localhost:3000` your app should now look like this:

Next, let's add a `.env` file to the root of our file. You will need two things to make the API work:
- [Helius Devnet RPC](helius.dev) to access the `mintCompressedNft` call
- [Base58 Private Key](https://docs.solana.com/terminology#private-key) exported from a wallet with Devnet Sol to fund the Irys upload and minting
Please, use a burner wallet in case you accidentally leak it somehow when uploading to GitHub.
Your `.env` should look like this:
```
# BASE58 PRIVATE KEY EXPORTED FROM WALLET
NEXT_PUBLIC_WALLET_PRIVATE_KEY=aBunchOfRandomCharactersAndNumbers
# HELIUS PUBLIC RPC URL
NEXT_PUBLIC_RPC_URL='https://devnet.helius-rpc.com/?api-key=YOUR_API_KEY_HERE'
```
Now, let's go ahead and drop an audio file (.wav or .mp3) into our public folder along with an image (.png or .jpeg) for the cNFT.
After that is set up, let's get our back-end going. You will want to create a `pages` directory at the root of your project and inside of that create an `api` directory.
Once you have an `api` directory create a file inside of it named `mint.tsx` and inject the following code:
```typescript=
import { NextApiRequest, NextApiResponse } from "next";
import Irys from "@irys/sdk";
import path from "path";
const fs = require("fs");
async function post(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") {
try {
const fileName = 'mert'
const audioName = 'Milkyway'
const privateKeySecret = process.env.NEXT_PUBLIC_WALLET_PRIVATE_KEY
const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL
const getIrys = async () => {
const url = "https://devnet.irys.xyz";
const token = "solana";
const privateKey = privateKeySecret;
const providerUrl = rpcUrl
const irys = new Irys({
url, // URL of the node you want to connect to
token, // Token used for payment
key: privateKey, //SOL private key in base58 format
config: { providerUrl: providerUrl }, // Optional provider URL, only required when using Devnet
});
return irys;
};
const uploadImage = async () => {
const irys = await getIrys();
const fileToUpload = `public/${fileName}.png`;
const token = "solana";
// Get size of file
const { size } = await fs.promises.stat(fileToUpload);
// Get cost to upload "size" bytes
const price = await irys.getPrice(size);
console.log(
`Uploading ${size} bytes costs ${irys.utils.fromAtomic(
price,
)} ${token}`,
);
// Fund the node
await irys.fund(price);
// Upload metadata
try {
const response = await irys.uploadFile(fileToUpload);
console.log(
`File uploaded ==> https://gateway.irys.xyz/${response.id}`,
);
return `https://gateway.irys.xyz/${response.id}`;
} catch (e) {
console.log("Error uploading file ", e);
}
};
const image_url = await uploadImage();
const uploadAudio = async () => {
const irys = await getIrys();
const fileToUpload = `public/${audioName}.wav`;
const token = "solana";
// Get size of file
const { size } = await fs.promises.stat(fileToUpload);
// Get cost to upload "size" bytes
const price = await irys.getPrice(size);
console.log(
`Uploading ${size} bytes costs ${irys.utils.fromAtomic(
price,
)} ${token}`,
);
// Fund the node
await irys.fund(price);
// Upload metadata
try {
const response = await irys.uploadFile(fileToUpload);
console.log(
`File uploaded ==> https://gateway.irys.xyz/${response.id}`,
);
return `https://gateway.irys.xyz/${response.id}`;
} catch (e) {
console.log("Error uploading file ", e);
}
};
const audio_url = await uploadAudio();
const mintCompressedNft = async () => {
const response = await fetch(rpcUrl!, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "helius-test",
method: "mintCompressedNft",
params: {
name: "Audio cNFT",
symbol: "HeliusDev",
owner: '7wK3jPMYjpZHZAghjersW6hBNMgi9VAGr75AhYRqR2n',
description: "Audio cNFT",
attributes: [
{
trait_type: "Name",
value: 'Milky Way Mert',
},
{
trait_type: "Description",
value: "Audio cNFT",
},
{
trait_type: "Audio File",
value: audio_url,
}
],
imageUrl: image_url,
externalUrl: "https://www.helius.dev/blog",
sellerFeeBasisPoints: 6500,
creators: [
{
address: '7wK3jPMYjpZHZAghjersW6hBNMgi9VAGr75AhYRqR2n',
share: 100,
},
],
},
}),
});
const { result } = await response.json();
console.log(`View on Xray: https://xray.helius.xyz/tx/${result.signature}?network=devnet`)
return result;
};
const response = await mintCompressedNft();
return res.status(200).json({
status: "success",
assetId: response.assetId,
});
} catch (error) {
console.log(error);
return res.status(500).json({ status: "error" });
}
}
}
export default async function GET(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "GET") {
return res.status(405).json({ error: "Method not allowed" });
} else if (req.method === "POST") {
return await post(req, res);
} else {
return res.status(405).json({ error: "Method not allowed" });
}
}
```
Let's walk through what is happening here.
I am using an image named `mert.png` and an audio file named `Milkyway.wav`. So on line 10 & 11 I have input those names accordingly, you will need to change these to your respective filenames.
```typescript=10
const fileName = 'mert'
const audioName = 'Milkyway'
```
On line 14 we establish the parameters for our Irys connection, since we are minting on Devnet we are using their Devnet node, however if you want to mint on Mainnet you will need to change this, you can find the list [here](https://docs.irys.xyz/overview/nodes).
This is also where we input the other necessary info for Irys to connect and pay for the uploads.
```typescript=14
const getIrys = async () => {
const url = "https://devnet.irys.xyz";
const token = "solana";
const privateKey = privateKeySecret;
const providerUrl = rpcUrl
const irys = new Irys({
url, // URL of the node you want to connect to
token, // Token used for payment
key: privateKey, //SOL private key in base58 format
config: { providerUrl: providerUrl }, // Optional provider URL, only required when using Devnet
});
return irys;
};
```
For `uploadImage` and `uploadAudio` we are doing the same thing just with two different files and types. The key thing to note here are the following lines:
```typescript=63
// Get size of file
const { size } = await fs.promises.stat(fileToUpload);
// Get cost to upload "size" bytes
const price = await irys.getPrice(size);
console.log(
`Uploading ${size} bytes costs ${irys.utils.fromAtomic(
price,
)} ${token}`,
);
// Fund the node
await irys.fund(price);
```
What Irys does here is gather the size of the respective file and determines the price of the upload/hosting, it then funds the node with the Private Key you are using. After upload, it returns a URL to your item on their gateway. This URL is what we then use for the Image URI and File URL.
Next, on line 89 we structure our `mintCompressedApi` call to Helius where the body of the call is the NFT metadata.
```typescript=89
const mintCompressedNft = async () => {
const response = await fetch(rpcUrl!, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "helius-test",
method: "mintCompressedNft",
params: {
name: "Audio cNFT",
symbol: "HeliusDev",
owner: '7wK3jPMYjpZHZAghjersW6hBNMgi9VAGr75AhYRqR2n',
description: "Audio cNFT",
attributes: [
{
trait_type: "Name",
value: 'Milky Way Mert',
},
{
trait_type: "Description",
value: "Audio cNFT",
},
{
trait_type: "Audio File",
value: audio_url,
}
],
imageUrl: image_url,
externalUrl: "https://www.helius.dev/blog",
sellerFeeBasisPoints: 6500,
creators: [
{
address: '7wK3jPMYjpZHZAghjersW6hBNMgi9VAGr75AhYRqR2n',
share: 100,
},
],
},
}),
});
const { result } = await response.json();
console.log(`View on Xray: https://xray.helius.xyz/tx/${result.signature}?network=devnet`)
return result;
};
const response = await mintCompressedNft();
```
Compressed NFT metadata is the exact same as a regular NFT. To keep it simple we are just using Name, Description, and Audio File as traits, but you could also include other details. [Here](https://6c6wh2uz4y24gydicfdt5kkaxrjbqlctace3ciacoynz4ekfwd7a.arweave.net/8L1j6pnmNcNgaBFHPqlAvFIYLFMAibEgAnYbnhFFsP4) is the metadata of an Audio NFT on a NFT Marketplace for reference.
One thing to note, the `owner` on line 102 is where this cNFT will be airdropped to after minting.
```typescript=102
owner: '7wK3jPMYjpZHZAghjersW6hBNMgi9VAGr75AhYRqR2n',
```
Now you are ready to spin back up your app with `npm run dev` and click "Create cNFT".
If all goes as planned with the upload and mint then you will see a URL displayed to XRAY in your terminal. If something fails or there is an error then check your terminal, the error from the back-end will not appear in your browser console.
Here is an example of how the txn will look on Xray, where you can click the cNFT and view the attributes.

Nice job! Now if you wanted to display these cNFT's on the front-end you can parse for the cNFTs by creator using the following call to [Helius'](https://docs.helius.dev/compression-and-das-api/digital-asset-standard-das-api/get-assets-by-creator) `getAssetsByCreator` endpoint.
```typescript=
const response = await fetch(url!, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
jsonrpc: "2.0",
id: "helius-test",
method: "getAssetsByCreator",
params: {
creatorAddress: address, // Required
onlyVerified: false, // Optional
page: 1, // Starts at 1
limit: 1000, // Max 1000
},
}),
});
```
From there you can map out the results and display the "Audio File" trait value within an `<audio />` tag.
Congrats, you now have a way to share audio as a compressed NFT!
*Disclaimer: If you plan to push this to production, I recommend using Pro plan on Vercel, this allows all back-end api calls to complete with out timing out.*
You can reference the complete code [here](https://github.com/maweiche/helius_music_minter).
Here are some more resources and tutorials to help build from here:
-[Mint cNFTs from an in-browser user generated image.](https://hackmd.io/@maweiche/S1u5i8rfa)
-[Create a NFT marketplace on Solana.](https://medium.com/coinmonks/how-to-develop-nft-marketplace-platform-on-solana-a0361faaf50c)
-[Deep Dive into Solana cNFTs](https://www.soldev.app/course/compressed-nfts)
---
### DeFi / Restaurant Operations / Real Estate Investing
For these 3 industries we are going to cover a single use case that would be applicable in each. Imagine trying to sell a double cheeseburger or a house listed in SOL, the price would fluctuate so often it would be hard to predict what the consumer would actually be paying at the point of sale. Typically, the best way to offer products, services, or tokens for sale is to use a stable coin like USDC. But, for a consumer swapping SOL to USDC could be confusing to them and next thing you know they are trying to pay with USDT or not at all.
This is where [Pyth Price Feeds](https://pyth.network/price-feeds) come into play. Pyth provides real-time data for the crypto market and others.
Here we will cover a simple Anchor program where the user can enter how much they want to pay in USDC and it will convert that amount to SOL at the current price during the time of that transaction. We will also generate a test to verify our program works.
Let's get started. First thing we want to do is open our terminal and execute the following command:
```
anchor init pyth
```
Once anchor completes the build open it up in your IDL and navigate to the `Cargo.toml` inside of the `programs/pyth` directory. We will want to add the `pyth-sdk-solana` to the program dependencies.
```rust
[dependencies]
anchor-lang = "0.26.0"
pyth-sdk-solana = "0.9.0"
```
Now, open `lib.rs` file inside of the `/src` directory an replace the existing code with the following:
```rust=
use anchor_lang::prelude::*;
use pyth_sdk_solana::*;
declare_id!("58V9VYXJ5QxWaFPNC8bq6xSnQJsWrDW39fjSkQnrx9Jn");
// This is the price feed id for SOL/USD on devnet
// https://pyth.network/developers/price-feed-ids#solana-devnet
const SOL_USD_PRICEFEED_ID : &str = "J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix";
const STALENESS_THRESHOLD : u64 = 60; // staleness threshold in seconds
#[program]
pub mod pyth {
use std::str::FromStr;
use anchor_lang::solana_program::{system_instruction, native_token::LAMPORTS_PER_SOL, program::invoke};
use super::*;
pub fn pay_usd(ctx: Context<PayUSD>, amount : u64) -> Result<()> {
if Pubkey::from_str(SOL_USD_PRICEFEED_ID) != Ok(ctx.accounts.sol_usd_price_account.key()){
return Err(error!(CustomError::WrongPriceFeedId))
};
let sol_usd_price_feed = load_price_feed_from_account_info(&ctx.accounts.sol_usd_price_account).unwrap();
let current_timestamp = Clock::get()?.unix_timestamp;
let current_price: Price = sol_usd_price_feed.get_price_no_older_than(current_timestamp, STALENESS_THRESHOLD).unwrap();
let amount_in_lamports = amount * LAMPORTS_PER_SOL * 10u64.pow(u32::try_from(-current_price.expo).unwrap()) / (u64::try_from(current_price.price).unwrap());
let transfer_instruction = system_instruction::transfer(&ctx.accounts.from.key(), &ctx.accounts.to.key(), amount_in_lamports);
invoke(&transfer_instruction, &ctx.accounts.to_account_infos())?;
Ok(())
}
}
#[derive(Accounts)]
#[instruction(amount : u64)]
pub struct PayUSD<'info> {
pub from : Signer<'info>,
/// CHECK : This is an unchecked receiver account
#[account(mut)]
pub to : AccountInfo<'info>,
/// CHECK : We will manually check this against the Pubkey of the price feed
pub sol_usd_price_account : AccountInfo<'info>,
pub system_program : Program<'info, System>
}
#[error_code]
pub enum CustomError {
WrongPriceFeedId
}
```
I have left comments with in the program to describe what's happening, but an overview is this:
- Program takes in a USDC amount
- Program gets current price of SOL with a 60 second threshold
- Program converts the USDC amount to Lamports based on the current price
- Program creates and invokes a transfer from the signers account
Now let's build/deploy and test. In your terminal run `anchor build`.
Because we want to test this on devnet, make sure your Solana CLI is set to it by running: `solana config set --url https://api.devnet.solana.com`.
And in your `Anchor.toml` in the root directory we want to update the cluster and program id at line 4.
```rust=4
[programs.devnet]
pyth = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
```
As well as the cluster on line 11
```rust=11
cluster = "Devnet"
```
Then execute `anchor deploy`, when that completes let's grab the Program Id from our terminal. You will want to update line 4 in the `lib.rs`, as well as your `Anchor.toml`
```rust=4
declare_id!("CE8dkMzNfG9Tfhehw6wdLM7qoYeDZTWDs19FVX7VXvLZ");
```
```rust=5
pyth = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
```
After editing those, run `anchor build` and `anchor deploy` for the new program id to take effect, and let's get testing!
Open the `pyth.ts` file in the `/tests` directory and replace the code with the following:
```typescript=
import * as anchor from "@project-serum/anchor";
import { Program } from "@project-serum/anchor";
import { Pyth } from "../target/types/pyth";
import { PublicKey} from "@solana/web3.js";
import { BN } from "bn.js";
describe("pyth", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Pyth as Program<Pyth>;
const wallet = anchor.workspace.Pyth.provider.wallet.payer;
const usdc_amount = new BN(50)
it("Send money", async () => {
// Add your test here.
const tx = await program.methods.payUsd(usdc_amount)
.accounts({solUsdPriceAccount: new PublicKey("J83w4HKfqxwcq3BEMMkPFSppX3gqekLyLJBexebFVkix"), to : new PublicKey("7wK3jPMYjpZHZAghjersW6hBNMgi9VAGr75AhYRqR2n")})
.signers([wallet])
.rpc();
console.log(`Txn complete: https://solana.fm/tx/${tx}?cluster=devnet-solana`)
});
});
```
For this test we are trying to execute a simple transaction of sending the equivalent of 50 USDC (~.5 SOL at time of writing), as seen on ln 13.
To run this test make sure you have Devnet SOL in your CLI wallet, if not run the command `solana airdrop 2`. Then execute:
```
anchor test
```
If successful, you will see the URL to the transaction printed with `1 Passing` in your terminal.

Nice job, you just created a Solana Program that takes in live-data from an oracle service. This is how Jupiter, Parcl, and all other programs in defi provide real-time data in their services.
You can reference the completed code [here](https://github.com/maweiche/helius_pyth_feed).
Here are some more resources and tutorials to help build from here:
- [Use Switchboard to create an Escrow Program](https://www.soldev.app/course/oracles)
- [Build a DEX on Solana with Anchor](https://coinsbench.com/build-a-dex-on-solana-using-anchor-1-1eccebce1641)
- [Build a simple DeFi dApp with Codigo.AI in under an hour](https://docs.google.com/document/d/1mNU4lnQW582T526FzX7rMnzTs_hLO2kBFodcMKkjdtE/edit#heading=h.6uo2tfn93ox)
---
### Physical Infrastructure Networks (DePin)
Solan DePin programs are difficult to test without the right tools (Raspberry Pi, Arduino, Water Pumps, Led's, etc.). If you are interested in walking through examples and have the hardware necessary then check out the work done by [Jonas Hahn](https://twitter.com/SolPlay_jonas).
https://github.com/solana-developers/solana-depin-examples
With examples of lights controlled by Solana Pay, a Solana Pay drink dispenser, and even a treausre chest utilizing the Helium Network, he is singlehandedly crushing the open-source DePin game.
If this area interests you give him a follow and check out his work!
---
## What avenues are best to find PMF and users?
Creating an awesome decentralized application with proper product market fit (PMF) and gaining users is challenging. Most builders will leverage twitter to showcase their projects, but with out an existing audience or twitter blue subscription it may not create any real traction. The most successful projects typically arise from the ashes of builders who relentlessly build during hackathons and incubators.
Generally speaking, Solana hackathons and incubators are filled with other builders which allows you to be surrounded by potential users. By attending and participating you can pick the brains of those who are already familiar with the ecosystem opposed to trying to teach your Web2 friend the basics of a wallet just to connect to your dapp. From workshops to bounties, these events can help strengthen your PMF and overall project.
But, don't get it confused, you shouldn't wait until then to start iterating on your idea. Find a problem now and begin building the solution. This way, when the next Solana Hackathon or Incubator starts, you have an idea or two with a foundation to build from and not just beginning to brainstorm.
Below is a list of Hackathons and Incubators to keep an eye out for and where you can find more info on them.
### Hackathons
**[Encode Club Hackathon](https://www.encode.club/solana) -** This hackathon is currently on-going through February 4th, 2024. Sponsored by the Solana Foundation with numerous bounties and workshops available, you can begin building your project while receiving mentorship from Encode Club's Solana program managers.
**[Solana Hackathon](https://solana.com/hackathon) -** Last year, Solana hosted two MAJOR hackathons: [Grizzlython](https://solana.com/news/solana-grizzlython-winners) and [Hyperdrive](https://solana.com/news/solana-hyperdrive-hackathon-winners). Solana usually hosts two hackathons a year with thousands of participants and hundreds of projects. If you really want to battle test your project and garner immense attention from the ecosystem, plug your email into Solana Hackathon page to get alerted when the next big one is announced.
### Incubators
**[MtnDAO](https://twitter.com/mtndao)** This PACKED hacker house hosted in Salt Lake City, Utah is easily the number one U.S. based Solana event for builders. With a long list of successul blockchain projects originating from this meet-up you will for sure walk away a stronger builder with more connections, plus you get to go skiing.
**[SuperteamDAO](https://superteam.fun/) -** With their Solana DAOs around the world, Superteam is known for building. Last year, they hosted Build Stations in each of their regions that were perfect for gathering with like-minded builders looking to ship the next biggest thing on Solana. Give your closest region a follow on [twitter](https://twitter.com/SuperteamDAO) to stay tuned for their next event closest to you.
**[Buildspace Nights & Weekends](https://buildspace.so/) -** This online accelator is packed with shippers. Although it's not limited to just Web3, this 6-week event is filled with builders creating anything and everything from novels to blockchain games. They provide workshops like how to get your first user and how to generate ROI, all while pushing you to ship and iterate. This community of builders always provides massive support and can help your project get the attention it deserves.
---
## Conclusion
In conclusion, technology is changing every day. No longer are we limited to the antiquated solutions created by and benefit a board of directors. Decentralized technology is scaling at a rapid pace and with Solana mechanics the pace will only continue to increase. Now is the time to build dApps that put the users back in control.
Now is the time to build on Solana.
---
## Further Reading and Additional Resources
- [Learn more about what's happening in the Solana Ecosystem](https://solana.com/pt/possible)
- [Why people are bullish on Solana DePin](https://www.theblock.co/post/252775/solana-depin-projects-gig-economy-next-level)
- [The future of Ticketing built on Solana: XP](https://)
---