Hello! Welcome back to your journey into Solana development.
In our last lesson, we reached a major milestone: we took our Rust code, compiled it into a Solana program, and deployed it to the network. We now have a live program on-chain, identified by its unique Program ID.
Today, we'll answer the next logical question: "How do I use it?" This lesson focuses on the crucial skill of interacting with your deployed program directly from the command line. Our learning outcome is to interact with a deployed program using the Solana CLI.
While you'll eventually build sophisticated frontends to interact with your programs, the Solana CLI is an indispensable tool for initial testing, debugging, and verification. Think of it as the curl or Postman of the blockchain world—it lets you send raw requests to your on-chain endpoint to confirm its behavior. This foundational skill will give you a much deeper understanding of how transactions work under the hood.
Recap: Where We Are
From the previous lesson, you should have:
- A compiled Solana program (
.sofile). - A Program ID from deploying that program to a local validator or Devnet.
- A running
solana-test-validatorinstance. - A local wallet keypair funded with SOL.
We will build on this foundation to send a transaction that calls an instruction within our deployed program.
Program Interaction: Core CLI Commands
Before we construct a transaction, let's review the CLI commands that allow us to "see" what's on the blockchain. These are our primary tools for verifying the state of our program and its data.
Viewing Program Details
First, you can always check the status of your deployed program using solana program show. This is the first "interaction" you should have after deployment to confirm everything is set up correctly.
solana program show <YOUR_PROGRAM_ID>
This command provides essential metadata.
The official Solana documentation, "Deploying Programs," provides a quick reference for CLI commands. Let's look at the solana program show command and the information it provides.
Please review the subsection "View Program Metadata". Focus on the example output and the meaning of each field: Program ID, Owner, ProgramData Address, Authority, and Data Length. Understanding these is key to managing your deployed programs.
The most important fields are:
- Program Id: The public address of your program.
- Authority: The public key of the wallet that is authorized to upgrade or close this program. By default, this is your local CLI wallet.
- Data Length: The amount of space in bytes your program's executable code occupies on the blockchain.
Viewing Account Data
The real magic happens when your program modifies data. Since Solana programs themselves are stateless, they write data to separate accounts. The solana account command lets you inspect the contents of any account on the network.
solana account <ACCOUNT_ADDRESS>
This command will show you the account's owner, lamport balance, and its data, usually encoded in base64. To get a more machine-readable output and see the raw data, you can use:
solana account <ACCOUNT_ADDRESS> --output json-compact
This will return a JSON object. The data field will contain the account's data as a base64 string. You can then use any base64 decoder to see the raw bytes, which should match the data structure you serialized with borsh in your program. This is an incredibly powerful debugging technique.
Deconstructing a Transaction
Now, how do we trigger an instruction that changes an account's data? We need to construct and send a transaction.
Any transaction, whether from a UI or the CLI, is fundamentally a bundle of one or more instructions sent to the network. Each instruction tells the Solana runtime:
program_id: Which program to execute.accounts: Which accounts to provide to the program.data: What instruction-specific data to pass (e.g., arguments for the function).
Watch this short clip to see how you can analyze any transaction on a block explorer to identify these three core components. The presenter uses TypeScript to rebuild the transaction, but the analytical process of finding the program ID, accounts, and instruction data is universal.
How to call any Solana Program [Solana Tutorial] - Aug 21st '24
The video "How to call any Solana Program" does an excellent job of reverse-engineering a live transaction. We'll focus on the deconstruction part to understand the pieces we need to assemble using the CLI.
Watch the segment from 03:18 to 05:40. The example is pump.fun, but the principles apply to any program. Notice how the presenter uses the block explorer to identify the program being called, the list of accounts involved (and their roles like writable or signer), and the raw Instruction Data.
Calling Your Program with solana-test-validator
The Solana CLI ecosystem is optimized for deploying and managing programs. For invoking custom instructions, the most common method is writing a client script. However, the solana-test-validator offers a powerful feature that allows us to call our program's instructions directly for testing purposes.
The solana-test-validator command has a --bpf-program flag. When you start the validator with this flag, it not only loads your program but also exposes a special RPC method called sendTransaction that can be called with a generic invoke instruction.
Let's walk through the steps with a hypothetical "counter" program.
Assumptions for our Counter Program:
- It has been deployed and we have its
PROGRAM_ID. - It has an instruction that takes no arguments (the instruction data is
[0]). - The instruction expects a single writable account, which it uses to store a
u64counter.
Step 1: Prepare Account and Validator
First, we need an account to store the counter. Let's create a new keypair for it.
# Create a keypair file for our data account
solana-keygen new -o ./counter-account.json
Keep the public key of this new account handy.
Now, stop your current solana-test-validator if it's running. Restart it with your program pre-loaded.
# Get the path to your deployed program's executable
# It's typically in your project's target/deploy/ directory
PROGRAM_EXECUTABLE_PATH=./target/deploy/my_program.so
# Get your program's ID (the public key from the keypair file)
PROGRAM_ID=$(solana-keygen pubkey ./target/deploy/my_program-keypair.json)
# Start the validator
solana-test-validator --bpf-program $PROGRAM_ID $PROGRAM_EXECUTABLE_PATH --reset
The --reset flag starts a clean ledger.
Step 2: Create and Fund the Data Account
Our program needs an account to write to. We can't just use the keypair; the account must exist on the ledger. We can create it with a simple CLI command. The size needs to be 8 bytes for our u64 counter.
# Get the public key of the account we're creating
COUNTER_ACCOUNT_PUBKEY=$(solana-keygen pubkey ./counter-account.json)
# Create the empty account on the local ledger, owned by our program
solana create-account --owner $PROGRAM_ID $COUNTER_ACCOUNT_PUBKEY 0.001 --space 8
This command creates an account at the address COUNTER_ACCOUNT_PUBKEY, allocates 8 bytes of space for data, funds it with enough SOL to be rent-exempt, and sets its owner to our PROGRAM_ID.
You can now verify this with solana account $COUNTER_ACCOUNT_PUBKEY. You will see it has 8 bytes of zeroed-out data.
Step 3: Invoke the Instruction
Now we can finally call our program. We use solana invoke, specifying the program ID and the account(s) to pass.
# Invoke the instruction in our program
solana invoke $PROGRAM_ID --keypair ./counter-account.json
The --keypair flag here is slightly confusing. It tells the command which account to pass to the instruction. Since our instruction expects one account, this is all we need.
Step 4: Verify the Result
If the command was successful, our program should have executed and written to the account. Let's check!
solana account $COUNTER_ACCOUNT_PUBKEY
Look at the data field. It should no longer be all zeros. If you use --output json-compact and decode the base64 data, you should see the bytes for the number 1 (e.g., 0100000000000000).
Congratulations! You have successfully interacted with your program using the Solana CLI.
Test your understanding!
You have a deployed program with the ID MyProg111.... It has an instruction that sets a "status message" in a data account. This instruction requires two accounts to be passed:
- A writable data account to store the message.
- A signer account representing the user who is authorized to change the message.
The data account's address is DataAcc222... and your personal wallet address is UserWall333....
Which solana invoke command would you use to call this instruction? (You may need to look up the syntax with solana invoke --help).
Show answer
The correct command is:
solana invoke MyProg111... --keypair DataAcc222... --keypair UserWall333...
The solana invoke command takes multiple --keypair arguments, passing each one as an account to the program. The order matters and must match what the program expects. The first keypair corresponds to the first account in the instruction's accounts array, and so on. Since the user's wallet is a signer, it must be included.
Conclusion
In this lesson, you've bridged the gap between deploying a program and using it. You learned how to leverage the Solana CLI not just for deployment, but as a powerful tool for verification and direct interaction on a local network.
Here are the key takeaways:
solana program showandsolana accountare your essential CLI tools for inspecting the on-chain state of your program and its related data accounts.- A transaction is built from instructions, and each instruction contains a Program ID, a list of Accounts, and serialized Data.
- The
solana-test-validatorprovides a testing environment where you can use commands likesolana create-accountandsolana invoketo directly test your program's logic without writing a full client application.
You now have a complete end-to-end workflow for native Solana development: writing, building, deploying, and interacting. This foundation is solid. However, building transactions by hand and using solana invoke is cumbersome. For real applications, we need a more developer-friendly approach.
That's where the Anchor framework comes in. In our next module, we will start fresh with Anchor. You will see how it dramatically simplifies development by abstracting away much of the boilerplate we've been writing manually, including serialization, account validation, and client-side transaction building. You'll be able to leverage your Rust skills in a much more productive and secure environment.
Can't find a good explanation? Sign up and we'll make it for you
Sign up