Interact with your Solana programs in Python
Tags: solana, python, anchor, testing
Published: 2023-05-05 08:38:52

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](https://github.com/kevinheavey).
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] (https://github.com/solana-developers/program-examples/tree/main/tokens/transfer-tokens/seahorse) 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:

```py
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
```
### Testing our Anchor program

Our code will compose of a base that looks like this:
```py
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())
```

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.
```py
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()
```
### Invoking instructions

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

```py
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)
```
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`.
```py
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)
```
We can now use this Pubkey in the instruction's accounts. This would look like:
```py
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)
```

### 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.

```py
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)
```

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:
```toml
[scripts]
test = "python3 tests/test.py"
```
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](https://twitter.com/valentinmadrid_) or join the [SolanaU](https://discord.gg/solanau) Discord Server.