# TronZap 통합 수정 지침서

> TronZap API 연동 전반의 버그 수정 + 비용 최적화

## 수정 목록

| # | 항목 | 파일 |
|---|------|------|
| 1 | waitForCompletion 상태값: `completed` → `success` | TronZapClient.ts |
| 2 | 에너지 상수: 65,000 → **100,000** | gas.ts |
| 3 | activate_address: true → **false** (비용 최적화) | ApprovalProcessor.ts |
| 4 | gas_tx_hash에 TronZap result.id 저장 | ApprovalProcessor.ts |
| 5 | createEnergyTransaction 요청 형식 (service+params) | TronZapClient.ts |
| 6 | CreateTransaction/CheckTransaction Result 인터페이스 | TronZapClient.ts |
| 7 | energyTx.total → energyTx.amount (호출 사이트) | 3개 파일 |

---

## 수정 1: waitForCompletion 상태값

**파일**: `packages/common/src/tronzap/TronZapClient.ts` — line ~295

TronZap 실제 응답: `status: "success"` (코드는 `"completed"` 대기 → 영원히 폴링 → 타임아웃)

```typescript
// BEFORE
if (result.status === 'completed') {

// AFTER
if (result.status === 'success' || result.status === 'completed') {
```

## 수정 2: 에너지 상수 100,000

**파일**: `packages/common/src/constants/gas.ts`

approve TX에 실제 필요한 에너지는 약 65,000이지만, 안전 마진 포함 **100,000** 사용.

```typescript
// BEFORE
export const TRON_APPROVE_ENERGY = 65_000;

// AFTER
export const TRON_APPROVE_ENERGY = 100_000;
```

> CollectionPoller/WithdrawalPoller의 TRON_TRANSFER_ENERGY도 동일하게 100,000 검토 필요.

## 수정 3: activate_address → false (비용 최적화)

**배경**:
- `activate_address: true` → TronZap이 계정 활성화 대행 → **1.4 TRX 추가 청구**
- 현재 코드는 이미 Step 2에서 **직접 0.1 TRX 전송**으로 활성화 중 → 이중 활성화/이중 비용!

**결론**: TronZap에서 활성화 불필요. 직접 0.1 TRX 전송이 훨씬 저렴.
에너지 위임은 미활성 주소에도 가능하므로, 순서상 문제 없음.

**파일**: `packages/wallet-activator/src/services/ApprovalProcessor.ts`

```typescript
// BEFORE
const energyTx = await tronZap.createEnergyTransaction({
  address: walletAddress.address,
  energy_amount: energyAmount,
  duration: 1,
  activate_address: true,           // ← 1.4 TRX 추가 비용
  external_id: `approval-${approval.id}`,
});

// AFTER
const energyTx = await tronZap.createEnergyTransaction({
  address: walletAddress.address,
  energy_amount: energyAmount,
  duration: 1,
  activate_address: false,          // ← 직접 0.1 TRX 전송으로 활성화 (Step 2)
  external_id: `approval-${approval.id}`,
});
```

**CollectionPoller.ts / WithdrawalPoller.ts**도 동일하게 확인:
- 이미 활성화된 지갑(HOT/MASTER)에 대한 집금/출금이므로 `activate_address: false` 유지.

## 수정 4: gas_tx_hash에 TronZap result.id 저장

**이유**: TRON은 EVM과 달리 "가스 전송" 대신 "에너지 임대"가 핵심.
wallet_approvals.gas_tx_hash에 TronZap 트랜잭션 ID를 저장하면 추적 가능.

**파일**: `packages/wallet-activator/src/services/ApprovalProcessor.ts`

processTronApproval() 내, 에너지 구매 직후:

```typescript
// BEFORE (gas_tx_hash 저장 없음 — TRX 활성화 전송 후에야 저장)
// ...에너지 구매 후 바로 cost 기록으로 넘어감...

// AFTER — 에너지 구매 직후 gas_tx_hash 저장
const energyTx = await tronZap.createEnergyTransaction({ ... });

// TronZap 트랜잭션 ID를 gas_tx_hash에 저장 (에너지 임대 추적)
await walletApprovalRepo.updateGasTxHash(approval.id, energyTx.id);
logger.info('TRON: TronZap energy transaction created', {
  approvalId: approval.id,
  tronZapTxId: energyTx.id,
  energyAmount,
  cost: energyTx.amount,
});
```

> 기존에는 Step 2의 activationTxHash를 gas_tx_hash에 저장하는 코드가 없었음.
> TronZap ID를 저장하면 TronZap 대시보드에서도 매칭 추적 가능.

## 수정 5: createEnergyTransaction 요청 구조

**파일**: `packages/common/src/tronzap/TronZapClient.ts`

`/v1/transaction/new` API는 `service` + `params` 중첩 구조 필수.

```typescript
// ── BEFORE (flat → 400 Bad Request) ──
return this.request<CreateTransactionResult>('/v1/transaction/new', {
  address: req.address,
  energy_amount: req.energy_amount,
  duration: req.duration,
  ...(req.activate_address !== undefined && { activate_address: req.activate_address }),
  ...(req.external_id && { external_id: req.external_id }),
});

// ── AFTER (service + params 중첩) ──
return this.request<CreateTransactionResult>('/v1/transaction/new', {
  service: 'energy',
  ...(req.external_id && { external_id: req.external_id }),
  params: {
    address: req.address,
    amount: req.energy_amount,
    duration: req.duration,
    ...(req.activate_address !== undefined && { activate_address: req.activate_address }),
  },
});
```

## 수정 6: 응답 인터페이스 업데이트

**파일**: `packages/common/src/tronzap/TronZapClient.ts`

### CreateTransactionResult
```typescript
// ── BEFORE ──
export interface CreateTransactionResult {
  id: string;
  status: string;
  address: string;
  energy_amount: number;
  duration: number;
  price: number;
  activation_fee: number;
  total: number;
}

// ── AFTER (실측 기반) ──
export interface CreateTransactionResult {
  id: string;              // TronZap 트랜잭션 ID
  status: string;          // 'new' | 'pending' | 'processing' | 'success' | 'failed'
  created_at: string;      // ISO 8601
  external_id?: string;
  service: string;         // 'energy'
  params: {
    address: string;
    amount: number;
    duration: number;
    activate_address?: boolean;
  };
  amount: number;          // 총 비용 (TRX 단위)
}
```

### CheckTransactionResult
```typescript
// ── AFTER ──
export interface CheckTransactionResult {
  id: string;
  status: string;          // 'new' | 'pending' | 'processing' | 'success' | 'failed'
  created_at: string;
  external_id?: string;
  service: string;
  params: {
    address: string;
    amount: number;
    duration: number;
    activate_address?: boolean;
  };
  amount: number;          // 총 비용 (TRX), check에서는 string "3.000000" 형태일 수 있음
  hash?: string;           // 완료 시 에너지 위임 TX hash
}
```

> ⚠️ `amount` 타입 주의: transaction/new 응답은 `number (4.4)`,
> transaction/check 응답은 `string ("3.000000")`.
> `trxToSun()` 호출 전 `Number()` 변환 권장:
> `const energyFeeNative = trxToSun(Number(energyTx.amount));`

## 수정 7: 호출 사이트 energyTx.total → energyTx.amount

### ApprovalProcessor.ts
```typescript
// BEFORE
const energyFeeNative = trxToSun(energyTx.total);
// AFTER
const energyFeeNative = trxToSun(Number(energyTx.amount));
```

### CollectionPoller.ts / WithdrawalPoller.ts — 동일 패턴
```typescript
const energyFeeNative = trxToSun(Number(energyTx.amount));
```

---

## 전체 수정 요약

| # | 파일 | 수정 |
|---|------|------|
| 1 | `common/src/tronzap/TronZapClient.ts` | waitForCompletion: `success` 추가 |
| 2 | `common/src/constants/gas.ts` | TRON_APPROVE_ENERGY: 65,000 → 100,000 |
| 3 | `wallet-activator/.../ApprovalProcessor.ts` | activate_address: false |
| 4 | `wallet-activator/.../ApprovalProcessor.ts` | updateGasTxHash(energyTx.id) 추가 |
| 5 | `common/src/tronzap/TronZapClient.ts` | createEnergyTransaction: service+params 구조 |
| 6 | `common/src/tronzap/TronZapClient.ts` | Result 인터페이스 실측 기반 업데이트 |
| 7 | 3개 파일 | energyTx.total → Number(energyTx.amount) |

## 비용 비교

| 항목 | BEFORE | AFTER |
|------|--------|-------|
| 에너지 임대 | 3 TRX (65k) | ~4.6 TRX (100k) |
| TronZap 활성화 | 1.4 TRX | 0 TRX |
| 직접 활성화 (0.1 TRX) | 0.1 TRX | 0.1 TRX |
| **합계** | **4.5 TRX** | **~4.7 TRX** |

> 에너지 100k로 올리면 단가가 약간 오르지만, TronZap 활성화비 1.4 TRX 절감으로 상쇄.
> 첫 번째 approve만 활성화 필요, 이후 같은 주소의 두 번째 approve는 활성화 스킵.

## 적용 순서

1. `common` 수정 (gas.ts + TronZapClient.ts) → `npm run build`
2. `wallet-activator` 수정 (ApprovalProcessor.ts)
3. `relayer-api` 수정 (CollectionPoller.ts, WithdrawalPoller.ts)
4. wallet_approvals id 5,6 → PENDING 리셋 (Cowork에서 처리)
5. wallet-activator 재기동
