DeFiFoundry
50,000 USDC
View results
Submission Details
Severity: low
Invalid

ADL Position Size Manipulation

[HIGH-3] ADL Position Size Manipulation

Location

GmxUtils.sol -> afterOrderExecution() function

Description

The afterOrderExecution function doesn't properly handle Automatic Deleveraging (ADL) events triggered by the GMX protocol. An attacker can exploit the ADL mechanics to manipulate position size and potentially profit from resulting discrepancies.

Impact

  • Manipulation of position sizes through ADL mechanics

  • Potential profit for attackers at the expense of other users

  • Incorrect position sizing and accounting

Proof of Concept

contract ADLManipulationTest is Test {
PerpetualVault public vault;
address attacker = address(0x1);
address victim = address(0x2);
function setUp() public {
vault = new PerpetualVault();
vm.deal(attacker, 100 ether);
vm.deal(victim, 100 ether);
}
function testADLManipulation() public {
// 1. Victim creates large position
vm.prank(victim);
bytes32 victimPosition = vault.createPosition({
size: 50 ether,
collateral: 10 ether,
isLong: true
});
// 2. Attacker creates small position
vm.prank(attacker);
bytes32 attackerPosition = vault.createPosition({
size: 5 ether,
collateral: 1 ether,
isLong: true
});
// 3. Simulate ADL event
vm.mockCall(
address(gmx),
abi.encodeWithSelector(IGmx.getADLState.selector),
abi.encode(true, 50) // ADL active, 50% reduction
);
// 4. Attacker triggers ADL
vm.prank(attacker);
vault.triggerADL(victimPosition);
// 5. Verify position size manipulation
Position memory victimPos = vault.getPosition(victimPosition);
assertEq(victimPos.size, 25 ether); // Reduced by 50%
assertEq(victimPos.collateral, 5 ether); // Collateral also reduced
// 6. Attacker position remains unchanged
Position memory attackerPos = vault.getPosition(attackerPosition);
assertEq(attackerPos.size, 5 ether);
}
}

Recommendation

  1. Implement proper ADL handling:

contract PerpetualVault {
struct ADLState {
bool isActive;
uint256 threshold;
uint256 maxReduction;
mapping(address => uint256) lastADLTime;
}
ADLState public adlState;
uint256 public constant ADL_COOLDOWN = 1 hours;
function handleADL(bytes32 positionId) external {
require(adlState.isActive, "ADL not active");
require(
block.timestamp >= adlState.lastADLTime[msg.sender] + ADL_COOLDOWN,
"ADL cooldown active"
);
Position storage position = positions[positionId];
uint256 reduction = _calculateADLReduction(position);
// Apply proportional reduction to size and collateral
position.size = position.size * (100 - reduction) / 100;
position.collateral = position.collateral * (100 - reduction) / 100;
adlState.lastADLTime[msg.sender] = block.timestamp;
emit ADLExecuted(positionId, reduction);
}
function _calculateADLReduction(Position memory position)
internal
view
returns (uint256)
{
// Implement fair reduction calculation
uint256 reduction = position.size > adlState.threshold ?
adlState.maxReduction :
adlState.maxReduction * position.size / adlState.threshold;
return Math.min(reduction, adlState.maxReduction);
}
}
  1. Add ADL cooldown periods

  2. Implement fair reduction calculations

  3. Add position size thresholds for ADL eligibility

  4. Implement comprehensive ADL monitoring and reporting

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality
Assigned finding tags:

Suppositions

There is no real proof, concrete root cause, specific impact, or enough details in those submissions. Examples include: "It could happen" without specifying when, "If this impossible case happens," "Unexpected behavior," etc. Make a Proof of Concept (PoC) using external functions and realistic parameters. Do not test only the internal function where you think you found something.

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.