# V1 → V2 Migration Gap Analysis (v2)

**작성일**: 2026-04-01
**기준 DDL**: v2.0 (40개 테이블)
**기존 마이그레이션 스크립트**: `CRYPTOMENTS_V1_TO_V2_MIGRATION.sql` (DDL v1.3 기준)
**로컬 DB**: v1 (`coin_payments`) + v2 (`cryptoments_db`) — localhost:3306, root

---

## 사용자 배제 항목

| 항목 | 사유 |
|---|---|
| FEE / SYSTEM 지갑 | v2에서 별도 생성 (GAS/RELAYER) — v1 데이터 이관 불필요 |
| partner_telegram_chats | 신규 봇으로 이동 예정, 이관 배제 |
| collection_queue | 스킵 |
| currency_prices | 스킵 |
| payment_links | 스킵 |

---

## 1. 이관 대상 데이터 현황

### V1 (coin_payments) — 이관 대상만

| 영역 | V1 테이블 | 건수 | V2 테이블 |
|---|---|---|---|
| **파트너 계정** | partners | 25 | partners |
| **환율 정책** | partner_settings | 25 | partner_exchange_rate_policies |
| **체인 활성화** | partner_chain_activations | 54 | partner_chain_configs |
| **Axim 설정** | partner_axim_settings | 11 | partner_axim_settings |
| **Axim 연결** | axim_wallet_connections | 88 | external_wallets |
| **외부 지갑 매핑** | external_wallet_mappings | 261 | (참고 — external_wallets에 합산) |
| **HD 지갑** | hd_wallets | 4 | hd_wallets |
| **지갑 (HOT+MASTER만)** | wallet_addresses | **456** | wallet_addresses |
| **지갑 키 (HOT+MASTER)** | wallet_private_keys | 456 | wallet_keys |
| **지갑 잔액 (HOT+MASTER)** | wallet_balances | 1,184 | wallet_balances |
| **지갑 인덱스 (HOT+MASTER)** | wallet_index_counters | 40 | wallet_index_manager |
| **Nonce (HOT+MASTER)** | nonce_manager | 10 | nonce_tracker |
| **관리자** | admin | 2 | admins |
| **트랜잭션** | transactions | 959 (GAS 제외) | deposits + withdrawals |
| **Axim 결제** | axim_payments | 172 | axim_payments |
| **콜백 로그** | callback_delivery_logs | 682 | webhook_delivery_logs |
| **Gasless approve** | gasless_prerequisites | 2 | wallet_approvals |
| **입금 예약** | deposit_reservations | 32 | deposit_reservations |

### V2 기존 데이터 현황 (이전 마이그레이션 흔적)

| V2 테이블 | 현재 건수 | 비고 |
|---|---|---|
| partners | 25 | ID 일치, 이미 이관됨 |
| partner_chain_configs | 44 | v1(54) 대비 10건 누락 |
| partner_axim_settings | 11 | 일치 |
| partner_exchange_rate_policies | 25 | 일치 |
| external_wallets | 88 | axim_wallet_connections 이관됨 |
| hd_wallets | 3 | v1(4) — ETH HD 미이관 |
| wallet_addresses | 428 | HOT(384)+MASTER(44) — v1 HOT(402)+MASTER(54) 대비 28건 부족 |
| wallet_keys | 428 | wallet_addresses와 동수 |
| wallet_balances | 1,098 | v1(1,184) 대비 86건 부족 |
| nonce_tracker | 3 | v1(10) 대비 7건 부족 |
| deposits | 202 | v1 DEPOSIT(279) 대비 77건 부족 |
| withdrawals | 94 | v1 WITHDRAWAL(120) 대비 26건 부족 |
| wallet_approvals | 18 | v2에서 신규 생성된 데이터 |

---

## 2. 기존 스크립트 DDL 드리프트 (v1.3 → v2.0)

기존 스크립트(`CRYPTOMENTS_V1_TO_V2_MIGRATION.sql`)에서 DDL v2.0과 호환되지 않는 부분.

### 2-1. partners INSERT — `business_name`, `contact_email` 제거됨

```
스크립트 INSERT 컬럼         v2.0 상태        해결
────────────────────      ─────────      ────────
business_name             ❌ 제거됨       INSERT에서 제거
contact_email             ❌ 제거됨       INSERT에서 제거
```

추가로 v1 `partner_settings.deposit_callback_url`을 v2 `partners.webhook_url`에 매핑해야 함.
현재 스크립트에는 webhook_url 매핑이 없어서 일부 파트너의 콜백 URL이 누락됨.

### 2-2. wallet_index_manager INSERT — `wallet_type` 제거, `partner_id` 네임스페이스로 변경

```
스크립트 INSERT 컬럼         v2.0 상태        해결
────────────────────      ─────────      ────────
wallet_type               ❌ 제거됨       partner_id로 통합
```

v2.0 구조: `UNIQUE(hd_wallet_id, network_id, partner_id)`

v1 wallet_index_counters에서 `wallet_type IN (HOT, MASTER)`인 행(40건)만 이관.
같은 `(hd_wallet_id, chain_type, partner_id)` 조합의 HOT/MASTER를 하나로 병합하되, `last_index = MAX(current_index)` 적용.

### 2-3. collection_queue — ★ 스킵 (사용자 결정)

### 2-4. payment_links — ★ 스킵 (사용자 결정)

### 2-5. currency_prices — ★ 스킵 (사용자 결정)

---

## 3. 기존 스크립트에 누락된 이관 (신규 SQL 필요)

### 3-1. ★ external_wallets (88건) — Axim 연결 정보

**V1**: `axim_wallet_connections` — 체인별 개별 컬럼
**V2**: `external_wallets` — JSON `wallet_addresses`

```sql
INSERT INTO cryptoments_db.external_wallets (
    partner_id, partner_user_id, connection_type, connection_id,
    wallet_addresses, connect_wallet_token, site_id, status,
    connected_at, revoked_at, created_at, updated_at
)
SELECT
    awc.partner_id,
    awc.partner_user_id,
    'AXIM',
    awc.connect_id,
    JSON_OBJECT(
        'BSC', COALESCE(awc.bsc_wallet_address, ''),
        'POLYGON', COALESCE(awc.polygon_wallet_address, ''),
        'TRON', COALESCE(awc.tron_wallet_address, '')
    ),
    awc.connect_wallet_token,
    awc.site_id,
    CASE awc.status
        WHEN 'ACTIVE' THEN 'CONNECTED'
        WHEN 'REVOKED' THEN 'REVOKED'
        ELSE 'CONNECTED'
    END,
    awc.connected_at,
    awc.revoked_at,
    awc.created_at,
    awc.updated_at
FROM coin_payments.axim_wallet_connections awc;
```

> **주의**: v1 `external_wallet_mappings`(261건)은 `axim_wallet_connections`의 체인별 언롤링 데이터.
> v2에서는 JSON으로 통합되므로 `axim_wallet_connections`만 이관하면 됨.
> `external_wallet_mappings`는 별도 이관 불필요.

### 3-2. partners.webhook_url 매핑 (v1: partner_settings callback URLs)

v1에서는 `deposit_callback_url`과 `withdrawal_callback_url`이 분리되어 있고,
v2에서는 `partners.webhook_url` 하나로 통합됨.

```sql
UPDATE cryptoments_db.partners p
JOIN coin_payments.partner_settings ps ON ps.partner_id = p.id
SET p.webhook_url = ps.deposit_callback_url
WHERE ps.deposit_callback_url IS NOT NULL
  AND ps.deposit_callback_url != '';
```

> **참고**: v2는 단일 webhook_url에 모든 이벤트를 전달. deposit_callback_url을 기준으로 매핑.

### 3-3. wallet_approvals (2건 — gasless_prerequisites ERC20_APPROVE)

```sql
INSERT INTO cryptoments_db.wallet_approvals (
    wallet_address_id, currency_id, network_id, spender_address,
    approve_tx_hash, status, approved_at, created_at, updated_at
)
SELECT
    gp.wallet_address_id,
    c.id,
    bn.id,
    gp.spender_address,
    gp.tx_hash,
    CASE gp.status
        WHEN 'COMPLETED' THEN 'APPROVED'
        WHEN 'PENDING' THEN 'PENDING'
        WHEN 'IN_PROGRESS' THEN 'PENDING'
        WHEN 'FAILED' THEN 'FAILED'
        ELSE 'PENDING'
    END,
    gp.completed_at,
    gp.created_at,
    gp.updated_at
FROM coin_payments.gasless_prerequisites gp
JOIN coin_payments.wallet_addresses wa ON wa.id = gp.wallet_address_id
JOIN cryptoments_db.blockchain_networks bn ON bn.chain_symbol = gp.chain_type
JOIN cryptoments_db.currencies c
    ON c.symbol = gp.currency_type AND c.network_id = bn.id
WHERE gp.prerequisite_type = 'ERC20_APPROVE';
```

---

## 4. 이관 가능 — 기존 스크립트 그대로 사용

아래 항목은 DDL v2.0에서도 컬럼 변경 없이 기존 스크립트 SQL을 그대로 실행 가능.

| Phase | 대상 | V1 건수 | 기존 스크립트 SQL | 비고 |
|---|---|---|---|---|
| 1-1 | blockchain_networks | 4 | UPDATE | ✅ 그대로 |
| 1-2 | currencies | 7 | UPDATE × 2 | ✅ 그대로 |
| 1-3 | admins | 2 | INSERT | ✅ 그대로 |
| 2-2 | partner_chain_configs | 54 | INSERT | ✅ 그대로 |
| 2-3 | partner_axim_settings | 11 | INSERT | ✅ 그대로 |
| 2-4 | partner_exchange_rate_policies | 25 | INSERT | ✅ 그대로 |
| 3-1 | hd_wallets | 4 | INSERT | ✅ 그대로 |
| 3-3 | wallet_keys | 456 (HOT+MASTER) | INSERT | ✅ 그대로 (wallet_address_id FK로 자동 필터) |
| 3-5 | nonce_tracker | 10 (HOT+MASTER) | INSERT | ✅ 그대로 (address JOIN으로 자동 필터) |
| 4-1 | deposits | 560 (DEPOSIT+SETTLEMENT_IN+INNER_IN) | INSERT | ✅ 그대로 |
| 4-2 | withdrawals | 397 (WITHDRAWAL+SETTLEMENT_OUT+INNER_OUT) | INSERT | ✅ 그대로 |
| 5-3 | axim_payments | 172 | INSERT | ✅ 그대로 |
| 5-5 | webhook_delivery_logs | 682 | INSERT | ✅ 그대로 |

---

## 5. 이관 시 수정 필요

| Phase | 대상 | 수정 내용 | 난이도 |
|---|---|---|---|
| **2-1** | **partners** | `business_name`, `contact_email` 컬럼 제거 | 쉬움 |
| **3-2** | **wallet_addresses** | `WHERE wallet_type IN ('HOT','MASTER')` 필터 추가 (FEE/SYSTEM 배제) | 쉬움 |
| **3-4** | **wallet_balances** | HOT+MASTER 지갑만 이관 (JOIN 조건 추가) | 쉬움 |
| **3-6** | **wallet_index_manager** | `wallet_type` 제거 → `partner_id`로 그룹핑, HOT/MASTER 행 병합 | 중간 |
| **7** | **AUTO_INCREMENT 동기화** | 스킵 테이블(collection_queue, payment_links 등) 제거 | 쉬움 |

---

## 6. 최종 마이그레이션 실행 순서

### Phase A: DDL 클린 스타트

```sql
DROP DATABASE IF EXISTS cryptoments_db;
SOURCE CRYPTOMENTS_V2_DDL.sql;  -- 40개 테이블 생성 + 초기 데이터(networks, currencies)
```

### Phase B: 기반 마스터 (기존 스크립트, 수정 없음)

| 순서 | 대상 | SQL |
|---|---|---|
| B-1 | blockchain_networks | UPDATE (기존 Phase 1-1) |
| B-2 | currencies | UPDATE × 2 (기존 Phase 1-2) |
| B-3 | admins | INSERT (기존 Phase 1-3) |

### Phase C: 파트너 데이터 (일부 수정)

| 순서 | 대상 | SQL | 수정 |
|---|---|---|---|
| C-1 | **partners** | INSERT (기존 Phase 2-1) | ⚠️ `business_name`, `contact_email` 제거 |
| C-2 | partner_chain_configs | INSERT (기존 Phase 2-2) | 그대로 |
| C-3 | partner_axim_settings | INSERT (기존 Phase 2-3) | 그대로 |
| C-4 | partner_exchange_rate_policies | INSERT (기존 Phase 2-4) | 그대로 |
| C-5 | **partners.webhook_url** | UPDATE (★ 신규) | partner_settings.deposit_callback_url 매핑 |

### Phase D: 지갑 인프라 (HOT + MASTER만)

| 순서 | 대상 | SQL | 수정 |
|---|---|---|---|
| D-1 | hd_wallets | INSERT (기존 Phase 3-1) | 그대로 |
| D-2 | **wallet_addresses** | INSERT (기존 Phase 3-2) | ⚠️ `WHERE wallet_type IN ('HOT','MASTER')` 추가 |
| D-3 | wallet_keys | INSERT (기존 Phase 3-3) | 그대로 (FK로 자동 필터) |
| D-4 | **wallet_balances** | INSERT (기존 Phase 3-4) | ⚠️ HOT+MASTER JOIN 조건 추가 |
| D-5 | nonce_tracker | INSERT (기존 Phase 3-5) | 그대로 |
| D-6 | **wallet_index_manager** | INSERT (기존 Phase 3-6) | ⚠️ 구조 변경 — wallet_type 제거, partner_id 그룹핑 |

### Phase E: Axim 연결 (★ 신규)

| 순서 | 대상 | SQL |
|---|---|---|
| E-1 | **external_wallets** | INSERT (★ 신규 — §3-1 참조) |

### Phase F: 트랜잭션

| 순서 | 대상 | SQL |
|---|---|---|
| F-1 | deposits | INSERT (기존 Phase 4-1) |
| F-2 | withdrawals | INSERT (기존 Phase 4-2) |
| F-3 | axim_payments | INSERT (기존 Phase 5-3) |
| F-4 | webhook_delivery_logs | INSERT (기존 Phase 5-5) |

### Phase G: 보조 (★ 신규)

| 순서 | 대상 | SQL |
|---|---|---|
| G-1 | wallet_approvals | INSERT (★ 신규 — §3-3 참조, 2건) |

### Phase H: AUTO_INCREMENT 동기화 + 검증

기존 스크립트 Phase 7~8에서 스킵 테이블 제거:
- ~~collection_queue~~ (스킵)
- ~~payment_links~~ (스킵)

동기화 대상: partners, hd_wallets, wallet_addresses, wallet_keys, wallet_balances, deposits, withdrawals, admin_audit_logs, partner_exchange_rate_policies, external_wallets, axim_payments, webhook_delivery_logs

---

## 7. 스킵 테이블 종합

### 사용자 결정에 의한 스킵

| V1 테이블 | V2 테이블 | 사유 |
|---|---|---|
| wallet_addresses (FEE:54, SYSTEM:44) | — | v2에서 GAS/RELAYER로 별도 생성 |
| partner_telegram_chats (10) | partner_telegram_configs | 신규 봇으로 이동 |
| collection_requests (54) | collection_queue | 스킵 |
| currency_prices (11) | currency_prices | 스킵 |
| payments_links (54) | payment_links | 스킵 |

### 데이터 없음/무의미

| V1 테이블 | 건수 | 사유 |
|---|---|---|
| admin_audit_logs | 0 | 데이터 없음 |
| gas_fee_transactions | 0 | 데이터 없음 |
| transactions (GAS_FEE 등) | 33 | GAS 트랜잭션, v2에서 gas_cost_records로 분리 |

### V2 전용 (이관 대상 아님)

deposit_address_pool, collection_batches, relayer_contracts, gas_cost_records, gas_invoices,
partner_withdrawal_policies, settlement_balances, settlement_daily_fees, settlement_realizations,
transaction_status_history, ledger_entries, withdrawal_address_whitelist, currency_price_history,
system_settings

---

## 8. 주의사항

### 8-1. webhook_url 매핑

v1에서는 `partner_settings`에 `deposit_callback_url`과 `withdrawal_callback_url`이 분리.
v2에서는 `partners.webhook_url` 하나로 통합.
현재 v2 데이터 확인 결과, 일부 파트너(id=2,3,4,9,11,14,17,18,22,23)의 webhook_url이 NULL인데
v1에는 콜백 URL이 설정되어 있음. → Phase C-5에서 반드시 매핑.

### 8-2. ETH HD 지갑

v1에 ETH hd_wallet(id=1)이 있지만 현재 v2에는 3개(BSC, POLYGON, TRON)만 있음.
ETH를 사용하는지 여부 확인 필요. v1 ETH HOT 지갑은 20개 존재.

### 8-3. partner_settings API 키 중복

v1 `partner_settings`에도 `api_key`, `api_secret`이 있으나,
`partners` 테이블의 것과 동일한 값임을 확인함 (25건 전수 SAME).
→ v2 `partners.api_key/api_secret_hash`만으로 충분, partner_settings의 키는 무시.

### 8-4. partner_withdrawal_policies

v1 `partner_settings.withdrawal_policy`(AUTO:22, MANUAL_ABOVE_THRESHOLD:3)를
v2 `partner_withdrawal_policies`에 이관할 수 있으나, v2 전용 테이블이므로 스킵 대상.
운영 시 수동 설정 필요.

### 8-5. deposit_reservations (32건)

v1에 32건이 있으나 v2 테이블 구조가 다름. 별도 분석 필요.
긴급도가 낮으므로 1차 마이그레이션에서는 스킵하고 필요 시 추후 이관.
