Rust

gRPC Request

use server::{HealthRequest, SendRequest};
use server::server_client::ServerClient;
use tonic::transport::{Channel};
use std::error::Error;
use tonic::metadata::AsciiMetadataValue;
use solana_client::rpc_client::RpcClient;
use solana_program::system_instruction;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signer};
use solana_sdk::transaction::Transaction;
use std::str::FromStr;
use base64::{engine::general_purpose, Engine as _};
use bincode;
use rand::Rng;

pub mod server {
    tonic::include_proto!("serverpb");
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // BlockRazor relay endpoint address
    let blzendpoint = "http://frankfurt.solana-grpc.blockrazor.xyz:80";
     // replace your solana rpc endpoint
	let mainnetrpc = "";
    // replace your authKey
    let authkey = "";
    // relace your private key(base58)
	let privatekey = "";
	// send mode
    let mode = "fast";

    // tip amount
	let tipamount  = 1_000_000;

    let tip_accounts = [
        "Gywj98ophM7GmkDdaWs4isqZnDdFCW7B46TXmKfvyqSm", 
        "FjmZZrFvhnqqb9ThCuMVnENaM3JGVuGWNyCAxRJcFpg9", 
        "6No2i3aawzHsjtThw81iq1EXPJN6rh8eSJCLaYZfKDTG", 
        "A9cWowVAiHe9pJfKAj3TJiN9VpbzMUq6E4kEvf5mUT22"];
    
    let mut rng = rand::thread_rng();
    let random_index = rng.gen_range(0..tip_accounts.len());
    let tip_account = tip_accounts[random_index];

    let channel = Channel::from_shared(blzendpoint.to_string())
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid URI: {}", e)))?
        .connect()
        .await
        .map_err(|e| Box::<dyn Error>::from(format!("Connection error: {}", e)))?;

    let apikeyvalue = AsciiMetadataValue::try_from(authkey)
        .map_err(|e| Box::<dyn Error>::from(format!("Invalid API key format: {}", e)))?;

    let mut client = ServerClient::new(channel);

    let mut request = tonic::Request::new(HealthRequest {});
    request.metadata_mut().insert("apikey", apikeyvalue.clone());
    let response = client.get_health(request).await?;
    println!("get health response={:?}", response.into_inner().status);

    let from = Keypair::from_base58_string(privatekey);
    let frompubkey = Signer::pubkey(&from);
    let topubkey = Pubkey::from_str(tip_account).expect("Failed to parse receivers pubkey");

    let rpcurl = String::from(mainnetrpc);
    let connection = RpcClient::new_with_commitment(rpcurl, CommitmentConfig::confirmed());

    let ix = system_instruction::transfer(&frompubkey, &topubkey, tipamount);
    let recent_blockhash = connection
        .get_latest_blockhash()
        .expect("Failed to get recent blockhash.");
    let tx = Transaction::new_signed_with_payer(&[ix], Some(&frompubkey), &[&from], recent_blockhash);

    // tx base64
    let serialized = bincode::serialize(&tx)?;
    let base64_encoded = general_purpose::STANDARD.encode(serialized);

    let mut tx_request: tonic::Request<SendRequest> = tonic::Request::new(SendRequest {
        transaction: base64_encoded,
        mode: mode.to_string(),
    });
    tx_request.metadata_mut().insert("apikey", apikeyvalue.clone());
    let response = client.send_transaction(tx_request).await?;
    println!("SEND TX RESPONSE={:?}", response);

    Ok(())
}

Proto

syntax = "proto3";

package serverpb;

option go_package = "./pb/serverpb";


service Server {
    rpc SendTransaction(SendRequest) returns(SendResponse) {};

    rpc GetHealth(HealthRequest) returns(HealthResponse) {};
}

message SendRequest {
    string transaction = 1;
    string mode = 2;
}

message SendResponse {
    string signature = 1;
}

message HealthRequest {
}

message HealthResponse {
    string status = 1;
}

Response Example

Success

SEND TX RESPONSE = Response {message: SendResponse {signature: "4VpQCBBw3qD32DmvjTSiVmFhzVZknP3PBeAGpjGqKfGf8vLfsKoSKG4nPKYs5M4wA1y5gqEBBmXqercPvmtcrBnq"},extensions: Extensions}

Error

Error: Status {code: Unknown,message: "Insufficient tip, please increase the tip amount and try again, at least 1000000 lamports",source: None}

Last updated