diff --git a/backend/alembic/versions/p6q7r8s9t0u1_referral_campaign.py b/backend/alembic/versions/p6q7r8s9t0u1_referral_campaign.py index da5d98e7..1902f8b4 100644 --- a/backend/alembic/versions/p6q7r8s9t0u1_referral_campaign.py +++ b/backend/alembic/versions/p6q7r8s9t0u1_referral_campaign.py @@ -41,10 +41,7 @@ def upgrade() -> None: "CREATE INDEX IF NOT EXISTS idx_rc_referree_month " "ON referral_campaigns (referree_id, reward_start_month, reward_expires_month)" ) - op.execute( - "CREATE INDEX IF NOT EXISTS idx_rc_partner " - "ON referral_campaigns (partner_id)" - ) + op.execute("CREATE INDEX IF NOT EXISTS idx_rc_partner ON referral_campaigns (partner_id)") op.execute(""" CREATE TABLE IF NOT EXISTS uat_wallet_ledger ( @@ -64,10 +61,7 @@ def upgrade() -> None: "ON uat_wallet_ledger (reference_fee_tx_id, entry_type, reason) " "WHERE reference_fee_tx_id IS NOT NULL" ) - op.execute( - "CREATE INDEX IF NOT EXISTS idx_uwl_month " - "ON uat_wallet_ledger (month)" - ) + op.execute("CREATE INDEX IF NOT EXISTS idx_uwl_month ON uat_wallet_ledger (month)") else: # SQLite (テスト環境) op.create_table( diff --git a/backend/app/aave/client.py b/backend/app/aave/client.py index 53bbd86a..993a76be 100644 --- a/backend/app/aave/client.py +++ b/backend/app/aave/client.py @@ -1193,8 +1193,7 @@ def make_aave_client( # build_deposit_txs / build_withdraw_tx が "Unknown asset" で失敗する (method2 バグ修正)。 if token_addresses and not hasattr(client, "token_addresses"): client.token_addresses = { - sym: Web3.to_checksum_address(addr) - for sym, addr in token_addresses.items() + sym: Web3.to_checksum_address(addr) for sym, addr in token_addresses.items() } return client raise ValueError(f"不明な AAVE_CLIENT_TYPE: {client_type!r} (dummy | web3)") diff --git a/backend/app/api/v1/fees.py b/backend/app/api/v1/fees.py index b00d01aa..3683f38d 100644 --- a/backend/app/api/v1/fees.py +++ b/backend/app/api/v1/fees.py @@ -443,9 +443,7 @@ def finalize_month_core( # --- UAT ウォレット台帳 記録 (冪等: 同月再実行でも重複しない) --- fee_txs_this_month = ( db.execute( - select(FeeTransaction).where( - FeeTransaction.calculation_month == month_start - ) + select(FeeTransaction).where(FeeTransaction.calculation_month == month_start) ) .scalars() .all() diff --git a/backend/app/auth/router.py b/backend/app/auth/router.py index 333cf4f9..7da00d74 100644 --- a/backend/app/auth/router.py +++ b/backend/app/auth/router.py @@ -23,6 +23,7 @@ from sqlalchemy.orm import Session from app.database import get_db +from app.referral import service as referral_service from .dependencies import require_active_user from .models import ( @@ -54,7 +55,6 @@ WalletLinkResponse, ) from .service import AuthService -from app.referral import service as referral_service limiter = Limiter(key_func=get_remote_address) diff --git a/backend/app/automation/ai_judgment_scheduler.py b/backend/app/automation/ai_judgment_scheduler.py index 32a117c7..9dd11fb3 100644 --- a/backend/app/automation/ai_judgment_scheduler.py +++ b/backend/app/automation/ai_judgment_scheduler.py @@ -20,13 +20,13 @@ from sqlalchemy import func, select from sqlalchemy.orm import Session +from app.aave.gas_estimator import estimate_static_gas_cost_usd from app.ai.judgment_log import get_judgment_logger from app.ai.models import AIDecision, AiDecisionFeature from app.ai.schemas import CrossValidationResult, RAGContext, TradeAction from app.ai.service import AIService from app.auth.constants import ExecutionPolicy from app.auth.models import InvestmentTier, User, normalize_tier -from app.aave.gas_estimator import estimate_static_gas_cost_usd from app.automation.aave_data_fetcher import AaveMarketData, fetch_aave_market_data_safe from app.data_feeds.context import MarketContext, build_market_context from app.database import SessionLocal diff --git a/backend/app/referral/service.py b/backend/app/referral/service.py index 303df74b..52562774 100644 --- a/backend/app/referral/service.py +++ b/backend/app/referral/service.py @@ -184,7 +184,7 @@ def _add_months(d: date, months: int) -> date: return date(year, month, 1) -def get_referral_earnings(db: Session, partner_id: int) -> dict: +def get_referral_earnings(db: Session, partner_id: int) -> dict[str, str | int | None]: """紹介キャンペーン収益サマリーを返す。 - referral_count: referrer_id == partner_id のユーザー数 diff --git a/backend/requirements.txt b/backend/requirements.txt index bddbe5ff..58f98869 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -10,6 +10,8 @@ pytest-asyncio>=0.23.0 # Web3 / Aave integration web3>=6.15.1 +# aiohttp は web3/ccxt 経由 transitive dep。3.14 で AsyncStreamReaderMixin 削除 (vcrpy Issue #995)。 +aiohttp<3.14 eth-account>=0.11.0 python-dotenv>=1.0.0 diff --git a/backend/tests/test_cryptact_csv.py b/backend/tests/test_cryptact_csv.py index d5bd5ea6..064ccae7 100644 --- a/backend/tests/test_cryptact_csv.py +++ b/backend/tests/test_cryptact_csv.py @@ -137,7 +137,15 @@ def test_csv_header_row(self, client_with_proposals: TestClient) -> None: content = r.content.decode("utf-8-sig") reader = csv.DictReader(io.StringIO(content)) assert reader.fieldnames == [ - "Timestamp", "Action", "Source", "Base", "Volume", "Price", "Counter", "Fee", "FeeCcy" + "Timestamp", + "Action", + "Source", + "Base", + "Volume", + "Price", + "Counter", + "Fee", + "FeeCcy", ] def test_only_executed_proposals_included(self, client_with_proposals: TestClient) -> None: @@ -161,7 +169,9 @@ def test_supply_maps_to_lending(self, client_with_proposals: TestClient) -> None content = r.content.decode("utf-8-sig") reader = csv.DictReader(io.StringIO(content)) rows = list(reader) - supply_row = next(row for row in rows if row["Base"] == "USDC" and row["Action"] == "LENDING") + supply_row = next( + row for row in rows if row["Base"] == "USDC" and row["Action"] == "LENDING" + ) assert supply_row["Action"] == "LENDING" assert supply_row["Source"] == "AAVE_V3" assert supply_row["Counter"] == "USD" diff --git a/backend/tests/test_fee_transfer_service.py b/backend/tests/test_fee_transfer_service.py index 06d04004..4a0ae773 100644 --- a/backend/tests/test_fee_transfer_service.py +++ b/backend/tests/test_fee_transfer_service.py @@ -378,7 +378,8 @@ def test_fee_usd_calculation(fee_jpy, sub_jpy, excess_jpy, rate, expected_usd): # Lane R: transfer_service / AllowanceService (EIP-2612 新インタフェース) # =========================================================================== -from app.fees.transfer_service import FeeTransferResult, FeeTransferService as NewFeeTransferService # noqa: E402 +from app.fees.transfer_service import FeeTransferResult # noqa: E402 +from app.fees.transfer_service import FeeTransferService as NewFeeTransferService # noqa: E402 class TestFeeTransferServiceDisabled: diff --git a/backend/tests/test_gas_estimator.py b/backend/tests/test_gas_estimator.py index d25d147f..0a265ba8 100644 --- a/backend/tests/test_gas_estimator.py +++ b/backend/tests/test_gas_estimator.py @@ -128,7 +128,12 @@ def test_supply_uses_supply_fallback_gas(self) -> None: "SUPPLY", eth_usd_price=eth_price, gas_price_wei=Decimal("20000000000") ) # 300000 * 20 Gwei = 0.006 ETH * $2000 = $12.00 - expected = Decimal(str(DEFAULT_FALLBACK_GAS_SUPPLY)) * Decimal("20000000000") / Decimal("1e18") * eth_price + expected = ( + Decimal(str(DEFAULT_FALLBACK_GAS_SUPPLY)) + * Decimal("20000000000") + / Decimal("1e18") + * eth_price + ) assert cost == expected def test_withdraw_uses_withdraw_fallback_gas(self) -> None: @@ -137,7 +142,12 @@ def test_withdraw_uses_withdraw_fallback_gas(self) -> None: cost = estimate_static_gas_cost_usd( "WITHDRAW", eth_usd_price=eth_price, gas_price_wei=Decimal("20000000000") ) - expected = Decimal(str(DEFAULT_FALLBACK_GAS_WITHDRAW)) * Decimal("20000000000") / Decimal("1e18") * eth_price + expected = ( + Decimal(str(DEFAULT_FALLBACK_GAS_WITHDRAW)) + * Decimal("20000000000") + / Decimal("1e18") + * eth_price + ) assert cost == expected def test_uses_fallback_eth_price_when_none(self) -> None: diff --git a/backend/tests/test_line_messaging.py b/backend/tests/test_line_messaging.py index 7a05f51b..ce8a5503 100644 --- a/backend/tests/test_line_messaging.py +++ b/backend/tests/test_line_messaging.py @@ -13,7 +13,6 @@ build_monthly_report_flex_bubble, ) - # ── build_monthly_report_flex_bubble ────────────────────────────────────────── diff --git a/docs/integration/backend_deps.md b/docs/integration/backend_deps.md index b5d882a1..7d6b8c90 100644 --- a/docs/integration/backend_deps.md +++ b/docs/integration/backend_deps.md @@ -157,6 +157,14 @@ ## backend/requirements.txt +### 変更 #3: aiohttp<3.14 固定(vcrpy 非互換 / CI green 回復)(PR #523 / 2026-06-04) +- **コミット範囲**: fix/ruff-i001-lint-fix +- **変更内容**: `aiohttp<3.14` を requirements.txt に追加 +- **理由**: aiohttp 3.14.0 で `AsyncStreamReaderMixin` が削除され vcrpy<=8.1.1 が壊れる + (upstream Issue #995 / PR #996 未リリース)。web3/ccxt 経由の transitive runtime dep を pin。 +- **影響範囲**: aiohttp の upper cap のみ。web3/ccxt との両立確認済み。 +- **承認**: fix/ruff-i001-lint-fix → main (PR #523) + ### 変更 #2: PyJWT[crypto] extra 追加(Privy ID Token 検証対応)(PR #155 / 2026-04-27) - **コミット範囲**: `c88dfd2` – `41f77d7` (feature/privy-did-storage) - **変更内容**: `PyJWT>=2.9.0` → `PyJWT[crypto]>=2.9.0` diff --git a/pyproject.toml b/pyproject.toml index a5d59814..14a45de4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -77,6 +77,7 @@ module = [ "tiktoken.*", "jose.*", "feedparser.*", + "reportlab.*", ] ignore_missing_imports = true