Writing a Raydium sniper in Rust part 4
This is part four of this series, where we are creating a Solana sniper bot for Raydium from scratch in Rust. See the first post here which includes an introduction about why we use a compiled language instead of JavaScript, as well as prerequisites and a brief introduction about me. You can find the latest GitHub source here, which will be updated with each new post.
Solana needs to be wrapped for most meme coin swaps, but is there another reason for this command?
Disclaimer
The information provided in this series of articles is for educational purposes only and should not be considered financial or investment advice. Trading, including sniping tokens, carries significant risks, and there is no guarantee of profit.
Any decisions you make based on the content shared here are solely your responsibility. I am not liable for any financial losses incurred as a result of implementing the strategies, code, or techniques discussed in these articles.
Always trade responsibly. Never risk more money than you can afford to lose. It is essential to conduct your own research and consult with a professional financial advisor if necessary before engaging in any trading activities.
What to expect today
Last time, we explored swap and initialization transactions, delving deeper into how to extract actionable data from Solana's blockchain for efficient token sniping. This includes identifying transaction patterns.
Today, we will learn how to build our own CLI command to wrap SOL to wSOL, the common currency for swapping meme coins. Additionally, we will have a short discussion on transaction optimization.
Wrapped Solana
The first question that might come to mind is: why do I need to wrap my Solana, and what does it even mean to wrap it?
Solana (SOL) is the native token of the Solana blockchain, used for gas fees and staking. wSOL, however, is an SPL token with the exact same value as SOL. Just like any meme coin is created as an SPL token, wSOL is also created using Solana's SPL token program.
Holding SOL in the form of wSOL means you are always ready to trade without needing to add extra instructions for wrapping your SOL when a new meme coin is released.
Creating a cli command
Now that we’ve covered the basics, let's create a CLI command to convert our regular, boring SOL into hot-swappable wSOL.
Create a new package with
Create the following files:With that, we have created all the files needed for the wrap SOL command we are going to build.
Fill shell/Cargo.toml
with
[package]
name = "shell"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
keywords.workspace = true
version.workspace = true
readme.workspace = true
categories.workspace = true
publish.workspace = true
[dependencies]
clap = { workspace = true }
utils = { workspace = true }
solana-client = { workspace = true }
solana-sdk = { workspace = true }
spl-token-client = { workspace = true }
spl-token = { workspace = true }
tokio = { workspace = true }
[lints]
workspace = true
We have added clap
, a common Rust library for creating CLI commands, which provides an easy syntax for defining command-line arguments directly from structs and supports hinting.
In addition to clap
we have also introduced the spl-token-client
and spl-token
libraries, which are used for wrapping SOL to wSOL.
Fill shell/src/main.rs
with
A lot happens in the 37 lines of code above in main.rs
, so lets break it down:
- At line 9, we define our shell and name it Solana Commands.
- At line 10, we provide a short description.
- From lines 11 to 14, we create the actual struct for the command shell, which takes in an
Enum Commands
At lines 17 to 19, we define our first command,WrapSol
with the argumentsWrapArgs
we will cover in another file.clap
automatically parsesWrapSol
aswrap-sol
, enabling us to run the shell command:cargo run wrap-sol
CLI output of running cargo run shell
The great thing about this layout is that if you wanted to create additional commands, you would simply add a new enum variant to Commands. You could then call it using:
cargo run shell
- From lines 26 to 36, we implement a simple match statement to handle WrapSol when it is passed as a command-line argument.
Fill shell/src/commands/mod.rs
with
We are making wrap_sol public so that it can be called elsewhere.
Fill shell/src/commands/mod.rs
with
A lot happened again, so let's go over it step by step.
Lines 9 to 15 define the `WrapArgs struct, which takes a float as an argument to specify how much SOL to wrap into wSOL.
WSOL transaction
Now, let's break down the instructions from lines 32 to 80.
/* prepare the wSOL account */
let in_token_client = Token::new(
Arc::clone(&program_client),
&spl_token::ID,
&spl_token::native_mint::id(),
None,
Arc::new(keypair_clone(&keypair)),
);
let user_in_token_account = in_token_client.get_associated_token_address(&keypair.pubkey());
let wsol_acc_exists = in_token_client
.get_account_info(&user_in_token_account)
.await;
/* ensure wsol token program has not been closed! */
if wsol_acc_exists.is_err() {
in_token_client.create_associated_token_account(
&keypair.pubkey()
).await.unwrap();
}
That wasn’t so bad, right? 😉
The remainder of the code simply handles the amount, signing, and actually sending the transaction. It then prints the transaction signature so we can look it up on-chain using sites like solana.fm and solscan.io.
Adding Private Key to Environment Variables
A word of caution:
While researching how to build my own Solana sniper, I reviewed numerous codebases, and almost all had one thing in common—they were malicious honeypots designed to steal unsuspecting victims' private keys and drain their entire SOL balance.
Always be extremely careful with the code you execute and where you store your private key. Even better, make sure you understand each line of the code before running any program that involves your private key.
The final step for wrapping SOL is to add our private key to our environment variables (env) so it can be used for signing and approving transactions in the wrap-sol
command.
Update pub fn new()
in utils/src/env/env.rs
to
Lines 15 to 16 read the PRIVATE_KEYPAIR
directive from the .env file and load the private keypair into memory.
Test run
With all that set up, let's test it out on the devnet and see if it works.
Before using your own private key, let's run a simple test on the Solana devnet using a newly generated random keypair and observe the results.
Be careful not to overwrite your current private key with this command unless you have a backup.
# Create a new key called private_key.json
solana-keygen new -o private_key.json
# Switch default config to use devnet
solana config set --url https://api.devnet.solana.com
# Get the public key of your wallet mine was 3Vy1sD9fwzUv6npTE8qM2o4DJGRRXdLryhyKfmjZByGu
solana-keygen pubkey private_key.json
# Request an airdrop with fake SOL on the devnet
solana airdrop 2 3Vy1sD9fwzUv6npTE8qM2o4DJGRRXdLryhyKfmjZByGu
Lastly, edit the .env file and add or replace this line: PRIVATE_KEYPAIR=private_key.json
Also, set the RPC_ENDPOINT to devnet while we are testing: RPC_ENDPOINT=https://api.devnet.solana.com
Once you have executed the commands from the previous steps, you should be all set to wrap some SOL into wSOL!
Solana.fm with my wallet
Lets execute
so we exchange 0.2 SOL for 0.2 WSOL.
Executing wSOL wrap command
As you can see, I got the signature.
If you get this error, it likely means you either forgot to change the RPC_ENDPOINT to devnet or did not successfully receive the 2 SOL airdrop.
Error wrapping sol
Great! Now, how do we confirm that the SOL was actually wrapped and not just burned? Navigate to my wallet to check.
Displaying wrapped sol
Why are we wrapping sol in a command?
We are wrapping SOL in a command because each instruction in a transaction increases computational usage (measured in compute units or CUs). The more CUs a transaction consumes, the less likely it is to be processed quickly.
Solana is designed to fit as many transactions as possible into a single block. Once a block is nearly full, some transactions will have to wait for the next block. However, if your swap transaction is small enough, it has a higher chance of being included faster potentially ahead of other, larger transactions that might be skipped.
Up next
In the next article, we will look into how to change our SOL to WSOL, which is needed for swapping, we will do this with a command line layer.