#[allow(unused_imports)]
use crate::protocol::asset::{AssetIdentifier, AssetRegistry, Chain};
use crate::protocol::intent::{AssetAmount, CrossChainIntent, ResolvedCrossChainOrder};
use crate::{enclave::EnclaveManager, ConclaveError, ConclaveResult};
use alloy::primitives::{Address as EthAddress, Keccak256};
use bitcoin::address::Address;
use bitcoin::key::PublicKey;
use bitcoin::Network;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
pub struct ChainAbstractionService {
enclave: Arc<dyn EnclaveManager>,
_assets: Arc<AssetRegistry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChainSignatureRequest {
pub target_chain: Chain,
pub payload: Vec<u8>,
pub derivation_path: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChainSignatureResponse {
pub signature_hex: String,
pub target_address: String,
}
impl ChainAbstractionService {
pub fn new(enclave: Arc<dyn EnclaveManager>, _assets: Arc<AssetRegistry>) -> Self {
Self { enclave, _assets }
}
pub fn resolve_intent(
&self,
intent: CrossChainIntent,
) -> ConclaveResult<ResolvedCrossChainOrder> {
let input_assets = vec![AssetAmount {
asset: intent.input_asset,
amount: intent.input_amount,
}];
let output_assets = vec![AssetAmount {
asset: intent.output_asset,
amount: intent.output_amount,
}];
Ok(ResolvedCrossChainOrder {
user: intent.recipient.clone(),
origin_chain_id: 1, open_deadline: 3600,
fill_deadline: 7200,
swapper: intent.recipient,
nonce: 0,
input_assets,
output_assets,
})
}
pub fn sign_for_chain(
&self,
request: ChainSignatureRequest,
) -> ConclaveResult<ChainSignatureResponse> {
let algorithm = match request.target_chain {
Chain::BITCOIN | Chain::ETHEREUM | Chain::STACKS | Chain::XrpLedger | Chain::TRON => {
crate::enclave::SigningAlgorithm::EcdsaSecp256k1
}
Chain::SOLANA | Chain::STELLAR | Chain::NEAR => {
crate::enclave::SigningAlgorithm::Ed25519
}
_ => crate::enclave::SigningAlgorithm::EcdsaSecp256k1,
};
let sign_request = crate::enclave::SignRequest {
algorithm,
message_hash: request.payload,
derivation_path: request.derivation_path,
key_id: "universal_master_key".to_string(), taproot_tweak: None,
};
let response = self.enclave.sign(sign_request)?;
let public_key_bytes = hex::decode(&response.public_key_hex)
.map_err(|e| ConclaveError::CryptoError(format!("Invalid public key hex: {}", e)))?;
let target_address = match request.target_chain {
Chain::BITCOIN => {
let pk = PublicKey::from_slice(&public_key_bytes).map_err(|e| {
ConclaveError::CryptoError(format!("Invalid Bitcoin PK: {}", e))
})?;
let compressed = pk.to_bytes();
let cpk = bitcoin::key::CompressedPublicKey::from_slice(&compressed)
.map_err(|e| ConclaveError::CryptoError(format!("Compression error: {}", e)))?;
Address::p2wpkh(cpk, Network::Bitcoin).to_string()
}
Chain::ETHEREUM | Chain::BASE | Chain::ARBITRUM | Chain::POLYGON => {
let mut hasher = Keccak256::new();
if public_key_bytes.len() == 65 && public_key_bytes[0] == 0x04 {
hasher.update(&public_key_bytes[1..]);
} else if public_key_bytes.len() == 33 {
let pk = secp256k1::PublicKey::from_slice(&public_key_bytes).map_err(|e| {
ConclaveError::CryptoError(format!("Invalid compressed PK: {}", e))
})?;
let uncompressed = pk.serialize_uncompressed();
hasher.update(&uncompressed[1..]);
} else {
return Err(ConclaveError::CryptoError(
"Invalid public key length for EVM".to_string(),
));
}
let hash = hasher.finalize();
EthAddress::from_slice(&hash[12..]).to_string()
}
Chain::SOLANA => {
if public_key_bytes.len() == 32 {
bs58::encode(&public_key_bytes).into_string()
} else {
return Err(ConclaveError::CryptoError(
"Invalid public key length for Solana".to_string(),
));
}
}
Chain::STELLAR => {
if public_key_bytes.len() == 32 {
format!("G{}", bs58::encode(&public_key_bytes).into_string())
} else {
return Err(ConclaveError::CryptoError(
"Invalid public key length for Stellar".to_string(),
));
}
}
Chain::NEAR => {
if public_key_bytes.len() == 32 {
format!("ed25519:{}", bs58::encode(&public_key_bytes).into_string())
} else {
return Err(ConclaveError::CryptoError(
"Invalid public key length for NEAR".to_string(),
));
}
}
Chain::XrpLedger => {
format!("r{}", bs58::encode(&public_key_bytes[..20]).into_string())
}
Chain::TRON => {
format!("T{}", bs58::encode(&public_key_bytes[..20]).into_string())
}
Chain::STACKS => {
format!("SP{}", hex::encode(&public_key_bytes[..20]))
}
Chain::COSMOS => {
format!("cosmos1{}", hex::encode(&public_key_bytes[..20]))
}
_ => "0x_fallback_address".to_string(),
};
Ok(ChainSignatureResponse {
signature_hex: response.signature_hex,
target_address,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::enclave::cloud::CloudEnclave;
#[test]
fn test_resolve_intent_logic() {
let enclave = Arc::new(CloudEnclave::new("http://localhost".to_string()).unwrap());
let assets = Arc::new(AssetRegistry::new());
let service = ChainAbstractionService::new(enclave, assets);
let intent = CrossChainIntent {
input_asset: AssetIdentifier {
chain: Chain::BITCOIN,
symbol: "BTC".to_string(),
},
output_asset: AssetIdentifier {
chain: Chain::ETHEREUM,
symbol: "USDC".to_string(),
},
input_amount: 1000000,
output_amount: 65000000000,
destination_chain: Chain::ETHEREUM,
recipient: "0xrecipient".to_string(),
};
let resolved = service.resolve_intent(intent).unwrap();
assert_eq!(resolved.input_assets.len(), 1);
assert_eq!(resolved.output_assets[0].asset.symbol, "USDC");
}
#[test]
fn test_sign_for_chain_bitcoin() {
let enclave = Arc::new(CloudEnclave::new("http://localhost".to_string()).unwrap());
let assets = Arc::new(AssetRegistry::new());
let service = ChainAbstractionService::new(enclave, assets);
let request = ChainSignatureRequest {
target_chain: Chain::BITCOIN,
payload: vec![0u8; 32],
derivation_path: "m/44'/0'/0'/0/0".to_string(),
};
let response = service.sign_for_chain(request).unwrap();
assert!(response.target_address.starts_with("bc1"));
}
#[test]
fn test_sign_for_chain_xrp() {
let enclave = Arc::new(CloudEnclave::new("http://localhost".to_string()).unwrap());
let assets = Arc::new(AssetRegistry::new());
let service = ChainAbstractionService::new(enclave, assets);
let request = ChainSignatureRequest {
target_chain: Chain::XrpLedger,
payload: vec![0u8; 32],
derivation_path: "m/44'/144'/0'/0/0".to_string(),
};
let response = service.sign_for_chain(request).unwrap();
assert!(response.target_address.starts_with("r"));
}
#[test]
fn test_sign_for_chain_stellar() {
let enclave = Arc::new(CloudEnclave::new("http://localhost".to_string()).unwrap());
let assets = Arc::new(AssetRegistry::new());
let service = ChainAbstractionService::new(enclave, assets);
let request = ChainSignatureRequest {
target_chain: Chain::STELLAR,
payload: vec![0u8; 32],
derivation_path: "m/44'/148'/0'/0/0".to_string(),
};
let response = service.sign_for_chain(request).unwrap();
assert!(response.target_address.starts_with("G"));
}
#[test]
fn test_sign_for_chain_near() {
let enclave = Arc::new(CloudEnclave::new("http://localhost".to_string()).unwrap());
let assets = Arc::new(AssetRegistry::new());
let service = ChainAbstractionService::new(enclave, assets);
let request = ChainSignatureRequest {
target_chain: Chain::NEAR,
payload: vec![0u8; 32],
derivation_path: "m/44'/397'/0'/0/0".to_string(),
};
let response = service.sign_for_chain(request).unwrap();
assert!(response.target_address.starts_with("ed25519:"));
}
}