Bug
When trading via the Zap contract, the `trade_history` table records the Zap contract address (`0xAe50C9444DA2Ac80B209dC8B416d1B4A7D3939B0`) as `user_address` instead of the actual user's wallet.
Example tx: https://basescan.org/tx/0x22a786af3c740d5edd26419c424a7684023732bde8a68b7cdfebb2a9e0d03324
Root Cause
Indexer reads the wrong field from the MCV2_Bond event.
The `Mint`/`Burn` events emit two address fields:
```solidity
event Mint(address indexed token, address indexed user, address receiver, uint256 amountMinted, address indexed reserveToken, uint256 reserveAmount);
event Burn(address indexed token, address indexed user, address receiver, uint256 amountBurned, address indexed reserveToken, uint256 refundAmount);
```
- `user` (indexed) = `msg.sender` — the caller (Zap contract for Zap trades)
- `receiver` (not indexed) = the actual wallet that receives/sends the tokens
The indexer currently reads `args.user`:
```typescript
// src/app/api/index/trade/route.ts:124
user_address: args.user.toLowerCase(),
// src/app/api/cron/trade-history/route.ts:218
user_address: args.user.toLowerCase(),
```
Should read `args.receiver` instead.
Fix
1. Fix the indexer — read `receiver` instead of `user`
In both trade indexing locations:
```typescript
user_address: args.receiver.toLowerCase(),
```
Files:
- `src/app/api/index/trade/route.ts` — line 124
- `src/app/api/cron/trade-history/route.ts` — line 218
2. Filter out intermediate Zap mints
When Zap does HUNT→PLOT conversion, it calls `BOND.mint(plotToken, ..., address(this))` — the `receiver` is the Zap contract itself (intermediate step). These should be excluded from trade history since they're internal transfers, not user trades.
Add filter: skip trades where `receiver` is the Zap contract address.
3. Backfill existing records
After deploying the fix, run a backfill to correct all existing `trade_history` rows where `user_address` is the Zap contract address. Re-decode the event logs from the original transactions and extract the `receiver` field.
```sql
-- Check how many records are affected
SELECT COUNT(*) FROM trade_history
WHERE user_address = '0xae50c9444da2ac80b209dc8b416d1b4a7d3939b0';
```
The backfill should:
- Query all `trade_history` rows where `user_address` = Zap contract
- For each, fetch the tx receipt from RPC
- Re-decode the Mint/Burn event and extract `receiver`
- Update the row with the correct `user_address`
- Delete rows where `receiver` = Zap contract (intermediate mints)
4. Also fix the backfill script
`src/app/api/backfill-user-address/route.ts:82` has the same bug — reads `args.user` instead of `args.receiver`.
Files to modify
- `src/app/api/index/trade/route.ts` — read `args.receiver`
- `src/app/api/cron/trade-history/route.ts` — read `args.receiver`
- `src/app/api/backfill-user-address/route.ts` — read `args.receiver`
- New: backfill migration/script to fix existing Zap trades
Branch
`task/613-fix-zap-trade-user`
Acceptance criteria
Bug
When trading via the Zap contract, the `trade_history` table records the Zap contract address (`0xAe50C9444DA2Ac80B209dC8B416d1B4A7D3939B0`) as `user_address` instead of the actual user's wallet.
Example tx: https://basescan.org/tx/0x22a786af3c740d5edd26419c424a7684023732bde8a68b7cdfebb2a9e0d03324
Root Cause
Indexer reads the wrong field from the MCV2_Bond event.
The `Mint`/`Burn` events emit two address fields:
```solidity
event Mint(address indexed token, address indexed user, address receiver, uint256 amountMinted, address indexed reserveToken, uint256 reserveAmount);
event Burn(address indexed token, address indexed user, address receiver, uint256 amountBurned, address indexed reserveToken, uint256 refundAmount);
```
The indexer currently reads `args.user`:
```typescript
// src/app/api/index/trade/route.ts:124
user_address: args.user.toLowerCase(),
// src/app/api/cron/trade-history/route.ts:218
user_address: args.user.toLowerCase(),
```
Should read `args.receiver` instead.
Fix
1. Fix the indexer — read `receiver` instead of `user`
In both trade indexing locations:
```typescript
user_address: args.receiver.toLowerCase(),
```
Files:
2. Filter out intermediate Zap mints
When Zap does HUNT→PLOT conversion, it calls `BOND.mint(plotToken, ..., address(this))` — the `receiver` is the Zap contract itself (intermediate step). These should be excluded from trade history since they're internal transfers, not user trades.
Add filter: skip trades where `receiver` is the Zap contract address.
3. Backfill existing records
After deploying the fix, run a backfill to correct all existing `trade_history` rows where `user_address` is the Zap contract address. Re-decode the event logs from the original transactions and extract the `receiver` field.
```sql
-- Check how many records are affected
SELECT COUNT(*) FROM trade_history
WHERE user_address = '0xae50c9444da2ac80b209dc8b416d1b4a7d3939b0';
```
The backfill should:
4. Also fix the backfill script
`src/app/api/backfill-user-address/route.ts:82` has the same bug — reads `args.user` instead of `args.receiver`.
Files to modify
Branch
`task/613-fix-zap-trade-user`
Acceptance criteria