hadv / hadv/ethaura

[Phase 2] Balance Change Preview for Transaction Simulation

Open
#142 0 comments 0 reactions 0 assignees View on GitHub
⛨ security enhancement UI/UX
Dominant language
JavaScript
Stars
2
Forks
0
PR merge metrics
No merged PRs in 30d

Description

## Overview

Implement balance change tracking during transaction simulation to show users exactly what assets they will send and receive before signing.

## Parent Issue

#140 - Implement Transaction Simulation for Enhanced Wallet Security

## Dependencies

- Requires #141 (Phase 1: Basic On-Chain Simulation) to be completed first

## Background

Phase 1 provides the core simulation infrastructure. This phase adds state diff tracking to show users:
- Token balance changes (before → after)
- ETH balance changes
- Token approval changes

## Technical Approach

### Balance Diff Tracking

```javascript
// frontend/src/lib/transactionSimulator.js

/**
* Simulate and track balance changes
* @param {Object} userOp - UserOperation to simulate
* @param {string} accountAddress - Smart account address
* @param {Array} tokenAddresses - Tokens to track
* @returns {Promise}
*/
export async function simulateWithBalanceChanges(userOp, accountAddress, tokenAddresses, provider) {
// 1. Get balances BEFORE simulation
const balancesBefore = await getBalances(accountAddress, tokenAddresses, provider)

// 2. Run simulation with state override
const simulationResult = await simulateUserOperation(userOp, provider, entryPointAddress)

// 3. Get balances AFTER simulation (using trace or state diff)
const balancesAfter = await getBalancesAfterSimulation(
userOp,
accountAddress,
tokenAddresses,
provider
)

// 4. Calculate diffs
const balanceChanges = calculateBalanceDiffs(balancesBefore, balancesAfter)

return {
...simulationResult,
balanceChanges,
}
}

/**
* Get token balances for an address
*/
async function getBalances(address, tokenAddresses, provider) {
const balances = {}

// ETH balance
balances.ETH = await provider.getBalance(address)

// ERC20 balances
for (const token of tokenAddresses) {
const contract = new ethers.Contract(token, ERC20_ABI, provider)
balances[token] = await contract.balanceOf(address)
}

return balances
}

/**
* Calculate balance differences
*/
function calculateBalanceDiffs(before, after) {
const changes = []

for (const [token, balanceBefore] of Object.entries(before)) {
const balanceAfter = after[token] || 0n
const change = balanceAfter - balanceBefore

if (change !== 0n) {
changes.push({
token,
symbol: getTokenSymbol(token),
icon: getTokenIcon(token),
before: formatBalance(balanceBefore, token),
after: formatBalance(balanceAfter, token),
change: formatBalance(change, token),
isPositive: change > 0n,
})
}
}

return changes
}
```

### Approval Change Tracking

```javascript
/**
* Track approval changes during simulation
*/
export async function trackApprovalChanges(userOp, accountAddress, tokenAddresses, provider) {
const approvalChanges = []

// Parse calldata for approve() calls
const approveCalls = parseApproveCalls(userOp.callData)

for (const call of approveCalls) {
const currentAllowance = await getAllowance(
call.token,
accountAddress,
call.spender,
provider
)

approvalChanges.push({
token: call.token,
symbol: getTokenSymbol(call.token),
spender: call.spender,
spenderName: await getContractName(call.spender), // e.g., "Uniswap V3 Router"
currentAllowance: formatBalance(currentAllowance, call.token),
newAllowance: formatBalance(call.amount, call.token),
isUnlimited: call.amount === MAX_UINT256,
})
}

return approvalChanges
}
```

### Simulation Result Structure

```javascript
const simulationResult = {
// From Phase 1
success: true,
willSucceed: true,
gasUsed: 150000n,
revertReason: null,

// New in Phase 2
balanceChanges: [
{
token: 'ETH',
symbol: 'ETH',
icon: '/assets/eth.svg',
before: '1.5',
after: '1.4',
change: '-0.1',
isPositive: false,
},
{
token: '0xA0b8...', // USDC address
symbol: 'USDC',
icon: '/assets/usdc.svg',
before: '0',
after: '150.5',
change: '+150.5',
isPositive: true,
},
],
approvalChanges: [
{
token: '0xA0b8...',
symbol: 'USDC',
spender: '0x68b3...',
spenderName: 'Uniswap V3 Router',
currentAllowance: '0',
newAllowance: '1000',
isUnlimited: false,
},
],
}
```

## Tasks

### Core Implementation
- [ ] Implement `getBalances()` to fetch ETH + token balances
- [ ] Implement `getBalancesAfterSimulation()` using trace or state diff
- [ ] Implement `calculateBalanceDiffs()` to compute changes
- [ ] Implement `trackApprovalChanges()` to detect approval modifications
- [ ] Implement `parseApproveCalls()` to extract approve() from calldata
- [ ] Add token metadata fetching (symbol, decimals, icon)

### UI Components
- [ ] Create `BalanceChanges.jsx` component:
```
┌─────────────────────────────────────┐
│ 📊 Balance Changes │
├─────────────────────────────────────┤
│ [ETH icon] ETH │
│ 1.5 → 1.4 -0.1 (red) │
├─────────────────────────────────────┤
│ [USDC icon] USDC │
│ 0 → 150.5 +150.5 (green) │
└─────────────────────────────────────┘
```
- [ ] Create `ApprovalChanges.jsx` component:
```
┌─────────────────────────────────────┐
│ 🔓 Approval Changes │
├─────────────────────────────────────┤
│ USDC → Uniswap V3 Router │
│ Allowance: 0 → 1,000 USDC │
└─────────────────────────────────────┘
```
- [ ] Add color coding: green for gains, red for losses
- [ ] Add token icons next to balance changes
- [ ] Format large numbers with commas (1,000,000)

### Integration
- [ ] Update `SwapConfirmationScreen.jsx` to show balance changes
- [ ] Update `TransactionSender.jsx` to show balance changes
- [ ] Pass token addresses to simulation based on transaction type

## Files to Create

1. **`frontend/src/components/BalanceChanges.jsx`** - Balance diff display
2. **`frontend/src/components/ApprovalChanges.jsx`** - Approval changes display
3. **`frontend/src/styles/BalanceChanges.css`** - Styling

## Files to Modify

1. **`frontend/src/lib/transactionSimulator.js`** - Add balance tracking
2. **`frontend/src/screens/SwapConfirmationScreen.jsx`** - Display balance changes
3. **`frontend/src/components/TransactionSender.jsx`** - Display balance changes

## Acceptance Criteria

- [ ] ETH balance change is displayed (before → after)
- [ ] Token balance changes are displayed for relevant tokens
- [ ] Approval changes are tracked and displayed
- [ ] Gains are shown in green, losses in red
- [ ] Token icons are displayed next to balances
- [ ] Balance changes update when simulation is re-run
- [ ] Works for ETH transfers, token transfers, and swaps

## Testing

- [ ] Test ETH send transaction (shows ETH decrease)
- [ ] Test token swap (shows token A decrease, token B increase)
- [ ] Test token approval (shows approval change)
- [ ] Test with multiple token changes
- [ ] Test formatting for large/small amounts

## UI Mockup

```
┌─────────────────────────────────────────┐
│ Transaction Simulation │
├─────────────────────────────────────────┤
│ │
│ 📊 Balance Changes │
│ ┌─────────────────────────────────┐ │
│ │ [🔷] ETH │ │
│ │ 1.500000 → 1.400000 │ │
│ │ -0.1 ETH 🔴 │ │
│ ├─────────────────────────────────┤ │
│ │ [💵] USDC │ │
│ │ 0.00 → 150.50 │ │
│ │ +150.50 USDC 🟢 │ │
│ └─────────────────────────────────┘ │
│ │
│ 🔓 Approvals │
│ ┌─────────────────────────────────┐ │
│ │ USDC → Uniswap V3 Router │ │
│ │ 0 → 150.50 USDC │ │
│ └─────────────────────────────────┘ │
│ │
│ ✅ Simulation Successful │
│ Estimated Gas: 150,000 │
│ │
└─────────────────────────────────────────┘
```

## Estimated Effort

**Medium** - 2-3 days

## Labels

`security`, `enhancement`, `frontend`, `ux`

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.