Sfoglia il codice sorgente

service/sol: Working SPL token deposits.

parazyd 4 anni fa
parent
commit
e3dffd044f
3 ha cambiato i file con 42 aggiunte e 69 eliminazioni
  1. 1 0
      Cargo.lock
  2. 2 1
      Cargo.toml
  3. 39 68
      src/service/sol.rs

+ 1 - 0
Cargo.lock

@@ -1225,6 +1225,7 @@ dependencies = [
  "smol",
  "solana-client",
  "solana-sdk",
+ "spl-associated-token-account",
  "spl-token",
  "toml",
  "tungstenite 0.15.0",

+ 2 - 1
Cargo.toml

@@ -65,6 +65,7 @@ rocksdb = {version = "0.16.0", default-features = false, features = ["lz4"]}
 spl-token = {version = "3.2.0", optional = true}
 solana-sdk = {version = "1.7.11", optional = true}
 solana-client = {version = "1.7.11", optional = true}
+spl-associated-token-account = {version = "1.0.3", optional = true}
 
 ## Cashier Bitcoin Dependencies
 bitcoin = {version = "0.27.0", optional = true }
@@ -82,4 +83,4 @@ features = ["async-std-runtime", "all-transport"]
 
 [features]
 btc = ["bitcoin", "secp256k1", "electrum-client"]
-sol = ["solana-sdk", "solana-client", "spl-token"]
+sol = ["solana-sdk", "solana-client", "spl-token", "spl-associated-token-account"]

+ 39 - 68
src/service/sol.rs

@@ -11,7 +11,6 @@ use serde::Serialize;
 use serde_json::{json, Value};
 use solana_client::{blockhash_query::BlockhashQuery, rpc_client::RpcClient};
 use solana_sdk::{
-    instruction::Instruction,
     native_token::lamports_to_sol,
     program_pack::Pack,
     pubkey::Pubkey,
@@ -20,6 +19,7 @@ use solana_sdk::{
     system_instruction,
     transaction::Transaction,
 };
+use spl_associated_token_account::{create_associated_token_account, get_associated_token_address};
 use tungstenite::Message;
 
 use crate::rpc::{jsonrpc, jsonrpc::JsonResult, websockets};
@@ -51,6 +51,8 @@ impl SolClient {
         let keypair: Keypair = deserialize(&keypair)?;
         let notify_channel = async_channel::unbounded();
 
+        debug!("Main SOL wallet pubkey: {:?}", &keypair.pubkey());
+
         let (rpc_server, wss_server) = match network {
             "mainnet" => (
                 "https://api.mainnet-beta.solana.com",
@@ -89,7 +91,7 @@ impl SolClient {
 
         // Derive token pubkey if mint was provided.
         let pubkey = if mint.is_some() {
-            get_associated_token_account(&keypair.pubkey(), &mint.unwrap()).0
+            get_associated_token_address(&keypair.pubkey(), &mint.unwrap())
         } else {
             keypair.pubkey()
         };
@@ -163,9 +165,10 @@ impl SolClient {
                     let params = n.params["result"]["value"].clone();
 
                     if mint.is_some() {
-                        cur_balance = params["data"]["info"]["tokenAmount"]["amount"]
-                            .as_u64()
-                            .unwrap();
+                        cur_balance = params["data"]["parsed"]["info"]["tokenAmount"]["amount"]
+                            .as_str()
+                            .unwrap()
+                            .parse()?;
                     } else {
                         cur_balance = params["lamports"].as_u64().unwrap();
                     }
@@ -200,7 +203,7 @@ impl SolClient {
 
         if mint.is_some() {
             let amnt = cur_balance - prev_balance;
-            let ui_amnt = amnt * decimals;
+            let ui_amnt = amnt / u64::pow(10, decimals as u32);
             debug!(target: "SOL BRIDGE", "Received {} {:?} tokens", ui_amnt, mint.unwrap());
             let _ = self.send_tok_to_main_wallet(&rpc, &mint.unwrap(), amnt, decimals, &keypair)?;
         } else {
@@ -221,81 +224,63 @@ impl SolClient {
         decimals: u64,
         keypair: &Keypair,
     ) -> Result<Signature> {
-        debug!(target: "SOL BRIDGE", "Sending {} {:?} tokens to main wallet", amount * decimals, mint);
+        debug!(target: "SOL BRIDGE", "Sending {} {:?} tokens to main wallet",
+                amount / u64::pow(10, decimals as u32), mint);
 
         if !account_is_initialized_mint(rpc, mint) {
             return Err(Error::ServicesError("Given mint is not valid"));
         }
 
         // The token account from our main wallet
-        let (main_tok_pk, _) = get_associated_token_account(&self.keypair.pubkey(), mint);
+        let main_tok_pk = get_associated_token_address(&self.keypair.pubkey(), mint);
         // The token account from the deposit wallet
-        let (temp_tok_pk, _) = get_associated_token_account(&keypair.pubkey(), mint);
+        let temp_tok_pk = get_associated_token_address(&keypair.pubkey(), mint);
 
-        // Account initialize instruction
-        let init_ix: Option<Instruction>;
+        let mut instructions = vec![];
 
         match rpc.get_account_data(&main_tok_pk) {
             Ok(v) => {
                 match spl_token::state::Account::unpack_from_slice(&v) {
                     // It's valid token data, and we consider account initialized.
-                    Ok(_) => init_ix = None,
+                    Ok(_) => {}
                     // Some other unexpected data.
                     Err(_) => return Err(Error::ServicesError("Invalid data on derived account")),
                 }
             }
             Err(_) => {
-                // Create an initialize instruction.
-                init_ix = Some(
-                    spl_token::instruction::initialize_account(
-                        &spl_token::id(),
-                        &main_tok_pk,
-                        mint,
-                        &self.keypair.pubkey(),
-                    )
-                    .unwrap(),
+                // Unitinialized, so we add a creation instruction
+                debug!("Main wallet token account is uninitialized. Adding init instruction.");
+                let init_ix = create_associated_token_account(
+                    &self.keypair.pubkey(), // fee payer
+                    &self.keypair.pubkey(), // wallet
+                    mint,
                 );
+                instructions.push(init_ix);
             }
         }
 
         // Transfer tokens from the deposit wallet to the main wallet
-        let tok_transfer_ix = spl_token::instruction::transfer(
+        let transfer_ix = spl_token::instruction::transfer_checked(
             &spl_token::id(),
             &temp_tok_pk,
+            mint,
             &main_tok_pk,
             &keypair.pubkey(),
-            &[&keypair.pubkey(), &self.keypair.pubkey()],
+            &[],
             amount,
+            decimals as u8,
         )
         .unwrap();
 
-        // Transfer the remanining balance from the deposit account, resulting
-        // in account close.
-        /*
-        let rent_balance = rpc
-            .get_balance(&keypair.pubkey())
-            .map_err(|err| SolFailed::from(err))?;
+        instructions.push(transfer_ix);
 
-        let lmp_transfer_ix = solana_sdk::system_instruction::transfer(
-            &keypair.pubkey(),
-            &self.keypair.pubkey(),
-            rent_balance,
-        );
-        */
-
-        let tx = if init_ix.is_some() {
-            Transaction::new_with_payer(
-                &[init_ix.unwrap(), tok_transfer_ix],
-                Some(&self.keypair.pubkey()),
-            )
-        } else {
-            Transaction::new_with_payer(&[tok_transfer_ix], Some(&self.keypair.pubkey()))
-        };
-
-        let signature = sign_and_send_transaction(&rpc, tx, vec![keypair, &self.keypair])?;
+        let tx = Transaction::new_with_payer(&instructions, Some(&self.keypair.pubkey()));
+        let signature = sign_and_send_transaction(&rpc, tx, vec![&self.keypair, keypair])?;
 
         debug!(target: "SOL BRIDGE", "Sent tokens to main wallet: {}", signature);
         Ok(signature)
+
+        // TODO: Close deposit account?
     }
 
     fn send_sol_to_main_wallet(
@@ -308,7 +293,7 @@ impl SolClient {
 
         let ix = system_instruction::transfer(&keypair.pubkey(), &self.keypair.pubkey(), amount);
         let tx = Transaction::new_with_payer(&[ix], Some(&self.keypair.pubkey()));
-        let signature = sign_and_send_transaction(&rpc, tx, vec![keypair, &self.keypair])?;
+        let signature = sign_and_send_transaction(&rpc, tx, vec![&self.keypair, keypair])?;
 
         debug!(target: "SOL BRIDGE", "Sent SOL to main wallet: {}", signature);
         Ok(signature)
@@ -325,7 +310,9 @@ impl NetworkClient for SolClient {
 
         let self2 = self.clone();
         // TODO: Option<Pubkey> for 2nd arg representing Token Mint account
-        smol::spawn(self2.handle_subscribe_request(keypair, None)).detach();
+        let mint = Pubkey::from_str("F4wkXLN5n1ckejfnJoahGpgW3ffRsrvS9GGVME6ckxS9").unwrap();
+        smol::spawn(self2.handle_subscribe_request(keypair, Some(mint))).detach();
+        //smol::spawn(self2.handle_subscribe_request(keypair, None)).detach();
 
         Ok(TokenSubscribtion {
             secret_key,
@@ -345,7 +332,9 @@ impl NetworkClient for SolClient {
 
         let self2 = self.clone();
         // TODO: Option<Pubkey> for 2nd arg representing Token Mint account
-        smol::spawn(self2.handle_subscribe_request(keypair, None)).detach();
+        let mint = Pubkey::from_str("F4wkXLN5n1ckejfnJoahGpgW3ffRsrvS9GGVME6ckxS9").unwrap();
+        smol::spawn(self2.handle_subscribe_request(keypair, Some(mint))).detach();
+        //smol::spawn(self2.handle_subscribe_request(keypair, None)).detach();
 
         Ok(public_key)
     }
@@ -374,21 +363,6 @@ impl NetworkClient for SolClient {
     }
 }
 
-/// Derive an associated token address from given owner and mint
-pub fn get_associated_token_account(owner: &Pubkey, mint: &Pubkey) -> (Pubkey, u8) {
-    let associated_token =
-        Pubkey::from_str("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL").unwrap();
-
-    Pubkey::find_program_address(
-        &[
-            &owner.to_bytes(),
-            &spl_token::id().to_bytes(),
-            &mint.to_bytes(),
-        ],
-        &associated_token,
-    )
-}
-
 /// Gets account token balance for given mint.
 /// Returns: (amount, decimals)
 pub fn get_account_token_balance(
@@ -406,10 +380,7 @@ pub fn get_account_token_balance(
 
 /// Check if given account is a valid token mint
 pub fn account_is_initialized_mint(rpc: &RpcClient, mint: &Pubkey) -> bool {
-    match rpc.get_token_supply(mint) {
-        Ok(_) => true,
-        Err(_) => false,
-    }
+    rpc.get_token_supply(mint).is_ok()
 }
 
 pub fn sign_and_send_transaction(