Reputation: 308
I connect to Solana websocket and subscribe to program data of Raydium decentralized exchange account (https://raydium.io/swap/) to listen to swap transactions.
I successfully connected, but I cannot find resourced on how to transform (decode & deserialize) an UiAccountData binary to something readable. What I believe am missing is the deserialization struct of a Raydium "swap".
const RAYDIUM_WALLET_ADDRESS: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C";
const SOLANA_WS_MAINNET: &str = "wss://api.mainnet-beta.solana.com";
const HELIUS_WS_MAINNET: &str = "wss://mainnet.helius-rpc.com";
const HELIUS_API_KEY: &str = "HELIUS_API_KEY";
#[derive(Debug, Serialize, Deserialize)]
pub struct RaydiumSwap {
pub instruction: u8,
pub amount_in: u64,
pub min_amount_out: u64,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenv::dotenv().ok();
let raydium_pubkey = Pubkey::from_str_const(RAYDIUM_WALLET_ADDRESS);
let helius_api_key = std::env::var(HELIUS_API_KEY).expect("HELIUS_API_KEY must be set");
tokio::spawn(async move {
println!("Connecting to Helius Solana Mainnet");
let solana = PubsubClient::new(&format!("{}?api-key={}", HELIUS_WS_MAINNET, helius_api_key))
.await
.expect("Cannot connect to Helius Solana Mainnet");
print!("Connected to Helius Solana Mainnet");
let rpc_account_info_config = RpcAccountInfoConfig {
commitment: Some(CommitmentConfig::confirmed()),
encoding: Some(UiAccountEncoding::Base64),
data_slice: None,
min_context_slot: None,
};
let rpc_program_accounts_config = RpcProgramAccountsConfig {
filters: None,
account_config: rpc_account_info_config,
with_context: None,
sort_results: None,
};
let subscription = solana
.program_subscribe(&raydium_pubkey, Some(rpc_program_accounts_config))
.await
.expect("Cannot subscribe to account");
println!("Subscribed to account");
let (mut account_stream, _unsubscribe) = subscription;
while let Some(response) = account_stream.next().await {
deserialize_raydium_swap(response);
}
});
loop {}
Ok(())
}
fn deserialize_raydium_swap(response: Response<RpcKeyedAccount>) {
if let UiAccountData::Binary(binary_str, _) = response.value.account.data {
// Step 1: Decode the base64 string into bytes
let decoded_bytes = BASE64_STANDARD.decode(binary_str).expect("Failed to decode base64");
let raydium_swap: RaydiumSwap = bincode::deserialize(&decoded_bytes).expect("Failed to deserialize data");
println!("{:?}", raydium_swap);
} else {
println!("The provided data is not of Binary type");
}
}
If I print out the response
, it looks like this:
Response {
context: RpcResponseContext {
slot: 322346382,
api_version: None,
},
value: RpcKeyedAccount {
pubkey: "6T5HPoskH55fa61GNXP4SSNSMQcmyeAo2h41TMjJ1Jct",
account: UiAccount {
lamports: 5324400,
data: Binary(
"9+3j9dfD3kazIT+6i/nIf6keR4GWKMOD4AvqfpjHoD4DuhBpz8P286cm3/If6FlkWTnsnxfd7RQupsSdPfJu9q5y6dmi83wZt4D+mYqLzfRAnwhRaEX6FLFVTiffWIsc/Rl8DBS2uCMBZSyXTmO8C3qn9v67NXreXSsZlvfMU/ygJ4R+enAqiaGpU9alUOF7CYe8A7MbBqBR5xw305nbKGfZK67PDo3xBpuIV/6rgYT7aH9jRhjANdrEOdwa6ztVmKDwAAAAAAHA+v6YxhTMcFzuNvBHHMaY2JcB8bpPm0fSdv/8nEQMSQbd9uHXZaGT2cvhRs7reawctIXtX1s3kTqM9YV+/wCpBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKk+iWorY4q5NIXH6uKU+mthvYnZavGm2oz7Q13SFAmrDv0ACQkJCI1SqZKsAAAaq/0BAAAAAD1hvckyBwAAnZGtAQAAAAAuM64n4AYAAGUFuWcAAAAA6gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
Base64,
),
owner: "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C",
executable: false,
rent_epoch: 18446744073709551615,
space: Some(
637,
),
},
},
}
So it seems I first have to base64
decode the response.value.account.data
and then deserialize it into a struct.
However, I am struggling to find out how to do that. When data is deserialized, I assume I will be able to filter through it only to process Swap transactions.
Please advise, thank you.
P.S. I am using Helius WebSockets server (which needs API key) just because Solana Mainnet's looks unstable to me and is slow.
Upvotes: 0
Views: 46