Carlos Mucuho A geologist-turned-programmer.

How to build a Solana Discord wallet with Python

19 min read 5553

In this tutorial, we will learn how to build a Solana Discord chat bot wallet with Python, discord.py, and the Solana Python SDK. This chat bot will be capable of creating an account, funding an account, checking an account balance, and sending Solana tokens to another account, all through Discord.

Solana is a public blockchain network that allows its users to create NFTs, finance applications, and other smart contract applications. Solana’s native token is called SOL, and according to coinmarketcap.com at the time of writing, has the seventh largest market cap among cryptocurrencies. A Solana wallet is an application that allows you to create Solana accounts, store, send, and receive SOL and other tokens, as well as interact with smart contracts.

Discord is a popular free voice, video, and text chat app with more than 350 million users that runs on Windows, macOS, Android, iOS, iPadOS, Linux, and in web browsers. A Discord chat bot is a bot that is capable of responding to commands and automating certain tasks, such as welcoming new members, moderating content, and banning rule breakers. The bot that we are going to create will help us create and manage a Solana wallet.

At the end of this tutorial you will have a Solana Discord wallet that looks like the following:

Gif demo of the final Solana Discord chat bot in action

Prerequisites

  • A Discord account
  • Discord installed or accessible on your device
  • Python 3.6+

Creating a Discord bot

In this section, we will create a new Discord bot account, retrieve the bot token, and invite the bot to one of our servers.

Using your preferred browser, navigate to the Discord developer portal and use your Discord account to sign in. Navigate to the Applications page and click on the New Application button.

Give your application a name, such as “Solana wallet,” and then press Create.

Create application screen in Discord

Next, navigate to the Bot tab and click on the Add Bot button to create a bot user.

Discord bot tab

Scroll down to the Build-A-Bot section of the page and click on the Copy button to copy the bot token. We will use this bot token in the next section, so keep it somewhere safe. This token should be kept private because anyone with access to this token will be able to control your bot.

After following the steps above, you have successfully created a bot account! Now in order to be able to interact with this bot account, you need to invite it to your server.

Navigate to the OAuth2 tab, and then to the URL Generator sub tab. In the Scopes section, select bot.

Discord OAuth2 tab

In the Bot Permissions section that appears below, select all the fields in the Text Permissions column. Go to the section where says Generated URL and click the Copy button to copy the bot invite URL.

Paste the copied URL in a new tab, select the server where you would like to add the bot, and click the Continue button.



Screenshot of button to add bot to discord server

Review the bot’s permissions, and when you are satisfied, click the Authorize button.

Now your bot will be invited to your server and you will be able interact with it once we code it.

Creating the project structure

In this section, we will create our project directory. Inside this directory, we will create and activate a virtual environment, then install the Python packages needed to build this chat bot. Lastly, we will create a file named .env and inside this file, store our Discord bot token.

Open a terminal window and enter the following commands:

mkdir solana-discord-wallet
cd solana-discord-wallet

Inside our working directory, create a virtual environment and activate it. If you are using a Unix or MacOS system, run the following commands:

python3 -m venv venv
source venv/bin/activate

If you are following the tutorial on Windows, run the following commands instead:

python -m venv venv
venvScriptsactivate

Now that we have created and activated our virtual environment, we can install the libraries that we need to create our application:

pip install discord.py solana python-dotenv 

In the command above we used pip, the Python package installer, to install the following packages that we are going to use in this project:

  • discord.py is a modern, easy to use, feature-rich, and async ready API wrapper for Discord which we will use to interact with Discord’s API
  • solana-py is a Solana Python library built on the JSON RPC API
  • python-dotenv is a library that reads the key-value pairs from a .env file and adds them as environment variables. We will use this module to retrieve our bot token that will be stored in the .env file

Now let’s start building our application. Create a file called .env, and paste in the Discord bot token that you saved in the previous section as BOT_TOKEN .

Creating the main.py file

In this section, we will create the Python script that will allow our Discord chat bot to send and receive messages.

In the root directory of your project, create a file called main.py. Open it using your favorite text editor and add the following code:

import os
import discord
from discord.ext import commands
from dotenv import load_dotenv

Here, we imported all the packages that we are going to need to allow our chat bot application to send and receive messages:

  • os will be used alongside python-dotenv to retrieve our Discord bot token from the .env file
  • The discord package will be used to interact with Discord’s API and create command handlers, thus allowing us to send and receive Discord messages

Add the following code to the bottom of the main.py file:

load_dotenv()
description = ''' A bot that allows you to create and manage a Solana wallet  '''
intents = discord.Intents.default()
bot = commands.Bot(command_prefix='/', description=description, intents=intents)

@bot.event
async def on_ready():
    print('Bot is online')
    print(bot.user.name)
    print(bot.user.id)
    print('------ n')

In the code above, we imported the environment variables stored in the .env file with a call to load_dotenv().

After loading the variables, we created a basic description for our bot, and set the bot’s intents to default . An intent is what allows a bot to subscribe to specific buckets of events, such as direct messages, reactions, or typing.


More great articles from LogRocket:


Then, we created a new bot instance and passed as arguments to the constructor a command prefix (/), the description, and the intents. We stored the bot instance in a variable named bot.

Lastly, we create an event listener for when the bot is running. When this event listener is triggered, we print a couple lines to the console saying that the bot is online, and showing the bot’s username and user ID.

Now, Add the following code below the on_ready() function :

@bot.command(description='Create a new solana account')
async def create(ctx):
    await ctx.send('Create account')

@bot.command(description='Fund your account')
async def fund(ctx):
    await ctx.send('Fund your account')

@bot.command(description='Check account balance')
async def balance(ctx):
    await ctx.send('Check account balance')

@bot.command(description='Send SOL to another account')
async def send(ctx):
    await ctx.send('Send SOL to another account')


bot.run(os.environ['BOT_TOKEN'])

In the block of code above we created all the command handlers for our chat bot . The code above determines which command the user is trying to call and takes the appropriate action.

Please notice how we don’t need to specify the command prefix in each command handler because we already did that when we created the bot instance.

Our chat bot wallet will be able to handle the following commands:

  • /create creates a new Solana account
  • /fund amount funds an existing Solana account with a certain amount of SOL
  • /balance checks the balance of an existing Solana account
  • /send amount receiver is responsible for sending a certain amount of SOL to another Solana account

For now, each command handler will only send back a text describing the action that the user wants to perform. In order to send a message to the user, we used the send() method provided by the context (ctx) object available in each command handler.

Lastly, we called the run() method provided by the bot object and passed the bot token as an argument to start our chat bot.

Your main.py file should look like the following:

import os
import discord
from discord.ext import commands
from dotenv import load_dotenv

load_dotenv()
description = ''' A bot that allows you to create and manage a Solana wallet  '''
intents = discord.Intents.default()
bot = commands.Bot(command_prefix='/', description=description, intents=intents)

@bot.event
async def on_ready():
    print('Bot is online')
    print(bot.user.name)
    print(bot.user.id)
    print('------ n')

@bot.command(description='Create a new solana account')
async def create(ctx):
    await ctx.send('Create account')

@bot.command(description='Fund your account')
async def fund(ctx):
    await ctx.send('Fund your account')

@bot.command(description='Check account balance')
async def balance(ctx):
    await ctx.send('Check account balance')

@bot.command(description='Send SOL to another account')
async def send(ctx):
    await ctx.send('Send SOL to another account')

bot.run(os.environ['BOT_TOKEN'])

Go to your terminal, and run the following command in order to start the application:

python main.py

Using your preferred Discord client, send the /create command to the bot, and you should get a response similar to the following :

The /Create command in Discord

Creating the wallet.py file

In this section, we will create the file that will allow us to create a Solana account, fund the account, check the balance of the account, and send funds from this account to another.

When you create a Solana account, a KeyPair object is generated. This object contains a public key and a corresponding private key for accessing the account.

A public key is analogous to an account number that can be publicly shared with anyone in order to receive funds, and a private key is what grants a Solana user ownership of the funds on a given account. As the name suggests, this private key should not be shared publicly.

A Solana account may hold funds called “lamports”. Lamports are fractional native tokens valued at 0.000000001 SOL.

In the root directory of your project, create a file named wallet.py. Open it using your favorite text editor and then add the following code:

from solana.keypair import Keypair
from solana.publickey import PublicKey
from solana.rpc.api import Client
from solana.transaction import Transaction
from solana.system_program import TransferParams, transfer

import json

solana_client = Client("https://api.devnet.solana.com")

Here, we imported the following objects from the Solana package :

  • Keypair, which will be used to create a new Solana account
  • PublicKey, which will convert a public key in a string format to a PublicKey object in order to send Solana tokens to another account
  • Client, to create a Solana client instance that will allow this application to interact with the Solana blockchain
  • Transaction, to create a Solana transaction. A transaction is instructions signed by a client using single or multiple KeyPairs and executed atomically with only two possible outcomes: success or failure
  • TransferParams, to create an object containing the parameters of a transfer funds transaction
  • transfer, to create an object that allows an account to send funds to another

After that, we imported json, which is used to store the created Solana account public key and private key in a file.

Lastly, we created a Solana client instance in a variable named solana_client and set the RPC endpoint to the devnet. An RPC (remote procedure call) endpoint is a URL to which requests for blockchain data can be sent.

Building a function to create a new Solana account

Add the following code to the bottom of the wallet.py:

def create_account(sender_username):
    try:
        kp = Keypair.generate()
        public_key = str(kp.public_key)
        secret_key = kp.secret_key

        data = {
            'public_key': public_key,
            'secret_key': secret_key.decode("latin-1"),
        }

        file_name = '{}.txt'.format(sender_username)
        with open(file_name, 'w') as outfile:
            json.dump(data, outfile)

        return public_key

    except Exception as e:
        print('error:', e)
        return None

The create_account() function created above receives as an argument the username of the user that sent the /create command and it is responsible for creating a new Solana account and storing the account details in a local .txt file.

We start the code by first generating a new Solana account KeyPair object and storing it in a variable called kp.

We then store it in an object called data, which is the stringified value of the generated account’s public_key and secret_key.

Lastly, we use the value stored in the variable called sender_username to create a .txt file, dump the data in it, and return the account’s public_key if there isn’t an exception. If something goes wrong, we return None.

Add the following code below the create_account() function:

def load_wallet(sender_username):
    try:
        file_name = '{}.txt'.format(sender_username)
        with open(file_name) as json_file:
            account = json.load(json_file)
            account['secret_key'] = account['secret_key'].encode("latin-1")
            return account

    except Exception as e:
        print(e)
        return None  

Here, we created a function named load_wallet(). This function receives as an argument the user’s username and uses it to retrieve his Solana account public and private key from a local .txt file, which is created when the create_account() function is called.

Creating a function to fund a Solana account

Add the following code below the load_wallet() function:

def fund_account(sender_username, amount):
    try:
        amount = int(1000000000 * amount)
        account = load_wallet(sender_username)
        resp = solana_client.request_airdrop(
            account['public_key'], amount)   
        print(resp)    

        transaction_id = resp['result']
        if transaction_id != None:
            return transaction_id
        else:
            return None

    except Exception as e:
        print('error:', e)
        return None

In the code above, we created a function named fund_account(). This function is responsible for requesting SOL for a specific account, and receives as an argument the username of the user who sent the /fund command, and the amount of SOL that the user is requesting.

First, we use some basic math to prevent Solana from converting the amount of SOL we wish to request to a fraction of what it should be. Say, for example, that we wish to request that one SOL is added to our account. If we just input “1” as the amount, Solana will convert this amount to 0.000000001. So in order to prevent this behavior, we multiply our desired amount by one billion (1,000,000,000).

Then, use the load_wallet() function to get the user’s Solana account data and store it in a variable named account.

Lastly, we use the request_airdrop() method provided by the solana_client object to request some SOL tokens for the account for which we provided the public key. If the request is successful, we return the transaction ID, but if something goes wrong we return None.

For us to consider the request successful, the request_airdrop() method should return a response similar to the following:

{
    "jsonrpc": "2.0",
    "result":"uK6gbLbhnTEgjgmwn36D5BRTRkG4AT8r7Q162TLnJzQnHUZVL9r6BYZVfRttrhmkmno6Fp4VQELzL4AiriCo61U",
    "id": 1
}

The jsonrpc that you see above is the protocol that was used, the id is the request ID, the result is the response results, and in this particular case, is a transaction ID.

You can check the details of a Solana transaction by first navigating to the Solana blockchain explorer, selecting the Devnet network, and entering the transaction ID that you see in the result property.

Creating a function to check the balance of a Solana account

Add the following code below the fund_account() method:

def get_balance(sender_username):
    try:
        account = load_wallet(sender_username)
        resp = solana_client.get_balance(account['public_key'])
        print(resp)
        balance = resp['result']['value'] / 1000000000
        data = {
            "publicKey": account['public_key'],
            "balance": str(balance),
        }
        return data
    except Exception as e:
        print('error:', e)
        return None

Here we created a function named get_balance(). This function receives as an argument the username of the user who sent the /balance command, and it is responsible for retrieving the user’s Solana account balance.

First, we use the load_wallet() method to get the user’s Solana account, and then we call the get_balance() method provided by the Solana client to get an account balance, pass the account public key as an argument, and assign the response to a variable named resp.

After retrieving the account balance, we divide the balance by one billion to make it more readable.

Lastly, we store the public key and account balance in an object named data, and then we return this object.

If the request sent by the get_balance() method was successful you should see a response similar to the following:

{
    "jsonrpc": "2.0", 
    "result": {
        "context": { "slot": 228 }, 
        "value": 0
    }, 
    "id": 1
}

The context that you see above is a RpcResponseContext JSON structure including a slot field at which the operation was evaluated. The value is the value returned by the operation itself, and in this case, is the account balance.

Creating a function to send SOL between Solana wallets

Add the following code below the get_balance() function:

def send_sol(sender_username, amount, receiver):
    try:
        account = load_wallet(sender_username)
        sender = Keypair.from_secret_key(account['secret_key'])
        amount = int(1000000000 * amount)

        txn = Transaction().add(transfer(TransferParams(
            from_pubkey=sender.public_key, to_pubkey=PublicKey(receiver), lamports=amount)))
        resp = solana_client.send_transaction(txn, sender)
        print(resp)

        transaction_id = resp['result']
        if transaction_id != None:
            return transaction_id
        else:
            return None

    except Exception as e:
        print('error:', e)
        return None

The send_sol() function created above receives as arguments the username of the user who sent the /send command, the amount of SOL that this user wishes to send, and the Solana account address where she wishes to send it. As the name suggests, this function is responsible for sending a certain amount of SOL to a Solana account address that the user provided.

First, we use the load_wallet() function to get the user’s Solana account, and then we store the user’s Solana account KeyPair in a variable named sender. The amount that she wishes to send is stored in a variable named amount.

We then create a transaction object, add to it the sender’s and the receiver’s public key, add the amount of SOL that she wishes to send, and assign this object to a variable named txn.

In order to send and sign the transaction, we call the send_transaction() method provided by the Solana client, pass as arguments the transaction object and the sender’s KeyPair, and then store the response in a variable named resp. The response of the request sent by the send_transaction() method is similar to the request_airdrop() method response that we saw earlier.

Lastly, we grab the transaction ID stored in the result property of the resp object, store it in a variable named transaction_id, and return it.

The wallet.py file should look similar to the following:

from solana.keypair import Keypair
from solana.publickey import PublicKey
from solana.rpc.api import Client
from solana.transaction import Transaction
from solana.system_program import TransferParams, transfer

import json

solana_client = Client("https://api.devnet.solana.com")


def create_account(sender_username):
    try:
        kp = Keypair.generate()
        public_key = str(kp.public_key)
        secret_key = kp.secret_key

        data = {
            'public_key': public_key,
            'secret_key': secret_key.decode("latin-1"),
        }

        file_name = '{}.txt'.format(sender_username)
        with open(file_name, 'w') as outfile:
            json.dump(data, outfile)

        return public_key

    except Exception as e:
        print('error:', e)
        return None


def load_wallet(sender_username):
    try:
        file_name = '{}.txt'.format(sender_username)
        with open(file_name) as json_file:
            account = json.load(json_file)
            account['secret_key'] = account['secret_key'].encode("latin-1")
            return account

    except Exception as e:
        print(e)
        return None   


def fund_account(sender_username, amount):
    try:
        amount = int(1000000000 * amount)
        account = load_wallet(sender_username)
        resp = solana_client.request_airdrop(
            account['public_key'], amount)   
        print(resp)    

        transaction_id = resp['result']
        if transaction_id != None:
            return transaction_id
        else:
            return None

    except Exception as e:
        print('error:', e)
        return None


def get_balance(sender_username):
    try:
        account = load_wallet(sender_username)
        resp = solana_client.get_balance(account['public_key'])
        print(resp)
        balance = resp['result']['value'] / 1000000000
        data = {
            "publicKey": account['public_key'],
            "balance": str(balance),
        }
        return data
    except Exception as e:
        print('error:', e)
        return None


def send_sol(sender_username, amount, receiver):
    try:
        account = load_wallet(sender_username)
        sender = Keypair.from_secret_key(account['secret_key'])
        amount = int(1000000000 * amount)

        txn = Transaction().add(transfer(TransferParams(
            from_pubkey=sender.public_key, to_pubkey=PublicKey(receiver), lamports=amount)))
        resp = solana_client.send_transaction(txn, sender)
        print(resp)

        transaction_id = resp['result']
        if transaction_id != None:
            return transaction_id
        else:
            return None

    except Exception as e:
        print('error:', e)
        return None                

Putting everything together

In the previous section, we created the file that contains the functions that will allow our chat bot to perform transactions in the Solana blockchain. In this section, we will integrate these functions with our chat bot’s command handlers.

Go to your main.py and add the following code to the import statements:

from wallet import create_account, fund_account, get_balance, send_sol

In the line of code above, we imported all the functions that we created in the previous sections in the wallet.py file. Let’s go through each command to integrate them into our chat bot’s command handlers.

The /create command

In the main.py file, replace the code in the /create command handler with the following:

async def create(ctx):
    sender_username = ctx.message.author
    try:
        public_key = create_account(sender_username)
        if public_key is not None:
            message = "Solana Account created successfully.n"
            message += "Your account public key is {}".format(public_key)
            await ctx.send(message)
        else:
            message = "Failed to create account.n"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to create account')
        return

Here, we get the username of the user who sent the /create command and store it in a variable named sender_username.

After that, we call the create_account() function in the wallet.py file, pass as an argument the user’s username, and store it in a variable named public_key. The the newly created Solana account’s public key is returned by the create_account() function.

We then use conditional logic to check if the value of public_key is not equal to None, and if this is the case, we store a message in a variable named message, saying that the Solana account was created successfully and showing public key. After that, we use the send() method to send the message to the user.

However, if the public_key is equal to None we send a message saying that the bot failed to create an account.

The /fund command

Now, replace the code in the /fund command handler with the following:

async def fund(ctx):
    sender_username = ctx.message.author
    incoming_msg = ctx.message.content
    try:
        amount = float(incoming_msg.split(" ")[1])
        if amount <= 2 :
            message = "Requesting {} SOL to your Solana account, please wait !!!".format(amount)
            await ctx.send(message)
            transaction_id = fund_account(sender_username, amount)
            if transaction_id is not None:
                message = "You have successfully requested {} SOL for your Solana account n".format(
                    amount)
                message += "The transaction id is {}".format(transaction_id)
                await ctx.send(message)
            else:
                message = "Failed to fund your Solana account"
                await ctx.send(message)
        else:
            message = "The maximum amount allowed is 2 SOL"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to fund account')
        return

In the code above, we get the username of the user who sent the /fund command and the message received, and then we store these values in variables named sender_username and incoming_msg, respectively.

We then retrieve the amount of SOL that the user would like to request from the message received and store it in a variable named amount.

After retrieving the amount we check if the amount isn’t greater than two, because at the time of writing this tutorial, two is the maximum amount of SOL that you can request. If the amount isn’t greater than two, we store a message in a variable named message saying that the amount that the user requested is being added to his account, and asking him to wait. We then use the send() method to send this message to the user.

After notifying the user, we call the fund_account() function in the wallet.py file. We pass as arguments the user’s username and the amount of SOL that he wishes to add to his account. After calling the fund_account() function, we store the transaction ID returned in a variable named transaction_id.

Lastly, we use conditional logic to check if the transaction ID isn’t equal to None, and if that is the case, we store a message in a variable named message, saying that the funds he requested were added to his account. We add to this message the transaction ID and then we send this message to the user.

However, if the transaction ID is equal to None we send a message saying that the bot failed to fund the account.

The /balance command

Now let’s do the /balance command. Replace the code in the /balance command handler with the following:

async def balance(ctx):
    sender_username = ctx.message.author
    try:
        data = get_balance(sender_username)
        if data is not None:
            public_key = data['publicKey']
            balance = data['balance']
            message = "Your Solana account {} balance is {} SOL".format(
                public_key, balance)
            await ctx.send(message)
        else:
            message = "Failed to retrieve balance"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to check account balance')
        return

Here, first, we get the username of the user who sent the /balance command and store it in a variable named sender_username.

We then call the get_balance() function in the wallet.py file. We pass as an argument the user’s username and store it in a variable named data as the object returned by this function. This object should contain the user’s Solana account public key and balance.

Lastly, we use conditional logic to check if the value returned is not equal to None. If that is the case we store a message in a variable named message, containing the user’s Solana account public key and balance, and then we send the message to the user.

However, if the value returned by the get_balance() is equal to None we send a message saying that the bot failed to retrieve the account balance.

The /send command

Moving on, replace the code in the /send command handler with the following:

async def send(ctx):
    sender_username = ctx.message.author
    incoming_msg = ctx.message.content
    try:
        split_msg = incoming_msg.split(" ")
        amount = float(split_msg[1])
        receiver = split_msg[2]
        message = "Sending {} SOL to {}, please wait !!!".format(
            amount, receiver)
        await ctx.send(message)
        transaction_id = send_sol(sender_username, amount, receiver)
        if transaction_id is not None:
            message = "You have successfully sent {} SOL to {} n".format(
                amount, receiver)
            message += "The transaction id is {}".format(transaction_id)
            await ctx.send(message)
        else:
            message = "Failed to send SOL"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to send SOL')
        return

In the code above, we get the username of the user who sent the /send command, the message received, and then we store these values in a variables named sender_username and incoming_msg respectively.

We then parse the incoming message, retrieve from it the amount of SOL that the user wishes to send and the receiver account’s address, and store these values in variables named amount and receiver, respectively.

After storing the amount and receiver, a message is sent to the user to notify that the amount of SOL she wishes to send is being sent to the receiver, and to ask the user to wait.

After notifying the user, we call the send_sol() function in the wallet.py file. We pass as arguments the user’s username, the amount of SOL she wishes to transfer, and the receiver’s address. We then store the transaction ID returned by this function in a variable named transaction_id.

Lastly, we use conditional logic to check if the transaction ID isn’t equal to None. If that is the case we store a message in a variable named message saying that the user successfully sent SOL to the desired account. We attach the transaction ID to the message and we send the message to the user.

However, if the value returned by the send_sol() function is equal to None we send a message saying that the bot failed to send SOL.

After replacing the code in each command handler, the main.py file should look like the following:

import os
import discord
from discord.ext import commands
from dotenv import load_dotenv
from wallet import create_account, fund_account, get_balance, send_sol

load_dotenv()
description = ''' A bot that allows you to create and manage a Solana wallet  '''
intents = discord.Intents.default()
bot = commands.Bot(command_prefix='/', description=description, intents=intents)

@bot.event
async def on_ready():
    print('Bot is online')
    print(bot.user.name)
    print(bot.user.id)
    print('------ n')

@bot.command(description='Create a new solana account')
async def create(ctx):
    sender_username = ctx.message.author
    try:
        public_key = create_account(sender_username)
        if public_key is not None:
            message = "Solana Account created successfully.n"
            message += "Your account public key is {}".format(public_key)
            await ctx.send(message)
        else:
            message = "Failed to create account.n"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to create account')
        return

@bot.command(description='Fund your account')
async def fund(ctx):
    sender_username = ctx.message.author
    incoming_msg = ctx.message.content
    try:
        amount = float(incoming_msg.split(" ")[1])
        if amount <= 2 :
            message = "Requesting {} SOL to your Solana account, please wait !!!".format(amount)
            await ctx.send(message)
            transaction_id = fund_account(sender_username, amount)
            if transaction_id is not None:
                message = "You have successfully requested {} SOL for your Solana account n".format(
                    amount)
                message += "The transaction id is {}".format(transaction_id)
                await ctx.send(message)
            else:
                message = "Failed to fund your Solana account"
                await ctx.send(message)
        else:
            message = "The maximum amount allowed is 2 SOL"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to fund account')
        return

@bot.command(description='Check your account balance')
async def balance(ctx):
    sender_username = ctx.message.author
    try:
        data = get_balance(sender_username)
        if data is not None:
            public_key = data['publicKey']
            balance = data['balance']
            message = "Your Solana account {} balance is {} SOL".format(
                public_key, balance)
            await ctx.send(message)
        else:
            message = "Failed to retrieve balance"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to check account balance')
        return

@bot.command(description='Send SOL to another account')
async def send(ctx):
    sender_username = ctx.message.author
    incoming_msg = ctx.message.content
    try:
        split_msg = incoming_msg.split(" ")
        amount = float(split_msg[1])
        receiver = split_msg[2]
        message = "Sending {} SOL to {}, please wait !!!".format(
            amount, receiver)
        await ctx.send(message)
        transaction_id = send_sol(sender_username, amount, receiver)
        if transaction_id is not None:
            message = "You have successfully sent {} SOL to {} n".format(
                amount, receiver)
            message += "The transaction id is {}".format(transaction_id)
            await ctx.send(message)
        else:
            message = "Failed to send SOL"
            await ctx.send(message)
    except Exception as e:
        print('error:',e)
        await ctx.send('Failed to send SOL')
        return

bot.run(os.environ['BOT_TOKEN'])

Go back to the terminal window running the main.py file, stop the process, and then run it again with the following command:

python main.py

Go to your preferred Discord client and send the /create command to your bot to create a new Solana account. You should see something similar to the following:

Copy the public key and store it somewhere for later use. Send the /create command again to generate a new Solana account.

Now, send the /fund 2 command to fund your Solana account with two SOL tokens. Feel free to change the amount to any value lower than two. You should see something similar to the following:

Screenshot of the fund 2 command in action

Make sure to test the other commands to make sure they each work as intended.

Conclusion

In this tutorial, you learned how to create a Solana Discord chat bot wallet capable of creating a Solana account, funding the account, retrieving the account’s balance, and sending SOL to another Solana account.

The Solana Discord wallet that we built in this tutorial isn’t ready for production yet, and I advise adding more features such as authentication and account balance checking before sending SOL to another account. The complete application code is available in this repository.

For more information about Solana and the discord.py package, please visit the Solana documentation and the discord.py documentation.

Join organizations like Bitso and Coinsquare who use LogRocket to proactively monitor their Web3 apps

Client-side issues that impact users’ ability to activate and transact in your apps can drastically affect your bottom line. If you’re interested in monitoring UX issues, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.https://logrocket.com/signup/

LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app or site. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.

Modernize how you debug web and mobile apps — .

Carlos Mucuho A geologist-turned-programmer.

9 Replies to “How to build a Solana Discord wallet with Python”

  1. Hey Carlos, thanks for the informative blog post. Would like you to clarify, did you mean 1 trillion or 1 billion, because the number and the words are contradicing themselves. I mean in the fund_account function. You put 1,000,000,000 but then say it as 1 trillion

  2. “secret_key”: “\u00d1R>\u00fe5\u0088\u009bm\u0014\u0095$u\u00aa\u00e3^\u00e8\u00a0\u0097\b\u00c…
    how to convert it to utf-8

  3. ‘charmap’ codec can’t encode character ‘\x9f’ in position 45: character maps to having this problem while decoding secret_key in create_account

  4. This is super informative and would love to give it a try! I only have one question – is it possible to use a custom token built on the Solana chain, such as by using the custom token address/ID?

  5. Hey, I have a question. Whenever I try to execute a command I get in my IDE, the error: ‘str’ object has no attribute ‘to_solders’. In discord I get the message “Failed to fund account”. Only the create command works for me. How can I solve this problem?

    1. Yo,
      After debbuging this a little bit, I found that the problem is when he is trying to use the public key,
      you should use PublicKey(account[“public_key”])
      instead of account[‘public_key’]
      hope that it helps to you or anyone that try to follow this tutorial 🙂

  6. Wow, nice tutorial thanks for sharing it. Does this still work? I was trying another youtube tutorial on creating wallets and transactions using solana.py and it doesn’t work anymore.

    Thanks!

Leave a Reply