How to Launch a Solana Presale Web App Without a Smart Contract Step-by-Step Guide For My Adaa.

How to Launch a Solana Presale Web App Without a Smart Contract Step-by-Step Guide For My Adaa.

This article guides you through creating a Solana presale web app using Next.js, React.js, and several essential packages. This tutorial assumes you have a basic understanding of Next.js, React.js, and JavaScript. The packages we’ll use are:

  • Next.js

  • Solana Wallet Adapter

  • Solana Token Package

  • Solana Web3 Package

  • TailwindCSS for styling

  • QuickNode account

Prerequisites

Ensure you have Node.js installed on your machine. Then, follow the steps below to set up your development environment.

Step 1: Create a Next.js App

First, create a new Next.js app with TailwindCSS:

npx create-next-app@latest
# Select yes for TailwindCSS

Step 2: Install Necessary Packages

Navigate to your project directory and install the required packages:

npm install @solana/wallet-adapter-react @solana/wallet-adapter-wallets @solana/wallet-adapter-react-ui @solana/web3.js @solana/spl-token tailwindcss

Step 3: Set Up QuickNode

Create an account on QuickNode and get your RPC endpoint for the Solana devnet. Set this as your endpoint in your code.

Step 4: Project Structure and Basic Setup

Clear the default Next.js content and create a providers file in the app directory.

Create a Wallet Layout Component

Create a new file WalletLayout.tsx in the app directory and add the following code:

"use client";

import * as web3 from '@solana/web3.js';
import * as walletAdapterReact from '@solana/wallet-adapter-react';
import * as walletAdapterWallets from '@solana/wallet-adapter-wallets';
import { WalletModalProvider, WalletMultiButton } from '@solana/wallet-adapter-react-ui';
import '@solana/wallet-adapter-react-ui/styles.css';

import { useConnection } from '@solana/wallet-adapter-react';
import { PropsWithChildren, useState } from 'react';

const QUICKNODE_RPC = 'YOUR_QUICKNODE_RPC_ENDPOINT';

const WalletLayout = ({ children }: PropsWithChildren) => {
    const [balance, setBalance] = useState<number | null>(0);
    const endpoint = QUICKNODE_RPC;
    const wallets = [
        new walletAdapterWallets.PhantomWalletAdapter(),
    ];

    return (
        <walletAdapterReact.ConnectionProvider endpoint={endpoint}>
            <walletAdapterReact.WalletProvider wallets={wallets}>
                <WalletModalProvider>
                    {children}
                </WalletModalProvider>
            </walletAdapterReact.WalletProvider>
        </walletAdapterReact.ConnectionProvider>
    );
};

export default WalletLayout;

Import this WalletLayout in your layout file and wrap it around your entire app to allow users to connect and transact with your presale.

This layout component sets up the Solana connection and wallet provider for the entire application, making it easy to access the wallet functionality across different components.

Step 5: Create the Presale UI

Create a simple UI to display the token price per SOL, an input for the amount of tokens to buy, and a button to send the transaction.

// pages/index.tsx
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { useState } from 'react';
import * as web3 from '@solana/web3.js';
import WalletLayout from '../components/WalletLayout';
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'
const TOKEN_ADDRESS = 'YOUR_TOKEN_ADDRESS'; // Change this to your token address

const Home = () => {
    const { connection } = useConnection();
    const [amount, setAmount] = useState(0);
    const [txSig, setTxSig] = useState(null)
    return (
        <WalletLayout>
            <div className="container mx-auto">
   <WalletMultiButton
        className='!border-brand-cyan w-full  !rounded-[20px] bg hover:!bg-[#161b19] transition-all duration-200'
      />
                <h1 className="text-4xl font-bold">Solana Token Presale</h1>
                <div className="mt-8">
                    <p>Token Price: 1 SOL</p>
                    <input
                        type="number"
                        className="mt-4 p-2 border"
                        placeholder="Amount of tokens"
                        onChange={(e) => setAmount(parseInt(e.target.value))}
                    />
                    <button
                        className="mt-4 p-2 bg-blue-500 text-white"

                    >
                        Buy Tokens
                    </button>
                </div>
                {txSig && <p>Transaction Signature: {txSig}</p>}
            </div>
        </WalletLayout>
    );
};

export default Home;

This file sets up the main UI for the presale page, including a connect wallet button using the Solana Wallet Adapter, an input for the number of tokens to buy, and a button to initiate the transaction.

Step 6: Create the useSendTokens Hook

Create a new file useSendTokens.tsx:

import { useState } from "react";
import { Keypair, PublicKey, sendAndConfirmTransaction, Transaction, TransactionExpiredBlockheightExceededError } from "@solana/web3.js";
import { getOrCreateAssociatedTokenAccount, createTransferInstruction } from "@solana/spl-token";
import { useConnection } from "@solana/wallet-adapter-react";
const MINT_ADDRESS = "YOUR_TOKEN_MINT_ADDRESS"; // Change this to your token address

const secret = [
    // Your secret key array
];
const FROM_KEYPAIR = Keypair.fromSecretKey(new Uint8Array(secret));

const useSendTokens = () => {
    const { connection } = useConnection();
    const [isOpen, setIsOpen] = useState(false);
    const [transactionUrl, setTransactionUrl] = useState("");
    const [success, setSuccess] = useState(false);
    const [errorMessage, setErrorMessage] = useState("");
    const [loading, setLoading] = useState(false);

    const sendTokens = async (TRANSFER_AMOUNT: number, DESTINATION_WALLET: PublicKey) => {
        setLoading(true);
        try {
            const [sourceAccount, destinationAccount] = await Promise.all([
                getOrCreateAssociatedTokenAccount(connection, FROM_KEYPAIR, new PublicKey(MINT_ADDRESS), FROM_KEYPAIR.publicKey),
                getOrCreateAssociatedTokenAccount(connection, FROM_KEYPAIR, new PublicKey(MINT_ADDRESS), DESTINATION_WALLET),
            ]);

            const latestBlockHash = await connection.getLatestBlockhash("confirmed");

            const sendTransaction = async () => {
                const tx = new Transaction();
                tx.add(createTransferInstruction(sourceAccount.address, destinationAccount.address, FROM_KEYPAIR.publicKey, TRANSFER_AMOUNT * 1e9));
                tx.recentBlockhash = latestBlockHash.blockhash;
                tx.feePayer = FROM_KEYPAIR.publicKey;

                try {
                    const signature = await sendAndConfirmTransaction(connection, tx, [FROM_KEYPAIR]);
                    return signature;
                } catch (error) {
                    console.error("Transaction failed:", error);
                    throw error;
                }
            };

            let signature;
            try {
                signature = await sendTransaction();
            } catch (error) {
                if (error instanceof TransactionExpiredBlockheightExceededError) {
                    console.log("Transaction expired, retrying...");
                    signature = await sendTransaction();
                } else {
                    throw error;
                }
            }

            setLoading(false);
            setIsOpen(true);
            setTransactionUrl(`https://explorer.solana.com/tx/${signature}`);
            setSuccess(true);
        } catch (error) {
            console.error(error);
            setLoading(false);
            setIsOpen(true);
            setErrorMessage(error.message);
        }
    };

    return {
        sendTokens,
        loading,
        isOpen,
        errorMessage,
        success,
        setIsOpen,
        transactionUrl,
    };
};

export default useSendTokens;

Step 7: Compiling Everything

At this point, you have all the necessary components to make the presale web app functional. Here’s a quick summary of how everything works together:

  1. WalletLayout Component: This wraps your application and provides the Solana connection and wallet functionality to the entire app.

  2. Presale UI: This is the main page where users can connect their wallet, specify the amount of tokens to buy, and initiate a transaction.

  3. useSendTokens Hook: This handles the logic for sending tokens from your wallet to the user’s wallet.

// pages/index.tsx
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
import { useState } from 'react';
import * as web3 from '@solana/web3.js';
import WalletLayout from '../components/WalletLayout';
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui'

const TOKEN_ADDRESS = 'YOUR_RECEIVING_ADDRESS'; // Change this to your token address

const Home = () => {
    const { publicKey, sendTransaction } = useWallet();
    const { connection } = useConnection();
    const [amount, setAmount] = useState(0);
    const [txSig, setTxSig] = useState(null);

    const handleTransaction = async () => {
        if (!publicKey) {
            toast.error("Please connect your wallet.");
            return;
        }

        const transaction = new web3.Transaction();
        const instruction = web3.SystemProgram.transfer({
            fromPubkey: publicKey,
            lamports: amount * web3.LAMPORTS_PER_SOL,
            toPubkey: new web3.PublicKey(TOKEN_ADDRESS),
        });

        transaction.add(instruction);

        try {
            const signature = await sendTransaction(transaction, connection);
            setTxSig(signature);
            await sendTokens(amount, publicKey);
            toast.success("Deposit Successful! Your token will appear in your wallet soon.");
        } catch (error) {
            console.error(error);
            toast.error("Transaction failed.");
        }
    };

    return (
        <WalletLayout>
  <WalletMultiButton
        className='!border-brand-cyan w-full  !rounded-[20px] bg hover:!bg-[#161b19] transition-all duration-200'
      />
            <div className="container mx-auto">
                <h1 className="text-4xl font-bold">Solana Token Presale</h1>
                <div className="mt-8">
                    <p>Token Price: 1 SOL</p>
                    <input
                        type="number"
                        className="mt-4 p-2 border"
                        placeholder="Amount of tokens"
                        onChange={(e) => setAmount(parseInt(e.target.value))}
                    />
                    <button
                        className="mt-4 p-2 bg-blue-500 text-white"
                        onClick={handleTransaction}
                    >
                        Buy Tokens
                    </button>
                </div>
                {txSig && <p>Transaction Signature: {txSig}</p>}
            </div>
        </WalletLayout>
    );
};

export default Home;

To see everything in action, run your Next.js app:

npm run dev

Visit http://localhost:3000 in your browser. You should see your presale page with a "Connect Wallet" button. Once you connect your wallet, you can specify the amount of tokens you want to buy and complete the transaction by clicking "Buy Tokens".

Conclusion

You have successfully created a Solana presale web app without writing a smart contract. This guide covered setting up a Next.js app, integrating Solana Wallet Adapter, creating the presale UI, and handling token transactions using the @solana/web3.js and @solana/spl-token packages. With this foundation, you can further customize and enhance your presale app to suit your specific requirements. Happy coding!