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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/swapper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ serde_serializers = { path = "../serde_serializers" }
number_formatter = { path = "../number_formatter" }

reqwest = { workspace = true, optional = true }
typeshare = { version = "1.0.4" }

bcs.workspace = true
sui-types = { workspace = true }
Expand Down
6 changes: 3 additions & 3 deletions crates/swapper/src/across/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,18 +61,18 @@ impl AcrossApi {
.map(|topic| topic.eq_ignore_ascii_case(FUNDS_DEPOSITED_TOPIC))
.unwrap_or(false)
})
.ok_or_else(|| SwapperError::NetworkError("FundsDeposited event not found".into()))?;
.ok_or_else(|| SwapperError::TransactionError("FundsDeposited event not found".into()))?;

if deposit_log.topics.len() < 3 {
return Err(SwapperError::NetworkError("invalid FundsDeposited topics".into()));
return Err(SwapperError::TransactionError("invalid FundsDeposited topics".into()));
}
// The deposit ID is in topics[2] (topics[0] is event signature, topics[1] is destination chain ID)
let deposit_id_hex = deposit_log.topics[2].clone();

// Convert hex deposit ID to decimal string
let deposit_id = if let Some(stripped) = deposit_id_hex.strip_prefix("0x") {
u64::from_str_radix(stripped, 16)
.map_err(|e| SwapperError::NetworkError(format!("Failed to parse deposit ID: {}", e)))?
.map_err(|e| SwapperError::TransactionError(format!("Failed to parse deposit ID: {}", e)))?
.to_string()
} else {
deposit_id_hex
Expand Down
4 changes: 2 additions & 2 deletions crates/swapper/src/across/config_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl ConfigStoreClient {
let result: TokenConfig = serde_json::from_str(&decoded).map_err(SwapperError::from)?;
Ok(result)
} else {
Err(SwapperError::ABIError("config call failed".into()))
Err(SwapperError::ComputeQuoteError("config call failed".into()))
}
}

Expand All @@ -83,7 +83,7 @@ impl ConfigStoreClient {
let call = EthereumRpc::Call(TransactionObject::new_call(&self.contract, data), BlockParameter::Latest);
let response: JsonRpcResult<String> = self.client.call_with_cache(&call, Some(CONFIG_CACHE_TTL)).await?;
let result = response.take()?;
let hex_data = HexDecode(result).map_err(|e| SwapperError::NetworkError(e.to_string()))?;
let hex_data = HexDecode(result).map_err(|e| SwapperError::ComputeQuoteError(e.to_string()))?;
let decoded = AcrossConfigStore::l1TokenConfigCall::abi_decode_returns(&hex_data).map_err(SwapperError::from)?;

let result: TokenConfig = serde_json::from_str(&decoded).map_err(SwapperError::from)?;
Expand Down
17 changes: 10 additions & 7 deletions crates/swapper/src/across/hubpool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl HubPoolClient {
}

pub fn decoded_paused_call3(&self, result: &IMulticall3::Result) -> Result<bool, SwapperError> {
let value = decode_call3_return::<HubPoolInterface::pausedCall>(result).map_err(|e| SwapperError::ABIError(e.to_string()))?;
let value = decode_call3_return::<HubPoolInterface::pausedCall>(result).map_err(|e| SwapperError::ComputeQuoteError(e.to_string()))?;
Ok(value)
}

Expand All @@ -59,10 +59,11 @@ impl HubPoolClient {

pub fn decoded_pooled_token_call3(&self, result: &IMulticall3::Result) -> Result<HubPoolInterface::PooledToken, SwapperError> {
if result.success {
let decoded = HubPoolInterface::pooledTokensCall::abi_decode_returns(&result.returnData).map_err(|e| SwapperError::ABIError(e.to_string()))?;
let decoded =
HubPoolInterface::pooledTokensCall::abi_decode_returns(&result.returnData).map_err(|e| SwapperError::ComputeQuoteError(e.to_string()))?;
Ok(decoded)
} else {
Err(SwapperError::ABIError("pooled token call failed".into()))
Err(SwapperError::ComputeQuoteError("pooled token call failed".into()))
}
}

Expand All @@ -89,7 +90,7 @@ impl HubPoolClient {

Ok(BigInt::from_bytes_le(Sign::Plus, &value.to_le_bytes::<32>()))
} else {
Err(SwapperError::ABIError("utilization call failed".into()))
Err(SwapperError::ComputeQuoteError("utilization call failed".into()))
}
}

Expand All @@ -98,15 +99,17 @@ impl HubPoolClient {
}

pub fn decoded_current_time(&self, result: &IMulticall3::Result) -> Result<u32, SwapperError> {
let value = decode_call3_return::<HubPoolInterface::getCurrentTimeCall>(result).map_err(|e| SwapperError::ABIError(e.to_string()))?;
value.try_into().map_err(|_| SwapperError::ABIError("decode current time failed".into()))
let value = decode_call3_return::<HubPoolInterface::getCurrentTimeCall>(result).map_err(|e| SwapperError::ComputeQuoteError(e.to_string()))?;
value
.try_into()
.map_err(|_| SwapperError::ComputeQuoteError("decode current time failed".into()))
}

pub async fn fetch_utilization(&self, pool_token: &Address, amount: U256) -> Result<BigInt, SwapperError> {
let call3 = self.utilization_call3(pool_token, amount);
let call = EthereumRpc::Call(TransactionObject::new_call(&self.contract, call3.callData.to_vec()), BlockParameter::Latest);
let result: String = self.client.request(call).await?;
let hex_data = HexDecode(result).map_err(|e| SwapperError::NetworkError(e.to_string()))?;
let hex_data = HexDecode(result).map_err(|e| SwapperError::ComputeQuoteError(e.to_string()))?;
let value = HubPoolInterface::liquidityUtilizationCurrentCall::abi_decode_returns(&hex_data).map_err(SwapperError::from)?;
let result = BigInt::from_bytes_le(num_bigint::Sign::Plus, &value.to_le_bytes::<32>());
Ok(result)
Expand Down
18 changes: 9 additions & 9 deletions crates/swapper/src/across/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,17 @@ impl Across {
create_eth_client(self.rpc_provider.clone(), chain)?
.multicall3(calls)
.await
.map_err(|e| SwapperError::NetworkError(e.to_string()))
.map_err(|e| SwapperError::ComputeQuoteError(e.to_string()))
}

async fn estimate_gas_transaction(&self, chain: Chain, tx: TransactionObject) -> Result<U256, SwapperError> {
let client = create_eth_client(self.rpc_provider.clone(), chain)?;
let client = create_eth_client(self.rpc_provider.clone(), chain).map_err(SwapperError::into_transaction_error)?;
let gas_hex = client
.estimate_gas(tx.from.as_deref(), &tx.to, tx.value.as_deref(), Some(tx.data.as_str()))
.await
.map_err(SwapperError::from)?;
.map_err(|err| SwapperError::TransactionError(err.to_string()))?;

let gas_biguint = biguint_from_hex_str(&gas_hex).map_err(|e| SwapperError::NetworkError(format!("Failed to parse gas estimate: {e}")))?;
let gas_biguint = biguint_from_hex_str(&gas_hex).map_err(|e| SwapperError::TransactionError(format!("Failed to parse gas estimate: {e}")))?;
let gas_bigint = BigInt::from_biguint(Sign::Plus, gas_biguint);
Self::bigint_to_u256(&gas_bigint)
}
Expand Down Expand Up @@ -319,7 +319,7 @@ impl Swapper for Across {
async fn fetch_quote(&self, request: &QuoteRequest) -> Result<Quote, SwapperError> {
// does not support same chain swap
if request.from_asset.chain() == request.to_asset.chain() {
return Err(SwapperError::NotSupportedPair);
return Err(SwapperError::NotSupportedAsset);
}

let input_is_native = request.from_asset.is_native();
Expand All @@ -330,11 +330,11 @@ impl Swapper for Across {
let _ = AcrossDeployment::deployment_by_chain(&request.from_asset.chain()).ok_or(SwapperError::NotSupportedChain)?;
let destination_deployment = AcrossDeployment::deployment_by_chain(&request.to_asset.chain()).ok_or(SwapperError::NotSupportedChain)?;
if !Self::is_supported_pair(&request.from_asset.asset_id(), &request.to_asset.asset_id()) {
return Err(SwapperError::NotSupportedPair);
return Err(SwapperError::NotSupportedAsset);
}

let input_asset = eth_address::convert_native_to_weth(&request.from_asset.asset_id()).ok_or(SwapperError::NotSupportedPair)?;
let output_asset = eth_address::convert_native_to_weth(&request.to_asset.asset_id()).ok_or(SwapperError::NotSupportedPair)?;
let input_asset = eth_address::convert_native_to_weth(&request.from_asset.asset_id()).ok_or(SwapperError::NotSupportedAsset)?;
let output_asset = eth_address::convert_native_to_weth(&request.to_asset.asset_id()).ok_or(SwapperError::NotSupportedAsset)?;
let original_output_asset = request.to_asset.asset_id();
let output_token = eth_address::parse_asset_id(&output_asset)?;

Expand Down Expand Up @@ -426,7 +426,7 @@ impl Swapper for Across {

// Check if bridge amount is too small
if remain_amount < gas_fee {
return Err(SwapperError::InputAmountTooSmall);
return Err(SwapperError::InputAmountError { min_amount: None });
}

let output_amount = remain_amount - gas_fee;
Expand Down
38 changes: 28 additions & 10 deletions crates/swapper/src/approval/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,27 @@ pub async fn check_approval_erc20_with_client<C>(
where
C: Client + Clone + std::fmt::Debug + Send + Sync + 'static,
{
let owner: Address = owner.as_str().parse().map_err(|_| SwapperError::InvalidAddress(owner))?;
let spender: Address = spender.as_str().parse().map_err(|_| SwapperError::InvalidAddress(spender))?;
let owner: Address = owner
.as_str()
.parse()
.map_err(|_| SwapperError::TransactionError(format!("Invalid address {owner}")))?;
let spender: Address = spender
.as_str()
.parse()
.map_err(|_| SwapperError::TransactionError(format!("Invalid address {spender}")))?;
let allowance_data = IERC20::allowanceCall { owner, spender }.abi_encode();
let allowance_call = EthereumRpc::Call(TransactionObject::new_call(&token, allowance_data), BlockParameter::Latest);

let result: String = client.request(allowance_call).await.map_err(SwapperError::from)?;
let decoded = HexDecode(result).map_err(|_| SwapperError::ABIError("failed to decode allowance_call result".into()))?;
let result: String = client
.request(allowance_call)
.await
.map_err(SwapperError::from)
.map_err(SwapperError::into_transaction_error)?;
let decoded = HexDecode(result).map_err(|_| SwapperError::TransactionError("failed to decode allowance_call result".into()))?;

let allowance = IERC20::allowanceCall::abi_decode_returns(&decoded).map_err(SwapperError::from)?;
let allowance = IERC20::allowanceCall::abi_decode_returns(&decoded)
.map_err(SwapperError::from)
.map_err(SwapperError::into_transaction_error)?;
if allowance < amount {
return Ok(ApprovalType::Approve(ApprovalData {
token: token.to_string(),
Expand Down Expand Up @@ -101,15 +113,21 @@ where
.abi_encode();
let permit2_call = EthereumRpc::Call(TransactionObject::new_call(permit2_contract, permit2_data), BlockParameter::Latest);

let result: String = client.request(permit2_call).await.map_err(SwapperError::from)?;
let decoded = HexDecode(result).map_err(|_| SwapperError::ABIError("failed to decode permit2 allowance result".into()))?;
let allowance_return = IAllowanceTransfer::allowanceCall::abi_decode_returns(&decoded).map_err(SwapperError::from)?;
let result: String = client
.request(permit2_call)
.await
.map_err(SwapperError::from)
.map_err(SwapperError::into_transaction_error)?;
let decoded = HexDecode(result).map_err(|_| SwapperError::TransactionError("failed to decode permit2 allowance result".into()))?;
let allowance_return = IAllowanceTransfer::allowanceCall::abi_decode_returns(&decoded)
.map_err(SwapperError::from)
.map_err(SwapperError::into_transaction_error)?;

let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs();
let expiration: u64 = allowance_return
._1
.try_into()
.map_err(|_| SwapperError::ABIError("failed to convert expiration to u64".into()))?;
.map_err(|_| SwapperError::TransactionError("failed to convert expiration to u64".into()))?;

if U256::from(allowance_return._0) < amount || expiration < timestamp {
return Ok(ApprovalType::Permit2(Permit2ApprovalData {
Expand All @@ -120,7 +138,7 @@ where
permit2_nonce: allowance_return
._2
.try_into()
.map_err(|_| SwapperError::ABIError("failed to convert nonce to u64".into()))?,
.map_err(|_| SwapperError::TransactionError("failed to convert nonce to u64".into()))?,
}));
}

Expand Down
4 changes: 2 additions & 2 deletions crates/swapper/src/approval/tron.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ pub async fn check_approval_tron(
amount: U256,
provider: Arc<dyn RpcProvider>,
) -> Result<ApprovalType, SwapperError> {
let client = create_tron_client(provider.clone()).map_err(|e| SwapperError::NetworkError(e.to_string()))?;
let client = create_tron_client(provider.clone()).map_err(|e| SwapperError::TransactionError(e.to_string()))?;
let allowance = client
.get_token_allowance(owner_address, token_address, spender_address)
.await
.map_err(|e| SwapperError::NetworkError(e.to_string()))?;
.map_err(|e| SwapperError::TransactionError(e.to_string()))?;
let amount_big = BigUint::from_bytes_be(&amount.to_be_bytes::<32>());
if allowance < amount_big {
return Ok(ApprovalType::Approve(ApprovalData {
Expand Down
13 changes: 8 additions & 5 deletions crates/swapper/src/cetus/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,10 @@ where
Some(ObjectDataOptions::default()),
);

let pool_datas: Vec<CetusPoolType> = sui_client.rpc_call(rpc_call).await.map_err(|e| SwapperError::NetworkError(e.to_string()))?;
let pool_datas: Vec<CetusPoolType> = sui_client
.rpc_call(rpc_call)
.await
.map_err(|e| SwapperError::ComputeQuoteError(e.to_string()))?;

let pool_quotes = top_pools
.into_iter()
Expand Down Expand Up @@ -287,11 +290,11 @@ where
// Execute gas_price and coin_assets fetching in parallel
let (gas_price_result, all_coin_assets_result) = join!(sui_client.get_gas_price(), sui_client.get_coin_assets(sender_address));

let gas_price_bigint = gas_price_result.map_err(|e| SwapperError::NetworkError(e.to_string()))?;
let gas_price_bigint = gas_price_result.map_err(|e| SwapperError::TransactionError(e.to_string()))?;
let gas_price = gas_price_bigint
.to_u64()
.ok_or_else(|| SwapperError::NetworkError("Failed to convert gas price to u64".into()))?;
let all_coin_assets = all_coin_assets_result.map_err(|e| SwapperError::NetworkError(e.to_string()))?;
.ok_or_else(|| SwapperError::TransactionError("Failed to convert gas price to u64".into()))?;
let all_coin_assets = all_coin_assets_result.map_err(|e| SwapperError::TransactionError(e.to_string()))?;

// Prepare swap params for tx building
let a2b = from_coin == route_data.coin_a;
Expand Down Expand Up @@ -323,7 +326,7 @@ where
let inspect_result = sui_client
.inspect_transaction_block(&quote.request.wallet_address, &tx_bytes)
.await
.map_err(|e| SwapperError::NetworkError(e.to_string()))?;
.map_err(|e| SwapperError::TransactionError(e.to_string()))?;
let gas_budget = GasBudgetCalculator::gas_budget(&inspect_result.effects.gas_used);

let coin_refs = all_coin_assets
Expand Down
4 changes: 2 additions & 2 deletions crates/swapper/src/chainlink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ impl ChainlinkPriceFeed {

// Price is in 8 decimals
pub fn decoded_answer(result: &IMulticall3::Result) -> Result<BigInt, SwapperError> {
let decoded =
decode_call3_return::<AggregatorInterface::latestRoundDataCall>(result).map_err(|_| SwapperError::ABIError("failed to decode answer".into()))?;
let decoded = decode_call3_return::<AggregatorInterface::latestRoundDataCall>(result)
.map_err(|_| SwapperError::ComputeQuoteError("failed to decode answer".into()))?;
let price = BigInt::from_le_bytes(&decoded.answer.to_le_bytes::<32>());
Ok(price)
}
Expand Down
Loading
Loading