Skip to content

"DOGE_USDT_Perp signature mismatch (403/2002) — instrument not in hardcoded fallback maps" #5

Description

@jpostius-dot

Bug: DOGE_USDT_Perp (y probablemente otros pares no-hardcodeados) falla al firmar órdenes — "Signature does not match payload" (403, code 2002)

Resumen

Al intentar arrancar (Start) un bot configurado con el par DOGE_USDT_Perp, la acción falla de forma consistente con:

HTTP 403: {"code":2002,"message":"Signature does not match payload","status":403}

El error ocurre al arrancar el bot (colocación de órdenes), no al guardar las credenciales — el guardado en /api/v2/auth/grvt-credentials pasa sin problema porque solo valida login() + getBalance(), un camino que no requiere firma EIP-712.

Pasos para reproducir

  1. Conectar credenciales GRVT válidas (API Key, API Secret/private key, Trading Address, Account ID) en el dashboard — el test de guardado pasa OK.
  2. Crear un bot nuevo con par DOGE_USDT_Perp, cualquier rango/niveles/inversión válidos.
  3. Entrar al detalle del bot y pulsar "Arrancar".
  4. Resultado: 403 — Signature does not match payload (code 2002).

Lo que ya se descartó como causa

  • Regeneré la API Key en grvt.io tres veces (tres pares de credenciales distintas), reintroduciendo Trading Address con checksum EIP-55 correcto (copiado con el botón de copiar, no tecleado). Mismo error en las tres.
  • Confirmé que el problema no es de sesión/caché: probé en ventana nueva, tras limpiar cookies del sitio, y tras cerrar/reabrir sesión.
  • El mismo flujo con ETH_USDT_Perp (mismas credenciales, mismo bot engine, mismo día) funciona correctamente — el bot arranca, coloca órdenes reales y queda en estado RUNNING sin ningún error de firma.

Hipótesis de causa raíz (basada en revisión del código)

En packages/bot/src/api/order-signer.ts:

const FALLBACK_INSTRUMENT_TO_ASSET_ID: Record<string, string> = {
  'ETH_USDT_Perp': '0x030401',
  'BTC_USDT_Perp': '0x030501',
};

Y en packages/bot/src/api/client.ts:

const instrumentSpecsCache = new Map<string, InstrumentSpec>([
  ['BTC_USDT_Perp', { ..., instrument_hash: '0x030501', base_decimals: 9 }],
  ['ETH_USDT_Perp', { ..., instrument_hash: '0x030401', base_decimals: 9 }],
  ['SOL_USDT_Perp', { ..., base_decimals: 9 }], // sin instrument_hash hardcodeado
]);

DOGE_USDT_Perp no aparece en ninguno de los dos mapas hardcodeados. Su instrument_hash y base_decimals dependen enteramente de que getInstruments() haya poblado el cache dinámico correctamente antes de firmar (el warmup ocurre una vez, al arrancar el proceso del engine completo — grid-engine.ts línea ~512, comentario explícito: "populate the instrument specs cache [...] for any pair we may trade (SOL, DOGE, etc.)").

Si ese warmup no captura bien instrument_hash/base_decimals para DOGE (por ejemplo, si el campo viene ausente o con otro nombre en la respuesta de /instruments para este par, o si el par se añadió a GRVT después del último restart del engine), order-signer.ts firmaría la orden con un assetID y/o contractSize que no coincide con lo que el backend de GRVT espera para ese instrumento — resultando en el rechazo de firma que reporto, sin lanzar el error explícito "Instrumento no soportado" que el propio código contempla como salvaguarda (esa comprobación solo dispara si instrument_hash es undefined, no si es un valor incorrecto).

Sugerencia

  • Verificar en logs de producción (con DEBUG_SIGN=1) qué instrument_hash y base_decimals se están usando realmente para DOGE_USDT_Perp al firmar, y compararlo contra la respuesta actual de /instruments para ese par.
  • Considerar añadir una validación adicional: si el instrument_hash viene del cache dinámico (no del fallback hardcodeado), hacer un refresh de getInstruments() justo antes de la primera firma de cada bot nuevo, en vez de depender solo del warmup de arranque del engine.

Entorno

  • Instancia usada: hosted (grvtbot.com)
  • Par afectado: DOGE_USDT_Perp
  • Par de control (funciona bien): ETH_USDT_Perp
  • Fecha: 3 julio 2026

Gracias por el proyecto — quedo disponible para dar más detalles o logs si hacen falta.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions