Written by
0xSolanaGirl
Published on
January 22, 2024
Copy link

Token Gating on Solana - A Solana Mobile Tutorial

Introduction

In early 2023, Solana Mobile launched a blockchain-powered smartphone. Now with over 15,000 activations, the Solana Saga is a custom Android device that enhances on-chain security with a chip-level seed vault integrated into the wallet experience. However, the mobile wallet experience is not unique to the Saga. The same mobile wallet adapter provides a standard flow to reduce complexity for developers releasing cross-platform. So, what sets the Saga apart?

As a rite of passage to Saga ownership, users mint the Saga Genesis Token – a soulbound NFT token. The token collection provides an accurate list of Solana Saga users’ primary addresses and enables activations around the community. The Saga Genesis Token has made airdropping a community easier than ever. Still, with users’ wallets quickly filling up like a thrift store, it may be time to explore bespoke solutions to engage the Saga holder community. One possibility is the development of token-gated dApps that provide users with customized and exclusive experiences. 

Token-gating is a way to display client-side information based on token ownership of the connected wallet. Token-gating dictates whether or not an individual can access information and can be used to allow exclusive access to communities like Saga DAO. This tutorial covers building a Solana token-gated dApp using the Helius DAS API and React Native, with the Saga Genesis Token as the example gating collection. This tutorial and demo can be modified to gate for any group of tokens by changing the token addresses and conditions. The GitHub repository can be found here: @helius_labs/saga-token-gating-tutorial.

Environment Set-up

This tutorial uses the setup of a PC connected to the Solana Saga via a USBC cable. If no Saga is available, an Android emulator or Android devices would also suffice. These steps may be skipped if testing on a Saga with developer mode enabled, with Android SDK tools and the latest version of NodeJS installed on the PC.

To configure a Saga for mobile development, head to settings and search Build Number. Tap the option 7 times until the ‘You are now a developer!’ pop-up is visible. Search and enable Developer Options and enable USB Debugging in the same dropdown.

On the computer, ensure NodeJS is installed and upgraded to v20.9.0. Install the Android SDK tool and set the ANDROID_HOME variable.

Using Git Bash, clone the demo repository from the Helius GitHub and open in a code editor. Use the git bash command prompt to cd into the app folder and run yarn to install the app. To run the app, enter the following command while a device is connected or with Android emulator running.

yarn react-native run-android

Token-gating for Solana Saga

A high-level overview of the token gating workflow:

  1. A user authorizes a session with their wallet, providing their public key to the dApp.
  2. The dApp gets data from the searchAssets DAS API endpoint.
  3. The response’s JSON is destructured to access the results.
  4. The results are validated, and token-ownership is determined.
  5. The verification state is set and the client is rendered accordingly.

Let’s dive into the code. First, starting off with constructing the request to the Helius DAS API to receive information about the user and their ownership of the Solana Genesis Token.

To receive information about Saga Genesis Token, the SagaGenesisToken variable is set to the collection’s token address. Replace this variable with another token address to token-gate for other collections.

A Helius API Key is required for the request and is intentionally left blank in the tutorial. The optimal key security practice is to use a proxy server to forward the requests. The @helius_labs/helius_rpc_proxy repository contains a Cloudflare worker that can be deployed with one click to be used as an RPC URL while hiding the API key.

import { PublicKey } from "@solana/web3.js";

export default async function findSagaGenesisToken(address: PublicKey) {
  // Replace this with the token address you want to gate for
  const SagaGenesisToken = '46pcSL5gmjBrPqGKFaLbbCmR6iVuLJbnQy13hAe7s6CC';


  // Populate your Helius API key
  const HELIUS_APIKEY = '';
  const url = `https://mainnet.helius-rpc.com/?api-key=${HELIUS_APIKEY}`


  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      jsonrpc: '2.0',
      id: 'my-id',
      method: 'searchAssets',
      params: {
        ownerAddress: address.toString(),
        grouping: ["collection", SagaGenesisToken],
        page: 1,
        limit: 10
      },
    }),
  });
 
  const { result } = await response.json();
  return (result.items)
};

The findSagaGenesisToken function takes the user’s wallet address as an argument and uses it in the parameters of the POST request sent to the Helius RPC URL. This request uses the searchAssets method to query for assets owned by ownerAddress and grouped by a specific collection. The API response is then structured into JSON and destructured to access the result.

The following is a response showing ownership of the Saga Genesis Token:

{
   "items":[
      {
         "authorities":[Array],
         "burnt":false,
         "compression":[Object],
         "content":[Object],
         "creators":[Array],
         "grouping":[Array],
         "id":[Token_Address],
         "interface":"V1_NFT",
         "mutable":true,
         "ownership":[Object],
         "royalty":[Object],
         "supply":[Object]
      }
   ],
   "limit":10,
   "page":1,
   "total":1
}

This is a response for a wallet without the Saga Genesis Token:

{
   "items":[],
   "limit":10,
   "page":1,
   "total":0
}

To use this function, navigate to the screens folder to find MainScreen.tsx. The app renders this functional component as the main screen where additional components such as the function may be imported and used in the user interface.

To call the token-gating function, import and place the findSagaGenesisToken function in a callback function. Here, the function is named handleConnectPress() since the callback function is invoked when users press the connect button.

// Checks if the user's wallet has a Saga Genesis Token
const handleConnectPress = useCallback(async () => {
  return await transact(async (wallet: Web3MobileWallet) => {
    const authorizationResult = await authorizeSession(wallet);

    const message = "Confirm that you own this wallet.";
    const messageBuffer = new Uint8Array(
      message.split("").map((c) => c.charCodeAt(0))
    );

    const signedMessages = await wallet.signMessages({
      addresses: [authorizationResult.address],
      payloads: [messageBuffer],
    });

    const verified = sign.detached.verify(
      messageBuffer,
      signedMessages[0],
      authorizationResult.publicKey.toBytes()
    );

    if (verified) {
      const result = await findSagaGenesisToken(authorizationResult.publicKey);

      // Checks that user has exactly 1 Saga Genesis Token
      if (result.length === 1) {
        setVerified(true);
      } else {
        setVerified(false);
      }
    } else {
      setVerified(false);
    }
  });
}, [authorizeSession]);

Within the asynchronous callback function, invoke the transact function from @solana-mobile/mobile-wallet-adapter to expose the Web3MobileWallet object, which can be used to authorize a session. The protocol will navigate the available wallets (defaulting to Phantom) and begin a session where the user must authorize the application in their wallet interface before they are redirected back to the app. The user must sign a message to validate ownership of the wallet. For the purpose of this tutorial, the verification occurs server-side. However, in production apps, the message verification must be done in the backend through an API to ensure legitimacy of account ownership.

Wallet authorization panel for demo Saga token-gated dApp
Authorizing mobile wallet adapter to connect to Phantom through demo app.

When the user’s wallet is authorized, their public key may be retrieved by destructuring the authorized results. Their publicKey can now be used to search for the ownership of the Saga Genesis Token. Calling findSagaGenesisToken with the user’s public key sends a request to the DAS API and returns the results of tokens found in an array. If the user has exactly one Saga Genesis Token, set the verification state to true. Otherwise, the verification state is set to false. 

With the verification state, conditionally render screens and components. In this tutorial, the states are: awaiting connection, verified, and unverified.

Find the conditional rendering logic on Mainscreen.tsx as a simplified representation of how one might display information based on the verification state. The GitHub repository can be modified or referenced for further personalization. An example implementation would be connecting token-gating to React Native Navigation to navigate between screens conditionally.

Unverified, token-gated, and invalid states of Saga Genesis Token ownership in the demo dApp
From left to right: Unverified & awaiting connection state, verified state, and unverified state conditionally rendered based on Saga Genesis Token ownership.

Applications of Token-gating

Token-gating is a novel concept introduced with Web3, where asset ownership grants the rights to access content. The logic applies to verticals such as security, consumer goods, publishing, and distribution. Discord security tools use token-gating for communities to provide limited access based on ownership of tokens and quantity of tokens owned. Consumer goods and services enable merchants to provide coupons, discounts, and sales opportunities to communities based on token-gating. This gives greater utility to asset ownership and creates a more tangible way of using digital assets for real-world functions. Asset ownership amplifies media and content sectors by monetizing ownership and providing custom experiences.

Here are a few things one can build with token-gating:

  • Exclusive referral codes and coupons
  • Substack-style blog
  • Airdrop claims 
  • Private dashboards 
  • Gamified storytelling dApps
  • Secret group chats 
  • On-chain survey
  • Customized gaming perks and events

The beauty of token-gating with the DAS API is that any token can be used. A DeFi app could require users to have a minimum amount of Solana to gain access, much like an NFT group chat requiring its members to verify they hold a minimum number of tokens. One could gate their dApp for SPL tokens, NFTs, SFTs, SBTs, cNFTs using the same logic with the DAS API.

Conclusion

This tutorial provides a simplified overview of using the DAS API to token-gate dApps built with React Native. The logic can be applied to any collection or tokens and may be adapted to specific use cases. Token-gating experiences provide exclusivity and value to communities, with the Saga Genesis Token enabling airdrops to a strong community and opening up many opportunities with creators and users alike. Web3 introduces self-custody ownership of digital goods. This translates into the emergence of communities bound by their ownership of assets. As the Solana Saga binds physical and digital technology together to deliver a Solana-native experience, applications must be built to enhance and reward that experience. Beyond gains, token-gating promotes the utilization of digital asset ownership in the new era of the internet.

Interested in more Saga mobile dApp development content? Join the 15,000+ Saga owners at Saga DAO and be the first to hear about the latest dApp releases, perks, and rewards. If this tutorial on token-gating inspired implementation strategies, chat with the developer community in the Helius Discord and stay up-to-date with Helius Blogs. Accelerate blockchain adoption and  build on the most performant technology with Helius.

Additional Resources