mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
service/sol: Initial stab at SPL token transfers.
This commit is contained in:
@@ -354,6 +354,7 @@ impl Cashierd {
|
||||
// This is supposed to be a token mint account now
|
||||
use drk::service::sol::account_is_initialized_mint;
|
||||
use drk::service::sol::SolFailed::BadSolAddress;
|
||||
use solana_client::rpc_client::RpcClient;
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
|
||||
let pubkey = match Pubkey::from_str(token_id) {
|
||||
@@ -362,7 +363,8 @@ impl Cashierd {
|
||||
};
|
||||
|
||||
// FIXME: Use network name from variable
|
||||
if !account_is_initialized_mint("devnet".to_string(), &pubkey) {
|
||||
let rpc = RpcClient::new("https://api.devnet.solana.com".to_string());
|
||||
if !account_is_initialized_mint(&rpc, &pubkey) {
|
||||
return Err(Error::CashierInvalidTokenId(
|
||||
"Given address is not a valid token mint".into(),
|
||||
));
|
||||
|
||||
@@ -11,8 +11,14 @@ use serde::Serialize;
|
||||
use serde_json::{json, Value};
|
||||
use solana_client::{blockhash_query::BlockhashQuery, rpc_client::RpcClient};
|
||||
use solana_sdk::{
|
||||
native_token::lamports_to_sol, program_pack::Pack, pubkey::Pubkey, signature::Signer,
|
||||
signer::keypair::Keypair, system_instruction, transaction::Transaction,
|
||||
instruction::Instruction,
|
||||
native_token::lamports_to_sol,
|
||||
program_pack::Pack,
|
||||
pubkey::Pubkey,
|
||||
signature::{Signature, Signer},
|
||||
signer::keypair::Keypair,
|
||||
system_instruction,
|
||||
transaction::Transaction,
|
||||
};
|
||||
use tungstenite::Message;
|
||||
|
||||
@@ -81,26 +87,30 @@ impl SolClient {
|
||||
) -> Result<()> {
|
||||
debug!(target: "SOL BRIDGE", "handle_subscribe_request()");
|
||||
|
||||
// Derive token pubkey if mint was provided.
|
||||
let pubkey = if mint.is_some() {
|
||||
get_associated_token_account(&keypair.pubkey(), &mint.unwrap()).0
|
||||
} else {
|
||||
keypair.pubkey()
|
||||
};
|
||||
|
||||
// Check if we're already subscribed
|
||||
if self.subscriptions.lock().await.contains(&keypair.pubkey()) {
|
||||
if self.subscriptions.lock().await.contains(&pubkey) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rpc = RpcClient::new(self.rpc_server.to_string());
|
||||
|
||||
// Fetch the current balance.
|
||||
let (prev_balance, decimals) = if mint.is_none() {
|
||||
let rpc = RpcClient::new(self.rpc_server.to_string());
|
||||
(
|
||||
rpc.get_balance(&keypair.pubkey())
|
||||
rpc.get_balance(&pubkey)
|
||||
.map_err(|err| SolFailed::from(err))?,
|
||||
9,
|
||||
)
|
||||
} else {
|
||||
get_account_token_balance(
|
||||
self.rpc_server.to_string(),
|
||||
&keypair.pubkey(),
|
||||
&mint.unwrap(),
|
||||
)
|
||||
.map_err(|err| SolFailed::from(err))?
|
||||
get_account_token_balance(&rpc, &pubkey, &mint.unwrap())
|
||||
.map_err(|err| SolFailed::from(err))?
|
||||
};
|
||||
|
||||
// WebSocket connection
|
||||
@@ -116,7 +126,7 @@ impl SolClient {
|
||||
|
||||
let subscription = jsonrpc::request(
|
||||
json!("accountSubscribe"),
|
||||
json!([json!(keypair.pubkey().to_string()), json!(sub_params)]),
|
||||
json!([json!(pubkey.to_string()), json!(sub_params)]),
|
||||
);
|
||||
|
||||
debug!(target: "SOLANA RPC", "--> {}", serde_json::to_string(&subscription)?);
|
||||
@@ -124,9 +134,6 @@ impl SolClient {
|
||||
.send(Message::text(serde_json::to_string(&subscription)?))
|
||||
.await?;
|
||||
|
||||
// Declare params here for longer variable lifetime.
|
||||
let params: Value;
|
||||
|
||||
// Subscription ID used for unsubscribing later.
|
||||
let mut sub_id: i64 = 0;
|
||||
|
||||
@@ -142,7 +149,7 @@ impl SolClient {
|
||||
JsonResult::Resp(r) => {
|
||||
// ACK
|
||||
debug!(target: "SOLANA RPC", "<-- {}", serde_json::to_string(&r)?);
|
||||
self.subscriptions.lock().await.push(keypair.pubkey());
|
||||
self.subscriptions.lock().await.push(pubkey);
|
||||
sub_id = r.result.as_i64().unwrap();
|
||||
}
|
||||
JsonResult::Err(e) => {
|
||||
@@ -153,7 +160,7 @@ impl SolClient {
|
||||
JsonResult::Notif(n) => {
|
||||
// Account updated
|
||||
debug!(target: "SOLANA RPC", "Got WebSocket notification");
|
||||
params = n.params["result"]["value"].clone();
|
||||
let params = n.params["result"]["value"].clone();
|
||||
|
||||
if mint.is_some() {
|
||||
cur_balance = params["data"]["info"]["tokenAmount"]["amount"]
|
||||
@@ -173,7 +180,7 @@ impl SolClient {
|
||||
.lock()
|
||||
.await
|
||||
.iter()
|
||||
.position(|p| p == &keypair.pubkey());
|
||||
.position(|p| p == &pubkey);
|
||||
if let Some(ind) = index {
|
||||
debug!("Removing subscription from list");
|
||||
self.subscriptions.lock().await.remove(ind);
|
||||
@@ -192,50 +199,119 @@ impl SolClient {
|
||||
}
|
||||
|
||||
if mint.is_some() {
|
||||
let ui_amnt = (cur_balance - prev_balance) * decimals;
|
||||
let amnt = cur_balance - prev_balance;
|
||||
let ui_amnt = amnt * decimals;
|
||||
debug!(target: "SOL BRIDGE", "Received {} {:?} tokens", ui_amnt, mint.unwrap());
|
||||
self.send_tok_to_main_wallet(&mint.unwrap(), cur_balance - prev_balance, &keypair)
|
||||
let _ = self.send_tok_to_main_wallet(&rpc, &mint.unwrap(), amnt, decimals, &keypair)?;
|
||||
} else {
|
||||
let ui_amnt = lamports_to_sol(cur_balance - prev_balance);
|
||||
let amnt = cur_balance - prev_balance;
|
||||
let ui_amnt = lamports_to_sol(amnt);
|
||||
debug!(target: "SOL BRIDGE", "Received {} SOL", ui_amnt);
|
||||
self.send_sol_to_main_wallet(cur_balance - prev_balance, &keypair)
|
||||
let _ = self.send_sol_to_main_wallet(&rpc, amnt, &keypair)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO
|
||||
fn send_tok_to_main_wallet(
|
||||
self: Arc<Self>,
|
||||
_mint: &Pubkey,
|
||||
_amount: u64,
|
||||
_keypair: &Keypair,
|
||||
) -> Result<()> {
|
||||
debug!(target: "SOL BRIDGE", "Sending tokens to main wallet");
|
||||
Ok(())
|
||||
}
|
||||
rpc: &RpcClient,
|
||||
mint: &Pubkey,
|
||||
amount: u64,
|
||||
decimals: u64,
|
||||
keypair: &Keypair,
|
||||
) -> Result<Signature> {
|
||||
debug!(target: "SOL BRIDGE", "Sending {} {:?} tokens to main wallet", amount * decimals, mint);
|
||||
|
||||
fn send_sol_to_main_wallet(self: Arc<Self>, amount: u64, keypair: &Keypair) -> Result<()> {
|
||||
debug!(target: "SOL BRIDGE", "Sending {} SOL to main wallet", lamports_to_sol(amount));
|
||||
|
||||
let rpc = RpcClient::new(self.rpc_server.to_string());
|
||||
|
||||
let ix = system_instruction::transfer(&keypair.pubkey(), &self.keypair.pubkey(), amount);
|
||||
|
||||
let mut tx = Transaction::new_with_payer(&[ix], Some(&self.keypair.pubkey()));
|
||||
|
||||
let bhq = BlockhashQuery::default();
|
||||
match bhq.get_blockhash_and_fee_calculator(&rpc, rpc.commitment()) {
|
||||
Err(_) => return Err(Error::ServicesError("Couldn't connect to RPC")),
|
||||
Ok(v) => tx.sign(&[keypair, &self.keypair], v.0),
|
||||
if !account_is_initialized_mint(rpc, mint) {
|
||||
return Err(Error::ServicesError("Given mint is not valid"));
|
||||
}
|
||||
|
||||
let signature = match rpc.send_and_confirm_transaction(&tx) {
|
||||
Ok(s) => s,
|
||||
Err(_) => return Err(Error::ServicesError("Failed to send transaction")),
|
||||
// The token account from our main wallet
|
||||
let (main_tok_pk, _) = get_associated_token_account(&self.keypair.pubkey(), mint);
|
||||
// The token account from the deposit wallet
|
||||
let (temp_tok_pk, _) = get_associated_token_account(&keypair.pubkey(), mint);
|
||||
|
||||
// Account initialize instruction
|
||||
let init_ix: Option<Instruction>;
|
||||
|
||||
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,
|
||||
// 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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer tokens from the deposit wallet to the main wallet
|
||||
let tok_transfer_ix = spl_token::instruction::transfer(
|
||||
&spl_token::id(),
|
||||
&temp_tok_pk,
|
||||
&main_tok_pk,
|
||||
&keypair.pubkey(),
|
||||
&[&keypair.pubkey(), &self.keypair.pubkey()],
|
||||
amount,
|
||||
)
|
||||
.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))?;
|
||||
|
||||
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()))
|
||||
};
|
||||
|
||||
debug!(target: "SOL BRIDGE", "Sent to main wallet: {}", signature);
|
||||
let signature = sign_and_send_transaction(&rpc, tx, vec![keypair, &self.keypair])?;
|
||||
|
||||
Ok(())
|
||||
debug!(target: "SOL BRIDGE", "Sent tokens to main wallet: {}", signature);
|
||||
Ok(signature)
|
||||
}
|
||||
|
||||
fn send_sol_to_main_wallet(
|
||||
self: Arc<Self>,
|
||||
rpc: &RpcClient,
|
||||
amount: u64,
|
||||
keypair: &Keypair,
|
||||
) -> Result<Signature> {
|
||||
debug!(target: "SOL BRIDGE", "Sending {} SOL to main wallet", lamports_to_sol(amount));
|
||||
|
||||
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])?;
|
||||
|
||||
debug!(target: "SOL BRIDGE", "Sent SOL to main wallet: {}", signature);
|
||||
Ok(signature)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,12 +392,10 @@ pub fn get_associated_token_account(owner: &Pubkey, mint: &Pubkey) -> (Pubkey, u
|
||||
/// Gets account token balance for given mint.
|
||||
/// Returns: (amount, decimals)
|
||||
pub fn get_account_token_balance(
|
||||
rpc_server: String,
|
||||
rpc: &RpcClient,
|
||||
address: &Pubkey,
|
||||
mint: &Pubkey,
|
||||
) -> SolResult<(u64, u64)> {
|
||||
let rpc = RpcClient::new(rpc_server);
|
||||
|
||||
let mint_account = rpc.get_account(mint)?;
|
||||
let token_account = rpc.get_account(address)?;
|
||||
let mint_data = spl_token::state::Mint::unpack_from_slice(&mint_account.data)?;
|
||||
@@ -331,14 +405,30 @@ pub fn get_account_token_balance(
|
||||
}
|
||||
|
||||
/// Check if given account is a valid token mint
|
||||
pub fn account_is_initialized_mint(rpc_server: String, mint: &Pubkey) -> bool {
|
||||
let rpc = RpcClient::new(rpc_server);
|
||||
pub fn account_is_initialized_mint(rpc: &RpcClient, mint: &Pubkey) -> bool {
|
||||
match rpc.get_token_supply(mint) {
|
||||
Ok(_) => true,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign_and_send_transaction(
|
||||
rpc: &RpcClient,
|
||||
mut tx: Transaction,
|
||||
signers: Vec<&Keypair>,
|
||||
) -> Result<Signature> {
|
||||
let bhq = BlockhashQuery::default();
|
||||
match bhq.get_blockhash_and_fee_calculator(rpc, rpc.commitment()) {
|
||||
Err(_) => return Err(Error::ServicesError("Couldn't connect to RPC")),
|
||||
Ok(v) => tx.sign(&signers, v.0),
|
||||
}
|
||||
|
||||
match rpc.send_and_confirm_transaction(&tx) {
|
||||
Ok(s) => Ok(s),
|
||||
Err(_) => Err(Error::ServicesError("Failed to send transaction")),
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for Keypair {
|
||||
fn encode<S: std::io::Write>(&self, s: S) -> Result<usize> {
|
||||
let key: Vec<u8> = self.to_bytes().to_vec();
|
||||
|
||||
Reference in New Issue
Block a user