DeFiFoundrySolidity
16,653 OP
View results
Submission Details
Severity: medium
Invalid

Transmuter Token Exchange Bypass in _freeFunds Function Forces Transaction Revert on All Withdrawals

Summary

The StrategyOp contract contains a critical vulnerability in its withdrawal mechanism that leaves user funds at risk of becoming locked in the contract. At the core of this issue lies a fundamental misunderstanding of the transmuter's token exchange protocol within the _freeFunds function.

The transmuter system operates on a two-phase withdrawal architecture where synthetic tokens must first undergo an exchange process before they can be withdrawn. However, the current implementation completely circumvents this crucial exchange step, instead attempting to withdraw tokens directly from their unexchanged state - an operation that will fail at the protocol level.

The severity of this vulnerability cannot be understated, as it effectively renders the entire withdrawal mechanism non-functional, creating a situation where user funds become inaccessible until the underlying issue is resolved.

Proof of Concept

Examining the current implementation reveals the architectural flaw:

https://github.com/Cyfrin/2024-12-alchemix/blob/main/src/StrategyOp.sol#L125

function _freeFunds(uint256 _amount) internal override {
uint256 totalAvailabe = transmuter.getUnexchangedBalance(address(this));
if (_amount > totalAvailabe) {
transmuter.withdraw(totalAvailabe, address(this));
} else {
transmuter.withdraw(_amount, address(this));
}
}

The transmuter interface exposes the exchange functionality that's being bypassed:

interface ITransmuter {
function exchange(uint256 _amount) external;
function getExchangedBalance(address _owner) external view returns (uint256);
function getUnexchangedBalance(address _owner) external view returns (uint256);
function withdraw(uint256 _amount, address _owner) external;
}

When a user initiates a withdrawal, their tokens exist in an unexchanged state within the transmuter. The current implementation attempts to withdraw these tokens directly, triggering a revert as the transmuter's protocol requires tokens to be exchanged before withdrawal. This creates a deadlock where funds remain trapped in their unexchanged state with no viable withdrawal path.

Recommended mitigation steps

The vulnerability requires a fundamental redesign of the _freeFunds function to properly integrate with the transmuter's exchange mechanism:

function _freeFunds(uint256 _amount) internal override {
uint256 unexchangedBalance = transmuter.getUnexchangedBalance(address(this));
uint256 exchangedBalance = transmuter.getExchangedBalance(address(this));
if (exchangedBalance >= _amount) {
transmuter.withdraw(_amount, address(this));
} else {
uint256 needToExchange = _amount - exchangedBalance;
if (needToExchange <= unexchangedBalance) {
transmuter.exchange(needToExchange);
transmuter.withdraw(_amount, address(this));
} else {
revert("Insufficient funds available");
}
}
}

This redesigned implementation aligns with the transmuter's protocol requirements by first assessing the available exchanged balance, performing any necessary token exchanges, and only then proceeding with withdrawals. The addition of proper balance validation prevents attempted withdrawals of unavailable funds, while maintaining protocol integrity.

Updates

Appeal created

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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