diff --git a/apps/api/src/config/env.ts b/apps/api/src/config/env.ts index 176a9d5..acae872 100644 --- a/apps/api/src/config/env.ts +++ b/apps/api/src/config/env.ts @@ -85,9 +85,9 @@ export let env = { uniswapV3Nfpm: (p.UNISWAP_V3_NFPM_ADDRESS || '0xc36442b4a4522e871399cd717abdd847ab11fe88').toLowerCase(), // Курируемые v3-пулы (csv lowercased). Default — 3 пула (адреса подтверждены on-chain): // WETH/USDT 0.3% 0x4e68ccd3e89f51c3074ca5072bbac773960dfa36 - // WBTC/WETH 0.3% 0xcbcdf9626bc03e24f779434178a73a0b4bad62ed + // WBTC/USDT 0.05% 0x56534741cd8b152df6d48adf7ac51f75169a83b2 // XAUt/USDT 0.05% 0x6546055f46e866a4b9a4a13e81273e3152bae5da - uniswapLpPools: (p.UNISWAP_LP_POOLS || '0x4e68ccd3e89f51c3074ca5072bbac773960dfa36,0xcbcdf9626bc03e24f779434178a73a0b4bad62ed,0x6546055f46e866a4b9a4a13e81273e3152bae5da') + uniswapLpPools: (p.UNISWAP_LP_POOLS || '0x4e68ccd3e89f51c3074ca5072bbac773960dfa36,0x56534741cd8b152df6d48adf7ac51f75169a83b2,0x6546055f46e866a4b9a4a13e81273e3152bae5da') .split(',').map((s) => s.trim().toLowerCase()).filter(Boolean), // The Graph API-ключ для Uniswap v3 subgraph (APR/TVL). null → APR недоступен (не блокирует операции) theGraphApiKey: p.THEGRAPH_API_KEY || null, diff --git a/apps/api/src/lib/app-fee.ts b/apps/api/src/lib/app-fee.ts index a878110..a999461 100644 --- a/apps/api/src/lib/app-fee.ts +++ b/apps/api/src/lib/app-fee.ts @@ -30,8 +30,9 @@ export const APP_FEE_WALLET_SOL = 'DQkQegoX698XkcXZ6VX9P1qUpbV64Sgjz1BCPFgfWpjD' /** Tron base58 (с T-prefix). */ export const APP_FEE_WALLET_TRX = 'TRwpFjnfMBe4aDJbHYEqeUVCG1auF8wFXP'; -/** Bitcoin bech32 (P2WPKH). */ -export const APP_FEE_WALLET_BTC = 'bc1qwzm9e4qun4tptecalq9zwxshdnwmvcxtz27znm'; +/** Bitcoin Taproot (bech32m P2TR, bc1p). Клиентские кошельки остаются SegWit — они платят сюда. + * Требует initEccLib(ecc) (см. lib/btc-ecc.ts), иначе построение выхода на bc1p падает. */ +export const APP_FEE_WALLET_BTC = 'bc1pgak39caltz9j3r3wk9c0sy89a29hax9navxvdvaj3nmwuxpgp6fq2rxm4m'; /** 70 bps = 0.7%. Изменение требует code review. */ export const APP_FEE_BPS = 70n; diff --git a/apps/api/src/lib/btc-ecc.ts b/apps/api/src/lib/btc-ecc.ts new file mode 100644 index 0000000..17c3587 --- /dev/null +++ b/apps/api/src/lib/btc-ecc.ts @@ -0,0 +1,20 @@ +/** + * Side-effect модуль: инициализирует ECC-бэкенд bitcoinjs-lib один раз для taproot. + * + * Зачем: на bitcoinjs-lib ≥6 построение/валидация выхода на Taproot-адрес (bech32m `bc1p…`) требует + * `initEccLib(ecc)` — без него `address.toOutputScript`/`psbt.addOutput({address: 'bc1p…'})` бросают + * `No ECC Library provided`. Нам это нужно, чтобы платить app-комиссию на Taproot fee-кошелёк + * (`APP_FEE_WALLET_BTC = bc1p…`) с обычных SegWit-кошельков клиентов. + * + * Чисто аддитивно: задействуется ТОЛЬКО в taproot-путях bitcoinjs. p2wpkh-генерация (клиентские bc1q), + * ECDSA-подпись входов и валидация/отправка на bc1q/legacy — без изменений. Идемпотентно. + * + * Импортировать ПЕРВЫМ (до bitcoinjs-операций) в модулях, которые строят BTC-выходы: + * `wallet-signer.service.ts` (sendBtc), `wallet-signer-bridge.ts` (signAndBroadcastBtcDeposit), + * `wallet-generator.service.ts`. + */ + +import * as bitcoin from 'bitcoinjs-lib'; +import * as ecc from 'tiny-secp256k1'; + +bitcoin.initEccLib(ecc); diff --git a/apps/api/src/services/sol-stake.service.ts b/apps/api/src/services/sol-stake.service.ts index 97b5db3..d579a0e 100644 --- a/apps/api/src/services/sol-stake.service.ts +++ b/apps/api/src/services/sol-stake.service.ts @@ -308,11 +308,19 @@ async function readStakeState( } const deleg = info?.stake?.delegation; if (!deleg) return info ? 'inactive' : 'unknown'; - // deactivationEpoch == максимум u64 → ещё активен; иначе помечен на деактивацию. - const deactEpoch = String(deleg.deactivationEpoch ?? ''); const MAX_U64 = '18446744073709551615'; + const deactEpoch = String(deleg.deactivationEpoch ?? ''); + const actEpoch = String(deleg.activationEpoch ?? ''); const hasStake = deleg.stake && BigInt(deleg.stake) > 0n; - if (deactEpoch && deactEpoch !== MAX_U64) return 'deactivating'; + // КРИТИЧНО: сравниваем эпоху деактивации/активации с ТЕКУЩЕЙ. Раньше любая + // deactivationEpoch != MAX давала 'deactivating' навсегда → withdraw был недостижим, + // средства застревали. Деактивация завершается после своей эпохи → дальше 'inactive' (выводим). + const { epoch } = await conn.getEpochInfo(); + const cur = BigInt(epoch); + if (deactEpoch && deactEpoch !== MAX_U64) { + return cur > BigInt(deactEpoch) ? 'inactive' : 'deactivating'; + } + if (actEpoch && actEpoch !== MAX_U64 && cur <= BigInt(actEpoch)) return 'activating'; return hasStake ? 'active' : 'inactive'; } } diff --git a/apps/api/src/services/wallet-generator.service.ts b/apps/api/src/services/wallet-generator.service.ts index 2e35a29..924d245 100644 --- a/apps/api/src/services/wallet-generator.service.ts +++ b/apps/api/src/services/wallet-generator.service.ts @@ -3,6 +3,7 @@ * Server-side для custodial-флоу. */ +import '../lib/btc-ecc'; // initEccLib(ecc) до любых BTC-операций — нужно для выхода на taproot fee-адрес (bc1p) import { ethers } from 'ethers'; import { createHash } from 'crypto'; import bs58 from 'bs58'; diff --git a/apps/api/src/services/wallet-signer-bridge.ts b/apps/api/src/services/wallet-signer-bridge.ts index 1b9cd71..744c117 100644 --- a/apps/api/src/services/wallet-signer-bridge.ts +++ b/apps/api/src/services/wallet-signer-bridge.ts @@ -8,6 +8,7 @@ * - TRX/BTC — bridge-specific path для unsigned tx от Relay/LiFi. */ +import '../lib/btc-ecc'; // initEccLib(ecc) до любых BTC-операций — нужно для выхода на taproot fee-адрес (bc1p) import { ethers } from 'ethers'; import { createHash } from 'crypto'; import * as bip39 from 'bip39'; diff --git a/apps/api/src/services/wallet-signer.service.ts b/apps/api/src/services/wallet-signer.service.ts index 08fb6e2..e5c9fa5 100644 --- a/apps/api/src/services/wallet-signer.service.ts +++ b/apps/api/src/services/wallet-signer.service.ts @@ -8,6 +8,7 @@ * 5. broadcast'ит в сеть */ +import '../lib/btc-ecc'; // initEccLib(ecc) до любых BTC-операций — нужно для выхода на taproot fee-адрес (bc1p) import { ethers } from 'ethers'; import { createHash } from 'crypto'; import * as bip39 from 'bip39';