Summary
The unregister
function in LLMOracleRegistry
uses the unsafe ERC20 approval pattern by directly setting a new allowance without first setting it to zero.
This can lead to front-running vulnerabilities with certain ERC20 tokens.
Vulnerability Details
The contract increases allowance without first resetting it to zero:
function unregister(LLMOracleKind kind) public returns (uint256 amount) {
amount = registrations[msg.sender][kind];
if (amount == 0) {
revert NotRegistered(msg.sender);
}
delete registrations[msg.sender][kind];
emit Unregistered(msg.sender, kind);
token.approve(msg.sender, token.allowance(address(this), msg.sender) + amount);
}
Impact
Double-spend of tokens possible
Excess withdrawals beyond intended amounts
Direct loss of funds for the protocol
Tools Used
Manual Review
Recommendations
Use SafeERC20's safeApprove/safeIncreaseAllowance:
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract LLMOracleRegistry is OwnableUpgradeable, UUPSUpgradeable {
using SafeERC20 for ERC20;
function unregister(LLMOracleKind kind) public returns (uint256 amount) {
amount = registrations[msg.sender][kind];
if (amount == 0) {
revert NotRegistered(msg.sender);
}
delete registrations[msg.sender][kind];
emit Unregistered(msg.sender, kind);
token.safeIncreaseAllowance(msg.sender, amount);
}
}
or Implement reset-and-approve pattern:
function unregister(LLMOracleKind kind) public returns (uint256 amount) {
amount = registrations[msg.sender][kind];
if (amount == 0) {
revert NotRegistered(msg.sender);
}
delete registrations[msg.sender][kind];
emit Unregistered(msg.sender, kind);
token.approve(msg.sender, 0);
token.approve(msg.sender, amount);
}