Instalar en Claude Code
Copiargit clone --depth 1 https://github.com/PurpleAILAB/Vigilo /tmp/state-sync-analysis && cp -r /tmp/state-sync-analysis/packages/claude/skills/protocol-analysis/state-sync-analysis ~/.claude/skills/state-sync-analysisDespués abre una sesión nueva de Claude Code; el skill carga automáticamente.
Definición
SKILL.md
# State Sync Analysis Framework
## Purpose
**"When can state become inconsistent? How can that gap be exploited?"**
Identify state inconsistency windows that occur during external calls and analyze attack vectors.
---
## 1. State Synchronization Dependencies
### 1.1 Same-Contract Dependencies
```solidity
// Dependencies between variables
uint256 totalSupply; // A
uint256 totalAssets; // B
// Invariant: B >= A * minRatio
// Risk: Inconsistency if A, B update order differs
```
### 1.2 Cross-Contract Dependencies
```solidity
// Contract A depends on Contract B state
function getCollateralValue() external view returns (uint256) {
return token.balanceOf(address(this)) * oracle.getPrice();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
// Contract A state Contract B state
}
```
### 1.3 View Function Dependencies
```solidity
// External protocol depends on our view function
function totalAssets() public view returns (uint256) {
return s_depositedAssets + s_assetsInAMM;
}
// Other protocol: ourVault.totalAssets() to calculate collateral value
```
---
## 2. State Inconsistency Windows
### 2.1 State Timeline During External Call
```
Time T0: Initial state
├── s_depositedAssets = 1000
├── s_assetsInAMM = 500
└── totalAssets() = 1500
Time T1: withdraw() called → state updated
├── s_depositedAssets -= 100 ← Updated
└── s_assetsInAMM = 500
Time T2: External call (token.safeTransfer)
└── ⚠️ Callback possible!
Time T2.5: During callback (INCONSISTENCY WINDOW)
├── s_depositedAssets = 900 (updated)
├── Actual tokens: Not yet transferred (1000)
└── totalAssets() = 900 + 500 = 1400 ← WRONG!
(Actual: 1000 + 500 = 1500)
Time T3: Transfer complete
└── State consistency restored
```
### 2.2 Inconsistency Window Types
| Type | Description | Risk Level |
|------|-------------|------------|
| **Pre-Update** | External call before state update | Critical |
| **Partial Update** | External call after partial state update | High |
| **Post-Update** | External call after all state updates | Low |
### 2.3 CEI Pattern and Inconsistency
```solidity
// SAFE: Checks → Effects → Interactions
function withdraw(uint256 amount) external {
// Checks
require(balances[msg.sender] >= amount);
// Effects (state updates first)
balances[msg.sender] -= amount;
totalAssets -= amount;
// Interactions (external calls last)
token.safeTransfer(msg.sender, amount);
}
// DANGER: Checks → Interactions → Effects
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
token.safeTransfer(msg.sender, amount); // @audit State inconsistent!
balances[msg.sender] -= amount; // Too late!
}
```
---
## 3. Timing Attack Patterns
### 3.1 Read-Only Reentrancy
```solidity
// Contract A (victim)
function totalAssets() public view returns (uint256) {
return s_depositedAssets + s_assetsInAMM;
}
function withdraw(uint256 assets) external {
s_depositedAssets -= assets; // State updated
// External call - callback occurs
token.safeTransfer(msg.sender, assets);
// During callback: totalAssets() returns stale value!
}
// Contract B (exploited by attacker)
function liquidate(address user) external {
uint256 collateral = vaultA.totalAssets(); // @audit Manipulated!
// If called during callback, gets lower collateral than actual
}
```
### 3.2 Cross-Contract State Race
```solidity
// When two contracts depend on same state
// Contract A: uses oracle.getPrice()
// Contract B: uses oracle.getPrice()
// Attack:
// 1. Call A.function() → reads oracle → external call
// 2. In callback, manipulate oracle
// 3. Call B.function() → reads manipulated oracle
// 4. A.function() returns → already corrupted
```
### 3.3 Flash Loan Timing
```solidity
// State manipulation within single transaction
flashLoanProvider.flashLoan(amount)
→ onFlashLoan() callback
→ manipulate pool reserves
→ call vulnerable function (sees manipulated state)
→ restore reserves
→ repay flash loan
```
---
## 4. State Dependency Graph Template
```
┌──────────────────────────────────────────────────┐
│ State Dependency Graph │
├──────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ reads ┌─────────────┐ │
│ │ Vault │ ───────────────► │ Price Oracle │ │
│ │ .sol │ └─────────────┘ │
│ └────┬────┘ ▲ │
│ │ │ manipulate│
│ │ callback │ │
│ ▼ ┌─────┴─────┐ │
│ ┌─────────┐ reads │ Attacker │ │
│ │ Token │ ◄────────────── │ Contract │ │
│ │ .sol │ └───────────┘ │
│ └─────────┘ │
│ │
│ Risk Path: Vault → callback → Attacker → Oracle│
│ → Vault reads manipulated Oracle │
│ │
└──────────────────────────────────────────────────┘
```
---
## 5. Output Format
```markdown
## State Sync Analysis Results
### State Dependencies
| Contract | Function | Dependent State | External Source |
|----------|----------|-----------------|-----------------|
| Vault | getCollateralValue | totalAssets | Oracle.getPrice |
| Pool | swap | reserves | Token.balanceOf |
### Inconsistency Windows
| Location | Function | External Call | Inconsistent Variable | CEI Compliant |
|----------|----------|---------------|----------------------|---------------|
| Vault.sol:142 | withdraw | safeTransfer | s_deposited | ❌ |
| Pool.sol:89 | deposit | transferFrom | userShares | ⚠️ |
### Timing Attack Scenarios
#### Read-Only Reentrancy via totalAssets()
```
T0: Vault.withdraw(100) called
T1: s_depositedAssets = 1000 → 900
T2: token.safeTransfer() → c