> For the complete documentation index, see [llms.txt](https://docs.ston.fi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.ston.fi/es/seccion-para-desarrolladores/quickstart/omniston.md).

# Guía de Omniston (React)

Esta guía te mostrará cómo crear una aplicación básica de intercambio de tokens usando el **Omniston** protocolo para intercambiar activos entre distintos DEX (STON.fi V1, STON.fi V2, DeDust, etc.). Integraremos la conectividad de la cartera con **TonConnect** (a través de `@tonconnect/ui-react`) para permitir que los usuarios conecten su cartera TON y realicen un intercambio. La guía está pensada para principiantes y asume una experiencia mínima con React.

> **Nota**: En esta demostración, aprovecharemos **Tailwind CSS** para el estilo en lugar de usar CSS personalizado. La configuración de Tailwind CSS ya está incluida en las instrucciones de abajo, así que no necesitas configurarlo por separado.

> **Nota**: Puedes usar cualquier gestor de paquetes (npm, yarn, pnpm o bun) para configurar tu proyecto de React. En este tutorial, lo mostraremos con **pnpm**.

***

## Índice

1. [Introducción](#id-1.-introduction)
2. [Configuración del proyecto](#id-2.-setting-up-the-project)
3. [Conexión de la cartera](#id-3.-connecting-the-wallet)
4. [Obtención de los activos disponibles](#id-4.-fetching-available-assets)
5. [Solicitud de una cotización](#id-5.-requesting-a-quote)
6. [Construcción de una transacción](#id-6.-building-a-transaction)
7. [Seguimiento de tu operación](#id-7.-tracking-your-trade)
8. [Prueba de tu intercambio](#id-8.-testing-your-swap)
9. [Conclusión](#id-9.-conclusion)
10. [Demostración en vivo](#id-10.-live-demo)
11. [Aplicación de ejemplo avanzada](#id-11.-advanced-example-app)
12. [Uso de agentes de IA para la implementación automatizada](#id-12.-using-ai-agents-for-automated-implementation)

***

## 1. Introducción

En este inicio rápido, construiremos una aplicación mínima de React para:

* Conectarse a una cartera TON (a través de **TonConnect UI**).
* Obtener los tokens disponibles de STON.fi y mostrarlos en menús desplegables.
* Solicitar una cotización (RFQ) a Omniston para la mejor ruta de intercambio (no se necesita un paso separado de "Simular"; Omniston obtiene las cotizaciones automáticamente).
* Construir y ejecutar una transacción de intercambio entre múltiples DEX.
* Seguir el estado de la operación hasta su finalización.

Usaremos:

* **`@ston-fi/omniston-sdk-react`** – Hooks de React para interactuar con Omniston (solicitar cotizaciones, seguir operaciones, etc.).
* **`@ston-fi/api`** – Obtener listas de tokens desde STON.fi (y potencialmente otros datos).
* **`@tonconnect/ui-react`** – Proporciona un botón de conexión de cartera TON basado en React y utilidades.
* **`@ton/core`** – Biblioteca de bajo nivel de TON utilizada para funciones avanzadas.

> **Nota de alcance**: Este inicio rápido es solo para TON. Muestra activos TON y direcciones de cartera TON. Para ver las cadenas compatibles y las reglas de coincidencia de cadenas, consulta [Omniston API v1beta8](/es/seccion-para-desarrolladores/omniston/v1beta8.md), [SDK de Node.js](/es/seccion-para-desarrolladores/omniston/sdk/nodejs.md)y [SDK de React](/es/seccion-para-desarrolladores/omniston/sdk/react.md).

***

## 2. Configuración del proyecto

### 2.1 Crear una aplicación de React

Primero, comprobemos si pnpm está instalado en tu sistema:

```bash
pnpm --version
```

Si ves un número de versión (como `10.4.0`), pnpm está instalado. Si obtienes un error, primero tendrás que instalar pnpm:

```bash
npm install -g pnpm
```

Ahora crearemos un nuevo proyecto de React usando **Vite**. Sin embargo, puedes usar la configuración de React que prefieras (Next.js, CRA, etc.).

Ejecuta el siguiente comando para crear un nuevo proyecto de React basado en Vite:

```bash
pnpm create vite --template react-ts
```

Cuando se te solicite, escribe el nombre que desees para el proyecto (por ejemplo, omniston-swap-app):

```
Nombre del proyecto: » omniston-swap-app
```

Luego entra en la carpeta:

```bash
cd omniston-swap-app
```

***

### 2.2 Instalar los paquetes necesarios

Dentro del directorio de tu nuevo proyecto de React, instala el SDK de Omniston, la UI de TonConnect, la biblioteca central de TON y la biblioteca de la API de STON.fi:

```bash
pnpm add @ston-fi/omniston-sdk-react @tonconnect/ui-react @ton/core @ston-fi/api
```

Después, instala Tailwind CSS y su plugin para Vite:

```bash
pnpm add tailwindcss @tailwindcss/vite
```

Además, instala el plugin de polyfills de Node.js para Vite, que es necesario para proporcionar Buffer y otras APIs de Node.js en el entorno del navegador (requerido por las bibliotecas de TON):

```bash
pnpm add vite-plugin-node-polyfills
```

Configura el plugin de Vite actualizando el archivo `vite.config.js` . Solo se requiere el `buffer` shim y el global `Buffer` para las bibliotecas de TON, así que la configuración se mantiene compacta:

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'

// https://vite.dev/config/
export default defineConfig({
  plugins: [
    react(),
    tailwindcss(),
    nodePolyfills({
      include: ['buffer'],
      globals: {
        Buffer: true,
      },
    }),
  ],
})
```

Luego, importa Tailwind CSS en tu archivo CSS principal. Abre `src/index.css` y reemplaza cualquier código existente con:

```css
@import "tailwindcss";
```

También puedes eliminar `src/App.css` (no lo necesitamos), y eliminar la declaración de importación `import './App.css'` de `src/App.tsx`.

Después de hacer estos cambios, puedes verificar que tu aplicación sigue funcionando correctamente iniciando el servidor de desarrollo:

```bash
pnpm install
pnpm dev
```

Esto debería abrir tu aplicación en modo de desarrollo, normalmente en `http://localhost:5173`. Deberías ver el logotipo y el texto de Vite + React sobre un fondo blanco liso. Como hemos eliminado el estilo predeterminado (App.css), la página se verá más simple que la plantilla predeterminada.

Si ves el logotipo y el texto, significa que tu configuración de Vite + React funciona correctamente. Asegúrate de que todo cargue sin errores antes de pasar al siguiente paso.

***

## 3. Conexión de la cartera

### 3.1 Agregar los proveedores necesarios

Abre **src/main.tsx** (punto de entrada predeterminado de Vite) y envuelve tu aplicación con ambos `TonConnectUIProvider` y `OmnistonProvider`. El `TonConnectUIProvider` hace que el contexto de TonConnect esté disponible para tu aplicación para la conectividad de la cartera, mientras que el `OmnistonProvider` habilita la funcionalidad de Omniston en toda tu aplicación. Además, apunta el proveedor de TonConnect a un archivo manifest (que crearemos a continuación) que describa tu aplicación a las carteras.

```tsx
// src/main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { TonConnectUIProvider } from '@tonconnect/ui-react';
import { Omniston, OmnistonProvider } from '@ston-fi/omniston-sdk-react';
import './index.css'
import App from './App.tsx'

const omniston = new Omniston({ apiUrl: "wss://omni-ws.ston.fi" });

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <TonConnectUIProvider 
      // Para fines de demostración, estamos usando una URL estática del manifest
      // Reemplázala por la tuya: manifestUrl={`${window.location.origin}/tonconnect-manifest.json`}`
      manifestUrl="https://gist.githubusercontent.com/mrruby/243180339f492a052aefc7a666cb14ee/raw/">
      <OmnistonProvider omniston={omniston}>
        <App />
      </OmnistonProvider>
    </TonConnectUIProvider>
  </StrictMode>,
)
```

***

### 3.2 Crear el manifiesto de TonConnect

En la **public** carpeta de tu proyecto, crea un archivo llamado **tonconnect-manifest.json**. Este manifiesto proporciona a las aplicaciones de cartera información sobre tu aplicación (como el nombre y el icono). Debes personalizar este manifiesto para tu propia aplicación. Aquí tienes un ejemplo:

```json
{
  "url": "https://omniston-demo.example.com",
  "name": "Omniston Swap Demo",
  "iconUrl": "https://omniston-demo.example.com/icon-192x192.png"
}
```

Asegúrate de actualizar estos campos para tu aplicación:

* **url**: La URL base desde la que se sirve tu aplicación
* **name**: El nombre visible de tu aplicación (esto es lo que las carteras mostrarán a los usuarios)
* **iconUrl**: Un enlace al icono de tu aplicación (debería ser una imagen PNG de 180×180)

Asegúrate de que este archivo sea accesible. Cuando se ejecute el servidor de desarrollo, deberías poder abrirlo en tu navegador en `http://localhost:5173/tonconnect-manifest.json`.

***

### 3.3 Añadir el botón de conectar cartera

En tu componente principal **App** (por ejemplo, **src/App.tsx**), importa e incluye el `TonConnectButton`. Por ejemplo:

```tsx
// src/App.tsx
import { TonConnectButton } from '@tonconnect/ui-react';

function App() {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-4">
      <h1 className="text-2xl font-bold mb-4">Omniston Swap Demo</h1>
      <TonConnectButton />
    </div>
  );
}

export default App;
```

***

## 4. Obtención de los activos disponibles

A continuación, recuperemos dinámicamente la lista de tokens (activos) que se pueden intercambiar en STON.fi. Para ello usamos el cliente de la API de STON.fi (`@ston-fi/api`). Aquí tienes un ejemplo simplificado que filtra los activos por liquidez (alta a media). Los guardaremos en el estado y los mostraremos en los menús desplegables Desde/Hacia.

Primero, añade las importaciones necesarias a lo que tenemos en `src/App.tsx`:

```tsx
import { useEffect, useState } from 'react';
import { StonApiClient, AssetTag, type AssetInfoV2 } from '@ston-fi/api';
```

Inicializa las variables de estado en tu componente App:

```tsx
function App() {
  const [assets, setAssets] = useState<AssetInfoV2[]>([]);
  const [fromAsset, setFromAsset] = useState<AssetInfoV2 | undefined>();
  const [toAsset, setToAsset] = useState<AssetInfoV2 | undefined>();
  const [amount, setAmount] = useState('');
```

Añade la lógica de obtención de activos con useEffect:

```tsx
  // obtener activos al montar
  useEffect(() => {
    const fetchAssets = async () => {
      try {
        const client = new StonApiClient();
        // Filtrar tokens con mayor liquidez para simplificar
        const condition = [
          AssetTag.LiquidityVeryHigh,
          AssetTag.LiquidityHigh,
          AssetTag.LiquidityMedium
        ] .join(' | ');
        const assetList = await client.queryAssets({ condition });

        setAssets(assetList);
        if (assetList.length > 0) {
          setFromAsset(assetList[0]);
        }
        if (assetList.length > 1) {
          setToAsset(assetList[1]);
        }
      } catch (err) {
        console.error('No se pudieron obtener los activos:', err);
      }
    };
    fetchAssets();
  }, []);
```

Crea la estructura principal de la interfaz:

```tsx
  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gradient-to-b from-blue-50 to-indigo-100 p-6">
      <div className="w-full max-w-md bg-white rounded-xl shadow-lg p-6 space-y-6">
        <div className="flex justify-between items-center">
          <h1 className="text-3xl font-bold text-indigo-700">Omniston Swap</h1>
          <TonConnectButton />
        </div>

        <div className="h-px bg-gray-200 w-full my-4"></div>
```

Añade los menús desplegables de selección de tokens:

```tsx
        {assets.length > 0 ? (
          <div className="space-y-6">
            {/* Desde */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                Desde
              </label>
              <select
                value={fromAsset?.contractAddress || ''}
                onChange={(e) => {
                  const selected = assets.find(a => a.contractAddress === e.target.value);
                  setFromAsset(selected);
                }}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map(asset => (
                  <option
                    key={asset.contractAddress}
                    value={asset.contractAddress}
                  >
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* Hacia */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                Hacia
              </label>
              <select
                value={toAsset?.contractAddress || ''}
                onChange={(e) => {
                  const selected = assets.find(a => a.contractAddress === e.target.value);
                  setToAsset(selected);
                }}
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              >
                {assets.map(asset => (
                  <option
                    key={asset.contractAddress}
                    value={asset.contractAddress}
                  >
                    {asset.meta?.symbol || asset.meta?.displayName || 'token'}
                  </option>
                ))}
              </select>
            </div>

            {/* Cantidad */}
            <div className="flex flex-col">
              <label className="text-sm font-medium text-gray-600 mb-1">
                Cantidad
              </label>
              <input
                type="text"
                value={amount}
                onChange={(e) => setAmount(e.target.value)}
                placeholder="0.0"
                className="w-full p-3 bg-gray-50 border border-gray-200 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 transition-all"
              />
            </div>
          </div>
```

Añade el estado de carga y cierra el componente:

```tsx
        ) : (
          <div className="flex justify-center items-center py-10">
            <div className="animate-pulse flex space-x-2">
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
              <div className="h-2 w-2 bg-indigo-500 rounded-full"></div>
            </div>
            <p className="ml-3 text-gray-600">Cargando activos...</p>
          </div>
        )}
      </div>

      <div className="mt-6 text-center text-xs text-gray-500">
        Con tecnología de Ston.fi
      </div>
    </div>
  );
}

export default App;
```

***

## 5. Solicitar una cotización

Usaremos el `useRfq` hook de `@ston-fi/omniston-sdk-react` para solicitar una cotización. Obtendrá cotizaciones automáticamente según los parámetros proporcionados.

Añade importaciones adicionales al inicio del archivo:

```typescript
import { useRfq, SettlementMethod, Blockchain, GaslessSettlement } from "@ston-fi/omniston-sdk-react";
```

Añade funciones de utilidad para convertir cantidades de tokens:

```typescript
// Convertir una cantidad en cadena de punto flotante a cadena de unidades base enteras
// Esencial para transacciones en blockchain que usan aritmética entera
function toBaseUnits(amount: string, decimals?: number) {
  return Math.floor(parseFloat(amount) * 10 ** (decimals ?? 9)).toString();
}

// Convertir unidades base enteras de nuevo a una cadena fija de 2 decimales para mostrar
function fromBaseUnits(baseUnits: string, decimals?: number) {
  return (parseInt(baseUnits) / 10 ** (decimals ?? 9)).toFixed(2);
}
```

Configura el `useRfq` hook para obtener cotizaciones automáticamente:

> **Ejemplo solo para TON**: Este fragmento mantiene ambos lados del intercambio en TON y usa el flujo heredado de solo TON del inicio rápido. Para integraciones entre cadenas, usa el `chain_id` mapa y las reglas de coincidencia de cadenas de la API de Omniston y la documentación del SDK enlazadas arriba.

```tsx
function App() {
  ...
  const { data: quote, isLoading: quoteLoading, error: quoteError } = useRfq({
    settlementMethods: [SettlementMethod.SETTLEMENT_METHOD_SWAP],
    bidAssetAddress: fromAsset
      ? { blockchain: Blockchain.TON, address: fromAsset.contractAddress }
      : undefined,
    askAssetAddress: toAsset
      ? { blockchain: Blockchain.TON, address: toAsset.contractAddress }
      : undefined,
    amount: {
      bidUnits: fromAsset ? toBaseUnits(amount, fromAsset.meta?.decimals) : '0'
    },
    settlementParams: {
      gaslessSettlement: GaslessSettlement.GASLESS_SETTLEMENT_POSSIBLE,
      maxPriceSlippageBps: 500,
    },
  }, {
    enabled: !!fromAsset?.contractAddress && !!toAsset?.contractAddress && amount !== ''
  });
```

Añade la sección de visualización de la cotización a tu tsx (insértala después del campo de entrada de cantidad):

```tsx
            {/* Sección de cotización */}
            <div className="pt-4">
              {quoteLoading && <p>Cargando cotización...</p>}
              {quoteError && <p className="text-red-500">Error: {String(quoteError)}</p>}
              {quote && 'quote' in quote && (
                <div className="p-4 bg-gray-50 rounded-lg border border-gray-200">
                  <p className="font-semibold text-gray-700">Información de la cotización</p>
                  <p className="text-sm text-gray-600">Resolutor: {quote.quote.resolverName}</p>
                  <p className="text-sm text-gray-600">Unidades de oferta: {fromBaseUnits(quote.quote.bidUnits, fromAsset?.meta?.decimals)}  {fromAsset?.meta?.symbol}</p>
                  <p className="text-sm text-gray-600">Unidades de demanda: {fromBaseUnits(quote.quote.askUnits, toAsset?.meta?.decimals)} {toAsset?.meta?.symbol}</p>
                </div>
              )}
            </div>
```

Cada vez que el usuario cambie el token o la cantidad, `useRfq` se actualizará automáticamente la cotización.

***

## 6. Construir una transacción y enviarla

Una vez que tengamos una cotización, podemos construir la transacción que ejecutará el intercambio. Usaremos el `useOmniston` hook para acceder a la instancia de Omniston y construir la transacción.

Reemplaza las importaciones de `@tonconnect/ui-react` y `@ston-fi/omniston-sdk-react` con:

```tsx
import { TonConnectButton, useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";
import {
  useRfq,
  SettlementMethod,
  Blockchain,
  GaslessSettlement,
  useOmniston,
  type QuoteResponseEvent_QuoteUpdated,
} from "@ston-fi/omniston-sdk-react";
```

Añade los hooks de conexión de cartera y la instancia de omniston:

```tsx
function App() {  
  // ... variables de estado existentes ...
  const walletAddress = useTonAddress();
  const [tonConnect] = useTonConnectUI();
  const omniston = useOmniston();
```

Crea la función para construir la transacción:

```tsx
  // ... después del hook useRfq ...
  async function buildTx(willTradedQuote: QuoteResponseEvent_QuoteUpdated | undefined) {
    if (!willTradedQuote || !walletAddress) {
      alert("Por favor, conecta tu cartera y asegúrate de que haya una cotización válida cargada.");
      return null;
    }

    try {
      const tx = await omniston.buildTransfer({
        quote: willTradedQuote.quote,
        sourceAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // la cartera que envía el token de oferta
        },
        destinationAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // la misma cartera que recibe el token de demanda
        },
        gasExcessAddress: {
          blockchain: Blockchain.TON,
          address: walletAddress, // el exceso de gas vuelve al remitente
        },
        useRecommendedSlippage: false, // usar el deslizamiento recomendado de la cotización
      });

      return tx.ton?.messages || [];
    } catch (err) {
      console.error("Error al construir la transacción:", err);
      alert("No se pudo construir la transacción. Revisa la consola para obtener detalles.");
      return null;
    }
  }
```

Añade la función de ejecución del intercambio:

```tsx
  async function handleSwap() {
    if (!quote || quote.type !== 'quoteUpdated') {
      alert("No hay una cotización válida disponible");
      return;
    }
    const willTradedQuote = quote;
    const messages = await buildTx(willTradedQuote);
    if (!messages) return;
    
    try {
      await tonConnect.sendTransaction({
        validUntil: Date.now() + 1000000,
        messages: messages.map((message) => ({
          address: message.targetAddress,
          amount: message.sendAmount,
          payload: message.payload,
        })),
      });
    } catch (err) {
      console.error("Error al enviar la transacción:", err);
      alert("No se pudo enviar la transacción. Revisa la consola para obtener detalles.");
    }
  }
```

Añade el botón Ejecutar intercambio (insértalo después de en la sección de cotización):

```tsx
        {quote && 'quote' in quote && (
        <>
          <div className="p-4 bg-gray-50 rounded-lg border border-gray-200">
            <p className="font-semibold text-gray-700">Información de la cotización</p>
            <p className="text-sm text-gray-600">Resolutor: {quote.quote.resolverName}</p>
            <p className="text-sm text-gray-600">Unidades de oferta: {fromBaseUnits(quote.quote.bidUnits, fromAsset?.meta?.decimals)}  {fromAsset?.meta?.symbol}</p>
            <p className="text-sm text-gray-600">Unidades de demanda: {fromBaseUnits(quote.quote.askUnits, toAsset?.meta?.decimals)} {toAsset?.meta?.symbol}</p>
          </div>
          <button
            onClick={handleSwap}
            className="mt-4 w-full bg-indigo-500 hover:bg-indigo-600 text-white font-medium py-3 px-4 rounded-lg transition-all"
          >
            Ejecutar intercambio
          </button>
        </>
      )}
```

***

## 7. Seguimiento de tu operación

### 7.1 Instalar el paquete TON

Seguiremos las operaciones usando el `useTrackTrade` hook de `@ston-fi/omniston-sdk-react`. Primero, asegúrate de tener el paquete `@ton/ton` instalado si aún no lo has hecho:

```bash
pnpm add @ton/ton
```

### 7.2 Uso del hook useTrackTrade

Después de haber construido y enviado la transacción de intercambio, puedes seguir su estado con `useTrackTrade`. Este hook toma el `quoteId` de la operación, la dirección de tu cartera y el hash de la transacción saliente. Comprueba periódicamente el estado en cadena de la operación, indicándote si está pendiente, liquidada o parcialmente ejecutada.

Reemplaza las importaciones de `@ton/ton` y `@ston-fi/omniston-sdk-react` para el seguimiento de operaciones:

```tsx
import {
  useRfq,
  SettlementMethod,
  Blockchain,
  GaslessSettlement,
  useOmniston,
  useTrackTrade,
  type QuoteResponseEvent_QuoteUpdated,
  type TradeStatus,
} from "@ston-fi/omniston-sdk-react";
import { TonClient, Address, Cell, beginCell, storeMessage } from "@ton/ton";
```

Añade variables de estado para el seguimiento:

```tsx
function App() {
  // ... variables de estado existentes ...
  const [outgoingTxHash, setOutgoingTxHash] = useState("");
  const [tradedQuote, setTradedQuote] = useState<QuoteResponseEvent_QuoteUpdated | null>(null);
```

Restablece el estado de seguimiento cuando cambien las entradas:

```tsx
  // Restablecer outgoingTxHash y tradedQuote cuando cambien las entradas
  useEffect(() => {
    setTradedQuote(null);
    setOutgoingTxHash("");
  }, [fromAsset, toAsset, amount]);
```

Actualiza el hook useRfq para dejar de obtener cotizaciones durante la ejecución de la operación:

```tsx
  const {
    data: quote,
    isLoading: quoteLoading,
    error: quoteError,
  } = useRfq({
    // ... configuración existente de useRfq ...
  }, {
    enabled:
      !!fromAsset?.contractAddress &&
      !!toAsset?.contractAddress &&
      amount !== "" &&
      // añade esto para dejar de obtener nuevas cotizaciones cuando hacemos una transacción
      !outgoingTxHash,
  });
```

Configura el hook de seguimiento de operaciones:

```tsx
  const {
    isLoading: trackingLoading,
    error: trackingError,
    data: tradeStatus,
  } = useTrackTrade({
    quoteId: tradedQuote?.quote?.quoteId || '',
    traderWalletAddress: {
      blockchain: Blockchain.TON,
      address: walletAddress || '',
    },
    outgoingTxHash,
  }, {
    enabled: !!tradedQuote?.quote?.quoteId && !!walletAddress && !!outgoingTxHash,
  });
```

Añadir una función auxiliar para traducir los resultados de la operación:

```tsx
  // Función para traducir el resultado de la operación a texto legible
const getTradeResultText = (status: TradeStatus) => {
    if (!status?.status?.tradeSettled) return "";
    
    const result = status.status.tradeSettled.result;
    switch (result) {
      case "TRADE_RESULT_FULLY_FILLED":
        return "La operación se completó con éxito y se ejecutó por completo";
      case "TRADE_RESULT_PARTIALLY_FILLED":
        return "La operación se ejecutó parcialmente: algo salió mal";
      case "TRADE_RESULT_ABORTED":
        return "La operación fue abortada";
      case "TRADE_RESULT_UNKNOWN":
      case "UNRECOGNIZED":
      default:
        return "Resultado de operación desconocido";
    }
  };
```

Añadir funciones utilitarias para la extracción del hash de la transacción:

```tsx
    // Función utilitaria para reintentar una operación asíncrona
    const retry = async (fn: () => Promise<string>, { retries = 5, delay = 1000 }): Promise<string> => {
      try {
        return await fn();
      } catch (error) {
        if (retries === 0) throw error;
        await new Promise(resolve => setTimeout(resolve, delay));
        return retry(fn, { retries: retries - 1, delay });
      }
    };

    const getTxByBOC = async (exBoc: string, walletAddress: string): Promise<string> => {
      if (!exBoc || !walletAddress) {
        throw new Error('Faltan parámetros obligatorios para el seguimiento de la transacción');
      }
  
      const client = new TonClient({
        endpoint: 'https://toncenter.com/api/v2/jsonRPC'
      });
  
      const myAddress = Address.parse(walletAddress);
  
      return retry(async () => {
        const transactions = await client.getTransactions(myAddress, {
          limit: 5,
        });
  
        for (const tx of transactions) {
          const inMsg = tx.inMessage;
          if (inMsg?.info.type === 'external-in') {
            const inBOC = inMsg?.body;
            if (typeof inBOC === 'undefined') {
              continue;
            }
  
            const extHash = Cell.fromBase64(exBoc).hash().toString('hex');
            const inHash = beginCell().store(storeMessage(inMsg)).endCell().hash().toString('hex');
  
            if (extHash === inHash) {
              return tx.hash().toString('hex');
            }
          }
        }
        throw new Error('Transacción no encontrada');
      }, { retries: 30, delay: 1000 });
    }; 
```

Actualiza la función handleSwap para capturar los detalles de la transacción:

```tsx
 async function handleSwap() {
    if (!quote || quote.type !== 'quoteUpdated') {
      alert("No hay una cotización válida disponible");
      return;
    }
    const messages = await buildTx(quote);
    if (!messages) return;
    
    try {
      setTradedQuote(quote);

      const res = await tonConnect.sendTransaction({
        validUntil: Date.now() + 1000000,
        messages: messages.map((message) => ({
          address: message.targetAddress,
          amount: message.sendAmount,
          payload: message.payload,
        })),
      });

      const exBoc = res.boc;
      const txHash = await getTxByBOC(exBoc, walletAddress);
      setOutgoingTxHash(txHash);

    } catch (err) {
      setTradedQuote(null);
      console.error("Error al enviar la transacción:", err);
      alert("No se pudo enviar la transacción. Revisa la consola para obtener detalles.");
    }
  }
```

Añadir la visualización del estado de la operación (insertar después de la línea divisoria):

```tsx
        {/* Estado de la operación */}
        {/* justo después de <div className="h-px bg-gray-200 w-full my-4"></div> */}
        <div className="mt-4 p-4 bg-gray-50 rounded-lg border border-gray-200">
          {trackingLoading && <p className="text-sm text-blue-600">Seguimiento de la operación...</p>}
          {trackingError && (
            <p className="text-sm text-orange-600">Error en el seguimiento de la operación: {String(trackingError)}</p>
          )}
          {tradeStatus?.status?.tradeSettled && (
            <p className="text-sm text-green-600">
              Resultado de la operación: {getTradeResultText(tradeStatus)}
            </p>
          )}
        </div>
```

Actualizar la visualización de errores en la sección de la cotización

```tsx
        {quoteError && !outgoingTxHash && <p className="text-red-500">Error: {String(quoteError)}</p>}
```

***

## 8. Probando tu intercambio

1. Inicia el servidor de desarrollo:

```bash
pnpm dev
```

2. Abre tu aplicación en el navegador en `http://localhost:5173`.
3. Conecta tu billetera TON mediante el botón "Conectar billetera".
4. Selecciona tokens de los menús desplegables e introduce una cantidad.
5. Omniston obtiene y muestra automáticamente una cotización. Confirma que sea válida.
6. Haz clic en "Ejecutar intercambio" para finalizar la transacción. Apruébala en tu billetera.
7. Una vez que el intercambio se complete en la cadena, los saldos de tu billetera deberían actualizarse en consecuencia.

***

## 9. Conclusión

¡Felicidades! Has creado una aplicación mínima de React + Vite con Tailwind CSS que:

* Se conecta a una billetera TON usando TonConnect.
* Obtiene dinámicamente los tokens disponibles desde STON.fi.
* Solicita cotizaciones en tiempo real (RFQs) desde Omniston automáticamente.
* Construye y envía transacciones de intercambio.

Siéntete libre de ampliar esta demo con:

* Configuraciones personalizadas de slippage.
* Mejor gestión de errores y notificaciones de éxito.
* Métodos adicionales de liquidación o lógica entre cadenas.
* Aprende cómo añadir comisiones de referido a tus intercambios de Omniston leyendo la [guía de comisiones de referido](/es/seccion-para-desarrolladores/omniston/referral-fees.md).

¡Feliz desarrollo con Omniston!

## 10. Demostración en vivo

Con esta demo de Replit, puedes:

* Abrir el proyecto directamente en tu navegador
* Hacer un fork del Replit para crear tu propia copia
* Ejecutar la aplicación para verla en acción
* Explorar y modificar el código para aprender cómo funciona
* Experimentar con diferentes funciones y cambios en la interfaz

{% embed url="<https://replit.com/@stonfi/omniston-swap-app?embed=true>" %}

Alternativamente, puedes ejecutar este ejemplo localmente clonando el repositorio de GitHub:

```bash
git clone https://github.com/mrruby/omniston-swap-app.git
cd omniston-swap-app
pnpm install
pnpm dev
```

Esto iniciará el servidor de desarrollo y podrás acceder a la aplicación en `http://localhost:5173`.

## 11. Aplicación de ejemplo avanzada

Para quienes buscan un enfoque más avanzado y con más funciones, también tenemos una aplicación de demostración de Omniston en Next.js que:

* Usa Next.js como un framework escalable
* Utiliza hooks y providers para una arquitectura elegante
* Demuestra una mejor gestión de errores, una administración de estado robusta y funciones adicionales de STON.fi y Omniston

Puedes explorar el código en nuestro repositorio:

[Aplicación de demostración React del SDK de Omniston](https://github.com/ston-fi/omniston-sdk/tree/main/examples/react-app)

O verla en acción en nuestra demo en vivo:

[Aplicación de demostración de Omniston](https://omniston.ston.fi/)

## 12. Uso de agentes de IA para la implementación automatizada

Para los desarrolladores que buscan acelerar su proceso de desarrollo, pueden aprovechar **agentes de programación de IA** para implementar automáticamente la funcionalidad de intercambio de Omniston descrita en esta guía. Aunque mostramos **Gemini CLI** en nuestro ejemplo (debido a su generoso nivel gratuito), puedes usar cualquier asistente de programación de IA, como **Claude Code**, **GitHub Copilot**, **Cursor Agent**o herramientas similares.

También puedes seguir una guía grabada:

{% embed url="<https://www.youtube.com/watch?v=7m2kfatz6PY>" %}

### 12.1 ¿Por qué agentes de IA?

Los agentes de programación de IA modernos pueden:

* Entender documentación compleja e implementar funciones completas
* Configurar automáticamente la estructura del proyecto y las dependencias
* Resolver errores comunes de configuración e instalación
* Proporcionar implementaciones funcionales en minutos en lugar de horas

### 12.2 Configuración con Gemini CLI (ejemplo)

Lo demostraremos con Gemini CLI, pero el enfoque funciona con cualquier agente de IA que pueda leer documentación y ejecutar comandos.

#### 12.2.1 Instalación de Gemini CLI

1. Instala Gemini CLI siguiendo las instrucciones en: <https://github.com/google-gemini/gemini-cli>
2. Autentícate con tu cuenta de Google cuando se te solicite. El nivel gratuito incluye:
   * 60 solicitudes al modelo por minuto
   * 1.000 solicitudes al modelo por día

#### 12.2.2 Configuración de la guía de implementación

1. Descarga el `GEMINI.md` archivo desde el gist: <https://gist.github.com/mrruby/311a9d96f12bc7303bb1046dc92092b3>
2. Renombra el archivo según tu agente de IA:
   * **Para Gemini CLI**: Usa `GEMINI.md` tal cual (no es necesario renombrarlo)
   * **Para Claude Code**: Renombra `GEMINI.md` a `CLAUDE.md`
   * **Para otros agentes de IA** (GitHub Copilot, Cursor, etc.): Renombra `GEMINI.md` a `AGENTS.md`
3. Crea un nuevo directorio para tu proyecto y coloca allí el archivo de la guía:

   ```bash
   mkdir my-omniston-app
   cd my-omniston-app
   # Coloca aquí el archivo renombrado (GEMINI.md, CLAUDE.md o AGENTS.md)
   ```

#### 12.2.3 Ejecución de la implementación automatizada

1. Desde dentro del directorio de tu proyecto, ejecuta Gemini CLI:

   ```bash
   gemini
   ```
2. Cuando se abra la interfaz de la CLI, escribe:

   ```
   Implementar Omniston según la Guía Rápida de Omniston
   ```
3. El agente de IA hará lo siguiente:
   * Pedir permiso para usar comandos como `pnpm`, `npm`etc.
   * Crear automáticamente la estructura del proyecto
   * Instalar todas las dependencias necesarias
   * Implementar la funcionalidad completa de intercambio
   * Configurar los componentes de la interfaz y los estilos
4. Si ocurre algún error durante el proceso:
   * Simplemente vuelve a pegar el mensaje de error al agente de IA
   * Analizará y corregirá el problema automáticamente
   * En la mayoría de los casos, la implementación se completa con éxito en un solo intento

### 12.3 Uso de otros agentes de IA

El mismo enfoque funciona con otros asistentes de programación de IA:

* **Gemini CLI**: Descarga `GEMINI.md` del gist y úsalo tal cual
* **Claude Code**: Descarga `GEMINI.md` del gist y renómbralo a `CLAUDE.md`y luego colócalo en tu proyecto y pide a Claude Code que implemente la guía
* **GitHub Copilot**: Descarga `GEMINI.md` del gist y renómbralo a `AGENTS.md`abre la guía en tu editor y usa Copilot Chat para implementarla paso a paso
* **Cursor Agent**: Descarga `GEMINI.md` del gist y renómbralo a `AGENTS.md`carga la documentación y solicita la implementación completa mediante el modo agente de Cursor
* **Otras herramientas de IA**: Descarga `GEMINI.md` y renómbralo a `AGENTS.md` para compatibilidad con la mayoría de los asistentes de IA

> **Nota**: El gist contiene `GEMINI.md` que funciona directamente con Gemini CLI. Para Claude Code, renómbralo a `CLAUDE.md`. Para otros agentes de IA (GitHub Copilot, Cursor, etc.), renómbralo a `AGENTS.md` para una mejor compatibilidad.

### 12.4 Beneficios de usar agentes de IA

* **Velocidad**: Obtén una implementación funcional en minutos en lugar de horas
* **Precisión**: Los agentes de IA siguen la guía rápida con exactitud
* **Gestión de errores**: Resuelven automáticamente la mayoría de los problemas comunes de configuración
* **Herramienta de aprendizaje**: Observa cómo se desarrolla la implementación paso a paso
* **Personalización**: Después de la configuración inicial, puedes modificar el código generado para adaptarlo a tus necesidades específicas
* **Rentable**: Muchas herramientas de programación de IA ofrecen niveles gratuitos (como Gemini CLI) o están incluidas en suscripciones existentes

### 12.5 Mejores prácticas

1. **Revisar el código**: Revisa siempre el código generado por IA antes de usarlo en producción
2. **Entender el flujo**: Usa el código generado como una herramienta de aprendizaje para entender el protocolo Omniston
3. **Personalizar**: Adapta el código generado a tus requisitos específicos
4. **Probar a fondo**: Prueba la implementación con cantidades pequeñas antes de procesar transacciones más grandes
5. **Seguridad**: Nunca subas claves API ni mnemónicos al control de versiones

Este enfoque es especialmente útil para:

* Desarrolladores nuevos en el ecosistema TON
* Prototipado rápido y pruebas de concepto
* Aprender con un ejemplo mediante una implementación funcional
* Evitar errores comunes de configuración e instalación
* Explorar rápidamente diferentes enfoques de implementación


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.ston.fi/es/seccion-para-desarrolladores/quickstart/omniston.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
