Summary
The exchange() function in the Transmuter contract allows users to exchange more tokens than the total available (totalUnexchanged), leading to potential unlimited minting of tokens and balance manipulation.
Vulnerability Details
In ITransmuter.sol, the exchange() function lacks proper balance checks:
function exchange(uint256 _amount) external;
function totalUnexchanged() external view returns (uint256);
function totalBuffered() external view returns (uint256);
PoC:
function testExchangeBalanceManipulation() public {
uint256 amount = 100e18;
vm.startPrank(attacker);
vm.mockCall(
address(transmuter),
abi.encodeWithSelector(ITransmuter.totalUnexchanged.selector),
abi.encode(amount)
);
vm.mockCall(
address(transmuter),
abi.encodeWithSelector(ITransmuter.totalBuffered.selector),
abi.encode(amount * 2)
);
transmuter.exchange(amount * 2);
assertEq(
transmuter.getExchangedBalance(attacker),
amount * 2,
"Exchange manipulation succeeded"
);
}
Impact
Unlimited token minting possible
Balance manipulation
Economic damage to the protocol
Potential drain of underlying assets
Hyperinflation of synthetic tokens
Tools Used
Recommendations
Add balance checks in exchange function:
function exchange(uint256 _amount) external {
require(_amount <= totalUnexchanged(), "Amount exceeds total unexchanged");
require(_amount <= getUnexchangedBalance(msg.sender), "Insufficient balance");
}
Implement exchange limits:
uint256 public constant MAX_EXCHANGE_RATIO = 50;
function exchange(uint256 _amount) external {
require(_amount <= totalUnexchanged() * MAX_EXCHANGE_RATIO / 100, "Exchange limit exceeded");
}
Add emergency pause mechanism:
bool public exchangePaused;
modifier whenExchangeNotPaused() {
require(!exchangePaused, "Exchange paused");
_;
}
function exchange(uint256 _amount) external whenExchangeNotPaused {
}