This guide provides a step-by-step overview of deploying a program on Sonic SVM. Familiarity with command-line tools is necessary.
1. Install CLI Tools
Ensure that the Solana command line tools are installed as you will be using these tools throughout the deployment process.
2. Write Your Program
Develop your program logic. For those using Rust with Anchor, input your program logic into the lib.rs file located in the src directory of your project. Confirm that your program compiles correctly and passes all tests.
3. Build Your Program
Compile your program to a BPF (Berkeley Packet Filter) executable, the format required for on-chain programs:
With Anchor
Run anchor build. Anchor manages the compilation details.
Without Anchor
Use cargo build-bpf for direct Rust usage.
4. Deploy Your Program
Before deploying your program, ensure your CLI is set to the Sonic Devnet network using the following command:
solana config set --url https://devnet.sonic.game
After deploying your building your program and configuring your RPC URL, you can deploy your program with the following command:
solana program deploy <PATH_TO_YOUR_COMPILED_PROGRAM>
Ensure that you have enough SOL in your wallet to cover deployment costs. You can get some devnet tokens from the Sonic faucet.
After configuring your network, you may now deploy your program to Sonic. Use the following commands based on your setup:
With Anchor
If the Anchor CLI is installed, you can deploy your program with the following command. This handles the deployment automatically.
anchor deploy
Without Anchor
If you're using the native solana method to author your program, you can also use the Solana CLI to deploy your program.
5. Verify Deployment
Post-deployment, check that your program operates as intended. Interact with your program using Solana's web3.js library or the CLI tools to send transactions.
Additional Tips:
Keep your program's keypair secure for future upgrades or interactions.
Monitor the network and your program's performance, particularly if it gains widespread usage.
Example: "Hello Sonic!" Program
This example implements a simple Greeting Counter Program on Sonic.
With Solana Native Program
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
pub counter: u32,
}
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
msg!("Hello, Sonic World!");
let accounts_iter = &mut accounts.iter();
let account = next_account_info(accounts_iter)?;
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
greeting_account.serialize(&mut *account.data.borrow_mut())?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
}
With Anchor Program
If you are building with Anchor, you may use the variant below. Please remember to change the program ID after building your program with anchor build before deploying it.
import {
Connection,
PublicKey,
Keypair,
Transaction,
TransactionInstruction,
SystemProgram,
sendAndConfirmTransaction
} from '@solana/web3.js';
import bs58 from 'bs58';
import * as borsh from 'borsh';
class GreetingAccount {
counter = 0;
constructor(fields?: { counter: number }) {
if (fields) {
this.counter = fields.counter;
}
}
}
const GreetingSchema = new Map([
[GreetingAccount, { kind: 'struct', fields: [['counter', 'u32']] }]
]);
const programId = new PublicKey('<REPLACE_WITH_YOUR_PROGRAM_ID>');
const connection = new Connection('https://devnet.sonic.game', 'confirmed');
const feePayer = Keypair.fromSecretKey(
bs58.decode(
'<REPLACE_WITH_YOUR_PRIVATE_KEY>'
)
);
async function sayHello() {
const GREETING_SIZE = borsh.serialize(
GreetingSchema,
new GreetingAccount()
).length;
const greetedAccountKeypair = new Keypair();
const greetedAccountPubkey = greetedAccountKeypair.publicKey;
const lamports =
await connection.getMinimumBalanceForRentExemption(GREETING_SIZE);
const createGreetingAccountIx = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
lamports,
newAccountPubkey: greetedAccountPubkey,
programId: programId,
space: GREETING_SIZE
});
// Create greet instruction
const greetIx = new TransactionInstruction({
keys: [
{
pubkey: greetedAccountPubkey,
isSigner: false,
isWritable: true
}
],
programId
});
const transaction = new Transaction().add(createGreetingAccountIx, greetIx);
const txHash = await sendAndConfirmTransaction(connection, transaction, [
feePayer,
greetedAccountKeypair
]);
console.log(`Use 'solana confirm -v ${txHash}' to see the logs`);
const greetingAccount = await connection.getAccountInfo(greetedAccountPubkey);
// Deserialize the account data
const deserializedAccountData = borsh.deserialize(
GreetingSchema,
GreetingAccount,
greetingAccount!.data
);
console.log(
'Sonic Greeting successful. Account data:',
deserializedAccountData
);
}
sayHello()
.then(() => console.log('Done'))
.catch(console.error);
With Anchor Program dApp
import {
Connection,
PublicKey,
Keypair,
Transaction,
SystemProgram,
sendAndConfirmTransaction
} from '@solana/web3.js';
import { Program, AnchorProvider, web3 } from '@project-serum/anchor';
import { IDL } from './idl/hello_sonic_world.json'; // Make sure you have the correct IDL file
const programId = new PublicKey('<REPLACE_WITH_YOUR_PROGRAM_ID>');
const connection = new Connection('https://devnet.solana.com', 'confirmed');
const feePayer = Keypair.fromSecretKey(
bs58.decode('<REPLACE_WITH_YOUR_PRIVATE_KEY>')
);
// Anchor setup
const wallet = new AnchorProvider(connection, feePayer, {
preflightCommitment: 'confirmed',
});
const program = new Program(IDL, programId, wallet);
async function sayHello() {
// Create the greeting account
const greetedAccountKeypair = Keypair.generate();
const greetedAccountPubkey = greetedAccountKeypair.publicKey;
const lamports = await connection.getMinimumBalanceForRentExemption(
8 + 4 + 32 // space for the account (discriminator, counter, and authority)
);
const createGreetingAccountIx = SystemProgram.createAccount({
fromPubkey: feePayer.publicKey,
lamports,
newAccountPubkey: greetedAccountPubkey,
programId: program.programId,
space: 8 + 4 + 32, // size of the GreetingAccount (8 discriminator + 4 counter + 32 authority)
});
const transaction = new Transaction().add(createGreetingAccountIx);
await sendAndConfirmTransaction(connection, transaction, [
feePayer,
greetedAccountKeypair,
]);
console.log(`Greeting account created with public key: ${greetedAccountPubkey.toBase58()}`);
// Initialize the greeting account
await program.rpc.initialize(feePayer.publicKey, {
accounts: {
greetingAccount: greetedAccountPubkey,
user: feePayer.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [greetedAccountKeypair, feePayer],
});
console.log('Greeting account initialized');
// Increment the greeting counter
await program.rpc.incrementGreeting({
accounts: {
greetingAccount: greetedAccountPubkey,
authority: feePayer.publicKey,
},
});
console.log('Greeting incremented');
// Fetch the account data
const account = await program.account.greetingAccount.fetch(
greetedAccountPubkey
);
console.log('Greeting successful. Account data:', account);
}
sayHello()
.then(() => console.log('Done'))
.catch(console.error);
Running the above script [either with Solana Native, or with Anchor] programs should output the following:
Use 'solana confirm -v TsCwj8s8meQFSfqUKdHqDQVzfUbqRcugP2gJ5kaGCj4v4D7aKvohqQQncvUFunqZVH9sS6jhESMjwe6SHGPfrXf' to see the logs
Sonic Greeting successful. Account data: GreetingAccount { counter: 1 }
Done
You can find an example of the executed transaction here. The program address for this program is here.