Documented lessons learned from debugging the floor position calculation bug, including missing tools and knowledge that would have accelerated the process. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
96 lines
No EOL
4.6 KiB
Markdown
96 lines
No EOL
4.6 KiB
Markdown
# Debugging Reflection: Floor Position Calculation Bug
|
|
|
|
## Summary
|
|
Fixed a critical bug where the floor position was placed at extreme ticks (141,200+) instead of bordering the anchor position due to incorrect token amount calculation in the discovery position logic.
|
|
|
|
## The Debugging Journey
|
|
|
|
### Initial Misunderstandings
|
|
1. **ETH Flow Direction**: Initially thought ETH flows OUT when traders buy KRAIKEN (wrong - it flows IN)
|
|
2. **Anchor Share Interpretation**: Misunderstood that 95% anchorShare reduces floor allocation to 90.1%
|
|
3. **Capital Inefficiency**: Didn't grasp that 0% means KRAIKEN is undervalued at 70% for reserves
|
|
|
|
### Key Discoveries
|
|
1. **VWAP Stores Price²**: Found that VWAP stores squared price, leading to incorrect ETH requirement calculations
|
|
2. **Operator Precedence Bug**: Fixed missing parentheses in midpoint calculation
|
|
3. **Final Root Cause**: Discovery position was calculating ETH amount instead of KRAIKEN amount
|
|
|
|
## Missing Knowledge & Tools
|
|
|
|
### 1. **Better Logging Infrastructure**
|
|
- **Issue**: Console.log wasn't working properly in Forge scripts
|
|
- **Impact**: Had to rely on events and CSV output instead of direct debugging
|
|
- **Ideal Solution**: Built-in structured logging that works in all contexts with:
|
|
- Decimal formatting for uint256 values
|
|
- Automatic unit conversion (wei to ether)
|
|
- Structured output for complex calculations
|
|
|
|
### 2. **Calculation Tracing Tools**
|
|
- **Issue**: Couldn't easily trace intermediate calculation values
|
|
- **Impact**: Had to create multiple test contracts to understand the math
|
|
- **Ideal Solution**: A calculation debugger that shows:
|
|
```
|
|
Outstanding Supply Calculation:
|
|
- Total Supply: 99,174,468 KRAIKEN
|
|
- Anchor KRAIKEN: 3,014,586 KRAIKEN ✓
|
|
- Discovery KRAIKEN: 0.00002 ETH ✗ (wrong token!)
|
|
- Final Outstanding: 96,159,882 KRAIKEN (should be 2,475,277)
|
|
```
|
|
|
|
### 3. **Token Flow Visualization**
|
|
- **Issue**: Hard to track which tokens go where during position minting
|
|
- **Impact**: Took longer to realize discovery amount was in wrong token
|
|
- **Ideal Solution**: Visual token flow diagram showing:
|
|
- Token types and amounts at each step
|
|
- Which calculations use ETH vs KRAIKEN
|
|
- Token destinations (pool vs liquidity manager)
|
|
|
|
### 4. **Unit Testing for Intermediate Values**
|
|
- **Issue**: Tests only validated final outcomes, not intermediate calculations
|
|
- **Impact**: Bug passed all tests despite wrong intermediate values
|
|
- **Ideal Solution**: Tests that assert on intermediate calculations:
|
|
```solidity
|
|
assertEq(discoveryAmount, expectedKraikenAmount, "Discovery should calculate KRAIKEN");
|
|
assertEq(outstandingAfterSubtraction, expectedOutstanding, "Outstanding should exclude positions");
|
|
```
|
|
|
|
### 5. **Type Safety for Token Amounts**
|
|
- **Issue**: Both ETH and KRAIKEN amounts are uint256, easy to mix up
|
|
- **Impact**: Wrong token amount was used without compile-time detection
|
|
- **Ideal Solution**: Typed token amounts:
|
|
```solidity
|
|
struct EthAmount { uint256 value; }
|
|
struct KraikenAmount { uint256 value; }
|
|
```
|
|
|
|
### 6. **Invariant Monitoring**
|
|
- **Issue**: No runtime checks for protocol invariants
|
|
- **Impact**: Extreme tick placement wasn't flagged as suspicious
|
|
- **Ideal Solution**: Runtime invariant checks:
|
|
- "Floor should be within X ticks of anchor in normal conditions"
|
|
- "Outstanding supply should approximate actual circulating tokens"
|
|
- "ETH scarcity shouldn't occur with net ETH inflows"
|
|
|
|
## Process Improvements
|
|
|
|
### What Worked Well
|
|
1. **CSV Output**: Positions CSV clearly showed the problem
|
|
2. **Event Logs**: EthScarcity events provided crucial calculation values
|
|
3. **User Feedback**: Your observation about token counts vs ETH was the key insight
|
|
|
|
### What Could Be Better
|
|
1. **Faster Iteration**: Need better tools to test hypotheses without full recompilation
|
|
2. **Root Cause Analysis**: Should have questioned "why 91M tokens?" earlier
|
|
3. **Token Type Awareness**: Need to always verify which token is being calculated
|
|
|
|
## Recommendations
|
|
|
|
1. **Add Calculation Assertions**: Add sanity checks in critical calculations
|
|
2. **Improve Test Coverage**: Test intermediate values, not just final results
|
|
3. **Document Token Types**: Clearly annotate when working with ETH vs KRAIKEN amounts
|
|
4. **Create Debug Mode**: Add a compile flag for detailed calculation logging
|
|
5. **Invariant Tests**: Add fuzzing tests that check protocol invariants hold
|
|
|
|
## Conclusion
|
|
|
|
The bug was subtle - using the correct Uniswap math function but for the wrong token. Better tooling for calculation visibility and type safety would have caught this much faster. The fix was simple once the root cause was identified, highlighting the importance of understanding exactly what each calculation represents. |