Documentation Index
Fetch the complete documentation index at: https://docs.phylax.systems/llms.txt
Use this file to discover all available pages before exploring further.
This is a fairly simple bug that let anyone mint rewardTokens.
Background: https://x.com/storming0x/status/1473321779250802693
What Happened
- The deposit function lets anyone mint rewardTokens if
- the from address is a contract and has an owner and where msg.sender == owner
- It is intended that the from address sends the underlying token to this contract
- But it does not check whether delegatedTransferERC20 is implemented correctly
- it can be a noop
- The attacker can mint rewardTokens to himself
Assertion Pattern
While this is an obvious bug, an invariant could have prevented an exploit of the bug.
Invariant:
function assertionIsSolvent() public view {
require(visr.balanceOf(address(adopter)) >= vvisr.totalSupply(), "Reward tokens are undercollateralized");
}
Alternatively, the assertion could test the correct behavior of the previous transaction.
The assertion could be triggered by a call to the deposit function.
function assertion_triggerDeposit_remainsSolvent() public view {
// Get pre-transaction state
PhEvm.ForkId memory preFork = _preTx();
uint256 preBalanceCollateral = visr.balanceOf(address(adopter));
uint256 preTotalSupplyRewards = vvisr.totalSupply();
// Get post-transaction state
PhEvm.ForkId memory postFork = _postTx();
uint256 postBalanceCollateral = visr.balanceOf(address(adopter));
uint256 postTotalSupplyRewards = vvisr.totalSupply();
// Compare ratios using cross-multiplication to avoid precision loss
// preBalance / preSupply >= postBalance / postSupply
// => preBalance * postSupply >= postBalance * preSupply
require(
preBalanceCollateral * postTotalSupplyRewards >= postBalanceCollateral * preTotalSupplyRewards,
"Reward tokens are undercollateralized"
);
}