Assume that the "starting time" or contract deployment time is at midnight i.e 00:00 .
Suppose short gets updated at timestamp: 5:05 . Protocol will store this in rounded-down form as `updatedAt` = 5:00
Suppose the protocol allows a user to exit (or flag, or some action) this short after `X` hours. Let X = 2.
So the user should ideally be able to exit after 7:05.
However, when he tries, at 7:06, his `adjustedTimestamp` is calculated (rounded-down) as 7:00. The `timeDiff` = 7:00 - 5:00 = 2 hours.
This is less than equal to X (or `resetLiquidationTime` or some deadline) i.e `timeDiff <= X`, so he won't be allowed to perform the action.
The user will have to wait another 54 minutes till 8:00 so that timeDiff is 3 hours.
Users cannot re-flag a short between the 16th & 17th hour. Shorters gets an extra hour to correct their cRatio.
pragma solidity 0.8.21;
import {Constants} from "contracts/libraries/Constants.sol";
import {Errors} from "contracts/libraries/Errors.sol";
import {U256, U88} from "contracts/libraries/PRBMathHelper.sol";
import {OBFixture} from "test/utils/OBFixture.sol";
import {STypes, SR} from "contracts/libraries/DataTypes.sol";
contract IncorrectReFlag is OBFixture {
using U256 for uint256;
using U88 for uint88;
function _initialAssertStruct() public {
r.ethEscrowed = 0;
r.ercEscrowed = DEFAULT_AMOUNT;
assertStruct(receiver, r);
s.ethEscrowed = 0;
s.ercEscrowed = 0;
assertStruct(sender, s);
e.ethEscrowed = 0;
e.ercEscrowed = 0;
assertStruct(extra, e);
t.ethEscrowed = 0;
t.ercEscrowed = 0;
assertStruct(tapp, t);
}
function _prepareAsk(uint80 askPrice, uint88 askAmount) public {
fundLimitBidOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, receiver);
fundLimitShortOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, sender);
fundLimitAskOpt(askPrice, askAmount, receiver);
assertEq(getShortRecordCount(sender), 1);
assertEq(
getShortRecord(sender, Constants.SHORT_STARTING_ID).collateral,
DEFAULT_AMOUNT.mulU88(DEFAULT_PRICE) * 6
);
_initialAssertStruct();
}
function _checkFlaggerAndUpdatedAt(
address _shorter,
uint16 _flaggerId,
uint256 _updatedAt
) public {
STypes.ShortRecord memory shortRecord =
getShortRecord(_shorter, Constants.SHORT_STARTING_ID);
assertEq(shortRecord.flaggerId, _flaggerId, "flagger id mismatch");
assertEq(shortRecord.updatedAt, _updatedAt, "updatedAt mismatch");
}
function _flagShortAndSkipTime(uint256 timeToSkip, address flagger) public {
_prepareAsk({askPrice: DEFAULT_PRICE, askAmount: DEFAULT_AMOUNT});
_setETH(2666 ether);
_checkFlaggerAndUpdatedAt({_shorter: sender, _flaggerId: 0, _updatedAt: 0});
vm.prank(flagger);
diamond.flagShort(asset, sender, Constants.SHORT_STARTING_ID, Constants.HEAD);
uint256 flagged = diamond.getOffsetTimeHours();
skipTimeAndSetEth({skipTime: timeToSkip, ethPrice: 2666 ether});
_checkFlaggerAndUpdatedAt({_shorter: sender, _flaggerId: 1, _updatedAt: flagged});
assertSR(
getShortRecord(sender, Constants.SHORT_STARTING_ID).status, SR.FullyFilled
);
depositEth(tapp, DEFAULT_TAPP);
}
function test_CantflagShortBetween16To17Hours() public {
address _flagger_01 = makeAddr("_flagger_01");
address _flagger_02 = makeAddr("_flagger_02");
_flagShortAndSkipTime({timeToSkip: 16 hours + 30 minutes, flagger: _flagger_01});
vm.prank(_flagger_02);
vm.expectRevert(Errors.MarginCallAlreadyFlagged.selector);
diamond.flagShort(asset, sender, Constants.SHORT_STARTING_ID, Constants.HEAD);
rewind(16 hours + 30 minutes);
skip(17 hours);
vm.prank(_flagger_02);
diamond.flagShort(asset, sender, Constants.SHORT_STARTING_ID, Constants.HEAD);
}
}
Manual review, foundry.