# TASK — Багфикс: vault_token в релейере должен быть ATA, а не PDA Репозиторий: `smart-updown-token`, файл `relayer/src/ops.ts`. ## Проблема (обнаружена на localnet E2E) Контракт переведён на SPL-токен (Фаза 1). В инструкциях `create_bet`/`close_bet` аккаунт `vault_token` — это **ATA-аккаунт токена, который принадлежит владельцу vaultPda** (PDA `[b"global", b"vault"]`). Правильный адрес: `getAssociatedTokenAddress(mint, vaultPda(programId))` (allowOwnerOffCurve по PDA-владельцу). Однако в релейере `createBet`/`closeBet` используется: ```ts vaultToken: vaultPda(program.programId), ``` Это передаёт адрес **самого PDA-владельца** (не токен-аккаунт) — контракт отвергнет транзакцию (wrong account), а `ensureVaultFunded` зовёт `getTokenAccountBalance(vaultToken)` где vaultToken = vaultPda → «could not find account». Т.е. ваулт ATA даже не создаётся в нужном месте. ## Что исправить (только `relayer/src/ops.ts`) 1. **`ensureVaultFunded`**: - Получать ATA ваулта: `const vaultAta = await getAssociatedTokenAddress(mint, vaultPda, true);` (третий аргумент `true` — allowOwnerOffCurve, иначе TokenOwnerOffCurveError). - Создавать/получать: `getOrCreateAssociatedTokenAccount(connection, admin, mint, vaultPda, true)`. - `getTokenAccountBalance` звать по **ATA-адресу** (`vaultAta`), не по vaultPda. 2. **`createBet`**: - `vaultToken: await getAssociatedTokenAddress(mint, vaultPda(program.programId), true)` 3. **`closeBet`**: - `vaultToken: await getAssociatedTokenAddress(mint, vaultPda(program.programId), true)` ## Гейты - `npm run typecheck` — 0 ошибок. - `npm run test` (check.ts) — зелёные проверки. - (Если можно) быстрый локальный smoke: создать ваулт ATA, налить токен, create/close. ## НЕ делать - Не трогать `program/` (контракт), не менять IDL. - Не трогать логику цен/ставок. - Не коммитить. ## Суть одным абзацем `vault_token` в контракте — токен-счёт (ATA), всегда считаемый через `getAssociatedTokenAddress(mint, vaultPda, true)`. Заменить все прямые использования `vaultPda(...)` как адреса acкаунта на ATA-адрес.