use super::{Plugin, Account, Address, PaymentOption, Transaction, Payment, Confirmation, Price};
use anyhow::{Result, anyhow};
use bigdecimal::BigDecimal;
use std::str::FromStr;
use bitcoin::{Transaction as BtcTransaction, consensus::deserialize, Address as BtcAddress};
pub struct BitcoinPlugin;
#[async_trait::async_trait]
impl Plugin for BitcoinPlugin {
fn currency(&self) -> &str { "BTC" }
fn chain(&self) -> &str { "BTC" }
fn decimals(&self) -> u8 { 8 }
async fn build_signed_payment(&self, payment_option: &PaymentOption, mnemonic: &str) -> Result<Transaction> {
Ok(Transaction {
txhex: "mock_btc_tx".into(),
txid: Some("mock_btc_txid".into()),
txkey: None,
})
}
async fn verify_payment(&self, payment_option: &PaymentOption, transaction: &Transaction) -> Result<bool> {
let tx_bytes = hex::decode(&transaction.txhex)?;
let btc_tx: BtcTransaction = deserialize(&tx_bytes)?;
let total_output_amount: u64 = btc_tx.output.iter()
.map(|output| output.value.to_sat())
.sum();
let expected_amount = payment_option.amount as u64;
if total_output_amount < expected_amount {
return Ok(false);
}
let payment_address = BtcAddress::from_str(&payment_option.address)
.map_err(|e| anyhow!("Invalid Bitcoin address: {}", e))?;
let has_matching_output = btc_tx.output.iter().any(|output| {
if let Ok(script_addr) = BtcAddress::from_script(&output.script_pubkey, bitcoin::Network::Bitcoin) {
script_addr == payment_address
} else {
false
}
});
Ok(has_matching_output)
}
async fn validate_address(&self, address: &str) -> Result<bool> {
Ok(address.starts_with("1") || address.starts_with("3") || address.starts_with("bc1"))
}
async fn get_transaction(&self, txid: &str) -> Result<Transaction> {
Ok(Transaction {
txhex: "mock_btc_tx".into(),
txid: Some(txid.to_string()),
txkey: None,
})
}
async fn broadcast_tx(&self, txhex: &str, txid: Option<&str>, _txkey: Option<&str>) -> Result<Transaction> {
Ok(Transaction {
txhex: txhex.to_string(),
txid: txid.map(String::from),
txkey: None,
})
}
async fn get_new_address(&self, _account: &Account, address: &Address) -> Result<String> {
Ok(address.value.clone())
}
async fn transform_address(&self, address: &str) -> Result<String> {
Ok(address.split(':').last().unwrap_or(address).to_string())
}
async fn get_confirmation(&self, _txid: &str) -> Result<Option<Confirmation>> {
Ok(Some(Confirmation {
confirmations: 6,
confirmed: true,
}))
}
async fn get_payments(&self, txid: &str) -> Result<Vec<Payment>> {
Ok(vec![Payment {
chain: self.chain().to_string(),
currency: self.currency().to_string(),
address: "mock_btc_address".to_string(),
amount: 100000000, txid: txid.to_string(),
}])
}
async fn parse_payments(&self, transaction: &Transaction) -> Result<Vec<Payment>> {
Ok(vec![])
}
async fn get_price(&self) -> Result<Price> {
Ok(Price {
currency: self.currency().to_string(),
price: BigDecimal::from_str("30000.00")?,
timestamp: chrono::Utc::now().timestamp(),
})
}
}