vault: do not allow flow when lock already expired

This commit is contained in:
Mark Spanbroek 2025-01-23 15:05:07 +01:00
parent 38f3a73c8f
commit cf875eb0d7
4 changed files with 37 additions and 19 deletions

View File

@ -3,19 +3,24 @@ pragma solidity 0.8.28;
type Timestamp is uint64; type Timestamp is uint64;
using {_notEquals as !=} for Timestamp global; using {_timestampEquals as ==} for Timestamp global;
using {_lessThan as <} for Timestamp global; using {_timestampNotEqual as !=} for Timestamp global;
using {_atMost as <=} for Timestamp global; using {_timestampLessThan as <} for Timestamp global;
using {_timestampAtMost as <=} for Timestamp global;
function _notEquals(Timestamp a, Timestamp b) pure returns (bool) { function _timestampEquals(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) == Timestamp.unwrap(b);
}
function _timestampNotEqual(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) != Timestamp.unwrap(b); return Timestamp.unwrap(a) != Timestamp.unwrap(b);
} }
function _lessThan(Timestamp a, Timestamp b) pure returns (bool) { function _timestampLessThan(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) < Timestamp.unwrap(b); return Timestamp.unwrap(a) < Timestamp.unwrap(b);
} }
function _atMost(Timestamp a, Timestamp b) pure returns (bool) { function _timestampAtMost(Timestamp a, Timestamp b) pure returns (bool) {
return Timestamp.unwrap(a) <= Timestamp.unwrap(b); return Timestamp.unwrap(a) <= Timestamp.unwrap(b);
} }

View File

@ -5,13 +5,18 @@ import "./Timestamps.sol";
type TokensPerSecond is int256; type TokensPerSecond is int256;
using {_negate as -} for TokensPerSecond global; using {_tokensPerSecondNegate as -} for TokensPerSecond global;
using {_equals as ==} for TokensPerSecond global; using {_tokensPerSecondEquals as ==} for TokensPerSecond global;
function _negate(TokensPerSecond rate) pure returns (TokensPerSecond) { function _tokensPerSecondNegate(
TokensPerSecond rate
) pure returns (TokensPerSecond) {
return TokensPerSecond.wrap(-TokensPerSecond.unwrap(rate)); return TokensPerSecond.wrap(-TokensPerSecond.unwrap(rate));
} }
function _equals(TokensPerSecond a, TokensPerSecond b) pure returns (bool) { function _tokensPerSecondEquals(
TokensPerSecond a,
TokensPerSecond b
) pure returns (bool) {
return TokensPerSecond.unwrap(a) == TokensPerSecond.unwrap(b); return TokensPerSecond.unwrap(a) == TokensPerSecond.unwrap(b);
} }

View File

@ -157,10 +157,8 @@ abstract contract VaultBase {
Timestamp expiry, Timestamp expiry,
Timestamp maximum Timestamp maximum
) internal { ) internal {
require( Lock memory existing = _getLock(controller, context);
Timestamp.unwrap(_getLock(controller, context).maximum) == 0, require(existing.maximum == Timestamp.wrap(0), AlreadyLocked());
AlreadyLocked()
);
require(expiry <= maximum, ExpiryPastMaximum()); require(expiry <= maximum, ExpiryPastMaximum());
_locks[controller][context] = Lock({expiry: expiry, maximum: maximum}); _locks[controller][context] = Lock({expiry: expiry, maximum: maximum});
} }
@ -170,10 +168,10 @@ abstract contract VaultBase {
Context context, Context context,
Timestamp expiry Timestamp expiry
) internal { ) internal {
Lock memory previous = _getLock(controller, context); Lock memory existing = _getLock(controller, context);
require(Timestamps.currentTime() < previous.expiry, LockExpired()); require(Timestamps.currentTime() < existing.expiry, LockExpired());
require(previous.expiry <= expiry, InvalidExpiry()); require(existing.expiry <= expiry, InvalidExpiry());
require(expiry <= previous.maximum, ExpiryPastMaximum()); require(expiry <= existing.maximum, ExpiryPastMaximum());
_locks[controller][context].expiry = expiry; _locks[controller][context].expiry = expiry;
} }
@ -185,8 +183,9 @@ abstract contract VaultBase {
TokensPerSecond rate TokensPerSecond rate
) internal { ) internal {
Lock memory lock = _getLock(controller, context); Lock memory lock = _getLock(controller, context);
require(lock.expiry != Timestamp.wrap(0), LockRequired());
Timestamp start = Timestamps.currentTime(); Timestamp start = Timestamps.currentTime();
require(lock.expiry != Timestamp.wrap(0), LockRequired());
require(start < lock.expiry, LockExpired());
uint64 duration = Timestamp.unwrap(lock.maximum) - Timestamp.unwrap(start); uint64 duration = Timestamp.unwrap(lock.maximum) - Timestamp.unwrap(start);
int256 total = int256(uint256(duration)) * TokensPerSecond.unwrap(rate); int256 total = int256(uint256(duration)) * TokensPerSecond.unwrap(rate);
Balance memory balance = _getBalance(controller, context, from); Balance memory balance = _getBalance(controller, context, from);

View File

@ -406,6 +406,15 @@ describe("Vault", function () {
) )
}) })
it("requires that the lock is not expired", async function () {
let expiry = (await currentTime()) + 20
await vault.lockup(context, expiry, expiry)
await advanceTimeTo(expiry)
await expect(vault.flow(context, sender, receiver, 2)).to.be.revertedWith(
"LockExpired"
)
})
describe("when a lock is set", async function () { describe("when a lock is set", async function () {
let expiry let expiry
let maximum let maximum