Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 22 additions & 15 deletions contracts/OutflowLimitedAMMSV.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ contract OutflowLimitedAMMSV is AccessManagedMSV {
* WARNING: changing the slotSize effectivelly resets the recorded outflows, so after this call (if slotSize
* changed), the delta will be zero.
*
* @param slotSize The duration in seconds of the timeframe used to limit the amount of outflows.
* @param slotSize The duration in seconds of the timeframe used to limit the amount of outflows. Setting slotSize
* to zero disables the outflow limit checks and the vault behaves like a normal AccessManagedMSV
* @param limit The max amount of outflows that will be allowed in a given time slot.
*/
function setupOutflowLimit(uint256 slotSize, uint256 limit) external {
Expand Down Expand Up @@ -132,26 +133,32 @@ contract OutflowLimitedAMMSV is AccessManagedMSV {
uint256 assets,
uint256 shares
) internal virtual override {
SlotIndex slot = _slotIndex();

// Check delta doesn't exceed the threshold
SlotIndex prevSlot = SlotIndex.wrap(SlotIndex.unwrap(slot) - 1);
LOMStorage storage $ = _getLOMStorage();
int256 deltaLastTwoSlots = -int256(assets) + $.assetsDelta[slot] + $.assetsDelta[prevSlot];
// To check the limit, uses TWO slots, the current one and the previous one. This is to avoid someone doing
// several operations in the slot limit, like withdrawal at 11:59PM and another withdrawal at 12:01 AM.
if (deltaLastTwoSlots < 0 && uint256(-deltaLastTwoSlots) > $.limit) revert LimitReached(deltaLastTwoSlots, $.limit);

// Update the delta and pass the message to parent contract
$.assetsDelta[slot] -= assets.toInt256();
if ($.slotSize != 0) {
SlotIndex slot = _slotIndex();

// Check delta doesn't exceed the threshold
SlotIndex prevSlot = SlotIndex.wrap(SlotIndex.unwrap(slot) - 1);
int256 deltaLastTwoSlots = -int256(assets) + $.assetsDelta[slot] + $.assetsDelta[prevSlot];
// To check the limit, uses TWO slots, the current one and the previous one. This is to avoid someone doing
// several operations in the slot limit, like withdrawal at 11:59PM and another withdrawal at 12:01 AM.
if (deltaLastTwoSlots < 0 && uint256(-deltaLastTwoSlots) > $.limit)
revert LimitReached(deltaLastTwoSlots, $.limit);

// Update the delta and pass the message to parent contract
$.assetsDelta[slot] -= assets.toInt256();
}
super._withdraw(caller, receiver, owner, assets, shares);
}

/// @inheritdoc ERC4626Upgradeable
function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual override {
// Just update the delta and pass the message to parent contract
SlotIndex slot = _slotIndex();
_getLOMStorage().assetsDelta[slot] += assets.toInt256();
LOMStorage storage $ = _getLOMStorage();
if ($.slotSize != 0) {
// Just update the delta and pass the message to parent contract
SlotIndex slot = _slotIndex();
$.assetsDelta[slot] += assets.toInt256();
}
super._deposit(caller, receiver, assets, shares);
}
}
12 changes: 10 additions & 2 deletions test/test-compound-v3-vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ const variants = [
tagit: tagit,
cToken: ADDRESSES.cUSDCv3,
supplyToken: ADDRESSES.USDC,
fixture: async () => {
fixture: async (accessManagedMSVClass = "AccessManagedMSV") => {
const { currency, adminAddr, swapConfig, admin, lp, lp2, guardian, anon, swapLibrary } = await setUp();
const CompoundV3InvestStrategy = await ethers.getContractFactory("CompoundV3InvestStrategy", {
libraries: {
Expand All @@ -344,7 +344,7 @@ const variants = [
});
const strategy = await CompoundV3InvestStrategy.deploy(ADDRESSES.cUSDCv3, ADDRESSES.REWARDS);
const AccessManager = await ethers.getContractFactory("AccessManager");
const AccessManagedMSV = await ethers.getContractFactory("AccessManagedMSV");
const AccessManagedMSV = await ethers.getContractFactory(accessManagedMSVClass);
const AccessManagedProxy = await ethers.getContractFactory("AccessManagedProxy");

const acMgr = await AccessManager.deploy(admin);
Expand Down Expand Up @@ -522,6 +522,14 @@ const variants = [
},
];

// Checks an OutflowLimitiedAMMSV without slotSize set behaves the same way as AccessManagedMSV
const compAccessVariant = variants.find((variant) => variant.name === "CompoundV3Strategy+AccessManaged");
variants.push({
...compAccessVariant,
name: "CompoundV3Strategy+OutflowLimitiedAMMSV",
fixture: async () => compAccessVariant.fixture("OutflowLimitedAMMSV"),
});

variants.forEach((variant) => {
describe(`${variant.name} contract tests`, function () {
before(async () => {
Expand Down
Loading