Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/swapper/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub mod models;
pub mod near_intents;
pub mod permit2_data;
pub mod proxy;
pub mod referrer;
pub mod relay;
pub mod slippage;
pub mod swapper;
pub mod thorchain;
Expand Down
2 changes: 1 addition & 1 deletion crates/swapper/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl ProviderType {
| SwapperProvider::Orca => SwapProviderMode::OnChain,
SwapperProvider::Mayan | SwapperProvider::Chainflip | SwapperProvider::NearIntents => SwapProviderMode::CrossChain,
SwapperProvider::Thorchain => SwapProviderMode::OmniChain(vec![Chain::Thorchain, Chain::Tron]),
SwapperProvider::Relay => SwapProviderMode::OmniChain(vec![Chain::Hyperliquid, Chain::Manta, Chain::Berachain]),
SwapperProvider::Relay => SwapProviderMode::OmniChain(vec![Chain::Bitcoin, Chain::Solana, Chain::Hyperliquid, Chain::Berachain]),
SwapperProvider::Across => SwapProviderMode::Bridge,
SwapperProvider::Hyperliquid => SwapProviderMode::OmniChain(vec![Chain::HyperCore, Chain::Hyperliquid]),
}
Expand Down
47 changes: 1 addition & 46 deletions crates/swapper/src/near_intents/model.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use serde::{Deserialize, Serialize};

pub const DEFAULT_REFERRAL: &str = "gemwallet";
pub const DEPOSIT_TYPE_ORIGIN: &str = "ORIGIN_CHAIN";
pub const RECIPIENT_TYPE_DESTINATION: &str = "DESTINATION_CHAIN";
pub const DEFAULT_WAIT_TIME_MS: u32 = 1_024;
Expand All @@ -18,29 +17,19 @@ pub struct QuoteRequest {
pub origin_asset: String,
pub destination_asset: String,
pub amount: String,
#[serde(default = "default_referral")]
pub referral: String,
pub recipient: String,
pub swap_type: SwapType,
#[serde(default = "default_slippage_tolerance")]
pub slippage_tolerance: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub app_fees: Option<Vec<AppFee>>,
#[serde(default = "default_deposit_type")]
pub deposit_type: String,
#[serde(default)]
pub refund_to: String,
#[serde(default = "default_refund_type")]
pub refund_type: String,
#[serde(default = "default_recipient_type")]
pub recipient_type: String,
#[serde(default)]
pub deadline: String,
#[serde(default = "default_quote_waiting_time_ms")]
pub quote_waiting_time_ms: u32,
#[serde(default)]
pub quote_waiting_time_ms: Option<u32>,
pub dry: bool,
#[serde(default)]
pub deposit_mode: DepositMode,
}

Expand Down Expand Up @@ -83,7 +72,6 @@ pub enum QuoteResponseResult {
pub struct Quote {
pub deposit_address: Option<String>,
pub deposit_memo: Option<String>,
#[serde(default)]
pub deposit_mode: Option<DepositMode>,
pub amount_in: String,
pub amount_in_formatted: String,
Expand All @@ -99,58 +87,25 @@ pub struct Quote {
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExecutionStatus {
#[serde(default)]
pub quote_response: Option<QuoteResponse>,
pub status: String,
#[serde(default)]
pub updated_at: String,
#[serde(default)]
pub swap_details: Option<SwapDetails>,
}

#[derive(Debug, Clone, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct SwapDetails {
#[serde(default)]
pub amount_in: Option<String>,
#[serde(default)]
pub amount_out: Option<String>,
#[serde(default)]
pub origin_chain_tx_hashes: Vec<TransactionDetails>,
#[serde(default)]
pub destination_chain_tx_hashes: Vec<TransactionDetails>,
#[serde(default)]
pub refunded_amount: Option<String>,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransactionDetails {
pub hash: String,
#[serde(default)]
pub explorer_url: Option<String>,
}

fn default_referral() -> String {
DEFAULT_REFERRAL.to_string()
}

fn default_deposit_type() -> String {
DEPOSIT_TYPE_ORIGIN.to_string()
}

fn default_refund_type() -> String {
DEPOSIT_TYPE_ORIGIN.to_string()
}

fn default_recipient_type() -> String {
RECIPIENT_TYPE_DESTINATION.to_string()
}

fn default_slippage_tolerance() -> u32 {
0
}

fn default_quote_waiting_time_ms() -> u32 {
DEFAULT_WAIT_TIME_MS
}
22 changes: 13 additions & 9 deletions crates/swapper/src/near_intents/provider.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use super::{
AppFee, DepositMode, ExecutionStatus, NearIntentsClient, QuoteRequest as NearQuoteRequest, QuoteResponse, QuoteResponseError, QuoteResponseResult, SwapType,
asset_id_from_near_intents, auto_quote_time_chains, deposit_memo_chains, get_near_intents_asset_id,
model::{DEFAULT_REFERRAL, DEFAULT_WAIT_TIME_MS, DEPOSIT_TYPE_ORIGIN, RECIPIENT_TYPE_DESTINATION},
model::{DEFAULT_WAIT_TIME_MS, DEPOSIT_TYPE_ORIGIN, RECIPIENT_TYPE_DESTINATION},
reserved_tx_fees, supported_assets,
};
use crate::{
FetchQuoteData, ProviderData, ProviderType, Quote, QuoteRequest, Route, RpcClient, RpcProvider, SwapResult, Swapper, SwapperChainAsset, SwapperError, SwapperMode,
SwapperProvider, SwapperQuoteAsset, SwapperQuoteData, client_factory::create_client_with_chain, amount_to_value, near_intents::client::base_url,
SwapperProvider, SwapperQuoteAsset, SwapperQuoteData, client_factory::create_client_with_chain, amount_to_value, near_intents::client::base_url, referrer::DEFAULT_REFERRER,
};
use alloy_primitives::U256;
use async_trait::async_trait;
Expand Down Expand Up @@ -126,15 +126,15 @@ where
let deposit_mode = Self::resolve_deposit_mode(&request.from_asset);
let from_chain = request.from_asset.asset_id().chain;
let to_chain = request.to_asset.asset_id().chain;
let quote_waiting_time_ms = Self::resolve_quote_waiting_time(from_chain, to_chain);
let quote_waiting_time_ms = Some(Self::resolve_quote_waiting_time(from_chain, to_chain));

let deadline = (Utc::now() + Duration::minutes(DEFAULT_DEADLINE_MINUTES)).to_rfc3339();

Ok(NearQuoteRequest {
origin_asset,
destination_asset,
amount,
referral: DEFAULT_REFERRAL.to_string(),
referral: DEFAULT_REFERRER.to_string(),
recipient: request.destination_address.clone(),
swap_type: mode,
slippage_tolerance: request.options.slippage.bps,
Expand Down Expand Up @@ -470,7 +470,11 @@ mod tests {
mod swap_integration_tests {
use super::*;
use crate::{FetchQuoteData, SwapperMode, SwapperQuoteAsset, SwapperSlippage, SwapperSlippageMode, alien::reqwest_provider::NativeProvider, models::Options};
use primitives::{AssetId, Chain, swap::SwapStatus};
use primitives::{
AssetId, Chain,
asset_constants::{USDC_ARB_ASSET_ID, USDC_BASE_ASSET_ID},
swap::SwapStatus,
};
use std::sync::Arc;

#[tokio::test]
Expand All @@ -492,10 +496,10 @@ mod swap_integration_tests {
};

let request = QuoteRequest {
from_asset: SwapperQuoteAsset::from(AssetId::new("arbitrum_0xaf88d065e77c8cc2239327c5edb3a432268e5831").unwrap()),
to_asset: SwapperQuoteAsset::from(AssetId::new("solana_epjfwdd5aufqssqem2qn1xzybapc8g4weggkzwytdt1v").unwrap()),
wallet_address: "0x2527D02599Ba641c19FEa793cD0F167589a0f10D".to_string(),
destination_address: "13QkxhNMrTPxoCkRdYdJ65tFuwXPhL5gLS2Z5Nr6gjRK".to_string(),
from_asset: SwapperQuoteAsset::from(AssetId::new(USDC_ARB_ASSET_ID).unwrap()),
to_asset: SwapperQuoteAsset::from(AssetId::new(USDC_BASE_ASSET_ID).unwrap()),
wallet_address: "0x514bcb1f9aabb904e6106bd1052b66d2706dbbb7".to_string(),
destination_address: "0x514bcb1f9aabb904e6106bd1052b66d2706dbbb7".to_string(),
value: "500000".to_string(),
mode: SwapperMode::ExactIn,
options,
Expand Down
13 changes: 0 additions & 13 deletions crates/swapper/src/proxy/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,6 @@ impl ProxyProvider<RpcClient> {

Self::new_with_path(SwapperProvider::Mayan, "mayan", assets, rpc_provider)
}

pub fn new_relay(rpc_provider: Arc<dyn RpcProvider>) -> Self {
Self::new_with_path(
SwapperProvider::Relay,
"relay",
vec![
SwapperChainAsset::All(Chain::Hyperliquid),
SwapperChainAsset::All(Chain::Manta),
SwapperChainAsset::All(Chain::Berachain),
],
rpc_provider,
)
}
}

#[async_trait]
Expand Down
4 changes: 0 additions & 4 deletions crates/swapper/src/proxy/provider_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,3 @@ pub fn new_panora(rpc_provider: Arc<dyn RpcProvider>) -> ProxyProvider<RpcClient
pub fn new_mayan(rpc_provider: Arc<dyn RpcProvider>) -> ProxyProvider<RpcClient> {
ProxyProvider::new_mayan(rpc_provider)
}

pub fn new_relay(rpc_provider: Arc<dyn RpcProvider>) -> ProxyProvider<RpcClient> {
ProxyProvider::new_relay(rpc_provider)
}
1 change: 1 addition & 0 deletions crates/swapper/src/referrer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const DEFAULT_REFERRER: &str = "gemwallet";
114 changes: 114 additions & 0 deletions crates/swapper/src/relay/asset.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use std::sync::LazyLock;

use gem_solana::WSOL_TOKEN_ADDRESS;
use primitives::{
AssetId, Chain,
asset_constants::{USDC_ARB_ASSET_ID, USDC_HYPEREVM_ASSET_ID, USDT_ARB_ASSET_ID, USDT_HYPEREVM_ASSET_ID},
};

use super::chain::{BITCOIN_CURRENCY, RelayChain};
use crate::{SwapperChainAsset, SwapperError, asset::*};

pub const EVM_ZERO_ADDRESS: &str = "0x0000000000000000000000000000000000000000";

pub static SUPPORTED_CHAINS: LazyLock<Vec<SwapperChainAsset>> = LazyLock::new(|| {
vec![
SwapperChainAsset::Assets(Chain::Bitcoin, vec![AssetId::from_chain(Chain::Bitcoin)]),
SwapperChainAsset::Assets(
Chain::Ethereum,
vec![
AssetId::from_chain(Chain::Ethereum),
AssetId::from_token(Chain::Ethereum, ETHEREUM_USDC_TOKEN_ID),
AssetId::from_token(Chain::Ethereum, ETHEREUM_USDT_TOKEN_ID),
],
),
SwapperChainAsset::Assets(
Chain::Solana,
vec![
AssetId::from_chain(Chain::Solana),
AssetId::from_token(Chain::Solana, SOLANA_USDC_TOKEN_ID),
AssetId::from_token(Chain::Solana, SOLANA_USDT_TOKEN_ID),
],
),
SwapperChainAsset::Assets(
Chain::SmartChain,
vec![
AssetId::from_chain(Chain::SmartChain),
AssetId::from_token(Chain::SmartChain, SMARTCHAIN_USDC_TOKEN_ID),
AssetId::from_token(Chain::SmartChain, SMARTCHAIN_USDT_TOKEN_ID),
],
),
SwapperChainAsset::Assets(Chain::Base, vec![AssetId::from_chain(Chain::Base), AssetId::from_token(Chain::Base, BASE_USDC_TOKEN_ID)]),
SwapperChainAsset::Assets(
Chain::Arbitrum,
vec![AssetId::from_chain(Chain::Arbitrum), USDC_ARB_ASSET_ID.into(), USDT_ARB_ASSET_ID.into()],
),
SwapperChainAsset::Assets(
Chain::Hyperliquid,
vec![AssetId::from_chain(Chain::Hyperliquid), USDC_HYPEREVM_ASSET_ID.into(), USDT_HYPEREVM_ASSET_ID.into()],
),
SwapperChainAsset::Assets(Chain::Berachain, vec![AssetId::from_chain(Chain::Berachain)]),
SwapperChainAsset::Assets(Chain::Manta, vec![AssetId::from_chain(Chain::Manta)]),
]
});

pub fn map_asset_to_relay_currency(asset_id: &AssetId, relay_chain: &RelayChain) -> Result<String, SwapperError> {
match relay_chain {
RelayChain::Bitcoin => Ok(BITCOIN_CURRENCY.to_string()),
RelayChain::Solana => {
if asset_id.is_native() {
Ok(WSOL_TOKEN_ADDRESS.to_string())
} else {
asset_id.token_id.clone().ok_or(SwapperError::NotSupportedAsset)
}
}
_ if relay_chain.is_evm() => {
if asset_id.is_native() {
Ok(EVM_ZERO_ADDRESS.to_string())
} else {
asset_id.token_id.clone().ok_or(SwapperError::NotSupportedAsset)
}
}
_ => Err(SwapperError::NotSupportedChain),
}
}

#[cfg(test)]
mod tests {
use super::*;
use primitives::Chain;

#[test]
fn test_evm_native_asset() {
let asset_id = AssetId::from_chain(Chain::Ethereum);
let relay_chain = RelayChain::Ethereum;
let result = map_asset_to_relay_currency(&asset_id, &relay_chain).unwrap();
assert_eq!(result, EVM_ZERO_ADDRESS);
}

#[test]
fn test_evm_token_asset() {
let token_address = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
let asset_id = AssetId::from_token(Chain::Ethereum, token_address);
let relay_chain = RelayChain::Ethereum;
let result = map_asset_to_relay_currency(&asset_id, &relay_chain).unwrap();
assert_eq!(result, token_address);
}

#[test]
fn test_solana_native_asset() {
let asset_id = AssetId::from_chain(Chain::Solana);
let relay_chain = RelayChain::Solana;
let result = map_asset_to_relay_currency(&asset_id, &relay_chain).unwrap();
assert_eq!(result, WSOL_TOKEN_ADDRESS);
}

#[test]
fn test_solana_token_asset() {
let mint_address = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
let asset_id = AssetId::from_token(Chain::Solana, mint_address);
let relay_chain = RelayChain::Solana;
let result = map_asset_to_relay_currency(&asset_id, &relay_chain).unwrap();
assert_eq!(result, mint_address);
}
}
Loading
Loading