DEV Community

Valentin Madrid
Valentin Madrid

Posted on

Interact with your Solana programs in Python

Most of client side interaction with Solana Programs is done in Typescript for now. That makes sense, because most frontend frameworks are built on top of Javascript.
But what if you want to use Python to interact with your program ? Let's see how this can be done in this short tutorial.

The library we'll be using is called AnchorPy, and was made by Kevin Heavey.
Link to the AnchorPy documentation: https://kevinheavey.github.io/anchorpy/

Let's create some tests for an Anchor program in Python. Here's a link to the program's repo we will the interacting with.

Install AnchorPy

First of all, install anchorpy and solders:
pip3 install anchorpy
pip3 install solders

Now, let's create a test file at tests/test.py.

First of all, let's import some dependencies:

import asyncio
from anchorpy import create_workspace, close_workspace, Context
from solders.system_program import ID as SYS_PROGRAM_ID
from solders.pubkey import Pubkey
from solders.keypair import Keypair
from solders.sysvar import RENT
Enter fullscreen mode Exit fullscreen mode

Testing our Anchor program

Our code will compose of a base that looks like this:

async def main():
    # Read the deployed program from the workspace.
    workspace = create_workspace()
    # The program from the workspace we want to use
    program = workspace["seahorse"]

    # Close all HTTP clients in the workspace.
    await close_workspace(workspace)

asyncio.run(main())
Enter fullscreen mode Exit fullscreen mode

Ok, now we can get to our test's logic. We have three instructions in our Anchor program. Let's get through them and document everything that is happening.

First, we define the two Program addresses for the Token program and the Associated Token Program. Then we generate a Keypair for the Token Mint we're going to create afterwards.

TOKEN_PROGRAM_ID = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
ASSOCITAED_TOKEN_PROGRAM_ID = Pubkey.from_string("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL")

# Create a Mint keypair. This will be our token mint.
mint = Keypair()
Enter fullscreen mode Exit fullscreen mode

Invoking instructions

Now, we call the create_token instruction from our program and print the transaction signature.

create_token = await program.rpc["create_token"](ctx=Context(accounts={
        "mint": mint.pubkey(),
        "signer": program.provider.wallet.payer.pubkey(),
        "system_program": SYS_PROGRAM_ID,
        "rent": RENT,
        "token_program": TOKEN_PROGRAM_ID
    }, signers=[program.provider.wallet.payer, mint]))

print("Create token signature: ", create_token)
Enter fullscreen mode Exit fullscreen mode

You may see some similarities with the Typescript Anchor library here. "create_token" is the instruction to be executed and the "accounts" context are the accounts passed into our instruction. The signers here are the Anchor workspace keypair and the Mint Keypair, needed to sign the creation of a Mint account. Pretty straightforward right ?

Derive PDA's

We want to create an associated token account now, for that we will need it's Public Key. You can do that by using find_program_adress.

associated_token_account_pubkey, nonce = Pubkey.find_program_address([bytes(program.provider.wallet.payer.pubkey()), bytes(TOKEN_PROGRAM_ID), bytes(mint.pubkey())], ASSOCITAED_TOKEN_PROGRAM_ID)
Enter fullscreen mode Exit fullscreen mode

We can now use this Pubkey in the instruction's accounts. This would look like:

create_associated_token_account = await program.rpc["create_associated_token_account"](ctx=Context(accounts={
        "mint": mint.pubkey(),
        "token_account" : associated_token_account_pubkey,
        "signer": program.provider.wallet.payer.pubkey(),
        "system_program": SYS_PROGRAM_ID,
        "rent": RENT,
        "token_program": TOKEN_PROGRAM_ID,
        "associated_token_program" : ASSOCITAED_TOKEN_PROGRAM_ID
    }, signers=[program.provider.wallet.payer]))

print("Create associated token account signature: ", create_associated_token_account)
Enter fullscreen mode Exit fullscreen mode

Pass arguments to instruction

Last, let's see how we can add additional arguments into our instruction by minting some tokens to our workspace Keypair. Here we are mining 1000 tokens to the Token Account created before.

mint_token = await program.rpc["mint_token"](1000, ctx=Context(accounts={
        "mint": mint.pubkey(),
        "recipient" : associated_token_account_pubkey,
        "signer": program.provider.wallet.payer.pubkey(),
        "system_program": SYS_PROGRAM_ID,
        "rent": RENT,
        "token_program": TOKEN_PROGRAM_ID,
    }, signers=[program.provider.wallet.payer]))

print("Mint token signature: ", mint_token)
Enter fullscreen mode Exit fullscreen mode

Now, we could run our tests in the command line, but let's do something more convenient. Let's go to the Anchor.toml file and change the current test script to this:

[scripts]
test = "python3 tests/test.py"
Enter fullscreen mode Exit fullscreen mode

You can now run anchor test --skip-local-validator and run the tests we wrote.

Conclusion

Thanks for reading this guide.
If you have any more questions, please contact me on Twitter or join the SolanaU Discord Server.

Top comments (0)