diff --git a/.env.production.example b/.env.production.example index 890fc854..4f1989bf 100644 --- a/.env.production.example +++ b/.env.production.example @@ -20,6 +20,6 @@ NEXT_PUBLIC_DIRECT_PATH_ENABLED=false # SOROSWAP API SOROSWAP_API_URL= # URL of the Soroswap API -# For development use http://localhost:4000/ +# For development use http://soroswap-api:4000 SOROSWAP_API_EMAIL= # Email for Soroswap API SOROSWAP_API_PASSWORD= # Password for Soroswap API \ No newline at end of file diff --git a/README.md b/README.md index 5e75df83..26dd63da 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 🌟 Soroswap Frontend @ Soroban Preview 10🌟 +# 🌟 Soroswap Frontend🌟 Welcome to Soroswap, a decentralized exchange (DEX) that draws inspiration from the Uniswap V2 protocol and is specifically tailored for the Soroban network. diff --git a/src/configs/protocols.config.json b/src/configs/protocols.config.json index c133920d..79f9dcda 100644 --- a/src/configs/protocols.config.json +++ b/src/configs/protocols.config.json @@ -14,6 +14,11 @@ "key": "phoenix", "value": true, "url": "https://docs.soroswap.finance/soroswap-aggregator/supported-amms" + }, + { + "key": "aqua", + "value": true, + "url": "https://docs.soroswap.finance/soroswap-aggregator/supported-amms" } ], "testnet": [ diff --git a/src/helpers/aggregator/index.ts b/src/helpers/aggregator/index.ts index 3aa1979c..dccd3785 100644 --- a/src/helpers/aggregator/index.ts +++ b/src/helpers/aggregator/index.ts @@ -5,11 +5,71 @@ export interface DexDistribution { path: string[]; parts: number; is_exact_in: boolean; + poolHashes: string[] | undefined; +} + +// #[contracttype] +// #[derive(Clone, Debug, Eq, PartialEq)] +// pub struct DexDistribution { +// pub protocol_id: String, +// pub path: Vec
, +// pub parts: u32, +// pub bytes: Option>> + +// } + +/** + * Converts an optional array of Base64-encoded strings to ScVal: Option>> + * + * @param poolHashes Optional array of Base64-encoded strings representing 32-byte hashes + * @returns ScVal representing Option>> + * @throws Error if any Base64 string is invalid or does not decode to 32 bytes + */ +export function poolHashesToScVal(poolHashes?: string[]): xdr.ScVal { + // Handle undefined or empty array: return Option::None + if (!poolHashes || poolHashes.length === 0) { + console.log("🚀 ~ poolHashesToScVal ~ poolHashes:", poolHashes) + return nativeToScVal(null); + + } + + // Convert each Base64 string to ScVal (BytesN<32>) + const scVec: xdr.ScVal[] = poolHashes.map((base64Str) => { + // Basic validation for non-empty string + if (!base64Str || typeof base64Str !== 'string') { + throw new Error(`Invalid Base64 string: ${base64Str}`); + } + + try { + // Decode Base64 string to Buffer + const buf = Buffer.from(base64Str, 'base64'); + + // Validate buffer length (must be 32 bytes for BytesN<32>) + if (buf.length !== 32) { + throw new Error( + `Expected 32 bytes, got ${buf.length} bytes for Base64 string: ${base64Str}` + ); + } + + // Create ScVal for BytesN<32> + return xdr.ScVal.scvBytes(buf); + } catch (e) { + throw new Error(`Invalid Base64 string: ${base64Str}`); + } + }); + + // Wrap the vector in an Option::Some + return xdr.ScVal.scvVec(scVec); } export const dexDistributionParser = (dexDistributionRaw: DexDistribution[]): xdr.ScVal => { + console.log("🚀 ~ dexDistributionRaw:", dexDistributionRaw) const dexDistributionScVal = dexDistributionRaw.map((distribution) => { return xdr.ScVal.scvMap([ + new xdr.ScMapEntry({ + key: xdr.ScVal.scvSymbol('bytes'), + val: poolHashesToScVal(distribution.poolHashes), + }), new xdr.ScMapEntry({ key: xdr.ScVal.scvSymbol('parts'), val: nativeToScVal(distribution.parts, { type: 'u32' }), @@ -24,6 +84,7 @@ export const dexDistributionParser = (dexDistributionRaw: DexDistribution[]): xd }), ]); }); + console.log("🚀 ~ dexDistributionScVal ~ dexDistributionScVal:", dexDistributionScVal) return xdr.ScVal.scvVec(dexDistributionScVal); }; diff --git a/src/hooks/useAggregator.tsx b/src/hooks/useAggregator.tsx index 696a2bda..e7a902a7 100644 --- a/src/hooks/useAggregator.tsx +++ b/src/hooks/useAggregator.tsx @@ -8,7 +8,7 @@ const aggregatorTestnet = process.env.NEXT_PUBLIC_AGGREGATOR_ENABLED_TESTNET === export const setAggregatorData = async (activeChainId: string) => { const response = await axios.get( - `https://raw.githubusercontent.com/soroswap/aggregator/refs/heads/main/public/${activeChainId}.contracts.json` + `https://raw.githubusercontent.com/soroswap/aggregator/refs/heads/aqua-adapter/public/${activeChainId}.contracts.json` ).catch((error) => { console.error('Error fetching aggregator data', error); console.warn('No address found Aggregator is disabled'); diff --git a/src/hooks/useSwapCallback.tsx b/src/hooks/useSwapCallback.tsx index e24ca9f5..eac194c4 100644 --- a/src/hooks/useSwapCallback.tsx +++ b/src/hooks/useSwapCallback.tsx @@ -9,7 +9,7 @@ import { dexDistributionParser, hasDistribution } from 'helpers/aggregator'; import { scValToJs } from 'helpers/convert'; import { formatTokenAmount } from 'helpers/format'; import { bigNumberToI128, bigNumberToU64 } from 'helpers/utils'; -import { useContext } from 'react'; +import { useContext, useEffect } from 'react'; import { InterfaceTrade, PlatformType, TradeType } from 'state/routing/types'; import { useUserSlippageToleranceWithDefault } from 'state/user/hooks'; import { useSWRConfig } from 'swr'; @@ -114,6 +114,32 @@ export function useSwapCallback( const { mutate } = useSWRConfig(); + // Logs de depuración para el proceso de swap + useEffect(() => { + if (trade) { + console.group('%c[Soroswap Debug] SwapCallback', 'color: #00aced; font-weight: bold'); + console.log('🚀 Tipo de operación:', trade.tradeType); + console.log('🔄 Plataforma seleccionada:', trade.platform); + console.log('💰 Moneda de entrada:', trade.inputAmount?.currency.code, 'cantidad:', trade.inputAmount?.value); + console.log('💰 Moneda de salida:', trade.outputAmount?.currency.code, 'cantidad:', trade.outputAmount?.value); + console.log('⚙️ Configuración de slippage:', allowedSlippage); + + if (trade.path) { + console.log('🛣️ Ruta de swap:', trade.path); + } + + if (trade.distribution) { + console.log('📊 Distribución:', trade.distribution); + } + + if (isUsingAggregator) { + console.log('🔀 Usando agregador para optimizar la ruta'); + } + + console.groupEnd(); + } + }, [trade, allowedSlippage, isUsingAggregator]); + const doSwap = async ( simulation?: boolean, ): Promise<