Skip to content
Merged
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: 1 addition & 1 deletion apps/api/src/auth/guard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use rocket::outcome::Outcome::{Error, Success};
use rocket::{Data, Request, State};
use serde::de::DeserializeOwned;
use std::sync::Arc;
use storage::models::DeviceRow;
use storage::Database;
use storage::models::DeviceRow;

fn error_outcome<'r, T>(req: &'r Request<'_>, status: Status, message: &str) -> Outcome<'r, T, String> {
req.local_cache(|| ErrorContext(message.to_string()));
Expand Down
5 changes: 4 additions & 1 deletion apps/api/src/referral/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ impl RewardsClient {
Ok(rewards)
}

#[allow(dead_code)]
pub fn change_username(&mut self, address: &str, new_username: &str) -> Result<Rewards, Box<dyn std::error::Error + Send + Sync>> {
Ok(self.database.client()?.rewards().change_username(address, new_username)?)
}
Expand Down Expand Up @@ -108,7 +109,9 @@ impl RewardsClient {
let daily_limit = self.database.client()?.config().get_config_i64(ConfigKey::ReferralPerIpDaily)?;
let weekly_limit = self.database.client()?.config().get_config_i64(ConfigKey::ReferralPerIpWeekly)?;
let global_daily_limit = self.database.client()?.config().get_config_i64(ConfigKey::ReferralUseDailyLimit)?;
self.ip_security_client.check_rate_limits(ip_address, daily_limit, weekly_limit, global_daily_limit).await?;
self.ip_security_client
.check_rate_limits(ip_address, daily_limit, weekly_limit, global_daily_limit)
.await?;

Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/referral/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub async fn create_referral(request: Authenticated<ReferralCode>, client: &Stat
Ok(client.lock().await.create_referral(&request.auth.address, &request.data.code).await?.into())
}

#[allow(dead_code)]
#[post("/rewards/referrals/update", format = "json", data = "<request>")]
pub async fn update_referral(request: Authenticated<ReferralCode>, client: &State<Mutex<RewardsClient>>) -> Result<ApiResponse<Rewards>, ApiError> {
Ok(client.lock().await.change_username(&request.auth.address, &request.data.code)?.into())
Expand Down
9 changes: 9 additions & 0 deletions crates/gem_evm/src/across/deployment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ impl AcrossDeployment {
chain_id,
spoke_pool: "0x09aea4b2242abC8bb4BB78D537A67a245A7bEC64",
}),
Chain::Monad => Some(Self {
chain_id,
spoke_pool: "0xd2ecb3afe598b746F8123CaE365a598DA831A449",
}),
Chain::SmartChain => Some(Self {
chain_id,
spoke_pool: "0x4e8E101924eDE233C13e2D8622DC8aED2872d505",
Expand All @@ -93,6 +97,8 @@ impl AcrossDeployment {
324 => "0x863859ef502F0Ee9676626ED5B418037252eFeb2".into(),
// SmartChain
56 => "0xAC537C12fE8f544D712d71ED4376a502EEa944d7".into(),
// Monad
143 => "0xeC41F75c686e376Ab2a4F18bde263ab5822c4511".into(),
// HyperEvm | Plasma
999 | 9745 => "0x5E7840E06fAcCb6d1c3b5F5E0d1d3d07F2829bba".into(),
_ => MULTICALL_HANDLER.into(),
Expand Down Expand Up @@ -122,6 +128,7 @@ impl AcrossDeployment {
(Chain::Blast, vec![WETH_BLAST_ASSET_ID.into()]),
(Chain::Ink, vec![WETH_INK_ASSET_ID.into(), USDT_INK_ASSET_ID.into()]),
(Chain::Unichain, vec![WETH_UNICHAIN_ASSET_ID.into(), USDC_UNICHAIN_ASSET_ID.into()]),
(Chain::Monad, vec![USDC_MONAD_ASSET_ID.into(), USDT_MONAD_ASSET_ID.into()]),
(Chain::SmartChain, vec![ETH_SMARTCHAIN_ASSET_ID.into()]),
(Chain::Plasma, vec![USDT_PLASMA_ASSET_ID.into()]),
])
Expand Down Expand Up @@ -166,6 +173,7 @@ impl AcrossDeployment {
USDC_POLYGON_ASSET_ID.into(),
USDC_UNICHAIN_ASSET_ID.into(),
USDC_HYPEREVM_ASSET_ID.into(),
USDC_MONAD_ASSET_ID.into(),
]),
},
// USDC on BSC decimals are 18
Expand Down Expand Up @@ -195,6 +203,7 @@ impl AcrossDeployment {
USDT_INK_ASSET_ID.into(),
USDT_HYPEREVM_ASSET_ID.into(),
USDT_PLASMA_ASSET_ID.into(),
USDT_MONAD_ASSET_ID.into(),
]),
},
// USDT on BSC decimals are 18
Expand Down
1 change: 1 addition & 0 deletions crates/gem_evm/src/chainlink/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ sol! {
}

pub const CHAINLINK_ETH_USD_FEED: &str = "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419";
pub const CHAINLINK_MON_USD_FEED: &str = "0xBcD78f76005B7515837af6b50c7C52BCf73822fb";
18 changes: 15 additions & 3 deletions crates/gem_evm/src/jsonrpc.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use alloy_primitives::hex;
use gem_jsonrpc::types::{JsonRpcRequest, JsonRpcRequestConvert};
use serde::{Deserialize, Serialize};
use serde_json::{Value, json};
Expand All @@ -24,7 +25,7 @@ impl TransactionObject {
gas: None,
gas_price: None,
value: None,
data: format!("0x{}", hex::encode(data)),
data: hex::encode_prefixed(data),
}
}

Expand All @@ -35,7 +36,7 @@ impl TransactionObject {
gas: None,
gas_price: None,
value: Some(value.to_string()),
data: format!("0x{}", hex::encode(data)),
data: hex::encode_prefixed(data),
}
}

Expand All @@ -46,7 +47,18 @@ impl TransactionObject {
gas: None,
gas_price: None,
value: None,
data: format!("0x{}", hex::encode(data)),
data: hex::encode_prefixed(data),
}
}

pub fn new_call_with_from_value(from: &str, to: &str, value: &str, data: Vec<u8>) -> Self {
Self {
from: Some(from.to_string()),
to: to.to_string(),
gas: None,
gas_price: None,
value: Some(value.to_string()),
data: hex::encode_prefixed(data),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/gem_rewards/src/transfer_provider/evm/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl EvmTransferProvider {

let load_data = client.get_transaction_load(load_input).await?;

let nonce = metadata.get_sequence()? as u64;
let nonce = metadata.get_sequence()?;
let chain_id: u64 = metadata.get_chain_id()?.parse()?;
let gas_limit = load_data.fee.gas_limit.to_u64().ok_or("Gas limit overflow")?;
let base_fee = fee_rate.gas_price_type.gas_price().to_u128().ok_or("Base fee overflow")?;
Expand Down
14 changes: 1 addition & 13 deletions crates/primitives/src/rewards.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ impl RewardEventType {
#[derive(Clone, Debug, Serialize, Deserialize)]
#[typeshare(swift = "Equatable, Hashable, Sendable")]
#[serde(rename_all = "camelCase")]
#[derive(Default)]
pub struct Rewards {
pub code: Option<String>,
pub referral_count: i32,
Expand All @@ -70,19 +71,6 @@ pub struct Rewards {
//pub level: Option<RewardLevel>,
}

impl Default for Rewards {
fn default() -> Self {
Self {
code: None,
referral_count: 0,
points: 0,
used_referral_code: None,
is_enabled: false,
redemption_options: vec![],
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[typeshare(swift = "Sendable")]
#[serde(rename_all = "camelCase")]
Expand Down
77 changes: 72 additions & 5 deletions crates/swapper/src/across/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,10 +240,33 @@ impl Across {
}
.abi_encode();
let tx = TransactionObject::new_call_to_value(deployment.spoke_pool, &value, data);
let gas_limit = self.estimate_gas_transaction(chain, tx).await.unwrap_or(U256::from(DEFAULT_FILL_GAS_LIMIT));
let gas_limit = self
.estimate_gas_transaction(chain, tx)
.await
.unwrap_or(U256::from(Self::get_default_fill_limit(chain)));
Ok((gas_limit, v3_relay_data))
}

fn get_default_fill_limit(chain: Chain) -> u64 {
match chain {
Chain::Monad => DEFAULT_FILL_GAS_LIMIT * 3,
_ => DEFAULT_FILL_GAS_LIMIT,
}
}

async fn usd_price_for_chain(&self, chain: Chain, existing_results: &[IMulticall3::Result]) -> Result<BigInt, SwapperError> {
let feed = ChainlinkPriceFeed::new_usd_feed_for_chain(chain).ok_or(SwapperError::NotSupportedChain)?;
if chain == Chain::Monad {
let results = create_eth_client(self.rpc_provider.clone(), Chain::Monad)?
.multicall3(vec![feed.latest_round_call3()])
.await
.map_err(|e| SwapperError::NetworkError(e.to_string()))?;
ChainlinkPriceFeed::decoded_answer(&results[0])
} else {
ChainlinkPriceFeed::decoded_answer(&existing_results[3])
}
}

pub fn update_v3_relay_data(
&self,
v3_relay_data: &mut V3RelayData,
Expand Down Expand Up @@ -310,6 +333,7 @@ impl Swapper for Across {
SwapperChainAsset::Assets(Chain::World, vec![WORLD_WETH.id.clone()]),
SwapperChainAsset::Assets(Chain::Ink, vec![INK_WETH.id.clone(), INK_USDT.id.clone()]),
SwapperChainAsset::Assets(Chain::Unichain, vec![UNICHAIN_WETH.id.clone(), UNICHAIN_USDC.id.clone()]),
SwapperChainAsset::Assets(Chain::Monad, vec![MONAD_USDC.id.clone(), MONAD_USDT.id.clone()]),
SwapperChainAsset::Assets(Chain::SmartChain, vec![SMARTCHAIN_ETH.id.clone()]),
SwapperChainAsset::Assets(Chain::Hyperliquid, vec![HYPEREVM_USDC.id.clone(), HYPEREVM_USDT.id.clone()]),
SwapperChainAsset::Assets(Chain::Plasma, vec![PLASMA_USDT.id.clone()]),
Expand Down Expand Up @@ -373,9 +397,9 @@ impl Swapper for Across {
hubpool_client.get_current_time(),
];

let eth_price_feed = ChainlinkPriceFeed::new_eth_usd_feed();
let gas_price_feed = ChainlinkPriceFeed::new_usd_feed_for_chain(request.to_asset.chain()).unwrap_or_else(ChainlinkPriceFeed::new_eth_usd_feed);
if !input_is_native {
calls.push(eth_price_feed.latest_round_call3());
calls.push(gas_price_feed.latest_round_call3());
}

let multicall_results = self.multicall3(hubpool_client.chain, calls).await?;
Expand Down Expand Up @@ -420,8 +444,8 @@ impl Swapper for Across {
.await?;
let mut gas_fee = gas_limit * gas_price;
if !input_is_native {
let eth_price = ChainlinkPriceFeed::decoded_answer(&multicall_results[3])?;
gas_fee = Self::calculate_fee_in_token(&gas_fee, &eth_price, 6);
let price = self.usd_price_for_chain(request.to_asset.chain(), &multicall_results).await?;
gas_fee = Self::calculate_fee_in_token(&gas_fee, &price, 6);
}

// Check if bridge amount is too small
Expand Down Expand Up @@ -563,10 +587,15 @@ mod tests {

let usdc_eth: AssetId = USDC_ETH_ASSET_ID.into();
let usdc_arb: AssetId = USDC_ARB_ASSET_ID.into();
let usdc_monad: AssetId = USDC_MONAD_ASSET_ID.into();
let usdt_eth: AssetId = USDT_ETH_ASSET_ID.into();
let usdt_monad: AssetId = USDT_MONAD_ASSET_ID.into();

assert!(Across::is_supported_pair(&weth_eth, &weth_op));
assert!(Across::is_supported_pair(&weth_op, &weth_arb));
assert!(Across::is_supported_pair(&usdc_eth, &usdc_arb));
assert!(Across::is_supported_pair(&usdc_monad, &usdc_eth));
assert!(Across::is_supported_pair(&usdt_monad, &usdt_eth));
assert!(Across::is_supported_pair(&weth_eth, &weth_bsc));

assert!(!Across::is_supported_pair(&weth_eth, &usdc_eth));
Expand Down Expand Up @@ -652,6 +681,44 @@ mod tests {
Ok(())
}

#[tokio::test]
async fn test_across_quote_eth_usdc_to_monad_usdc() -> Result<(), SwapperError> {
let network_provider = Arc::new(NativeProvider::default());
let swap_provider = Across::boxed(network_provider.clone());
let options = Options {
slippage: 100.into(),
fee: None,
preferred_providers: vec![],
use_max_amount: false,
};

let wallet = "0x9b1fe00135e0ff09389bfaeff0c8f299ec818d4a";
let from_asset: AssetId = USDC_ETH_ASSET_ID.into();
let to_asset: AssetId = USDC_MONAD_ASSET_ID.into();
let request = QuoteRequest {
from_asset: from_asset.into(),
to_asset: to_asset.into(),
wallet_address: wallet.into(),
destination_address: wallet.into(),
value: "50000000".into(), // 50 USDC
mode: SwapperMode::ExactIn,
options,
};

let now = SystemTime::now();
let quote = swap_provider.fetch_quote(&request).await?;
let elapsed = SystemTime::now().duration_since(now).unwrap();

println!("<== elapsed: {:?}", elapsed);
println!("<== quote: {:?}", quote);
assert!(quote.to_value.parse::<u64>().unwrap() > 0);

let quote_data = swap_provider.fetch_quote_data(&quote, FetchQuoteData::EstimateGas).await?;
println!("<== quote_data: {:?}", quote_data);

Ok(())
}

#[tokio::test]
async fn test_get_swap_result() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let network_provider = Arc::new(NativeProvider::default());
Expand Down
15 changes: 14 additions & 1 deletion crates/swapper/src/chainlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use num_traits::FromBytes;

use crate::SwapperError;
use gem_evm::{
chainlink::contract::{AggregatorInterface, CHAINLINK_ETH_USD_FEED},
chainlink::contract::{AggregatorInterface, CHAINLINK_ETH_USD_FEED, CHAINLINK_MON_USD_FEED},
multicall3::{IMulticall3, create_call3, decode_call3_return},
};

Expand All @@ -18,6 +18,19 @@ impl ChainlinkPriceFeed {
}
}

pub fn new_usd_feed_for_chain(chain: primitives::Chain) -> Option<ChainlinkPriceFeed> {
match chain {
primitives::Chain::Monad => Some(Self::new_mon_usd_feed()),
_ => Some(Self::new_eth_usd_feed()),
}
}

pub fn new_mon_usd_feed() -> ChainlinkPriceFeed {
ChainlinkPriceFeed {
contract: CHAINLINK_MON_USD_FEED.into(),
}
}

pub fn latest_round_call3(&self) -> IMulticall3::Call3 {
create_call3(&self.contract, AggregatorInterface::latestRoundDataCall {})
}
Expand Down
Loading