diff --git a/example_solver/Cargo.lock b/example_solver/Cargo.lock index 7d742de..f62671f 100644 --- a/example_solver/Cargo.lock +++ b/example_solver/Cargo.lock @@ -793,7 +793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive 0.10.3", - "hashbrown 0.12.3", + "hashbrown 0.13.2", ] [[package]] @@ -901,7 +901,7 @@ dependencies = [ "base64 0.21.7", "bytemuck", "cf-guest 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", - "derive_more", + "derive_more 0.99.17", "guestchain 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", "hex-literal", "ibc", @@ -1104,7 +1104,7 @@ source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=f dependencies = [ "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "guestchain 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", "ibc-client-tendermint-types", "ibc-core-client-context", @@ -1128,7 +1128,7 @@ source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=u dependencies = [ "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "guestchain 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", "ibc-client-tendermint-types", "ibc-core-client-context", @@ -1677,6 +1677,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "dialoguer" version = "0.10.4" @@ -2328,12 +2348,15 @@ dependencies = [ "base64 0.22.1", "bincode", "bridge-escrow", + "derive_more 1.0.0", "dotenv", + "env_logger", "ethers", "futures", "hex", "lazy_static", "lib 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", + "log", "num-bigint 0.4.5", "num-traits", "reqwest", @@ -2687,7 +2710,7 @@ source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=f dependencies = [ "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "ibc-core-client-context", "ibc-core-commitment-types", "ibc-core-host", @@ -2709,7 +2732,7 @@ source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=u dependencies = [ "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "ibc-core-client-context", "ibc-core-commitment-types", "ibc-core-host", @@ -2766,9 +2789,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] [[package]] name = "hashbrown" @@ -3065,7 +3085,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core", "ibc-proto", @@ -3087,7 +3107,7 @@ name = "ibc-client-tendermint" version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ - "derive_more", + "derive_more 0.99.17", "ibc-client-tendermint-types", "ibc-core-client", "ibc-core-commitment-types", @@ -3178,7 +3198,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core-client-types", "ibc-core-commitment-types", @@ -3211,7 +3231,7 @@ name = "ibc-core-client-context" version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-client-tendermint-types", "ibc-core-client-types", @@ -3229,7 +3249,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core-commitment-types", "ibc-core-host-types", @@ -3246,7 +3266,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-primitives", "ibc-proto", @@ -3273,7 +3293,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core-client-types", "ibc-core-commitment-types", @@ -3307,7 +3327,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core-channel-types", "ibc-core-client-types", @@ -3327,7 +3347,7 @@ name = "ibc-core-host" version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core-channel-types", "ibc-core-client-context", @@ -3346,7 +3366,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-app-transfer-types", "ibc-client-tendermint", @@ -3370,7 +3390,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-primitives", "serde", @@ -3381,7 +3401,7 @@ name = "ibc-core-router" version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core-channel-types", "ibc-core-host-types", @@ -3396,7 +3416,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-core-host-types", "ibc-primitives", @@ -3422,7 +3442,7 @@ version = "0.50.0" source = "git+https://github.com/mina86/ibc-rs?rev=f07276383091f75b7ee8bff6fd434f8214ac5054#f07276383091f75b7ee8bff6fd434f8214ac5054" dependencies = [ "borsh 0.10.3", - "derive_more", + "derive_more 0.99.17", "displaydoc", "ibc-proto", "prost", @@ -3760,7 +3780,7 @@ dependencies = [ "borsh 0.10.3", "bs58 0.5.1", "bytemuck", - "derive_more", + "derive_more 0.99.17", "sha2 0.10.8", "solana-program", "stdx 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", @@ -3775,7 +3795,7 @@ dependencies = [ "borsh 0.10.3", "bs58 0.5.1", "bytemuck", - "derive_more", + "derive_more 0.99.17", "sha2 0.10.8", "solana-program", "stdx 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", @@ -3938,7 +3958,7 @@ name = "memory" version = "0.0.0" source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge#a5438346ffc7824d447c09fad6c1970a536ffb97" dependencies = [ - "derive_more", + "derive_more 0.99.17", ] [[package]] @@ -3946,7 +3966,7 @@ name = "memory" version = "0.0.0" source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade#a4183a1e99eb9ff4d9e2c5e0d0324581a14d64fd" dependencies = [ - "derive_more", + "derive_more 0.99.17", ] [[package]] @@ -4819,7 +4839,7 @@ version = "0.0.0" source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge#a5438346ffc7824d447c09fad6c1970a536ffb97" dependencies = [ "const_format", - "derive_more", + "derive_more 0.99.17", "ibc-core-client-context", "ibc-proto", "prost", @@ -4831,7 +4851,7 @@ version = "0.0.0" source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade#a4183a1e99eb9ff4d9e2c5e0d0324581a14d64fd" dependencies = [ "const_format", - "derive_more", + "derive_more 0.99.17", "ibc-core-client-context", "ibc-proto", "prost", @@ -5357,7 +5377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" dependencies = [ "cfg-if", - "derive_more", + "derive_more 0.99.17", "parity-scale-codec", "scale-info-derive", ] @@ -5440,7 +5460,7 @@ dependencies = [ "base64 0.21.7", "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "lib 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", "memory 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", "sha2 0.10.8", @@ -5457,7 +5477,7 @@ dependencies = [ "base64 0.21.7", "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "lib 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", "memory 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", "sha2 0.10.8", @@ -5989,7 +6009,7 @@ dependencies = [ "base64 0.21.7", "bytemuck", "cf-guest 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", - "derive_more", + "derive_more 0.99.17", "guestchain 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", "hex-literal", "ibc", @@ -6027,7 +6047,7 @@ dependencies = [ "base64 0.21.7", "bytemuck", "cf-guest 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", - "derive_more", + "derive_more 0.99.17", "guestchain 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", "hex-literal", "ibc", @@ -6449,7 +6469,7 @@ dependencies = [ "base64 0.21.7", "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "guestchain 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", "lib 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge)", "solana-program", @@ -6464,7 +6484,7 @@ dependencies = [ "base64 0.21.7", "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "guestchain 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", "lib 0.0.0 (git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade)", "solana-program", @@ -7456,7 +7476,7 @@ version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b8090d0eef9ad57b1b913b5e358e26145c86017e87338136509b94383a4af25" dependencies = [ - "derive_more", + "derive_more 0.99.17", "flex-error", "serde", "tendermint", @@ -7834,7 +7854,7 @@ dependencies = [ "base64 0.21.7", "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "ibc-core-channel-types", "ibc-core-client-types", "ibc-core-connection-types", @@ -7851,7 +7871,7 @@ dependencies = [ "base64 0.21.7", "borsh 0.10.3", "bytemuck", - "derive_more", + "derive_more 0.99.17", "ibc-core-channel-types", "ibc-core-client-types", "ibc-core-connection-types", @@ -8096,7 +8116,7 @@ version = "0.0.0" source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=fast-bridge#a5438346ffc7824d447c09fad6c1970a536ffb97" dependencies = [ "const_format", - "derive_more", + "derive_more 0.99.17", "ibc-core-client-context", "ibc-core-commitment-types", "ibc-primitives", @@ -8112,7 +8132,7 @@ version = "0.0.0" source = "git+https://github.com/ComposableFi/emulated-light-client.git?branch=upgrade#a4183a1e99eb9ff4d9e2c5e0d0324581a14d64fd" dependencies = [ "const_format", - "derive_more", + "derive_more 0.99.17", "ibc-core-client-context", "ibc-core-commitment-types", "ibc-primitives", @@ -8207,7 +8227,7 @@ dependencies = [ "arrayvec", "base64 0.21.7", "bytes", - "derive_more", + "derive_more 0.99.17", "ethabi", "ethereum-types", "futures", diff --git a/example_solver/Cargo.toml b/example_solver/Cargo.toml index 0d3423e..e843c5f 100644 --- a/example_solver/Cargo.toml +++ b/example_solver/Cargo.toml @@ -35,6 +35,9 @@ web3 = "0.19.0" bridge-escrow = { git = "https://github.com/ComposableFi/emulated-light-client.git", branch = "upgrade", package = "bridge-escrow" } solana-ibc = { git = "https://github.com/ComposableFi/emulated-light-client.git", branch = "fast-bridge", features = ["cpi"] } lib = { git = "https://github.com/ComposableFi/emulated-light-client.git", branch = "fast-bridge", features = ["solana-program"] } +derive_more = { version = "1.0.0", features = ["deref", "deref_mut"] } +log = "0.4.21" +env_logger = "0.9.3" [patch.crates-io] # aes-gcm-siv 0.10.3 and curve25519-dalek 3.x pin zeroize to <1.4 diff --git a/example_solver/src/chains/ethereum.rs b/example_solver/src/chains/ethereum.rs index 70c19b7..d486c95 100644 --- a/example_solver/src/chains/ethereum.rs +++ b/example_solver/src/chains/ethereum.rs @@ -90,7 +90,7 @@ pub mod ethereum_chain { "stateMutability": "payable", "type": "function" }]"# - ); + ); abigen!( UsdtContract, @@ -508,18 +508,16 @@ pub mod ethereum_chain { } } - pub async fn ethereum_simulate_swap( - token_in: &str, - amount_in: &str, - token_out: &str, + pub async fn ethereum_quote( + token_in: Address, + amount_in: BigInt, + token_out: Address, ) -> BigInt { let rpc_url = env::var("ETHEREUM_RPC").expect("ETHEREUM_RPC must be set"); let provider = Provider::::try_from(rpc_url) .map_err(|e| e.to_string()) .unwrap(); let provider = Arc::new(provider); - let token_in = Address::from_str(token_in).unwrap(); - let token_out = Address::from_str(token_out).unwrap(); let token0_decimals = get_evm_token_decimals(&ERC20::new(token_in, provider.clone())).await; let token1_decimals = get_evm_token_decimals(&ERC20::new(token_out, provider.clone())).await; @@ -527,9 +525,9 @@ pub mod ethereum_chain { let paraswap_params = ParaswapParams { side: "SELL".to_string(), chain_id: 1, - amount_in: BigInt::from_str(amount_in).unwrap(), - token_in: token_in, - token_out: token_out, + amount_in, + token_in, + token_out, token0_decimals: token0_decimals as u32, token1_decimals: token1_decimals as u32, wallet_address: Address::from_str(SOLVER_ADDRESSES.get(0).unwrap()).unwrap(), diff --git a/example_solver/src/chains/solana.rs b/example_solver/src/chains/solana.rs index 797fa11..c97a6f5 100644 --- a/example_solver/src/chains/solana.rs +++ b/example_solver/src/chains/solana.rs @@ -1,6 +1,6 @@ pub mod solana_chain { use crate::chains::*; - use crate::routers::jupiter::create_token_account; + use crate::routers::jupiter::{create_token_account, Quote}; use crate::routers::jupiter::jupiter_swap; use crate::routers::jupiter::quote; use crate::routers::jupiter::Memo as Jup_Memo; @@ -341,23 +341,18 @@ pub mod solana_chain { } } - pub async fn solana_simulate_swap( - dst_chain_user: &str, - token_in: &str, - token_out: &str, + pub async fn solana_quote( + dst_chain_user: Pubkey, + token_in: Pubkey, + token_out: Pubkey, amount_in: u64, - ) -> String { - let memo_json = json!({ - "user_account": dst_chain_user, - "token_in": token_in, - "token_out": token_out, - "amount": amount_in, - "slippage_bps": 100 - }); - - let memo = match Jup_Memo::from_json(&memo_json.to_string()) { - Ok(memo) => memo, - Err(_) => return "0".to_string(), + ) -> anyhow::Result { + let memo = Jup_Memo { + user_account: dst_chain_user, + token_in, + token_out, + amount: amount_in, + slippage_bps: 100, }; let quote_config = QuoteConfig { @@ -367,12 +362,7 @@ pub mod solana_chain { ..QuoteConfig::default() }; - let quotes = match quote(memo.token_in, memo.token_out, memo.amount, quote_config).await { - Ok(quotes) => quotes, - Err(_) => return "0".to_string(), - }; - - BigInt::from(quotes.out_amount).to_string() + Ok(quote(memo.token_in, memo.token_out, memo.amount, quote_config).await?) } pub async fn solana_send_funds_to_user( diff --git a/example_solver/src/main.rs b/example_solver/src/main.rs index f2cfdb7..6050723 100644 --- a/example_solver/src/main.rs +++ b/example_solver/src/main.rs @@ -1,9 +1,9 @@ mod chains; mod routers; -use crate::chains::ethereum::ethereum_chain::handle_ethereum_execution; +use crate::chains::ethereum::ethereum_chain::{ethereum_quote, handle_ethereum_execution}; use crate::chains::mantis::mantis_chain::handle_mantis_execution; -use crate::chains::solana::solana_chain::handle_solana_execution; +use crate::chains::solana::solana_chain::{handle_solana_execution, solana_quote}; use crate::chains::OperationInput; use crate::chains::OperationOutput; use crate::chains::PostIntentInfo; @@ -13,25 +13,41 @@ use crate::chains::SOLVER_ID; use crate::chains::SOLVER_PRIVATE_KEY; use crate::routers::get_simulate_swap_intent; use chains::create_keccak256_signature; +use derive_more::{Deref, DerefMut}; +use ethers::core::rand; +use ethers::prelude::{Signature, H256}; use ethers::types::U256; +use futures::stream::SplitSink; use futures::{SinkExt, StreamExt}; +use log::{debug, error}; +use serde::{Deserialize, Serialize}; use serde_json::json; use serde_json::Value; +use solana_sdk::pubkey::Pubkey; use spl_associated_token_account::get_associated_token_address; use std::env; -use ethers::prelude::{Signature, H256}; -use futures::stream::SplitSink; -use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use std::sync::atomic::AtomicU32; +use std::sync::Arc; +use ethers::abi::Address; +use num_bigint::BigInt; +use num_traits::Num; use tokio::net::TcpStream; -use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream}; -use tokio_tungstenite::tungstenite::Error; +use tokio::sync::Mutex; use tokio_tungstenite::tungstenite::protocol::Message; +use tokio_tungstenite::tungstenite::Error; +use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream}; + +type RequestId = u32; +type SolverId = String; #[derive(Serialize)] #[serde(tag = "type", rename_all = "snake_case")] enum ClientMessage { SolverRegister(SignedPayload), AuctionBid(SignedPayload), + QuoteResponse(QuoteResponse), + ErrorResponse(ErrorResponse), } #[derive(Deserialize)] @@ -41,6 +57,28 @@ enum ServerMessage { NewIntent(NewIntentMessage), AuctionResult(AuctionResultMessage), ErrorResponse(ErrorResponse), + QuoteRequest(QuoteRequest), +} + +#[derive(Serialize, Deserialize)] +struct QuoteRequest { + pub token_in: String, + pub amount_in: U256, + pub token_out: String, + pub network: Network +} + +#[derive(Deserialize, Serialize)] +struct QuoteResponse { + pub token_out: String, + pub amount_out: U256, +} + +#[derive(Deserialize, Serialize)] +struct SolverQuoteResponse { + solver_id: SolverId, + #[serde(flatten)] + response: QuoteResponse, } #[derive(Serialize)] @@ -68,6 +106,20 @@ struct SignedPayload { signature: Signature, } +#[derive(Serialize, Deserialize, Deref, DerefMut)] +pub struct Identified { + #[deref] + #[deref_mut] + payload: T, + pub(crate) id: RequestId, +} + +impl Identified { + pub fn new(payload: T, id: RequestId) -> Self { + Identified { payload, id } + } +} + #[derive(Deserialize)] struct NewIntentMessage { intent_id: String, @@ -81,166 +133,284 @@ struct AuctionResultMessage { message: String, } -#[derive(Deserialize)] +#[derive(Deserialize, Serialize)] struct ErrorResponse { message: String, } +struct Solver { + solver_id: SolverId, + request_id: Arc, + ws_sender: SplitSink>, Message>, +} + +#[derive(Copy, Clone)] +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +enum Network { + Solana, + Ethereum, +} + #[tokio::main] async fn main() { dotenv::dotenv().ok(); - let server_addr = env::var("COMPOSABLE_ENDPOINT") - .unwrap_or_else(|_| String::from("ws://34.78.217.187:8080")); + env_logger::init(); + let server_addr = + env::var("COMPOSABLE_ENDPOINT").unwrap_or_else(|_| String::from("ws://34.78.217.187:8080")); let (ws_stream, _) = connect_async(format!("{}/ws", server_addr)) .await .expect("Failed to connect"); let (mut ws_sender, mut ws_receiver) = ws_stream.split(); - let register_request = SolverRegisterRequest { + let mut solver = Solver { solver_id: SOLVER_ID.to_string(), - solver_addresses: SOLVER_ADDRESSES.into_iter().map(ToString::to_string).collect(), + request_id: Arc::new(AtomicU32::new(0)), + ws_sender, }; - let register_request = create_keccak256_signature(register_request, SOLVER_PRIVATE_KEY.to_string()) - .await - .unwrap(); + let register_request = SolverRegisterRequest { + solver_id: solver.solver_id.clone(), + solver_addresses: SOLVER_ADDRESSES + .into_iter() + .map(ToString::to_string) + .collect(), + }; + + let register_request = + create_keccak256_signature(register_request, SOLVER_PRIVATE_KEY.to_string()) + .await + .unwrap(); // Serialize the message let message = ClientMessage::SolverRegister(register_request); - let message_text = serde_json::to_string(&message).unwrap(); - - ws_sender - .send(Message::Text(message_text)) - .await - .expect("Failed to send initial message"); + solver.identify_and_send(&message).await.unwrap(); while let Some(msg) = ws_receiver.next().await { - handle_message(&mut ws_sender, msg).await; + if let Err(e) = solver + .handle_message(msg) + .await { + error!("Error handling message: {}", e); + + } } + // TODO: auto-reconnect println!("Auctioneer went down, please reconnect"); } -async fn handle_message(ws_sender: &mut SplitSink>, Message>, msg: Result) -> Result, ()> { - match msg { - Ok(Message::Text(text)) => { - let server_message: ServerMessage = match serde_json::from_str(&text) { - Ok(msg) => msg, - Err(err) => { - eprintln!("Failed to parse server message: {:?}", err); - return Ok(Some(())); - } - }; +impl Solver { + pub async fn send_raw(&mut self, val: &T) -> anyhow::Result<()> { + let val = serde_json::to_string(val)?; + let message = Message::Text(val); + self.ws_sender.send(message).await?; + Ok(()) + } + + pub async fn identify_and_send(&mut self, val: &T) -> anyhow::Result<()> { + let req_id = self + .request_id + .fetch_add(1, std::sync::atomic::Ordering::SeqCst); + let ident_val = Identified::new(val, req_id); + self.send_raw(&ident_val).await + } + + pub async fn reply( + &mut self, + val: &T, + request_id: RequestId, + ) -> anyhow::Result<()> { + let ident_val = Identified::new(val, request_id); + self.send_raw(&ident_val).await + } + + async fn handle_message(&mut self, msg: Result) -> anyhow::Result> { + match msg { + Ok(Message::Text(text)) => { + debug!("Received message: {}", text); + let ident_server_message: Identified = + match serde_json::from_str(&text) { + Ok(msg) => msg, + Err(err) => { + eprintln!("Failed to parse server message: {:?}", err); + return Ok(Some(())); + } + }; - match server_message { - ServerMessage::SolverRegisterResponse(response) => { - println!("Solver registered: {}", response.message); + let id = ident_server_message.id; + if let Err(e) = self.process_server_message(ident_server_message).await { + error!("Error processing server message: {}", e); + self.reply( + &ClientMessage::ErrorResponse(ErrorResponse { + message: format!("Error processing message: {}", e), + }), + id, + ).await?; } - ServerMessage::NewIntent(new_intent) => { - // Participate in auction - let intent_id = new_intent.intent_id; - let intent_info = new_intent.intent; - - // Calculate best quote - let final_amount = get_simulate_swap_intent( - &intent_info, - &intent_info.src_chain, - &intent_info.dst_chain, - &"USDT".to_string(), - ) - .await; + Ok(Some(())) + } + Ok(Message::Close(_)) | Err(_) => Ok(None), + _ => Ok(Some(())), + } + } - // Decide whether to participate - let amount_out_min = if let OperationOutput::SwapTransfer(transfer_output) = &intent_info.outputs { - U256::from_dec_str(&transfer_output.amount_out).unwrap_or(U256::zero()) - } else { - U256::zero() + async fn process_server_message(&mut self, ident_server_message: Identified) -> anyhow::Result<()> { + let server_message = ident_server_message.payload; + let req_id = ident_server_message.id; + match server_message { + ServerMessage::SolverRegisterResponse(response) => { + println!("Solver registered: {}", response.message); + } + ServerMessage::NewIntent(new_intent) => { + // Participate in auction + let intent_id = new_intent.intent_id; + let intent_info = new_intent.intent; + + // Calculate best quote + let final_amount = get_simulate_swap_intent( + &intent_info, + &intent_info.src_chain, + &intent_info.dst_chain, + &"USDT".to_string(), + ) + .await; + + // Decide whether to participate + let amount_out_min = if let OperationOutput::SwapTransfer(transfer_output) = + &intent_info.outputs + { + U256::from_dec_str(&transfer_output.amount_out).unwrap_or(U256::zero()) + } else { + U256::zero() + }; + + let final_amount_u256 = + U256::from_dec_str(&final_amount).unwrap_or(U256::zero()); + + println!( + "User wants {} token_out, you can provide {} token_out (after fees and commission)", + amount_out_min, final_amount_u256 + ); + + if final_amount_u256 > amount_out_min { + // Create AuctionBidRequest + let auction_bid_request = AuctionBidRequest { + intent_id: intent_id.clone(), + solver_id: SOLVER_ID.to_string(), + amount: final_amount.clone(), }; - let final_amount_u256 = U256::from_dec_str(&final_amount).unwrap_or(U256::zero()); + // Create signature + let auction_bid_request = create_keccak256_signature( + auction_bid_request, + SOLVER_PRIVATE_KEY.to_string(), + ) + .await + .unwrap(); - println!( - "User wants {} token_out, you can provide {} token_out (after fees and commission)", - amount_out_min, final_amount_u256 - ); + // Serialize the message + let message = ClientMessage::AuctionBid(auction_bid_request); + self.reply(&message, req_id).await.unwrap(); - if final_amount_u256 > amount_out_min { - // Create AuctionBidRequest - let auction_bid_request = AuctionBidRequest { - intent_id: intent_id.clone(), - solver_id: SOLVER_ID.to_string(), - amount: final_amount.clone(), - }; - - // Create signature - let auction_bid_request = create_keccak256_signature(auction_bid_request, SOLVER_PRIVATE_KEY.to_string()) - .await - .unwrap(); - - // Serialize the message - let message = ClientMessage::AuctionBid(auction_bid_request); - let message_text = serde_json::to_string(&message).unwrap(); - - ws_sender - .send(Message::Text(message_text)) - .await - .expect("Failed to send auction bid message"); - - // Store the intent - { - let mut intents = INTENTS.write().await; - intents.insert(intent_id.clone(), intent_info); - } + // Store the intent + { + let mut intents = INTENTS.write().await; + intents.insert(intent_id.clone(), intent_info); } } - ServerMessage::AuctionResult(auction_result) => { - let intent_id = auction_result.intent_id; - let amount = auction_result.amount; - - if let Some(amount) = amount { - println!("Auction result for {}: {}", intent_id, auction_result.message); - - // Retrieve the intent - let intent = { - let intents = INTENTS.read().await; - intents.get(&intent_id).cloned() - }; - - if let Some(intent) = intent { - if auction_result.message.contains("won") { - // Handle execution - let handle_result = match intent.dst_chain.as_str() { - "solana" => handle_solana_execution(&intent, &intent_id, &amount).await, - "ethereum" => handle_ethereum_execution(&intent, &intent_id, &amount, intent.src_chain == intent.dst_chain).await, - "mantis" => handle_mantis_execution(&intent, &intent_id, &amount).await, - _ => Err("Unsupported destination chain".to_string()), - }; - - if let Err(err) = handle_result { - eprintln!("Failed to handle execution: {}", err); + } + ServerMessage::AuctionResult(auction_result) => { + let intent_id = auction_result.intent_id; + let amount = auction_result.amount; + + if let Some(amount) = amount { + println!( + "Auction result for {}: {}", + intent_id, auction_result.message + ); + + // Retrieve the intent + let intent = { + let intents = INTENTS.read().await; + intents.get(&intent_id).cloned() + }; + + if let Some(intent) = intent { + if auction_result.message.contains("won") { + // Handle execution + let handle_result = match intent.dst_chain.as_str() { + "solana" => { + handle_solana_execution(&intent, &intent_id, &amount) + .await } - } + "ethereum" => { + handle_ethereum_execution( + &intent, + &intent_id, + &amount, + intent.src_chain == intent.dst_chain, + ) + .await + } + "mantis" => { + handle_mantis_execution(&intent, &intent_id, &amount) + .await + } + _ => Err("Unsupported destination chain".to_string()), + }; - // Remove the intent - { - let mut intents = INTENTS.write().await; - intents.remove(&intent_id); + if let Err(err) = handle_result { + eprintln!("Failed to handle execution: {}", err); } - } else { - eprintln!("Intent not found for intent_id: {}", intent_id); } + + // Remove the intent + { + let mut intents = INTENTS.write().await; + intents.remove(&intent_id); + } + } else { + eprintln!("Intent not found for intent_id: {}", intent_id); } } - ServerMessage::ErrorResponse(error_response) => { - eprintln!("Error from server: {}", error_response.message); - } } - Ok(Some(())) + ServerMessage::ErrorResponse(error_response) => { + eprintln!("Error from server: {}", error_response.message); + } + ServerMessage::QuoteRequest(req) => { + let quote_market = self.quote_market(req.network, &req).await?; + let message = ClientMessage::QuoteResponse(QuoteResponse { + token_out: req.token_out, + amount_out: quote_market, + }); + self.reply(&message, req_id).await.unwrap(); + } } - Ok(Message::Close(_)) | Err(_) => Ok(None), - _ => { - Ok(Some(())) + Ok(()) + } + + async fn quote_market( + &self, + network: Network, + quote_request: &QuoteRequest, + ) -> anyhow::Result { + match network { + Network::Solana => { + let dest_user = Pubkey::default(); + let token_in = Pubkey::from_str("e_request.token_in)?; + let amount_in = quote_request.amount_in.as_u64(); + let token_out = Pubkey::from_str("e_request.token_out)?; + let quote = solana_quote(dest_user, token_in, token_out, amount_in).await?; + Ok(quote.out_amount.into()) + } + Network::Ethereum => { + let token_in = Address::from_str("e_request.token_in)?; + let token_out = Address::from_str("e_request.token_out)?; + let amount_in = BigInt::from_str_radix("e_request.amount_in.to_string(), 16)?; + let amount_out = ethereum_quote(token_in, amount_in, token_out).await; + Ok(U256::from_dec_str(&amount_out.to_string()).unwrap()) + } } } -} \ No newline at end of file +} diff --git a/example_solver/src/routers/mod.rs b/example_solver/src/routers/mod.rs index 543cc74..9bf9579 100644 --- a/example_solver/src/routers/mod.rs +++ b/example_solver/src/routers/mod.rs @@ -5,16 +5,18 @@ pub mod paraswap; // use serde_json::Value; use crate::chains::*; use crate::PostIntentInfo; -use ethereum::ethereum_chain::ethereum_simulate_swap; +use ethereum::ethereum_chain::ethereum_quote; use lazy_static::lazy_static; use num_bigint::BigInt; use num_traits::ToPrimitive; use num_traits::Zero; -use solana::solana_chain::solana_simulate_swap; +use solana::solana_chain::solana_quote; use std::collections::HashMap; use std::env; use std::str::FromStr; use std::sync::Arc; +use anchor_lang::prelude::Pubkey; +use ethers::abi::Address; use tokio::sync::RwLock; lazy_static! { @@ -94,8 +96,11 @@ pub async fn get_simulate_swap_intent( if !bridge_token_address_src.eq_ignore_ascii_case(&token_in) { // simulate token_in -> USDT if src_chain == "ethereum" { + let token_in = Address::from_str(&token_in).unwrap(); + let bridge_token_address_src = Address::from_str(&bridge_token_address_src).unwrap(); + let amount_in = amount_in.parse::().unwrap(); amount_out_src_chain = - ethereum_simulate_swap(&token_in, &amount_in, bridge_token_address_src).await; + ethereum_quote(token_in, amount_in, bridge_token_address_src).await; } else if src_chain == "solana" || src_chain == "mantis" { if src_chain == "mantis" { let tokens = MANTIS_TOKENS.read().await; @@ -112,16 +117,18 @@ pub async fn get_simulate_swap_intent( } if !amount_out_src_chain.is_zero() { - amount_out_src_chain = BigInt::from_str( - &solana_simulate_swap( - &src_chain_user, - &token_in, - &bridge_token_address_src, - BigInt::from_str(&amount_in).unwrap().to_u64().unwrap(), + let src_chain_user = Pubkey::from_str(&src_chain_user).unwrap(); + let token_in = Pubkey::from_str(&token_in).unwrap(); + let bridge_token_address_src = Pubkey::from_str(&bridge_token_address_src).unwrap(); + amount_out_src_chain = BigInt::from( + solana_quote( + src_chain_user, + token_in, + bridge_token_address_src, + amount_in.parse::().unwrap(), ) - .await, + .await.unwrap().out_amount, ) - .unwrap(); } } } @@ -161,10 +168,11 @@ pub async fn get_simulate_swap_intent( { // simulate USDT -> token_out if dst_chain == "ethereum" { + let token_in = Address::from_str(&token_in).unwrap(); + let bridge_token_address_src = Address::from_str(&bridge_token_address_src).unwrap(); + let amount_in = amount_in.parse::().unwrap(); final_amount_out = - ethereum_simulate_swap(bridge_token_address_src, &final_amount_out, &token_out) - .await - .to_string(); + ethereum_quote(token_in, amount_in, bridge_token_address_src).await.to_string(); } else if dst_chain == "solana" || dst_chain == "mantis" { if src_chain == "mantis" { let tokens = MANTIS_TOKENS.read().await; @@ -181,13 +189,16 @@ pub async fn get_simulate_swap_intent( } if final_amount_out != "0" { - final_amount_out = solana_simulate_swap( - &dst_chain_user, + let dst_chain_user = Pubkey::from_str(&dst_chain_user).unwrap(); + let token_out = Pubkey::from_str(&token_out).unwrap(); + let bridge_token_address_dst = Pubkey::from_str(&bridge_token_address_dst).unwrap(); + final_amount_out = solana_quote( + dst_chain_user, bridge_token_address_dst, - &token_out, + token_out, amount_in_dst_chain.to_u64().unwrap(), ) - .await; + .await.unwrap().out_amount.to_string(); } } }