From c0e026a88a8bce9993ae91d59da0a20353fc5e91 Mon Sep 17 00:00:00 2001 From: ysq Date: Thu, 20 Nov 2025 11:21:27 +0800 Subject: [PATCH] feat: add wrap_wsol_to_sol function with seed account optimization --- Cargo.toml | 2 +- README.md | 4 +- README_CN.md | 4 +- examples/wsol_wrapper/src/main.rs | 36 ++++++++++++++--- src/common/spl_token.rs | 29 ++++++++++++++ src/lib.rs | 36 +++++++++++++++++ src/trading/common/wsol_manager.rs | 62 +++++++++++++++++++++++++++++- 7 files changed, 161 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9394b7b..b7766ab 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sol-trade-sdk" -version = "3.3.2" +version = "3.3.3" edition = "2021" authors = [ "William ", diff --git a/README.md b/README.md index 4a9876e..03da86a 100644 --- a/README.md +++ b/README.md @@ -87,14 +87,14 @@ Add the dependency to your `Cargo.toml`: ```toml # Add to your Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.3.2" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.3.3" } ``` ### Use crates.io ```toml # Add to your Cargo.toml -sol-trade-sdk = "3.3.2" +sol-trade-sdk = "3.3.3" ``` ## 🛠️ Usage Examples diff --git a/README_CN.md b/README_CN.md index d5d8aba..be8fb6b 100755 --- a/README_CN.md +++ b/README_CN.md @@ -87,14 +87,14 @@ git clone https://github.com/0xfnzero/sol-trade-sdk ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.3.2" } +sol-trade-sdk = { path = "./sol-trade-sdk", version = "3.3.3" } ``` ### 使用 crates.io ```toml # 添加到您的 Cargo.toml -sol-trade-sdk = "3.3.2" +sol-trade-sdk = "3.3.3" ``` ## 🛠️ 使用示例 diff --git a/examples/wsol_wrapper/src/main.rs b/examples/wsol_wrapper/src/main.rs index c99ecf9..5845afa 100644 --- a/examples/wsol_wrapper/src/main.rs +++ b/examples/wsol_wrapper/src/main.rs @@ -6,7 +6,10 @@ use std::sync::Arc; #[tokio::main] async fn main() -> Result<(), Box> { println!("🔄 WSOL Wrapper Example"); - println!("This example demonstrates how to wrap SOL to WSOL and unwrap WSOL back to SOL"); + println!("This example demonstrates:"); + println!("1. Wrapping SOL to WSOL"); + println!("2. Partial unwrapping WSOL back to SOL using seed account"); + println!("3. Closing WSOL account and unwrapping remaining balance"); // Initialize SolanaTrade client let solana_trade = create_solana_trade_client().await?; @@ -24,15 +27,36 @@ async fn main() -> Result<(), Box> { } Err(e) => { println!("❌ Failed to wrap SOL to WSOL: {}", e); + return Ok(()); } } - // Wait a moment before unwrapping - println!("\n⏳ Waiting 3 seconds before unwrapping..."); + // Wait a moment before partial unwrapping + println!("\n⏳ Waiting 3 seconds before partial unwrapping..."); tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - // Example 2: Close WSOL account and unwrap all remaining balance - println!("\n🔒 Example 2: Closing WSOL account and unwrapping remaining balance"); + // Example 2: Unwrap half of the WSOL back to SOL using seed account + println!("\n🔄 Example 2: Unwrapping half of WSOL back to SOL using seed account"); + let unwrap_amount = wrap_amount / 2; // Half of the wrapped amount + println!("Unwrapping {} lamports (0.0005 SOL) back to SOL using seed account...", unwrap_amount); + + match solana_trade.wrap_wsol_to_sol(unwrap_amount).await { + Ok(signature) => { + println!("✅ Successfully unwrapped half of WSOL back to SOL using seed account!"); + println!("Transaction signature: {}", signature); + println!("Explorer: https://solscan.io/tx/{}", signature); + } + Err(e) => { + println!("❌ Failed to unwrap WSOL to SOL: {}", e); + } + } + + // Wait a moment before final unwrapping + println!("\n⏳ Waiting 3 seconds before final unwrapping..."); + tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; + + // Example 3: Close WSOL account and unwrap all remaining balance + println!("\n🔒 Example 3: Closing WSOL account and unwrapping remaining balance"); println!("Closing WSOL account and unwrapping all remaining balance to SOL..."); match solana_trade.close_wsol().await { @@ -53,7 +77,7 @@ async fn main() -> Result<(), Box> { /// Create and initialize SolanaTrade client async fn create_solana_trade_client() -> Result> { println!("🚀 Initializing SolanaTrade client..."); - let payer = Keypair::from_base58_string("use_your_payer_keypair_here"); + let payer = Keypair::from_base58_string("R26eUYq691PC7AFh6yNyC285qr5tZfu43SAYhKGNZrpRVmrwDhR254bSS7Z1e87LWCmPNmiPvYkz9kA9hXyQD2m"); let rpc_url = "https://api.mainnet-beta.solana.com".to_string(); let commitment = CommitmentConfig::confirmed(); let swqos_configs: Vec = vec![SwqosConfig::Default(rpc_url.clone())]; diff --git a/src/common/spl_token.rs b/src/common/spl_token.rs index 823dbe6..c094394 100644 --- a/src/common/spl_token.rs +++ b/src/common/spl_token.rs @@ -30,6 +30,35 @@ pub fn close_account( Ok(Instruction { program_id: *token_program_id, accounts, data }) } +pub fn transfer( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + destination_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + amount: u64, + signers: &[&Pubkey], +) -> Result { + // Transfer + let mut data = Vec::with_capacity(9); + data.push(3); // Instruction discriminator for Transfer + data.extend_from_slice(&amount.to_le_bytes()); + + let mut accounts = Vec::with_capacity(3 + signers.len()); + accounts.push(AccountMeta::new(*source_pubkey, false)); + accounts.push(AccountMeta::new(*destination_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*owner_pubkey, signers.is_empty())); + + for signer in signers.iter() { + accounts.push(AccountMeta::new_readonly(**signer, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + pub fn initialize_account3( token_program_id: &Pubkey, account_pubkey: &Pubkey, diff --git a/src/lib.rs b/src/lib.rs index be4903a..7a2b9b1 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -663,4 +663,40 @@ impl SolanaTrade { let signature = self.rpc.send_and_confirm_transaction(&transaction).await?; Ok(signature.to_string()) } + + /// 将 WSOL 转换为 SOL,使用 seed 账户 + /// + /// 这个函数实现以下步骤: + /// 1. 使用 super::seed::create_associated_token_account_use_seed 创建 WSOL seed 账号 + /// 2. 使用 get_associated_token_address_with_program_id_use_seed 获取该账号的 ATA 地址 + /// 3. 添加从用户 WSOL ATA 转账到该 seed ATA 账号的指令 + /// 4. 添加关闭 WSOL seed 账号的指令 + /// + /// # Arguments + /// * `amount` - 要转换的 WSOL 数量(以 lamports 为单位) + /// + /// # Returns + /// * `Ok(String)` - 交易签名 + /// * `Err(anyhow::Error)` - 如果交易执行失败 + /// + /// # Errors + /// + /// 此函数在以下情况下会返回错误: + /// - 用户 WSOL ATA 中余额不足 + /// - seed 账户创建失败 + /// - 转账指令执行失败 + /// - 交易执行或确认失败 + /// - 网络或 RPC 错误 + pub async fn wrap_wsol_to_sol(&self, amount: u64) -> Result { + use crate::trading::common::wsol_manager::wrap_wsol_to_sol as wrap_wsol_to_sol_internal; + use solana_sdk::transaction::Transaction; + + let recent_blockhash = self.rpc.get_latest_blockhash().await?; + let instructions = wrap_wsol_to_sol_internal(&self.payer.pubkey(), amount)?; + + let mut transaction = Transaction::new_with_payer(&instructions, Some(&self.payer.pubkey())); + transaction.sign(&[&*self.payer], recent_blockhash); + let signature = self.rpc.send_and_confirm_transaction(&transaction).await?; + Ok(signature.to_string()) + } } diff --git a/src/trading/common/wsol_manager.rs b/src/trading/common/wsol_manager.rs index 00cbe14..ffde170 100644 --- a/src/trading/common/wsol_manager.rs +++ b/src/trading/common/wsol_manager.rs @@ -1,5 +1,7 @@ use crate::common::{ - fast_fn::create_associated_token_account_idempotent_fast, spl_token::close_account, + fast_fn::create_associated_token_account_idempotent_fast, + spl_token::close_account, + seed::{create_associated_token_account_use_seed, get_associated_token_address_with_program_id_use_seed}, }; use smallvec::SmallVec; use solana_sdk::{instruction::Instruction, message::AccountMeta, pubkey::Pubkey}; @@ -92,3 +94,61 @@ pub fn wrap_sol_only(payer: &Pubkey, amount_in: u64) -> SmallVec<[Instruction; 2 insts } + +/// 将 WSOL 转换为 SOL,使用 seed 账户 +/// 1. 使用 super::seed::create_associated_token_account_use_seed 创建 WSOL seed 账号 +/// 2. 使用 get_associated_token_address_with_program_id_use_seed 获取该账号的 ATA 地址 +/// 3. 添加从用户 WSOL ATA 转账到该 seed ATA 账号的指令 +/// 4. 添加关闭 WSOL seed 账号的指令 +pub fn wrap_wsol_to_sol( + payer: &Pubkey, + amount: u64, +) -> Result, anyhow::Error> { + let mut instructions = Vec::new(); + + // 1. 创建 WSOL seed 账户 + let seed_account_instructions = create_associated_token_account_use_seed( + payer, + payer, + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + )?; + instructions.extend(seed_account_instructions); + + // 2. 获取 seed 账户的 ATA 地址 + let seed_ata_address = get_associated_token_address_with_program_id_use_seed( + payer, + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + )?; + + // 3. 获取用户的 WSOL ATA 地址 + let user_wsol_ata = crate::common::fast_fn::get_associated_token_address_with_program_id_fast( + payer, + &crate::constants::WSOL_TOKEN_ACCOUNT, + &crate::constants::TOKEN_PROGRAM, + ); + + // 4. 添加从用户 WSOL ATA 转账到 seed ATA 的指令 + let transfer_instruction = crate::common::spl_token::transfer( + &crate::constants::TOKEN_PROGRAM, + &user_wsol_ata, + &seed_ata_address, + payer, + amount, + &[], + )?; + instructions.push(transfer_instruction); + + // 5. 添加关闭 WSOL seed 账户的指令 + let close_instruction = close_account( + &crate::constants::TOKEN_PROGRAM, + &seed_ata_address, + payer, + payer, + &[], + )?; + instructions.push(close_instruction); + + Ok(instructions) +}