From 9f34293957d7d237dcd409edadf7d4ec1971df63 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:32:49 +0100 Subject: [PATCH 01/59] chore: init SchedulerV3 contract & interface --- .../contracts/VestingSchedulerV3.sol | 654 ++++++++++++++++++ .../interface/IVestingSchedulerV3.sol | 378 ++++++++++ 2 files changed, 1032 insertions(+) create mode 100644 packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol create mode 100644 packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol new file mode 100644 index 0000000000..1db4e7e624 --- /dev/null +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -0,0 +1,654 @@ +// SPDX-License-Identifier: AGPLv3 +// solhint-disable not-rely-on-time +pragma solidity ^0.8.0; + +import { + ISuperfluid, + ISuperToken, + SuperAppDefinitions +} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; +import {SuperAppBase} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBase.sol"; +import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol"; +import {IVestingSchedulerV3} from "./interface/IVestingSchedulerV3.sol"; +import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { + using SuperTokenV1Library for ISuperToken; + + ISuperfluid public immutable HOST; + mapping(bytes32 => VestingSchedule) public vestingSchedules; // id = keccak(supertoken, sender, receiver) + + uint32 public constant MIN_VESTING_DURATION = 7 days; + uint32 public constant START_DATE_VALID_AFTER = 3 days; + uint32 public constant END_DATE_VALID_BEFORE = 1 days; + + struct ScheduleAggregate { + ISuperToken superToken; + address sender; + address receiver; + bytes32 id; + VestingSchedule schedule; + } + + constructor(ISuperfluid host) { + // Superfluid SuperApp registration. This is a dumb SuperApp, only for front-end tx batch calls. + uint256 configWord = SuperAppDefinitions.APP_LEVEL_FINAL | SuperAppDefinitions.BEFORE_AGREEMENT_CREATED_NOOP + | SuperAppDefinitions.AFTER_AGREEMENT_CREATED_NOOP | SuperAppDefinitions.BEFORE_AGREEMENT_UPDATED_NOOP + | SuperAppDefinitions.AFTER_AGREEMENT_UPDATED_NOOP | SuperAppDefinitions.BEFORE_AGREEMENT_TERMINATED_NOOP + | SuperAppDefinitions.AFTER_AGREEMENT_TERMINATED_NOOP; + host.registerApp(configWord); + HOST = host; + } + + /// @dev IVestingScheduler.createVestingSchedule implementation. + function createVestingSchedule( + ISuperToken superToken, + address receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint256 cliffAmount, + uint32 endDate, + uint32 claimValidityDate, + bytes memory ctx + ) external returns (bytes memory newCtx) { + newCtx = ctx; + address sender = _getSender(ctx); + + _validateAndCreateVestingSchedule( + ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: _normalizeStartDate(startDate), + claimValidityDate: claimValidityDate, + cliffDate: cliffDate, + flowRate: flowRate, + cliffAmount: cliffAmount, + endDate: endDate, + remainderAmount: 0 + }) + ); + } + + /// @dev IVestingScheduler.createVestingSchedule implementation. + function createVestingSchedule( + ISuperToken superToken, + address receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint256 cliffAmount, + uint32 endDate, + uint32 claimValidityDate + ) external { + _validateAndCreateVestingSchedule( + ScheduleCreationParams({ + superToken: superToken, + sender: msg.sender, + receiver: receiver, + startDate: _normalizeStartDate(startDate), + claimValidityDate: claimValidityDate, + cliffDate: cliffDate, + flowRate: flowRate, + cliffAmount: cliffAmount, + endDate: endDate, + remainderAmount: 0 + }) + ); + } + + /// @dev IVestingScheduler.createVestingSchedule implementation. + /// @dev Note: VestingScheduler (V1) compatible function + function createVestingSchedule( + ISuperToken superToken, + address receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint256 cliffAmount, + uint32 endDate, + bytes memory ctx + ) external returns (bytes memory newCtx) { + newCtx = ctx; + address sender = _getSender(ctx); + + _validateAndCreateVestingSchedule( + ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: _normalizeStartDate(startDate), + claimValidityDate: 0, + cliffDate: cliffDate, + flowRate: flowRate, + cliffAmount: cliffAmount, + endDate: endDate, + remainderAmount: 0 + }) + ); + } + + /// @dev IVestingScheduler.createVestingScheduleFromAmountAndDuration implementation. + /// @dev Note: creating from amount and duration is the preferred way + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + bytes memory ctx + ) external returns (bytes memory newCtx) { + newCtx = ctx; + address sender = _getSender(ctx); + + _validateAndCreateVestingSchedule( + mapCreateVestingScheduleParams( + superToken, + sender, + receiver, + totalAmount, + totalDuration, + _normalizeStartDate(startDate), + cliffPeriod, + claimPeriod + ) + ); + } + + /// @dev IVestingScheduler.createVestingScheduleFromAmountAndDuration implementation. + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod + ) external { + _validateAndCreateVestingSchedule( + mapCreateVestingScheduleParams( + superToken, + msg.sender, + receiver, + totalAmount, + totalDuration, + _normalizeStartDate(startDate), + cliffPeriod, + claimPeriod + ) + ); + } + + /// @dev IVestingScheduler.mapCreateVestingScheduleParams implementation. + function mapCreateVestingScheduleParams( + ISuperToken superToken, + address sender, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod + ) public pure override returns (ScheduleCreationParams memory params) { + uint32 claimValidityDate = claimPeriod != 0 ? startDate + claimPeriod : 0; + + uint32 endDate = startDate + totalDuration; + int96 flowRate = SafeCast.toInt96(SafeCast.toInt256(totalAmount / totalDuration)); + uint96 remainderAmount = SafeCast.toUint96(totalAmount - (SafeCast.toUint256(flowRate) * totalDuration)); + + if (cliffPeriod == 0) { + params = ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: startDate, + claimValidityDate: claimValidityDate, + cliffDate: 0, + flowRate: flowRate, + cliffAmount: 0, + endDate: endDate, + remainderAmount: remainderAmount + }); + } else { + uint256 cliffAmount = SafeMath.mul(cliffPeriod, SafeCast.toUint256(flowRate)); + params = ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: startDate, + claimValidityDate: claimValidityDate, + cliffDate: startDate + cliffPeriod, + flowRate: flowRate, + cliffAmount: cliffAmount, + endDate: endDate, + remainderAmount: remainderAmount + }); + } + } + + function _validateAndCreateVestingSchedule(ScheduleCreationParams memory params) private { + // Note: Vesting Scheduler V2 doesn't allow start date to be in the past. + // V1 did but didn't allow cliff and flow to be in the past though. + if (params.startDate < block.timestamp) revert TimeWindowInvalid(); + if (params.endDate <= END_DATE_VALID_BEFORE) revert TimeWindowInvalid(); + + if (params.receiver == address(0) || params.receiver == params.sender) revert AccountInvalid(); + if (address(params.superToken) == address(0)) revert ZeroAddress(); + if (params.flowRate <= 0) revert FlowRateInvalid(); + if (params.cliffDate != 0 && params.startDate > params.cliffDate) revert TimeWindowInvalid(); + if (params.cliffDate == 0 && params.cliffAmount != 0) revert CliffInvalid(); + + uint32 cliffAndFlowDate = params.cliffDate == 0 ? params.startDate : params.cliffDate; + // Note: Vesting Scheduler V2 allows cliff and flow to be in the schedule creation block, V1 didn't. + if ( + cliffAndFlowDate < block.timestamp || cliffAndFlowDate >= params.endDate + || cliffAndFlowDate + START_DATE_VALID_AFTER >= params.endDate - END_DATE_VALID_BEFORE + || params.endDate - cliffAndFlowDate < MIN_VESTING_DURATION + ) revert TimeWindowInvalid(); + + // Note : claimable schedule created with a claim validity date equal to 0 is considered regular schedule + if (params.claimValidityDate != 0 && params.claimValidityDate < cliffAndFlowDate) { + revert TimeWindowInvalid(); + } + + bytes32 id = _getId(address(params.superToken), params.sender, params.receiver); + if (vestingSchedules[id].endDate != 0) revert ScheduleAlreadyExists(); + + vestingSchedules[id] = VestingSchedule({ + cliffAndFlowDate: cliffAndFlowDate, + endDate: params.endDate, + flowRate: params.flowRate, + cliffAmount: params.cliffAmount, + remainderAmount: params.remainderAmount, + claimValidityDate: params.claimValidityDate + }); + + emit VestingScheduleCreated( + params.superToken, + params.sender, + params.receiver, + params.startDate, + params.cliffDate, + params.flowRate, + params.endDate, + params.cliffAmount, + params.claimValidityDate, + params.remainderAmount + ); + } + + /// @dev IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. + function createAndExecuteVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + bytes memory ctx + ) external returns (bytes memory newCtx) { + newCtx = _validateAndCreateAndExecuteVestingScheduleFromAmountAndDuration( + superToken, receiver, totalAmount, totalDuration, ctx + ); + } + + /// @dev IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. + function createAndExecuteVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration + ) external { + _validateAndCreateAndExecuteVestingScheduleFromAmountAndDuration( + superToken, receiver, totalAmount, totalDuration, bytes("") + ); + } + + /// @dev IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. + function _validateAndCreateAndExecuteVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + bytes memory ctx + ) private returns (bytes memory newCtx) { + newCtx = ctx; + address sender = _getSender(ctx); + + _validateAndCreateVestingSchedule( + mapCreateVestingScheduleParams( + superToken, + sender, + receiver, + totalAmount, + totalDuration, + _normalizeStartDate(0), + 0, // cliffPeriod + 0 // claimValidityDate + ) + ); + + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + + _validateBeforeCliffAndFlow(agg.schedule, /* disableClaimCheck: */ false); + assert(_executeCliffAndFlow(agg)); + } + + /// @dev IVestingScheduler.updateVestingSchedule implementation. + function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) + external + returns (bytes memory newCtx) + { + newCtx = ctx; + address sender = _getSender(ctx); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + if (endDate <= block.timestamp) revert TimeWindowInvalid(); + + // Note: Claimable schedules that have not been claimed cannot be updated + + // Only allow an update if 1. vesting exists 2. executeCliffAndFlow() has been called + if (schedule.cliffAndFlowDate != 0 || schedule.endDate == 0) revert ScheduleNotFlowing(); + + vestingSchedules[agg.id].endDate = endDate; + // Note: Nullify the remainder amount when complexity of updates is introduced. + vestingSchedules[agg.id].remainderAmount = 0; + + emit VestingScheduleUpdated( + superToken, + sender, + receiver, + schedule.endDate, + endDate, + 0 // remainderAmount + ); + } + + /// @dev IVestingScheduler.deleteVestingSchedule implementation. + function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) + external + returns (bytes memory newCtx) + { + newCtx = ctx; + address sender = _getSender(ctx); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + if (schedule.endDate != 0) { + delete vestingSchedules[agg.id]; + emit VestingScheduleDeleted(superToken, sender, receiver); + } else { + revert ScheduleDoesNotExist(); + } + } + + /// @dev IVestingScheduler.executeCliffAndFlow implementation. + function executeCliffAndFlow(ISuperToken superToken, address sender, address receiver) + external + returns (bool success) + { + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + if (schedule.claimValidityDate != 0) { + _validateAndClaim(agg); + _validateBeforeCliffAndFlow(schedule, /* disableClaimCheck: */ true); + if (block.timestamp >= _gteDateToExecuteEndVesting(schedule)) { + _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ true); + success = _executeVestingAsSingleTransfer(agg); + } else { + success = _executeCliffAndFlow(agg); + } + } else { + _validateBeforeCliffAndFlow(schedule, /* disableClaimCheck: */ false); + success = _executeCliffAndFlow(agg); + } + } + + function _validateBeforeCliffAndFlow(VestingSchedule memory schedule, bool disableClaimCheck) private view { + if (schedule.cliffAndFlowDate == 0) { + revert AlreadyExecuted(); + } + + if (!disableClaimCheck && schedule.claimValidityDate != 0) { + revert ScheduleNotClaimed(); + } + + // Ensure that that the claming date is after the cliff/flow date and before the claim validity date + if (schedule.cliffAndFlowDate > block.timestamp || _lteDateToExecuteCliffAndFlow(schedule) < block.timestamp) { + revert TimeWindowInvalid(); + } + } + + function _lteDateToExecuteCliffAndFlow(VestingSchedule memory schedule) private pure returns (uint32) { + if (schedule.cliffAndFlowDate == 0) { + revert AlreadyExecuted(); + } + + if (schedule.claimValidityDate != 0) { + return schedule.claimValidityDate; + } else { + return schedule.cliffAndFlowDate + START_DATE_VALID_AFTER; + } + } + + function _validateAndClaim(ScheduleAggregate memory agg) private { + VestingSchedule memory schedule = agg.schedule; + + // Ensure that the caller is the sender or the receiver if the vesting schedule requires claiming. + if (msg.sender != agg.sender && msg.sender != agg.receiver) { + revert CannotClaimScheduleOnBehalf(); + } + + if (schedule.claimValidityDate < block.timestamp) { + revert TimeWindowInvalid(); + } + + delete vestingSchedules[agg.id].claimValidityDate; + emit VestingClaimed(agg.superToken, agg.sender, agg.receiver, msg.sender); + } + + /// @dev IVestingScheduler.executeCliffAndFlow implementation. + function _executeCliffAndFlow(ScheduleAggregate memory agg) private returns (bool success) { + VestingSchedule memory schedule = agg.schedule; + + // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. + delete vestingSchedules[agg.id].cliffAndFlowDate; + delete vestingSchedules[agg.id].cliffAmount; + + // Compensate for the fact that flow will almost always be executed slightly later than scheduled. + uint256 flowDelayCompensation = (block.timestamp - schedule.cliffAndFlowDate) * uint96(schedule.flowRate); + + // If there's cliff or compensation then transfer that amount. + if (schedule.cliffAmount != 0 || flowDelayCompensation != 0) { + // Note: Super Tokens revert, not return false, i.e. we expect always true here. + assert(agg.superToken.transferFrom(agg.sender, agg.receiver, schedule.cliffAmount + flowDelayCompensation)); + } + // Create a flow according to the vesting schedule configuration. + agg.superToken.createFlowFrom(agg.sender, agg.receiver, schedule.flowRate); + emit VestingCliffAndFlowExecuted( + agg.superToken, + agg.sender, + agg.receiver, + schedule.cliffAndFlowDate, + schedule.flowRate, + schedule.cliffAmount, + flowDelayCompensation + ); + + return true; + } + + function _executeVestingAsSingleTransfer(ScheduleAggregate memory agg) private returns (bool success) { + VestingSchedule memory schedule = agg.schedule; + + delete vestingSchedules[agg.id]; + + uint256 totalVestedAmount = _getTotalVestedAmount(schedule); + + // Note: Super Tokens revert, not return false, i.e. we expect always true here. + assert(agg.superToken.transferFrom(agg.sender, agg.receiver, totalVestedAmount)); + + emit VestingCliffAndFlowExecuted( + agg.superToken, + agg.sender, + agg.receiver, + schedule.cliffAndFlowDate, + 0, // flow rate + schedule.cliffAmount, + totalVestedAmount - schedule.cliffAmount // flow delay compensation + ); + + emit VestingEndExecuted( + agg.superToken, + agg.sender, + agg.receiver, + schedule.endDate, + 0, // Early end compensation + false // Did end fail + ); + + return true; + } + + function _getTotalVestedAmount(VestingSchedule memory schedule) private pure returns (uint256) { + return schedule.cliffAmount + schedule.remainderAmount + + (schedule.endDate - schedule.cliffAndFlowDate) * SafeCast.toUint256(schedule.flowRate); + } + + /// @dev IVestingScheduler.executeEndVesting implementation. + function executeEndVesting(ISuperToken superToken, address sender, address receiver) + external + returns (bool success) + { + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ false); + + // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. + delete vestingSchedules[agg.id]; + + // If vesting is not running, we can't do anything, just emit failing event. + if (_isFlowOngoing(superToken, sender, receiver)) { + // delete first the stream and unlock deposit amount. + superToken.deleteFlowFrom(sender, receiver); + + uint256 earlyEndCompensation = schedule.endDate >= block.timestamp + ? (schedule.endDate - block.timestamp) * uint96(schedule.flowRate) + schedule.remainderAmount + : 0; + + // Note: we consider the compensation as failed if the stream is still ongoing after the end date. + bool didCompensationFail = schedule.endDate < block.timestamp; + if (earlyEndCompensation != 0) { + // Note: Super Tokens revert, not return false, i.e. we expect always true here. + assert(superToken.transferFrom(sender, receiver, earlyEndCompensation)); + } + + emit VestingEndExecuted( + superToken, sender, receiver, schedule.endDate, earlyEndCompensation, didCompensationFail + ); + } else { + emit VestingEndFailed(superToken, sender, receiver, schedule.endDate); + } + + return true; + } + + function _validateBeforeEndVesting(VestingSchedule memory schedule, bool disableClaimCheck) private view { + if (schedule.endDate == 0) { + revert AlreadyExecuted(); + } + + if (!disableClaimCheck && schedule.claimValidityDate != 0) { + revert ScheduleNotClaimed(); + } + + if (_gteDateToExecuteEndVesting(schedule) > block.timestamp) { + revert TimeWindowInvalid(); + } + } + + function _gteDateToExecuteEndVesting(VestingSchedule memory schedule) private pure returns (uint32) { + if (schedule.endDate == 0) { + revert AlreadyExecuted(); + } + + return schedule.endDate - END_DATE_VALID_BEFORE; + } + + /// @dev IVestingScheduler.getVestingSchedule implementation. + function getVestingSchedule(address superToken, address sender, address receiver) + external + view + returns (VestingSchedule memory) + { + return vestingSchedules[_getId(address(superToken), sender, receiver)]; + } + + function _getVestingScheduleAggregate(ISuperToken superToken, address sender, address receiver) + private + view + returns (ScheduleAggregate memory) + { + bytes32 id = _getId(address(superToken), sender, receiver); + return ScheduleAggregate({ + superToken: superToken, + sender: sender, + receiver: receiver, + id: id, + schedule: vestingSchedules[id] + }); + } + + function _normalizeStartDate(uint32 startDate) private view returns (uint32) { + // Default to current block timestamp if no start date is provided. + if (startDate == 0) { + return uint32(block.timestamp); + } + return startDate; + } + + /// @dev IVestingScheduler.getMaximumNeededTokenAllowance implementation. + function getMaximumNeededTokenAllowance(VestingSchedule memory schedule) external pure override returns (uint256) { + uint256 maxFlowDelayCompensationAmount = + schedule.cliffAndFlowDate == 0 ? 0 : START_DATE_VALID_AFTER * SafeCast.toUint256(schedule.flowRate); + uint256 maxEarlyEndCompensationAmount = + schedule.endDate == 0 ? 0 : END_DATE_VALID_BEFORE * SafeCast.toUint256(schedule.flowRate); + + if (schedule.claimValidityDate == 0) { + return schedule.cliffAmount + schedule.remainderAmount + maxFlowDelayCompensationAmount + + maxEarlyEndCompensationAmount; + } else if (schedule.claimValidityDate >= _gteDateToExecuteEndVesting(schedule)) { + return _getTotalVestedAmount(schedule); + } else { + return schedule.cliffAmount + schedule.remainderAmount + + (schedule.claimValidityDate - schedule.cliffAndFlowDate) * SafeCast.toUint256(schedule.flowRate) + + maxEarlyEndCompensationAmount; + } + } + + /// @dev get sender of transaction from Superfluid Context or transaction itself. + function _getSender(bytes memory ctx) private view returns (address sender) { + if (ctx.length != 0) { + if (msg.sender != address(HOST)) revert HostInvalid(); + sender = HOST.decodeCtx(ctx).msgSender; + } else { + sender = msg.sender; + } + // This is an invariant and should never happen. + assert(sender != address(0)); + } + + /// @dev get flowRate of stream + function _isFlowOngoing(ISuperToken superToken, address sender, address receiver) private view returns (bool) { + return superToken.getFlowRate(sender, receiver) != 0; + } + + function _getId(address superToken, address sender, address receiver) private pure returns (bytes32) { + return keccak256(abi.encodePacked(superToken, sender, receiver)); + } +} diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol new file mode 100644 index 0000000000..888bb6613f --- /dev/null +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: AGPLv3 +pragma solidity ^0.8.0; + +import {ISuperToken} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; + +interface IVestingSchedulerV3 { + error TimeWindowInvalid(); + error AccountInvalid(); + error ZeroAddress(); + error HostInvalid(); + error FlowRateInvalid(); + error CliffInvalid(); + error ScheduleAlreadyExists(); + error ScheduleDoesNotExist(); + error ScheduleNotFlowing(); + error CannotClaimScheduleOnBehalf(); + error AlreadyExecuted(); + error ScheduleNotClaimed(); + + /** + * @dev Vesting configuration provided by user. + * @param cliffAndFlowDate Date of flow start and cliff execution (if a cliff was specified) + * @param endDate End date of the vesting + * @param claimValidityDate Date before which the claimable schedule must be claimed + * @param flowRate For the stream + * @param cliffAmount Amount to be transferred at the cliff + * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" + */ + struct VestingSchedule { + uint32 cliffAndFlowDate; + uint32 endDate; + int96 flowRate; + uint256 cliffAmount; + uint96 remainderAmount; + uint32 claimValidityDate; + } + + /** + * @dev Parameters used to create vesting schedules + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param startDate Timestamp when the vesting should start + * @param claimValidityDate Date before which the claimable schedule must be claimed + * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff + * @param flowRate The flowRate for the stream + * @param cliffAmount The amount to be transferred at the cliff + * @param endDate The timestamp when the stream should stop. + * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" + * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + */ + struct ScheduleCreationParams { + ISuperToken superToken; + address sender; + address receiver; + uint32 startDate; + uint32 claimValidityDate; + uint32 cliffDate; + int96 flowRate; + uint256 cliffAmount; + uint32 endDate; + uint96 remainderAmount; + } + + /** + * @dev Event emitted on creation of a new vesting schedule + * @param superToken SuperToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param startDate Timestamp when the vesting starts + * @param claimValidityDate Date before which the claimable schedule must be claimed + * @param cliffDate Timestamp of the cliff + * @param flowRate The flowRate for the stream + * @param endDate The timestamp when the stream should stop + * @param cliffAmount The amount to be transferred at the cliff + * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" + */ + event VestingScheduleCreated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint32 endDate, + uint256 cliffAmount, + uint32 claimValidityDate, + uint96 remainderAmount + ); + + /** + * @dev Creates a new vesting schedule + * @dev If a non-zero cliffDate is set, the startDate has no effect other than being logged in an event. + * @dev If cliffDate is set to zero, the startDate becomes the cliff (transfer cliffAmount and start stream). + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param startDate Timestamp when the vesting should start + * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff + * @param flowRate The flowRate for the stream + * @param cliffAmount The amount to be transferred at the cliff + * @param endDate The timestamp when the stream should stop. + * @param claimValidityDate Date before which the claimable schedule must be claimed + * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + */ + function createVestingSchedule( + ISuperToken superToken, + address receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint256 cliffAmount, + uint32 endDate, + uint32 claimValidityDate, + bytes memory ctx + ) external returns (bytes memory newCtx); + + /** + * @dev See IVestingScheduler.createVestingSchedule overload for more details. + */ + function createVestingSchedule( + ISuperToken superToken, + address receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint256 cliffAmount, + uint32 endDate, + bytes memory ctx + ) external returns (bytes memory newCtx); + + /** + * @dev See IVestingScheduler.createVestingSchedule overload for more details. + */ + function createVestingSchedule( + ISuperToken superToken, + address receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint256 cliffAmount, + uint32 endDate, + uint32 claimValidityDate + ) external; + + /** + * @dev Creates a new vesting schedule + * @dev The function makes it more intuitive to create a vesting schedule compared to the original function. + * @dev The function calculates the endDate, cliffDate, cliffAmount, flowRate, etc, based on the input arguments. + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param totalAmount The total amount to be vested + * @param totalDuration The total duration of the vestingß + * @param startDate Timestamp when the vesting should start + * @param cliffPeriod The cliff period of the vesting + * @param claimPeriod The claim availability period + * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + */ + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + bytes memory ctx + ) external returns (bytes memory newCtx); + + /** + * @dev See IVestingScheduler.createVestingScheduleFromAmountAndDuration overload for more details. + */ + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod + ) external; + + /** + * @dev Returns all relevant information related to a new vesting schedule creation + * @dev based on the amounts and durations. + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param totalAmount The total amount to be vested + * @param totalDuration The total duration of the vestingß + * @param startDate Timestamp when the vesting should start + * @param cliffPeriod The cliff period of the vesting + * @param claimPeriod The claim availability period + */ + function mapCreateVestingScheduleParams( + ISuperToken superToken, + address sender, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod + ) external returns (ScheduleCreationParams memory params); + + /** + * @dev Creates a new vesting schedule + * @dev The function calculates the endDate, cliffDate, cliffAmount, flowRate, etc, based on the input arguments. + * @dev The function creates the vesting schedule with start date set to current timestamp, + * @dev and executes the start (i.e. creation of the flow) immediately. + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param totalAmount The total amount to be vested + * @param totalDuration The total duration of the vesting + * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + */ + function createAndExecuteVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + bytes memory ctx + ) external returns (bytes memory newCtx); + + /** + * @dev See IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. + */ + function createAndExecuteVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration + ) external; + + /** + * @dev Event emitted on update of a vesting schedule + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param oldEndDate Old timestamp when the stream should stop + * @param endDate New timestamp when the stream should stop + */ + event VestingScheduleUpdated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 oldEndDate, + uint32 endDate, + uint96 remainderAmount + ); + + /** + * @dev Updates the end date for a vesting schedule which already reached the cliff + * @notice When updating, there's no restriction to the end date other than not being in the past + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param endDate The timestamp when the stream should stop + * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + */ + function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) + external + returns (bytes memory newCtx); + + /** + * @dev Event emitted on deletion of a vesting schedule + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + */ + event VestingScheduleDeleted(ISuperToken indexed superToken, address indexed sender, address indexed receiver); + + /** + * @dev Event emitted on end of a vesting that failed because there was no running stream + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param endDate The timestamp when the stream should stop + */ + event VestingEndFailed( + ISuperToken indexed superToken, address indexed sender, address indexed receiver, uint32 endDate + ); + + /** + * @dev Deletes a vesting schedule + * @param superToken The superToken to be vested + * @param receiver Vesting receiver + * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + */ + function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) + external + returns (bytes memory newCtx); + + /** + * @dev Emitted when the cliff of a scheduled vesting is executed + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param cliffAndFlowDate The timestamp when the stream should start + * @param flowRate The flowRate for the stream + * @param cliffAmount The amount you would like to transfer at the startDate when you start streaming + * @param flowDelayCompensation Adjusted amount transferred to receiver. (elapse time from config and tx timestamp) + */ + event VestingCliffAndFlowExecuted( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 cliffAndFlowDate, + int96 flowRate, + uint256 cliffAmount, + uint256 flowDelayCompensation + ); + + /** + * @dev Emitted when a claimable vesting schedule is claimed + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param claimer Account that claimed the vesting (can only be sender or receiver) + */ + event VestingClaimed( + ISuperToken indexed superToken, address indexed sender, address indexed receiver, address claimer + ); + + /** + * @dev Executes a cliff (transfer and stream start) + * @notice Intended to be invoked by a backend service + * @param superToken SuperToken to be streamed + * @param sender Account who will be send the stream + * @param receiver Account who will be receiving the stream + */ + function executeCliffAndFlow(ISuperToken superToken, address sender, address receiver) + external + returns (bool success); + + /** + * @dev Emitted when the end of a scheduled vesting is executed + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param endDate The timestamp when the stream should stop + * @param earlyEndCompensation adjusted close amount transferred to receiver. + * @param didCompensationFail adjusted close amount transfer fail. + */ + event VestingEndExecuted( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 endDate, + uint256 earlyEndCompensation, + bool didCompensationFail + ); + + /** + * @dev Executes the end of a vesting (stop stream) + * @notice Intended to be invoked by a backend service + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + */ + function executeEndVesting(ISuperToken superToken, address sender, address receiver) + external + returns (bool success); + + /** + * @dev Gets data currently stored for a vesting schedule + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + */ + function getVestingSchedule(address superToken, address sender, address receiver) + external + view + returns (VestingSchedule memory); + + /** + * @dev Estimates the maximum possible ERC-20 token allowance needed for the vesting schedule + * @dev to work properly under all circumstances. + * @param vestingSchedule A vesting schedule (doesn't have to exist) + */ + function getMaximumNeededTokenAllowance(VestingSchedule memory vestingSchedule) external returns (uint256); +} From 3f35eb1beed0178d7c01f2798ba41584275229a3 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:37:20 +0100 Subject: [PATCH 02/59] feat: Enhance vesting schedule tracking and update mechanism --- .../contracts/VestingSchedulerV3.sol | 88 ++++++++++++++++++- .../interface/IVestingSchedulerV3.sol | 13 ++- 2 files changed, 93 insertions(+), 8 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 1db4e7e624..4057564fd5 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -255,6 +255,10 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { revert TimeWindowInvalid(); } + // Calculate the total amount to be vested over the entire schedule (includes cliff and streamed amount) + uint256 totalAmount = + params.cliffAmount + params.remainderAmount + (params.endDate - cliffAndFlowDate) * uint96(params.flowRate); + bytes32 id = _getId(address(params.superToken), params.sender, params.receiver); if (vestingSchedules[id].endDate != 0) revert ScheduleAlreadyExists(); @@ -264,7 +268,9 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { flowRate: params.flowRate, cliffAmount: params.cliffAmount, remainderAmount: params.remainderAmount, - claimValidityDate: params.claimValidityDate + claimValidityDate: params.claimValidityDate, + totalAmount: totalAmount, + alreadyVestedAmount: 0 }); emit VestingScheduleCreated( @@ -337,6 +343,65 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { } /// @dev IVestingScheduler.updateVestingSchedule implementation. + function updateVestingSchedule(ISuperToken superToken, address receiver, uint256 totalAmount, bytes memory ctx) + external + returns (bytes memory newCtx) + { + newCtx = ctx; + address sender = _getSender(ctx); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule storage schedule = vestingSchedules[agg.id]; + + // Only allow an update vesting exists + if (schedule.endDate == 0) revert ScheduleDoesNotExist(); + + // Dont allow update on schedule that should already have ended + if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); + + // Update the total amount to be vested over the entire schedule (includes cliff and streamed amount) + schedule.totalAmount = totalAmount; + + // Update the amount already vested and the flow rate if the schedule has already started + if (schedule.cliffAndFlowDate == 0) { + // Get the current flow rate and the timestamp of the last flow update + (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); + + // Calculate the amount vested since the last flow update + uint256 vestedSinceLastUpdate = (block.timestamp - lastUpdated) * uint96(currentFlowRate); + + // Accrue the amount already vested + schedule.alreadyVestedAmount += vestedSinceLastUpdate; + + // Ensure that the new total amount is not less than the amount already vested + if (schedule.alreadyVestedAmount >= totalAmount) revert InvalidUpdate(); + + // Calculate the new flow rate and remainder amount + schedule.flowRate = SafeCast.toInt96( + SafeCast.toInt256(totalAmount - schedule.alreadyVestedAmount) + / SafeCast.toInt256(schedule.endDate - block.timestamp) + ); + schedule.remainderAmount = + SafeCast.toUint96((totalAmount - schedule.alreadyVestedAmount) % (schedule.endDate - block.timestamp)); + + // Update the flow from sender to receiver with the new calculated flow rate + superToken.updateFlowFrom(sender, receiver, schedule.flowRate); + } else { + schedule.flowRate = SafeCast.toInt96( + SafeCast.toInt256(totalAmount - schedule.cliffAmount) + / SafeCast.toInt256(schedule.endDate - schedule.cliffAndFlowDate) + ); + schedule.remainderAmount = + SafeCast.toUint96((totalAmount - schedule.cliffAmount) % (schedule.endDate - schedule.cliffAndFlowDate)); + } + + /// FIXME : review events parameters + emit VestingScheduleUpdated( + superToken, sender, receiver, schedule.endDate, schedule.endDate, schedule.remainderAmount + ); + } + + /// @dev IVestingScheduler.updateVestingSchedule implementation. + /// FIXME : review this function function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) external returns (bytes memory newCtx) @@ -466,9 +531,13 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { if (schedule.cliffAmount != 0 || flowDelayCompensation != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. assert(agg.superToken.transferFrom(agg.sender, agg.receiver, schedule.cliffAmount + flowDelayCompensation)); + + vestingSchedules[agg.id].alreadyVestedAmount += schedule.cliffAmount + flowDelayCompensation; } + // Create a flow according to the vesting schedule configuration. agg.superToken.createFlowFrom(agg.sender, agg.receiver, schedule.flowRate); + emit VestingCliffAndFlowExecuted( agg.superToken, agg.sender, @@ -534,15 +603,26 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { // If vesting is not running, we can't do anything, just emit failing event. if (_isFlowOngoing(superToken, sender, receiver)) { + // Get the current flow rate and the timestamp of the last flow update + (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); + + // Calculate the amount vested since the last flow update + uint256 totalVestedAmount = + schedule.alreadyVestedAmount + (block.timestamp - lastUpdated) * uint96(currentFlowRate); + // delete first the stream and unlock deposit amount. superToken.deleteFlowFrom(sender, receiver); - uint256 earlyEndCompensation = schedule.endDate >= block.timestamp - ? (schedule.endDate - block.timestamp) * uint96(schedule.flowRate) + schedule.remainderAmount - : 0; + uint256 earlyEndCompensation = + totalVestedAmount < schedule.totalAmount ? schedule.totalAmount - totalVestedAmount : 0; + + // uint256 earlyEndCompensation = schedule.endDate >= block.timestamp + // ? (schedule.endDate - block.timestamp) * uint96(schedule.flowRate) + schedule.remainderAmount + // : 0; // Note: we consider the compensation as failed if the stream is still ongoing after the end date. bool didCompensationFail = schedule.endDate < block.timestamp; + if (earlyEndCompensation != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. assert(superToken.transferFrom(sender, receiver, earlyEndCompensation)); diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index 888bb6613f..3270a72447 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -16,15 +16,18 @@ interface IVestingSchedulerV3 { error CannotClaimScheduleOnBehalf(); error AlreadyExecuted(); error ScheduleNotClaimed(); + error InvalidUpdate(); /** * @dev Vesting configuration provided by user. * @param cliffAndFlowDate Date of flow start and cliff execution (if a cliff was specified) * @param endDate End date of the vesting - * @param claimValidityDate Date before which the claimable schedule must be claimed - * @param flowRate For the stream - * @param cliffAmount Amount to be transferred at the cliff - * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" + * @param flowRate Flow rate of the stream in tokens per second + * @param cliffAmount Amount to be transferred at the cliff date + * @param remainderAmount Amount to be transferred at the end to account for rounding errors + * @param claimValidityDate Date before which the claimable schedule must be claimed (0 if not claimable) + * @param totalAmount Total amount to be vested over the entire schedule (includes cliff and streamed amount) + * @param alreadyVestedAmount Amount that has already been vested */ struct VestingSchedule { uint32 cliffAndFlowDate; @@ -33,6 +36,8 @@ interface IVestingSchedulerV3 { uint256 cliffAmount; uint96 remainderAmount; uint32 claimValidityDate; + uint256 totalAmount; + uint256 alreadyVestedAmount; } /** From de3c99155b3e8634f02e9723147b5fe125873adf Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:17:43 +0100 Subject: [PATCH 03/59] refactor: Rename updateVestingSchedule method to updateVestingScheduleAmount --- .../scheduler/contracts/VestingSchedulerV3.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 4057564fd5..af89da504c 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -343,10 +343,12 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { } /// @dev IVestingScheduler.updateVestingSchedule implementation. - function updateVestingSchedule(ISuperToken superToken, address receiver, uint256 totalAmount, bytes memory ctx) - external - returns (bytes memory newCtx) - { + function updateVestingScheduleAmount( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + bytes memory ctx + ) external returns (bytes memory newCtx) { newCtx = ctx; address sender = _getSender(ctx); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); From bc09688ca63efaa830040f310cb119efa00136fb Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:18:01 +0100 Subject: [PATCH 04/59] test: draft comprehensive test suite for VestingSchedulerV3 --- .../scheduler/test/VestingSchedulerV3.t.sol | 2393 +++++++++++++++++ 1 file changed, 2393 insertions(+) create mode 100644 packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol new file mode 100644 index 0000000000..7222cc2082 --- /dev/null +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -0,0 +1,2393 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import {ISuperToken} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.sol"; +import { + FlowOperatorDefinitions, + ISuperfluid, + BatchOperation, + ISuperApp +} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; +import {IVestingSchedulerV3} from "./../contracts/interface/IVestingSchedulerV3.sol"; +import {VestingSchedulerV3} from "./../contracts/VestingSchedulerV3.sol"; +import {FoundrySuperfluidTester} from + "@superfluid-finance/ethereum-contracts/test/foundry/FoundrySuperfluidTester.t.sol"; +import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol"; +import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; +import "forge-std/console.sol"; + +/// @title VestingSchedulerTests +/// @notice Look at me , I am the captain now - Elvijs +contract VestingSchedulerV3Tests is FoundrySuperfluidTester { + using SuperTokenV1Library for ISuperToken; + + event VestingScheduleCreated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint32 endDate, + uint256 cliffAmount, + uint32 claimValidityDate, + uint96 remainderAmount + ); + + event VestingScheduleUpdated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 oldEndDate, + uint32 endDate, + uint96 remainderAmount + ); + + event VestingScheduleDeleted(ISuperToken indexed superToken, address indexed sender, address indexed receiver); + + event VestingCliffAndFlowExecuted( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 cliffAndFlowDate, + int96 flowRate, + uint256 cliffAmount, + uint256 flowDelayCompensation + ); + + event VestingEndExecuted( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 endDate, + uint256 earlyEndCompensation, + bool didCompensationFail + ); + + event VestingEndFailed( + ISuperToken indexed superToken, address indexed sender, address indexed receiver, uint32 endDate + ); + + event VestingClaimed( + ISuperToken indexed superToken, address indexed sender, address indexed receiver, address claimer + ); + + event Transfer(address indexed from, address indexed to, uint256 value); + + /// @dev This is required by solidity for using the SuperTokenV1Library in the tester + VestingSchedulerV3 public vestingScheduler; + + /// @dev Constants for Testing + uint256 immutable BLOCK_TIMESTAMP = 100; + uint32 immutable START_DATE = uint32(BLOCK_TIMESTAMP + 1); + uint32 immutable CLIFF_DATE = uint32(BLOCK_TIMESTAMP + 10 days); + int96 constant FLOW_RATE = 1000000000; + uint256 constant CLIFF_TRANSFER_AMOUNT = 1 ether; + uint32 immutable CLAIM_VALIDITY_DATE = uint32(BLOCK_TIMESTAMP + 15 days); + uint32 immutable END_DATE = uint32(BLOCK_TIMESTAMP + 20 days); + bytes constant EMPTY_CTX = ""; + bytes constant NON_EMPTY_CTX = abi.encode(alice); + uint256 internal _expectedTotalSupply = 0; + + constructor() FoundrySuperfluidTester(3) { + vestingScheduler = new VestingSchedulerV3(sf.host); + } + + /// SETUP AND HELPERS + function setUp() public virtual override { + super.setUp(); + vm.warp(BLOCK_TIMESTAMP); + } + + function _setACL_AUTHORIZE_FULL_CONTROL(address user, int96 flowRate) private { + vm.startPrank(user); + sf.host.callAgreement( + sf.cfa, + abi.encodeCall( + sf.cfa.updateFlowOperatorPermissions, + ( + superToken, + address(vestingScheduler), + FlowOperatorDefinitions.AUTHORIZE_FULL_CONTROL, + flowRate, + new bytes(0) + ) + ), + new bytes(0) + ); + vm.stopPrank(); + } + + function _arrangeAllowances(address sender, int96 flowRate) private { + // ## Superfluid ACL allowance and permissions + _setACL_AUTHORIZE_FULL_CONTROL(sender, flowRate); + + // ## ERC-20 allowance for cliff and compensation transfers + vm.startPrank(sender); + superToken.approve(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + } + + function _createVestingScheduleWithDefaultData(address sender, address receiver) private { + vm.startPrank(sender); + vestingScheduler.createVestingSchedule( + superToken, receiver, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + vm.stopPrank(); + } + + function _createClaimableVestingScheduleWithDefaultData(address sender, address receiver) private { + vm.startPrank(sender); + vestingScheduler.createVestingSchedule( + superToken, + receiver, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + vm.stopPrank(); + } + + function _createClaimableVestingScheduleWithClaimDateAfterEndDate( + address sender, + address receiver, + uint256 delayAfterEndDate + ) private { + vm.startPrank(sender); + vestingScheduler.createVestingSchedule( + superToken, + receiver, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + END_DATE + uint32(delayAfterEndDate), + EMPTY_CTX + ); + vm.stopPrank(); + } + + function assertAreScheduleCreationParamsEqual( + IVestingSchedulerV3.ScheduleCreationParams memory params1, + IVestingSchedulerV3.ScheduleCreationParams memory params2 + ) internal pure { + require(params1.superToken == params2.superToken, "SuperToken mismatch"); + require(params1.receiver == params2.receiver, "Receiver mismatch"); + require(params1.startDate == params2.startDate, "StartDate mismatch"); + require(params1.claimValidityDate == params2.claimValidityDate, "ClaimValidityDate mismatch"); + require(params1.cliffDate == params2.cliffDate, "CliffDate mismatch"); + require(params1.flowRate == params2.flowRate, "FlowRate mismatch"); + require(params1.cliffAmount == params2.cliffAmount, "CliffAmount mismatch"); + require(params1.endDate == params2.endDate, "EndDate mismatch"); + require(params1.remainderAmount == params2.remainderAmount, "RemainderAmount mismatch"); + } + + function testAssertScheduleDoesNotExist(address superToken, address sender, address receiver) public view { + VestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(superToken, sender, receiver); + VestingSchedulerV3.VestingSchedule memory deletedSchedule; + + assertEq(schedule.cliffAndFlowDate, deletedSchedule.cliffAndFlowDate, "cliffAndFlowDate mismatch"); + assertEq(schedule.endDate, deletedSchedule.endDate, "endDate mismatch"); + assertEq(schedule.flowRate, deletedSchedule.flowRate, "flowRate mismatch"); + assertEq(schedule.cliffAmount, deletedSchedule.cliffAmount, "cliffAmount mismatch"); + assertEq(schedule.remainderAmount, deletedSchedule.remainderAmount, "remainderAmount mismatch"); + assertEq(schedule.claimValidityDate, deletedSchedule.claimValidityDate, "claimValidityDate mismatch"); + } + + function _getExpectedSchedule( + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint256 cliffAmount, + uint32 endDate + ) public view returns (IVestingSchedulerV3.VestingSchedule memory expectedSchedule) { + if (startDate == 0) { + startDate = uint32(block.timestamp); + } + + uint32 cliffAndFlowDate = cliffDate == 0 ? startDate : cliffDate; + + expectedSchedule = IVestingSchedulerV3.VestingSchedule({ + cliffAndFlowDate: cliffAndFlowDate, + endDate: endDate, + claimValidityDate: 0, + flowRate: flowRate, + cliffAmount: cliffAmount, + remainderAmount: 0, + totalAmount: cliffAmount + (endDate - cliffAndFlowDate) * uint96(flowRate), + alreadyVestedAmount: 0 + }); + } + + function _getExpectedScheduleFromAmountAndDuration( + uint256 totalAmount, + uint32 totalDuration, + uint32 cliffPeriod, + uint32 startDate, + uint32 claimPeriod + ) public view returns (IVestingSchedulerV3.VestingSchedule memory expectedSchedule) { + if (startDate == 0) { + startDate = uint32(block.timestamp); + } + + int96 flowRate = SafeCast.toInt96(SafeCast.toInt256(totalAmount / totalDuration)); + + uint32 cliffDate; + uint32 cliffAndFlowDate; + uint256 cliffAmount; + if (cliffPeriod > 0) { + cliffDate = startDate + cliffPeriod; + cliffAmount = cliffPeriod * SafeCast.toUint256(flowRate); + cliffAndFlowDate = cliffDate; + } else { + cliffDate = 0; + cliffAmount = 0; + cliffAndFlowDate = startDate; + } + + uint32 endDate = startDate + totalDuration; + + uint96 remainderAmount = SafeCast.toUint96(totalAmount - SafeCast.toUint256(flowRate) * totalDuration); + + expectedSchedule = IVestingSchedulerV3.VestingSchedule({ + cliffAndFlowDate: cliffAndFlowDate, + endDate: endDate, + flowRate: flowRate, + cliffAmount: cliffAmount, + remainderAmount: remainderAmount, + claimValidityDate: claimPeriod == 0 ? 0 : startDate + claimPeriod, + totalAmount: totalAmount, + alreadyVestedAmount: 0 + }); + } + + /// TESTS + + function test_vesting_scheduler_is_superapp() public { + assertTrue(sf.host.isApp(ISuperApp(address(new VestingSchedulerV3(sf.host))))); + } + + function testCreateVestingSchedule() public { + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + ); + + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, EMPTY_CTX + ); + vm.stopPrank(); + + //assert storage data + VestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertTrue(schedule.cliffAndFlowDate == CLIFF_DATE, "schedule.cliffAndFlowDate"); + assertTrue(schedule.endDate == END_DATE, "schedule.endDate"); + assertTrue(schedule.flowRate == FLOW_RATE, "schedule.flowRate"); + assertTrue(schedule.cliffAmount == CLIFF_TRANSFER_AMOUNT, "schedule.cliffAmount"); + } + + function test_createVestingSchedule_v1_overload() public { + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + ); + _createVestingScheduleWithDefaultData(alice, bob); + vm.startPrank(alice); + //assert storage data + VestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertTrue(schedule.cliffAndFlowDate == CLIFF_DATE, "schedule.cliffAndFlowDate"); + assertTrue(schedule.endDate == END_DATE, "schedule.endDate"); + assertTrue(schedule.flowRate == FLOW_RATE, "schedule.flowRate"); + assertTrue(schedule.cliffAmount == CLIFF_TRANSFER_AMOUNT, "schedule.cliffAmount"); + } + + function testCannotCreateVestingScheduleWithWrongData() public { + vm.startPrank(alice); + // revert with superToken = 0 + vm.expectRevert(IVestingSchedulerV3.ZeroAddress.selector); + vestingScheduler.createVestingSchedule( + ISuperToken(address(0)), + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + 0, + EMPTY_CTX + ); + + // revert with receivers = sender + vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, alice, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + + // revert with receivers = address(0) + vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, address(0), START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + + // revert with flowRate = 0 + vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, 0, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + + // revert with cliffDate = 0 but cliffAmount != 0 + vm.expectRevert(IVestingSchedulerV3.CliffInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, 0, 0, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + + // revert with startDate < block.timestamp && cliffDate = 0 + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, uint32(block.timestamp - 1), 0, FLOW_RATE, 0, END_DATE, 0, EMPTY_CTX + ); + + // revert with endDate = 0 + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, 0, 0, EMPTY_CTX + ); + + // revert with cliffAndFlowDate < block.timestamp + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, 0, uint32(block.timestamp) - 1, FLOW_RATE, 0, END_DATE, 0, EMPTY_CTX + ); + + // revert with cliffAndFlowDate >= endDate + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0, EMPTY_CTX + ); + + // revert with cliffAndFlowDate + startDateValidFor >= endDate - endDateValidBefore + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0, EMPTY_CTX + ); + + // revert with startDate > cliffDate + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, CLIFF_DATE + 1, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + + // revert with vesting duration < 7 days + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE + 2 days, 0, EMPTY_CTX + ); + } + + function testCannotCreateVestingScheduleIfDataExist() public { + _createVestingScheduleWithDefaultData(alice, bob); + vm.expectRevert(IVestingSchedulerV3.ScheduleAlreadyExists.selector); + _createVestingScheduleWithDefaultData(alice, bob); + } + + function testUpdateVestingSchedule() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + ); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + vm.stopPrank(); + vm.startPrank(alice); + vestingScheduler.updateVestingSchedule(superToken, bob, uint32(END_DATE + 1000), EMPTY_CTX); + //assert storage data + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertTrue(schedule.cliffAndFlowDate == 0, "schedule.cliffAndFlowDate"); + assertTrue(schedule.endDate == END_DATE + 1000, "schedule.endDate"); + } + + function test_updateVestingSchedule_invalidEndDate() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + ); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + vm.stopPrank(); + vm.startPrank(alice); + + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.updateVestingSchedule(superToken, bob, uint32(initialTimestamp - 1), EMPTY_CTX); + + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.updateVestingSchedule(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); + } + + function testCannotUpdateVestingScheduleIfNotRunning() public { + _createVestingScheduleWithDefaultData(alice, bob); + vm.startPrank(alice); + vm.expectRevert(IVestingSchedulerV3.ScheduleNotFlowing.selector); + vestingScheduler.updateVestingSchedule(superToken, bob, END_DATE, EMPTY_CTX); + } + + function testCannotUpdateVestingScheduleIfDataDontExist() public { + vm.startPrank(alice); + vm.expectRevert(IVestingSchedulerV3.ScheduleNotFlowing.selector); + vestingScheduler.updateVestingSchedule(superToken, bob, END_DATE, EMPTY_CTX); + } + + function testDeleteVestingSchedule() public { + _createVestingScheduleWithDefaultData(alice, bob); + vm.startPrank(alice); + vm.expectEmit(true, true, true, true); + emit VestingScheduleDeleted(superToken, alice, bob); + vestingScheduler.deleteVestingSchedule(superToken, bob, EMPTY_CTX); + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + + function testCannotDeleteVestingScheduleIfDataDontExist() public { + vm.startPrank(alice); + vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vestingScheduler.deleteVestingSchedule(superToken, bob, EMPTY_CTX); + } + + function testExecuteCliffAndFlowWithCliffAmount() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + uint256 finalTimestamp = block.timestamp + 10 days - 3600; + vm.warp(finalTimestamp); + vm.expectEmit(true, true, true, true); + uint256 timeDiffToEndDate = END_DATE > block.timestamp ? END_DATE - block.timestamp : 0; + uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + uint256 aliceFinalBalance = superToken.balanceOf(alice); + uint256 bobFinalBalance = superToken.balanceOf(bob); + uint256 aliceShouldStream = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); + assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + + vm.expectRevert(IVestingSchedulerV3.AlreadyExecuted.selector); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + } + + function testExecuteCliffAndFlowWithoutCliffAmountOrAdjustment() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, 0, END_DATE, 0, EMPTY_CTX + ); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + vm.startPrank(admin); + vm.warp(CLIFF_DATE); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted(superToken, alice, bob, CLIFF_DATE, FLOW_RATE, 0, 0); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + vm.warp(END_DATE); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, 0, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + uint256 aliceFinalBalance = superToken.balanceOf(alice); + uint256 bobFinalBalance = superToken.balanceOf(bob); + uint256 aliceShouldStream = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE); + assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); + assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + } + + function testExecuteCliffAndFlowWithUpdatedEndDate() public { + uint32 NEW_END_DATE = END_DATE - 1000; + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + vm.stopPrank(); + vm.prank(alice); + vm.expectEmit(true, true, true, true); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, 0); + vestingScheduler.updateVestingSchedule(superToken, bob, NEW_END_DATE, EMPTY_CTX); + uint256 finalTimestamp = block.timestamp + 10 days - 3600; + vm.warp(finalTimestamp); + vm.expectEmit(true, true, true, true); + uint256 timeDiffToEndDate = NEW_END_DATE > block.timestamp ? NEW_END_DATE - block.timestamp : 0; + uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, NEW_END_DATE, adjustedAmountClosing, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + uint256 aliceFinalBalance = superToken.balanceOf(alice); + uint256 bobFinalBalance = superToken.balanceOf(bob); + uint256 aliceShouldStream = (NEW_END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); + assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + } + + function testExecuteCliffAndFlowRevertClosingTransfer() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + vm.stopPrank(); + vm.startPrank(alice); + superToken.transferAll(eve); + vm.stopPrank(); + vm.startPrank(admin); + uint256 earlyEndTimestamp = block.timestamp + 10 days - 3600; + vm.warp(earlyEndTimestamp); + + vm.expectRevert(); + vestingScheduler.executeEndVesting(superToken, alice, bob); + + uint256 finalTimestamp = END_DATE + 1; + vm.warp(finalTimestamp); + + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, 0, true); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + } + + function testCannotExecuteEndVestingBeforeTime() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.executeEndVesting(superToken, alice, bob); + } + + function testCannotExecuteCliffAndFlowBeforeTime() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + } + + function testCannotExecuteEndWithoutStreamRunning() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + vm.stopPrank(); + vm.startPrank(alice); + superToken.deleteFlow(alice, bob); + vm.stopPrank(); + vm.startPrank(admin); + uint256 finalTimestamp = block.timestamp + 10 days - 3600; + vm.warp(finalTimestamp); + vm.expectEmit(true, true, true, true); + emit VestingEndFailed(superToken, alice, bob, END_DATE); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + } + + // # Vesting Scheduler V2 tests + + function testCreateAndExecuteImmediately() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + // # Create schedule + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + uint32 startAndCliffDate = uint32(block.timestamp); + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + startAndCliffDate, + startAndCliffDate, + FLOW_RATE, + END_DATE, + CLIFF_TRANSFER_AMOUNT, + 0, + 0 + ); + + vestingScheduler.createVestingSchedule( + superToken, + bob, + startAndCliffDate, + startAndCliffDate, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + 0, + EMPTY_CTX + ); + // --- + + // # Execute start + vm.expectEmit(); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT); + + vm.expectEmit(); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, startAndCliffDate, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, uint256(0) + ); + vm.stopPrank(); + + vm.startPrank(admin); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + vm.stopPrank(); + + assertTrue(success, "executeVesting should return true"); + // --- + + // # Execute end + uint256 finalTimestamp = END_DATE - 3600; + vm.warp(finalTimestamp); + + uint256 timeDiffToEndDate = END_DATE > block.timestamp ? END_DATE - block.timestamp : 0; + uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); + + vm.expectEmit(); + emit Transfer(alice, bob, adjustedAmountClosing); + + vm.expectEmit(); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); + vm.startPrank(admin); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + vm.stopPrank(); + assertTrue(success, "executeCloseVesting should return true"); + + uint256 aliceFinalBalance = superToken.balanceOf(alice); + uint256 bobFinalBalance = superToken.balanceOf(bob); + uint256 aliceShouldStream = (END_DATE - startAndCliffDate) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); + assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + // --- + } + + function test_createScheduleFromAmountAndDuration_reverts() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 0, // amount + 1209600, // duration + uint32(block.timestamp), // startDate + 604800, // cliffPeriod + 0, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with cliff and start in history."); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 1 ether, // amount + 1209600, // duration + uint32(block.timestamp - 1), // startDate + 0, // cliffPeriod + 0, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with overflow."); + vm.expectRevert("SafeCast: value doesn't fit in 96 bits"); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + type(uint256).max, // amount + 1209600, // duration + uint32(block.timestamp), // startDate + 0, // cliffPeriod + 0, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with underflow/overflow."); + vm.expectRevert(); // todo: the right error + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 1 ether, // amount + type(uint32).max, // duration + uint32(block.timestamp), // startDate + 0, // cliffPeriod + 0, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with start date in history."); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 1 ether, // amount + 1209600, // duration + uint32(block.timestamp - 1), // startDate + 604800, // cliffPeriod + 0, // claimPeriod + EMPTY_CTX + ); + } + + function testNewFunctionScheduleCreationWithoutCliff(uint8 randomizer) public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + + uint32 startDate = uint32(block.timestamp); + uint256 totalVestedAmount = 105_840_000; // a value perfectly divisible by a week + uint32 vestingDuration = 1 weeks; + int96 expectedFlowRate = 175; // totalVestedAmount / vestingDuration + uint32 expectedEndDate = startDate + vestingDuration; + + vm.expectEmit(); + emit VestingScheduleCreated(superToken, alice, bob, startDate, 0, expectedFlowRate, expectedEndDate, 0, 0, 0); + + vm.startPrank(alice); + bool useCtx = randomizer % 2 == 0; + if (useCtx) { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + totalVestedAmount, + vestingDuration, + startDate, + 0, // cliffPeriod + 0, // claimPeriod + EMPTY_CTX + ); + } else { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + totalVestedAmount, + vestingDuration, + startDate, + 0, // cliffPeriod + 0 // claimPeriod + ); + } + vm.stopPrank(); + } + + function testNewFunctionScheduleCreationWithCliff(uint8 randomizer) public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + + uint32 startDate = uint32(block.timestamp); + uint256 totalVestedAmount = 103_680_000; // a value perfectly divisible + uint32 vestingDuration = 1 weeks + 1 days; + uint32 cliffPeriod = 1 days; + + int96 expectedFlowRate = 150; // (totalVestedAmount - cliffAmount) / (vestingDuration - cliffPeriod) + uint256 expectedCliffAmount = 12960000; + uint32 expectedCliffDate = startDate + cliffPeriod; + uint32 expectedEndDate = startDate + vestingDuration; + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + startDate, + expectedCliffDate, + expectedFlowRate, + expectedEndDate, + expectedCliffAmount, + 0, + 0 + ); + + vm.startPrank(alice); + bool useCtx = randomizer % 2 == 0; + if (useCtx) { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0, EMPTY_CTX + ); + } else { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0 + ); + } + vm.stopPrank(); + } + + struct BigTestData { + uint256 beforeSenderBalance; + uint256 beforeReceiverBalance; + uint256 afterSenderBalance; + uint256 afterReceiverBalance; + uint32 expectedCliffDate; + uint32 expectedStartDate; + address claimer; + IVestingSchedulerV3.VestingSchedule expectedSchedule; + } + + // Claimable Vesting Schedules tests + function test_createScheduleFromAmountAndDuration_executeCliffAndFlow_executeEndVesting_withClaim( + uint256 totalAmount, + uint32 totalDuration, + uint32 cliffPeriod, + uint32 startDate, + uint32 claimPeriod, + uint8 randomizer + ) public { + // Assume + randomizer = SafeCast.toUint8(bound(randomizer, 1, type(uint8).max)); + + if (startDate != 0) { + startDate = SafeCast.toUint32(bound(startDate, block.timestamp, 2524600800)); + } + + totalDuration = SafeCast.toUint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION(), 9125 days)); + vm.assume(cliffPeriod <= totalDuration - vestingScheduler.MIN_VESTING_DURATION()); + + claimPeriod = SafeCast.toUint32(bound(claimPeriod, 1, 9125 days)); + vm.assume(claimPeriod > (cliffPeriod > 0 ? startDate + cliffPeriod : startDate)); + vm.assume(claimPeriod < totalDuration - vestingScheduler.END_DATE_VALID_BEFORE()); + + BigTestData memory $; + + $.beforeSenderBalance = superToken.balanceOf(alice); + $.beforeReceiverBalance = superToken.balanceOf(bob); + + totalAmount = bound(totalAmount, 1, $.beforeSenderBalance); + vm.assume(totalAmount >= totalDuration); + vm.assume(totalAmount / totalDuration <= SafeCast.toUint256(type(int96).max)); + + assertTrue( + vestingScheduler.getVestingSchedule(address(superToken), alice, bob).endDate == 0, + "Schedule should not exist" + ); + + // Arrange + $.expectedSchedule = + _getExpectedScheduleFromAmountAndDuration(totalAmount, totalDuration, cliffPeriod, startDate, claimPeriod); + $.expectedCliffDate = cliffPeriod == 0 ? 0 : $.expectedSchedule.cliffAndFlowDate; + $.expectedStartDate = startDate == 0 ? uint32(block.timestamp) : startDate; + + // Assume we're not getting liquidated at the end: + vm.assume( + $.beforeSenderBalance + >= totalAmount + vestingScheduler.END_DATE_VALID_BEFORE() * SafeCast.toUint256($.expectedSchedule.flowRate) + ); + + console.log("Total amount: %s", totalAmount); + console.log("Total duration: %s", totalDuration); + console.log("Cliff period: %s", cliffPeriod); + console.log("Claim period: %s", claimPeriod); + console.log("Start date: %s", startDate); + console.log("Randomizer: %s", randomizer); + console.log("Expected start date: %s", $.expectedStartDate); + console.log("Expected claim date: %s", $.expectedSchedule.claimValidityDate); + console.log("Expected cliff date: %s", $.expectedCliffDate); + console.log("Expected cliff & flow date: %s", $.expectedSchedule.cliffAndFlowDate); + console.log("Expected end date: %s", $.expectedSchedule.endDate); + console.log("Expected flow rate: %s", SafeCast.toUint256($.expectedSchedule.flowRate)); + console.log("Expected cliff amount: %s", $.expectedSchedule.cliffAmount); + console.log("Expected remainder amount: %s", $.expectedSchedule.remainderAmount); + console.log("Sender balance: %s", $.beforeSenderBalance); + + // Arrange allowance + assertTrue(superToken.allowance(alice, address(vestingScheduler)) == 0, "Let's start without any allowance"); + + vm.startPrank(alice); + superToken.revokeFlowPermissions(address(vestingScheduler)); + superToken.setFlowPermissions( + address(vestingScheduler), + true, // allowCreate + false, // allowUpdate + true, // allowDelete, + $.expectedSchedule.flowRate + ); + superToken.approve( + address(vestingScheduler), vestingScheduler.getMaximumNeededTokenAllowance($.expectedSchedule) + ); + vm.stopPrank(); + + // Intermediary `mapCreateVestingScheduleParams` test + assertAreScheduleCreationParamsEqual( + IVestingSchedulerV3.ScheduleCreationParams( + superToken, + alice, + bob, + $.expectedStartDate, + $.expectedSchedule.claimValidityDate, + $.expectedCliffDate, + $.expectedSchedule.flowRate, + $.expectedSchedule.cliffAmount, + $.expectedSchedule.endDate, + $.expectedSchedule.remainderAmount + ), + vestingScheduler.mapCreateVestingScheduleParams( + superToken, alice, bob, totalAmount, totalDuration, $.expectedStartDate, cliffPeriod, claimPeriod + ) + ); + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + $.expectedStartDate, + $.expectedCliffDate, + $.expectedSchedule.flowRate, + $.expectedSchedule.endDate, + $.expectedSchedule.cliffAmount, + $.expectedSchedule.claimValidityDate, + $.expectedSchedule.remainderAmount + ); + + // Act + vm.startPrank(alice); + if (randomizer % 3 == 0) { + console.log("Using the overload without superfluid context."); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod + ); + } else { + console.log("Using the overload with superfluid context."); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX + ); + } + vm.stopPrank(); + + // Assert + IVestingSchedulerV3.VestingSchedule memory actualSchedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertEq( + actualSchedule.cliffAndFlowDate, + $.expectedSchedule.cliffAndFlowDate, + "schedule created: cliffAndFlowDate not expected" + ); + assertEq(actualSchedule.flowRate, $.expectedSchedule.flowRate, "schedule created: flowRate not expected"); + assertEq( + actualSchedule.cliffAmount, $.expectedSchedule.cliffAmount, "schedule created: cliffAmount not expected" + ); + assertEq(actualSchedule.endDate, $.expectedSchedule.endDate, "schedule created: endDate not expected"); + assertEq( + actualSchedule.remainderAmount, + $.expectedSchedule.remainderAmount, + "schedule created: remainderAmount not expected" + ); + assertEq( + actualSchedule.claimValidityDate, + $.expectedSchedule.claimValidityDate, + "schedule created: claimValidityDate not expected" + ); + + // Act + console.log("Executing cliff and flow."); + uint32 randomFlowDelay = ($.expectedSchedule.claimValidityDate - $.expectedSchedule.cliffAndFlowDate); + vm.warp($.expectedSchedule.cliffAndFlowDate + randomFlowDelay); + + $.claimer = randomizer % 2 == 0 ? bob : alice; + + vm.prank($.claimer); + vm.expectEmit(); + emit VestingClaimed(superToken, alice, bob, $.claimer); + vm.expectEmit(); + emit VestingCliffAndFlowExecuted( + superToken, + alice, + bob, + $.expectedSchedule.cliffAndFlowDate, + $.expectedSchedule.flowRate, + $.expectedSchedule.cliffAmount, + randomFlowDelay * SafeCast.toUint256($.expectedSchedule.flowRate) + ); + assertTrue(vestingScheduler.executeCliffAndFlow(superToken, alice, bob)); + vm.stopPrank(); + + // Assert + actualSchedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertEq(actualSchedule.cliffAndFlowDate, 0, "schedule started: cliffAndFlowDate not expected"); + assertEq(actualSchedule.cliffAmount, 0, "schedule started: cliffAmount not expected"); + assertEq(actualSchedule.flowRate, $.expectedSchedule.flowRate, "schedule started: flowRate not expected"); + assertEq(actualSchedule.endDate, $.expectedSchedule.endDate, "schedule started: endDate not expected"); + assertEq( + actualSchedule.remainderAmount, + $.expectedSchedule.remainderAmount, + "schedule started: remainderAmount not expected" + ); + + if (randomizer % 7 != 0) { + // # Test end execution on time. + + console.log("Executing end vesting early."); + uint32 randomEarlyEndTime = + (vestingScheduler.END_DATE_VALID_BEFORE() - (vestingScheduler.END_DATE_VALID_BEFORE() / randomizer)); + vm.warp($.expectedSchedule.endDate - randomEarlyEndTime); + vm.expectEmit(); + uint256 earlyEndCompensation = randomEarlyEndTime * SafeCast.toUint256($.expectedSchedule.flowRate) + + $.expectedSchedule.remainderAmount; + emit VestingEndExecuted(superToken, alice, bob, $.expectedSchedule.endDate, earlyEndCompensation, false); + + // Act + assertTrue(vestingScheduler.executeEndVesting(superToken, alice, bob)); + + // Assert + $.afterSenderBalance = superToken.balanceOf(alice); + $.afterReceiverBalance = superToken.balanceOf(bob); + + assertEq( + $.afterSenderBalance, + $.beforeSenderBalance - totalAmount, + "Sender balance should decrease by totalAmount" + ); + assertEq( + $.afterReceiverBalance, + $.beforeReceiverBalance + totalAmount, + "Receiver balance should increase by totalAmount" + ); + } else { + // # Test end execution delayed. + + console.log("Executing end vesting late."); + uint32 randomLateEndDelay = (totalDuration / randomizer); + vm.warp($.expectedSchedule.endDate + randomLateEndDelay); // There is some chance of overflow here. + + if (randomizer % 13 == 0) { + vm.startPrank(alice); + superToken.deleteFlow(alice, bob); + vm.stopPrank(); + + vm.expectEmit(); + emit VestingEndFailed(superToken, alice, bob, $.expectedSchedule.endDate); + } else { + vm.expectEmit(); + emit VestingEndExecuted(superToken, alice, bob, $.expectedSchedule.endDate, 0, true); + } + + // Act + assertTrue(vestingScheduler.executeEndVesting(superToken, alice, bob)); + + // Assert + $.afterSenderBalance = superToken.balanceOf(alice); + $.afterReceiverBalance = superToken.balanceOf(bob); + + assertLt( + $.afterSenderBalance, + $.beforeSenderBalance - totalAmount + $.expectedSchedule.remainderAmount, + "Sender balance should decrease by at least totalAmount" + ); + assertGt( + $.afterReceiverBalance, + $.beforeReceiverBalance + totalAmount - $.expectedSchedule.remainderAmount, + "Receiver balance should increase by at least totalAmount" + ); + } + + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + + vm.warp(type(uint32).max); + assertEq( + $.afterSenderBalance, + superToken.balanceOf(alice), + "After the schedule has ended, the sender's balance should never change." + ); + } + + function test_createScheduleFromAmountAndDuration_executeCliffAndFlow_executeEndVesting_withClaim_withSingleTransfer( + uint256 totalAmount, + uint32 totalDuration, + uint32 cliffPeriod, + uint32 startDate, + uint32 claimPeriod, + uint8 randomizer + ) public { + // Assume + randomizer = SafeCast.toUint8(bound(randomizer, 1, type(uint8).max)); + + if (startDate != 0) { + startDate = SafeCast.toUint32(bound(startDate, block.timestamp, 2524600800)); + } + + totalDuration = SafeCast.toUint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION(), 9125 days)); + vm.assume(cliffPeriod <= totalDuration - vestingScheduler.MIN_VESTING_DURATION()); + + claimPeriod = SafeCast.toUint32(bound(claimPeriod, 1, 9125 days)); + vm.assume(claimPeriod > (startDate + totalDuration - vestingScheduler.END_DATE_VALID_BEFORE())); + + BigTestData memory $; + + $.beforeSenderBalance = superToken.balanceOf(alice); + $.beforeReceiverBalance = superToken.balanceOf(bob); + + totalAmount = bound(totalAmount, 1, $.beforeSenderBalance); + vm.assume(totalAmount >= totalDuration); + vm.assume(totalAmount / totalDuration <= SafeCast.toUint256(type(int96).max)); + + assertTrue( + vestingScheduler.getVestingSchedule(address(superToken), alice, bob).endDate == 0, + "Schedule should not exist" + ); + + // Arrange + $.expectedSchedule = + _getExpectedScheduleFromAmountAndDuration(totalAmount, totalDuration, cliffPeriod, startDate, claimPeriod); + $.expectedCliffDate = cliffPeriod == 0 ? 0 : $.expectedSchedule.cliffAndFlowDate; + $.expectedStartDate = startDate == 0 ? uint32(block.timestamp) : startDate; + + // Assume we're not getting liquidated at the end: + vm.assume( + $.beforeSenderBalance + >= totalAmount + vestingScheduler.END_DATE_VALID_BEFORE() * SafeCast.toUint256($.expectedSchedule.flowRate) + ); + + console.log("Total amount: %s", totalAmount); + console.log("Total duration: %s", totalDuration); + console.log("Cliff period: %s", cliffPeriod); + console.log("Claim period: %s", claimPeriod); + console.log("Start date: %s", startDate); + console.log("Randomizer: %s", randomizer); + console.log("Expected start date: %s", $.expectedStartDate); + console.log("Expected claim date: %s", $.expectedSchedule.claimValidityDate); + console.log("Expected cliff date: %s", $.expectedCliffDate); + console.log("Expected cliff & flow date: %s", $.expectedSchedule.cliffAndFlowDate); + console.log("Expected end date: %s", $.expectedSchedule.endDate); + console.log("Expected flow rate: %s", SafeCast.toUint256($.expectedSchedule.flowRate)); + console.log("Expected cliff amount: %s", $.expectedSchedule.cliffAmount); + console.log("Expected remainder amount: %s", $.expectedSchedule.remainderAmount); + console.log("Sender balance: %s", $.beforeSenderBalance); + + // Arrange allowance + assertTrue(superToken.allowance(alice, address(vestingScheduler)) == 0, "Let's start without any allowance"); + + vm.startPrank(alice); + superToken.revokeFlowPermissions(address(vestingScheduler)); + superToken.approve( + address(vestingScheduler), vestingScheduler.getMaximumNeededTokenAllowance($.expectedSchedule) + ); + vm.stopPrank(); + + // Intermediary `mapCreateVestingScheduleParams` test + assertAreScheduleCreationParamsEqual( + IVestingSchedulerV3.ScheduleCreationParams( + superToken, + alice, + bob, + $.expectedStartDate, + $.expectedSchedule.claimValidityDate, + $.expectedCliffDate, + $.expectedSchedule.flowRate, + $.expectedSchedule.cliffAmount, + $.expectedSchedule.endDate, + $.expectedSchedule.remainderAmount + ), + vestingScheduler.mapCreateVestingScheduleParams( + superToken, alice, bob, totalAmount, totalDuration, $.expectedStartDate, cliffPeriod, claimPeriod + ) + ); + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + $.expectedStartDate, + $.expectedCliffDate, + $.expectedSchedule.flowRate, + $.expectedSchedule.endDate, + $.expectedSchedule.cliffAmount, + $.expectedSchedule.claimValidityDate, + $.expectedSchedule.remainderAmount + ); + + // Act + vm.startPrank(alice); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX + ); + vm.stopPrank(); + + // Assert + IVestingSchedulerV3.VestingSchedule memory actualSchedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertEq( + actualSchedule.cliffAndFlowDate, + $.expectedSchedule.cliffAndFlowDate, + "schedule created: cliffAndFlowDate not expected" + ); + assertEq(actualSchedule.flowRate, $.expectedSchedule.flowRate, "schedule created: flowRate not expected"); + assertEq( + actualSchedule.cliffAmount, $.expectedSchedule.cliffAmount, "schedule created: cliffAmount not expected" + ); + assertEq(actualSchedule.endDate, $.expectedSchedule.endDate, "schedule created: endDate not expected"); + assertEq( + actualSchedule.remainderAmount, + $.expectedSchedule.remainderAmount, + "schedule created: remainderAmount not expected" + ); + assertEq( + actualSchedule.claimValidityDate, + $.expectedSchedule.claimValidityDate, + "schedule created: claimValidityDate not expected" + ); + + // Act + console.log("Executing cliff and flow."); + vm.warp( + $.expectedSchedule.endDate - vestingScheduler.END_DATE_VALID_BEFORE() + /* random delay: */ + + ( + $.expectedSchedule.claimValidityDate + - ($.expectedSchedule.endDate - vestingScheduler.END_DATE_VALID_BEFORE()) + ) / randomizer + ); + + $.claimer = randomizer % 2 == 0 ? bob : alice; + + vm.prank($.claimer); + + vm.expectEmit(); + emit VestingClaimed(superToken, alice, bob, $.claimer); + vm.expectEmit(); + emit VestingCliffAndFlowExecuted( + superToken, + alice, + bob, + $.expectedSchedule.cliffAndFlowDate, + 0, + $.expectedSchedule.cliffAmount, + totalAmount - $.expectedSchedule.cliffAmount + ); + + vm.expectEmit(); + emit VestingEndExecuted(superToken, alice, bob, $.expectedSchedule.endDate, 0, false); + + assertTrue(vestingScheduler.executeCliffAndFlow(superToken, alice, bob)); + vm.stopPrank(); + + $.afterSenderBalance = superToken.balanceOf(alice); + $.afterReceiverBalance = superToken.balanceOf(bob); + + assertEq( + $.afterSenderBalance, $.beforeSenderBalance - totalAmount, "Sender balance should decrease by totalAmount" + ); + assertEq( + $.afterReceiverBalance, + $.beforeReceiverBalance + totalAmount, + "Receiver balance should increase by totalAmount" + ); + + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + + vm.warp(type(uint32).max); + assertEq( + $.afterSenderBalance, + superToken.balanceOf(alice), + "After the schedule has ended, the sender's balance should never change." + ); + } + + function test_createAndExecuteVestingScheduleFromAmountAndDuration(uint256 _totalAmount, uint32 _totalDuration) + public + { + _totalDuration = SafeCast.toUint32(bound(_totalDuration, uint32(7 days), uint32(365 days))); + _totalAmount = bound(_totalAmount, 1 ether, 100 ether); + + int96 flowRate = SafeCast.toInt96(SafeCast.toInt256(_totalAmount / _totalDuration)); + + uint96 remainderAmount = SafeCast.toUint96(_totalAmount - (SafeCast.toUint256(flowRate) * _totalDuration)); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, flowRate); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, + alice, + bob, + uint32(block.timestamp), + 0, + flowRate, + uint32(block.timestamp) + _totalDuration, + 0, + 0, + remainderAmount + ); + + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted(superToken, alice, bob, uint32(block.timestamp), flowRate, 0, 0); + + vestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration( + superToken, bob, _totalAmount, _totalDuration, EMPTY_CTX + ); + + vm.stopPrank(); + } + + function test_createAndExecuteVestingScheduleFromAmountAndDuration_noCtx( + uint256 _totalAmount, + uint32 _totalDuration + ) public { + _totalDuration = SafeCast.toUint32(bound(_totalDuration, uint32(7 days), uint32(365 days))); + _totalAmount = bound(_totalAmount, 1 ether, 100 ether); + + int96 flowRate = SafeCast.toInt96(SafeCast.toInt256(_totalAmount / _totalDuration)); + + uint96 remainderAmount = SafeCast.toUint96(_totalAmount - (SafeCast.toUint256(flowRate) * _totalDuration)); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, flowRate); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, + alice, + bob, + uint32(block.timestamp), + 0, + flowRate, + uint32(block.timestamp) + _totalDuration, + 0, + 0, + remainderAmount + ); + + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted(superToken, alice, bob, uint32(block.timestamp), flowRate, 0, 0); + + vestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration( + superToken, bob, _totalAmount, _totalDuration + ); + + vm.stopPrank(); + } + + function test_createClaimableVestingSchedule() public { + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, + alice, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + END_DATE, + CLIFF_TRANSFER_AMOUNT, + CLAIM_VALIDITY_DATE, + 0 + ); + + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + vm.stopPrank(); + + vm.startPrank(alice); + //assert storage data + VestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertTrue(schedule.cliffAndFlowDate == CLIFF_DATE, "schedule.cliffAndFlowDate"); + assertTrue(schedule.endDate == END_DATE, "schedule.endDate"); + assertTrue(schedule.flowRate == FLOW_RATE, "schedule.flowRate"); + assertTrue(schedule.claimValidityDate == CLAIM_VALIDITY_DATE, "schedule.claimValidityDate"); + assertTrue(schedule.cliffAmount == CLIFF_TRANSFER_AMOUNT, "schedule.cliffAmount"); + } + + function test_createClaimableVestingSchedule_claimValidity() public { + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, + alice, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + END_DATE, + CLIFF_TRANSFER_AMOUNT, + CLAIM_VALIDITY_DATE, + 0 + ); + + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + vm.stopPrank(); + + vm.startPrank(alice); + //assert storage data + VestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertTrue(schedule.cliffAndFlowDate == CLIFF_DATE, "schedule.cliffAndFlowDate"); + assertTrue(schedule.endDate == END_DATE, "schedule.endDate"); + assertTrue(schedule.flowRate == FLOW_RATE, "schedule.flowRate"); + assertTrue(schedule.claimValidityDate == CLAIM_VALIDITY_DATE, "schedule.claimValidityDate"); + assertTrue(schedule.cliffAmount == CLIFF_TRANSFER_AMOUNT, "schedule.cliffAmount"); + } + + function test_createClaimableVestingSchedule_noCtx() public { + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, + alice, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + END_DATE, + CLIFF_TRANSFER_AMOUNT, + CLAIM_VALIDITY_DATE, + 0 + ); + + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE + ); + vm.stopPrank(); + + vm.startPrank(alice); + //assert storage data + VestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertTrue(schedule.cliffAndFlowDate == CLIFF_DATE, "schedule.cliffAndFlowDate"); + assertTrue(schedule.endDate == END_DATE, "schedule.endDate"); + assertTrue(schedule.flowRate == FLOW_RATE, "schedule.flowRate"); + assertTrue(schedule.claimValidityDate == CLAIM_VALIDITY_DATE, "schedule.flowRate"); + assertTrue(schedule.cliffAmount == CLIFF_TRANSFER_AMOUNT, "schedule.cliffAmount"); + } + + function test_createClaimableVestingSchedule_wrongData() public { + vm.startPrank(alice); + // revert with superToken = 0 + vm.expectRevert(IVestingSchedulerV3.ZeroAddress.selector); + vestingScheduler.createVestingSchedule( + ISuperToken(address(0)), + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + + // revert with receivers = sender + vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, + alice, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + + // revert with receivers = address(0) + vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, + address(0), + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + + // revert with flowRate = 0 + vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, 0, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + ); + + // revert with cliffDate = 0 but cliffAmount != 0 + vm.expectRevert(IVestingSchedulerV3.CliffInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, 0, 0, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + ); + + // revert with startDate < block.timestamp && cliffDate = 0 + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, uint32(block.timestamp - 1), 0, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + ); + + // revert with endDate = 0 + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, 0, CLAIM_VALIDITY_DATE, EMPTY_CTX + ); + + // revert with cliffAndFlowDate < block.timestamp + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, bob, 0, uint32(block.timestamp) - 1, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + ); + + // revert with cliffAndFlowDate >= endDate + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + CLIFF_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + + // revert with cliffAndFlowDate + startDateValidFor >= endDate - endDateValidBefore + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + CLIFF_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + + // revert with startDate > cliffDate + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, + bob, + CLIFF_DATE + 1, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + + // revert with vesting duration < 7 days + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + CLIFF_DATE + 2 days, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + + // revert with invalid claim validity date (before schedule/cliff start) + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLIFF_DATE - 1, + EMPTY_CTX + ); + } + + function test_createClaimableVestingSchedule_dataExists() public { + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + vm.stopPrank(); + + vm.expectRevert(IVestingSchedulerV3.ScheduleAlreadyExists.selector); + + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, + bob, + START_DATE, + CLIFF_DATE, + FLOW_RATE, + CLIFF_TRANSFER_AMOUNT, + END_DATE, + CLAIM_VALIDITY_DATE, + EMPTY_CTX + ); + vm.stopPrank(); + } + + function test_createClaimableVestingScheduleFromAmountAndDuration_withoutCliff(uint8 randomizer) public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + + uint32 startDate = uint32(block.timestamp); + uint256 totalVestedAmount = 105_840_000; // a value perfectly divisible by a week + uint32 vestingDuration = 1 weeks; + uint32 claimPeriod = 1 days; + int96 expectedFlowRate = 175; // totalVestedAmount / vestingDuration + uint32 expectedEndDate = startDate + vestingDuration; + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, alice, bob, startDate, 0, expectedFlowRate, expectedEndDate, 0, startDate + claimPeriod, 0 + ); + vm.startPrank(alice); + bool useCtx = randomizer % 2 == 0; + if (useCtx) { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + totalVestedAmount, + vestingDuration, + startDate, + 0, // cliffPeriod + claimPeriod, + EMPTY_CTX + ); + } else { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + totalVestedAmount, + vestingDuration, + startDate, + 0, // cliffPeriod + claimPeriod + ); + } + vm.stopPrank(); + } + + function test_createClaimableVestingScheduleFromAmountAndDuration_withoutCliff_noStartDate() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + + uint256 totalVestedAmount = 105_840_000; // a value perfectly divisible by a week + uint32 vestingDuration = 1 weeks; + uint32 claimPeriod = 2 days; + int96 expectedFlowRate = 175; // totalVestedAmount / vestingDuration + uint32 expectedEndDate = uint32(block.timestamp) + vestingDuration; + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + uint32(block.timestamp), + 0, + expectedFlowRate, + expectedEndDate, + 0, + uint32(block.timestamp) + claimPeriod, + 0 + ); + + vm.startPrank(alice); + + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, 0, 0, claimPeriod + ); + vm.stopPrank(); + } + + function test_createClaimableVestingScheduleFromAmountAndDuration_withCliff(uint8 randomizer) public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + + uint32 startDate = uint32(block.timestamp); + uint256 totalVestedAmount = 103_680_000; // a value perfectly divisible + uint32 vestingDuration = 1 weeks + 1 days; + uint32 cliffPeriod = 1 days; + uint32 claimPeriod = cliffPeriod + 1 days; + + int96 expectedFlowRate = 150; // (totalVestedAmount - cliffAmount) / (vestingDuration - cliffPeriod) + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + startDate, + startDate + cliffPeriod, + expectedFlowRate, + startDate + vestingDuration, + 12960000, + startDate + claimPeriod, + 0 + ); + + vm.startPrank(alice); + bool useCtx = randomizer % 2 == 0; + if (useCtx) { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX + ); + } else { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, claimPeriod + ); + } + vm.stopPrank(); + } + + function test_createClaimableVestingScheduleFromAmountAndDuration_withCliff_noStartDate() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + + uint256 totalVestedAmount = 103_680_000; // a value perfectly divisible + uint32 vestingDuration = 1 weeks + 1 days; + uint32 cliffPeriod = 1 days; + + int96 expectedFlowRate = 150; // (totalVestedAmount - cliffAmount) / (vestingDuration - cliffPeriod) + uint256 expectedCliffAmount = 12960000; + uint32 expectedCliffDate = uint32(block.timestamp) + cliffPeriod; + uint32 claimPeriod = expectedCliffDate + 1 days; + uint32 expectedEndDate = uint32(block.timestamp) + vestingDuration; + + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + uint32(block.timestamp), + expectedCliffDate, + expectedFlowRate, + expectedEndDate, + expectedCliffAmount, + uint32(block.timestamp) + claimPeriod, + 0 + ); + + vm.startPrank(alice); + + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, 0, cliffPeriod, claimPeriod + ); + + vm.stopPrank(); + } + + function test_createClaimableScheduleFromAmountAndDuration_wrongData() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 0, // amount + 1209600, // duration + uint32(block.timestamp), // startDate + 604800, // cliffPeriod + 15 days, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with cliff and start in history."); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 1 ether, // amount + 1209600, // duration + uint32(block.timestamp - 1), // startDate + 0, // cliffPeriod + 15 days, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with overflow."); + vm.expectRevert("SafeCast: value doesn't fit in 96 bits"); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + type(uint256).max, // amount + 1209600, // duration + uint32(block.timestamp), // startDate + 0, // cliffPeriod + 15 days, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with underflow/overflow."); + vm.expectRevert(); // todo: the right error + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 1 ether, // amount + type(uint32).max, // duration + uint32(block.timestamp), // startDate + 0, // cliffPeriod + 15 days, // claimPeriod + EMPTY_CTX + ); + + console.log("Revert with start date in history."); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + 1 ether, // amount + 1209600, // duration + uint32(block.timestamp - 1), // startDate + 604800, // cliffPeriod + 15 days, // claimPeriod + EMPTY_CTX + ); + } + + function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_receiverClaim() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + + vm.prank(bob); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + uint256 finalTimestamp = block.timestamp + 10 days - 3600; + vm.warp(finalTimestamp); + vm.expectEmit(true, true, true, true); + uint256 timeDiffToEndDate = END_DATE > block.timestamp ? END_DATE - block.timestamp : 0; + uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + uint256 aliceFinalBalance = superToken.balanceOf(alice); + uint256 bobFinalBalance = superToken.balanceOf(bob); + uint256 aliceShouldStream = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); + assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + + function test_executeCliffAndFlow_claimAfterEndDate(uint256 delayAfterEndDate, uint256 claimDate, uint8 randomizer) + public + { + randomizer = SafeCast.toUint8(bound(randomizer, 1, type(uint8).max)); + + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + uint256 totalExpectedAmount = CLIFF_TRANSFER_AMOUNT + (END_DATE - CLIFF_DATE) * SafeCast.toUint256(FLOW_RATE); + + delayAfterEndDate = bound(delayAfterEndDate, 1, 1e8); + claimDate = bound(claimDate, END_DATE - vestingScheduler.END_DATE_VALID_BEFORE(), END_DATE + delayAfterEndDate); + + _createClaimableVestingScheduleWithClaimDateAfterEndDate(alice, bob, delayAfterEndDate); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.warp(claimDate); + + address claimer = randomizer % 2 == 0 ? bob : alice; + vm.expectEmit(true, true, true, false); + emit VestingClaimed(superToken, alice, bob, claimer); + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, totalExpectedAmount); + + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, 0, CLIFF_TRANSFER_AMOUNT, totalExpectedAmount - CLIFF_TRANSFER_AMOUNT + ); + + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, 0, false); + + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertEq(vestingScheduler.getMaximumNeededTokenAllowance(schedule), totalExpectedAmount); + + vm.prank(claimer); + assertTrue(vestingScheduler.executeCliffAndFlow(superToken, alice, bob)); + + assertEq(superToken.balanceOf(alice), aliceInitialBalance - totalExpectedAmount); + assertEq(superToken.balanceOf(bob), bobInitialBalance + totalExpectedAmount); + + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + + function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_senderClaim() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + vm.stopPrank(); + assertTrue(success, "executeVesting should return true"); + uint256 finalTimestamp = block.timestamp + 10 days - 3600; + vm.warp(finalTimestamp); + vm.expectEmit(true, true, true, true); + uint256 timeDiffToEndDate = END_DATE > block.timestamp ? END_DATE - block.timestamp : 0; + uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + uint256 aliceFinalBalance = superToken.balanceOf(alice); + uint256 bobFinalBalance = superToken.balanceOf(bob); + uint256 aliceShouldStream = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); + assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + } + + function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_cannotClaimOnBehalf(address _claimer) public { + vm.assume(_claimer != address(0) && _claimer != alice && _claimer != bob); + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + vm.prank(_claimer); + vm.expectRevert(IVestingSchedulerV3.CannotClaimScheduleOnBehalf.selector); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertEq(success, false); + } + + function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_claimBeforeStart() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + uint256 startTimestamp = vestingScheduler.getVestingSchedule(address(superToken), alice, bob).cliffAndFlowDate; + vm.warp(startTimestamp - 1); + + vm.prank(bob); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertEq(success, false); + } + + function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_claimAfterValidityDate() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.warp(CLAIM_VALIDITY_DATE + 1); + vm.prank(bob); + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertEq(success, false); + } + + function test_executeCliffAndFlow_cannotReexecute() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.warp(CLAIM_VALIDITY_DATE - 1); + vm.startPrank(bob); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertEq(success, true); + vm.expectRevert(IVestingSchedulerV3.AlreadyExecuted.selector); + success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertEq(success, false); + vm.stopPrank(); + } + + function test_getMaximumNeededTokenAllowance_should_end_with_zero_if_extreme_ranges_are_used( + uint256 totalAmount, + uint32 totalDuration, + uint32 cliffPeriod, + uint32 startDate, + uint8 randomizer + ) public { + // Assume + randomizer = SafeCast.toUint8(bound(randomizer, 1, type(uint8).max)); + + if (startDate != 0) { + startDate = SafeCast.toUint32(bound(startDate, block.timestamp, 2524600800)); + } + + totalDuration = SafeCast.toUint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION(), 18250 days)); + vm.assume(cliffPeriod <= totalDuration - vestingScheduler.MIN_VESTING_DURATION()); + + uint256 beforeSenderBalance = superToken.balanceOf(alice); + + totalAmount = bound(totalAmount, 1, beforeSenderBalance); + vm.assume(totalAmount >= totalDuration); + vm.assume(totalAmount / totalDuration <= SafeCast.toUint256(type(int96).max)); + + // Arrange + IVestingSchedulerV3.VestingSchedule memory expectedSchedule = + _getExpectedScheduleFromAmountAndDuration(totalAmount, totalDuration, cliffPeriod, startDate, 0); + + // Assume we're not getting liquidated at the end: + vm.assume( + beforeSenderBalance + >= totalAmount + vestingScheduler.END_DATE_VALID_BEFORE() * SafeCast.toUint256(expectedSchedule.flowRate) + ); + + // Arrange allowance + vm.assume(superToken.allowance(alice, address(vestingScheduler)) == 0); + + vm.startPrank(alice); + superToken.revokeFlowPermissions(address(vestingScheduler)); + superToken.setFlowPermissions( + address(vestingScheduler), + true, // allowCreate + false, // allowUpdate + true, // allowDelete, + expectedSchedule.flowRate + ); + superToken.approve(address(vestingScheduler), vestingScheduler.getMaximumNeededTokenAllowance(expectedSchedule)); + vm.stopPrank(); + + // Act + vm.startPrank(alice); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, 0, EMPTY_CTX + ); + vm.stopPrank(); + + // Act + vm.warp(expectedSchedule.cliffAndFlowDate + (vestingScheduler.START_DATE_VALID_AFTER())); + assertTrue(vestingScheduler.executeCliffAndFlow(superToken, alice, bob)); + + if (randomizer % 2 == 0) { + // Let's set the allowance again half-way through. + vm.startPrank(alice); + superToken.approve( + address(vestingScheduler), + vestingScheduler.getMaximumNeededTokenAllowance( + vestingScheduler.getVestingSchedule(address(superToken), alice, bob) + ) + ); + vm.stopPrank(); + } + + // Act + vm.warp(expectedSchedule.endDate - (vestingScheduler.END_DATE_VALID_BEFORE())); + assertTrue(vestingScheduler.executeEndVesting(superToken, alice, bob)); + + // Assert + assertEq(superToken.allowance(alice, address(vestingScheduler)), 0, "No allowance should be left"); + (,,, int96 flowRateAllowance) = superToken.getFlowPermissions(alice, address(vestingScheduler)); + assertEq(flowRateAllowance, 0, "No flow rate allowance should be left"); + + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + + function test_executeEndVesting_scheduleNotClaimed() public { + _createClaimableVestingScheduleWithDefaultData(alice, bob); + vm.expectRevert(IVestingSchedulerV3.ScheduleNotClaimed.selector); + vestingScheduler.executeEndVesting(superToken, alice, bob); + } + + function test_getMaximumNeededTokenAllowance_with_claim_should_end_with_zero_if_extreme_ranges_are_used( + uint256 totalAmount, + uint32 totalDuration, + uint32 cliffPeriod, + uint32 startDate, + uint32 claimPeriod, + uint8 randomizer + ) public { + // Assume + randomizer = SafeCast.toUint8(bound(randomizer, 1, type(uint8).max)); + + if (startDate != 0) { + startDate = SafeCast.toUint32(bound(startDate, block.timestamp, 2524600800)); + } + + claimPeriod = SafeCast.toUint32(bound(claimPeriod, 1, 18250 days)); + vm.assume(claimPeriod >= cliffPeriod); + + totalDuration = SafeCast.toUint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION(), 18250 days)); + vm.assume(cliffPeriod <= totalDuration - vestingScheduler.MIN_VESTING_DURATION()); + + uint256 beforeSenderBalance = superToken.balanceOf(alice); + + totalAmount = bound(totalAmount, 1, beforeSenderBalance); + vm.assume(totalAmount >= totalDuration); + vm.assume(totalAmount / totalDuration <= SafeCast.toUint256(type(int96).max)); + + // Arrange + IVestingSchedulerV3.VestingSchedule memory expectedSchedule = + _getExpectedScheduleFromAmountAndDuration(totalAmount, totalDuration, cliffPeriod, startDate, claimPeriod); + + // Assume we're not getting liquidated at the end: + vm.assume( + beforeSenderBalance + >= totalAmount + vestingScheduler.END_DATE_VALID_BEFORE() * SafeCast.toUint256(expectedSchedule.flowRate) + ); + + // Arrange allowance + vm.assume(superToken.allowance(alice, address(vestingScheduler)) == 0); + + vm.startPrank(alice); + superToken.revokeFlowPermissions(address(vestingScheduler)); + bool willThereBeFullTransfer = + expectedSchedule.claimValidityDate >= expectedSchedule.endDate - vestingScheduler.END_DATE_VALID_BEFORE(); + if (!willThereBeFullTransfer) { + // No flow needed in this case. + superToken.setFlowPermissions( + address(vestingScheduler), + true, // allowCreate + false, // allowUpdate + true, // allowDelete, + expectedSchedule.flowRate + ); + } + superToken.approve(address(vestingScheduler), vestingScheduler.getMaximumNeededTokenAllowance(expectedSchedule)); + vm.stopPrank(); + + // Act + vm.startPrank(alice); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX + ); + vm.stopPrank(); + + // Act + vm.warp(expectedSchedule.claimValidityDate); + vm.startPrank(randomizer % 3 == 0 ? alice : bob); // Both sender and receiver can execute + assertTrue(vestingScheduler.executeCliffAndFlow(superToken, alice, bob)); + vm.stopPrank(); + + if (randomizer % 2 == 0) { + // Let's set the allowance again half-way through. + vm.startPrank(alice); + superToken.approve( + address(vestingScheduler), + vestingScheduler.getMaximumNeededTokenAllowance( + vestingScheduler.getVestingSchedule(address(superToken), alice, bob) + ) + ); + vm.stopPrank(); + } + + // Act + if (!willThereBeFullTransfer) { + vm.warp(expectedSchedule.endDate - vestingScheduler.END_DATE_VALID_BEFORE()); + assertTrue(vestingScheduler.executeEndVesting(superToken, alice, bob)); + } + + // Assert + assertEq(superToken.allowance(alice, address(vestingScheduler)), 0, "No allowance should be left"); + (,,, int96 flowRateAllowance) = superToken.getFlowPermissions(alice, address(vestingScheduler)); + assertEq(flowRateAllowance, 0, "No flow rate allowance should be left"); + + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + + function test_getSender_throws_when_invalid_host() public { + vm.expectRevert(IVestingSchedulerV3.HostInvalid.selector); + + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, NON_EMPTY_CTX + ); + vm.stopPrank(); + } + + function test_getSender_works_in_a_batch_call() public { + // Create a vesting schedule to update with a batch call that uses the context + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + _arrangeAllowances(alice, FLOW_RATE); + vm.stopPrank(); + + vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + + uint32 newEndDate = type(uint32).max - 1234; + + // Setting up a batch call. Superfluid Protocol will replace the emtpy context with data about the sender. That's where the sender is retrieved from. + ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1); + ops[0] = ISuperfluid.Operation({ + operationType: BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_APP_ACTION, + target: address(vestingScheduler), + data: abi.encodeCall(vestingScheduler.updateVestingSchedule, (superToken, bob, newEndDate, EMPTY_CTX)) + }); + + // Act + vm.prank(alice); + sf.host.batchCall(ops); + vm.stopPrank(); + + // Assert + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertEq(schedule.endDate, newEndDate); + } +} From 4b48ed086803fc521eba67c41bdc414ba0efa5e6 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:31:33 +0100 Subject: [PATCH 05/59] refactor: Improve VestingSchedulerV3 schedule update logic - Rename `updateVestingSchedule` to `updateVestingScheduleEndDate` - Optimize schedule update mechanism with more precise vesting calculations - Simplify flow rate and remainder amount handling - Update error handling and edge case management --- .../contracts/VestingSchedulerV3.sol | 92 +++++++++++-------- .../interface/IVestingSchedulerV3.sol | 2 +- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index af89da504c..8575261733 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -352,48 +352,42 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { newCtx = ctx; address sender = _getSender(ctx); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule storage schedule = vestingSchedules[agg.id]; + VestingSchedule memory schedule = agg.schedule; - // Only allow an update vesting exists + // Ensure vesting exists if (schedule.endDate == 0) revert ScheduleDoesNotExist(); // Dont allow update on schedule that should already have ended if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); // Update the total amount to be vested over the entire schedule (includes cliff and streamed amount) - schedule.totalAmount = totalAmount; + vestingSchedules[agg.id].totalAmount = totalAmount; // Update the amount already vested and the flow rate if the schedule has already started if (schedule.cliffAndFlowDate == 0) { // Get the current flow rate and the timestamp of the last flow update (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); - // Calculate the amount vested since the last flow update - uint256 vestedSinceLastUpdate = (block.timestamp - lastUpdated) * uint96(currentFlowRate); - // Accrue the amount already vested - schedule.alreadyVestedAmount += vestedSinceLastUpdate; + vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); + uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; // Ensure that the new total amount is not less than the amount already vested - if (schedule.alreadyVestedAmount >= totalAmount) revert InvalidUpdate(); + if (totalVestedAmount >= totalAmount) revert InvalidUpdate(); // Calculate the new flow rate and remainder amount - schedule.flowRate = SafeCast.toInt96( - SafeCast.toInt256(totalAmount - schedule.alreadyVestedAmount) + vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + SafeCast.toInt256(totalAmount - totalVestedAmount) / SafeCast.toInt256(schedule.endDate - block.timestamp) ); - schedule.remainderAmount = - SafeCast.toUint96((totalAmount - schedule.alreadyVestedAmount) % (schedule.endDate - block.timestamp)); // Update the flow from sender to receiver with the new calculated flow rate - superToken.updateFlowFrom(sender, receiver, schedule.flowRate); + superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); } else { - schedule.flowRate = SafeCast.toInt96( + vestingSchedules[agg.id].flowRate = SafeCast.toInt96( SafeCast.toInt256(totalAmount - schedule.cliffAmount) / SafeCast.toInt256(schedule.endDate - schedule.cliffAndFlowDate) ); - schedule.remainderAmount = - SafeCast.toUint96((totalAmount - schedule.cliffAmount) % (schedule.endDate - schedule.cliffAndFlowDate)); } /// FIXME : review events parameters @@ -402,9 +396,9 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { ); } - /// @dev IVestingScheduler.updateVestingSchedule implementation. + /// @dev IVestingSchedulerV3.updateVestingScheduleEndDate implementation. /// FIXME : review this function - function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) + function updateVestingScheduleEndDate(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) external returns (bytes memory newCtx) { @@ -413,25 +407,43 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); VestingSchedule memory schedule = agg.schedule; - if (endDate <= block.timestamp) revert TimeWindowInvalid(); + // Ensure vesting exists + if (schedule.endDate == 0) revert ScheduleDoesNotExist(); - // Note: Claimable schedules that have not been claimed cannot be updated + // Dont allow update on schedule that should already have ended + if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); - // Only allow an update if 1. vesting exists 2. executeCliffAndFlow() has been called - if (schedule.cliffAndFlowDate != 0 || schedule.endDate == 0) revert ScheduleNotFlowing(); + // Ensure end date is in the future + if (endDate <= block.timestamp) revert TimeWindowInvalid(); + // Update the schedule end date vestingSchedules[agg.id].endDate = endDate; - // Note: Nullify the remainder amount when complexity of updates is introduced. - vestingSchedules[agg.id].remainderAmount = 0; - emit VestingScheduleUpdated( - superToken, - sender, - receiver, - schedule.endDate, - endDate, - 0 // remainderAmount - ); + // Update the amount already vested and the flow rate if the schedule has already started + if (schedule.cliffAndFlowDate == 0) { + // Get the current flow rate and the timestamp of the last flow update + (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); + + // Accrue the amount already vested + vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); + uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; + + // Calculate the new flow rate + vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + SafeCast.toInt256(schedule.totalAmount - totalVestedAmount) + / SafeCast.toInt256((endDate - block.timestamp)) + ); + + // Update the flow from sender to receiver with the new calculated flow rate + superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); + } else { + vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + SafeCast.toInt256(schedule.totalAmount - schedule.cliffAmount) + / SafeCast.toInt256(endDate - schedule.cliffAndFlowDate) + ); + } + + emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, 0); } /// @dev IVestingScheduler.deleteVestingSchedule implementation. @@ -615,15 +627,15 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { // delete first the stream and unlock deposit amount. superToken.deleteFlowFrom(sender, receiver); - uint256 earlyEndCompensation = - totalVestedAmount < schedule.totalAmount ? schedule.totalAmount - totalVestedAmount : 0; - - // uint256 earlyEndCompensation = schedule.endDate >= block.timestamp - // ? (schedule.endDate - block.timestamp) * uint96(schedule.flowRate) + schedule.remainderAmount - // : 0; - // Note: we consider the compensation as failed if the stream is still ongoing after the end date. - bool didCompensationFail = schedule.endDate < block.timestamp; + bool didCompensationFail = true; + uint256 earlyEndCompensation; + + if (schedule.endDate >= block.timestamp) { + earlyEndCompensation = + totalVestedAmount < schedule.totalAmount ? schedule.totalAmount - totalVestedAmount : 0; + didCompensationFail = false; + } if (earlyEndCompensation != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index 3270a72447..bc28a942cb 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -259,7 +259,7 @@ interface IVestingSchedulerV3 { * @param endDate The timestamp when the stream should stop * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) */ - function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) + function updateVestingScheduleEndDate(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) external returns (bytes memory newCtx); From e1c0c7f366ab526a9fbeb69abf24bb265f9ef53e Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 25 Feb 2025 12:31:44 +0100 Subject: [PATCH 06/59] test: Enhance VestingSchedulerV3 test coverage for schedule updates - Add comprehensive test cases for `updateVestingScheduleEndDate` and `updateVestingScheduleAmount` - Improve error handling tests for non-existent vesting schedules - Refactor existing tests to use new method signatures - Optimize test scenarios for end date and amount updates --- .../scheduler/test/VestingSchedulerV3.t.sol | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 7222cc2082..8dadcdde23 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -415,7 +415,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.executeCliffAndFlow(superToken, alice, bob); vm.stopPrank(); vm.startPrank(alice); - vestingScheduler.updateVestingSchedule(superToken, bob, uint32(END_DATE + 1000), EMPTY_CTX); + vestingScheduler.updateVestingScheduleEndDate(superToken, bob, uint32(END_DATE + 1000), EMPTY_CTX); //assert storage data IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -440,23 +440,21 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(alice); vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); - vestingScheduler.updateVestingSchedule(superToken, bob, uint32(initialTimestamp - 1), EMPTY_CTX); + vestingScheduler.updateVestingScheduleEndDate(superToken, bob, uint32(initialTimestamp - 1), EMPTY_CTX); vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); - vestingScheduler.updateVestingSchedule(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); + vestingScheduler.updateVestingScheduleEndDate(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); } - function testCannotUpdateVestingScheduleIfNotRunning() public { - _createVestingScheduleWithDefaultData(alice, bob); + function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { vm.startPrank(alice); - vm.expectRevert(IVestingSchedulerV3.ScheduleNotFlowing.selector); - vestingScheduler.updateVestingSchedule(superToken, bob, END_DATE, EMPTY_CTX); - } + vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vestingScheduler.updateVestingScheduleEndDate(superToken, bob, END_DATE, EMPTY_CTX); - function testCannotUpdateVestingScheduleIfDataDontExist() public { - vm.startPrank(alice); - vm.expectRevert(IVestingSchedulerV3.ScheduleNotFlowing.selector); - vestingScheduler.updateVestingSchedule(superToken, bob, END_DATE, EMPTY_CTX); + newAmount = bound(newAmount, 1, type(uint256).max); + vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vestingScheduler.updateVestingScheduleAmount(superToken, bob, newAmount, EMPTY_CTX); + vm.stopPrank(); } function testDeleteVestingSchedule() public { @@ -542,16 +540,18 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { } function testExecuteCliffAndFlowWithUpdatedEndDate() public { - uint32 NEW_END_DATE = END_DATE - 1000; uint256 aliceInitialBalance = superToken.balanceOf(alice); uint256 bobInitialBalance = superToken.balanceOf(bob); - _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; vm.warp(initialTimestamp); + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); vm.expectEmit(true, true, true, true); emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); @@ -559,28 +559,38 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { emit VestingCliffAndFlowExecuted( superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation ); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertTrue(success, "executeVesting should return true"); vm.stopPrank(); + + uint32 NEW_END_DATE = END_DATE - 1000; vm.prank(alice); vm.expectEmit(true, true, true, true); emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, 0); - vestingScheduler.updateVestingSchedule(superToken, bob, NEW_END_DATE, EMPTY_CTX); + vestingScheduler.updateVestingScheduleEndDate(superToken, bob, NEW_END_DATE, EMPTY_CTX); + uint256 finalTimestamp = block.timestamp + 10 days - 3600; vm.warp(finalTimestamp); + + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(alice, bob); + uint256 adjustedAmountClosing = schedule.totalAmount + - (schedule.alreadyVestedAmount + (block.timestamp - lastUpdated) * uint96(currentFlowRate)); + vm.expectEmit(true, true, true, true); - uint256 timeDiffToEndDate = NEW_END_DATE > block.timestamp ? NEW_END_DATE - block.timestamp : 0; - uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); emit Transfer(alice, bob, adjustedAmountClosing); vm.expectEmit(true, true, true, true); emit VestingEndExecuted(superToken, alice, bob, NEW_END_DATE, adjustedAmountClosing, false); success = vestingScheduler.executeEndVesting(superToken, alice, bob); assertTrue(success, "executeCloseVesting should return true"); - uint256 aliceFinalBalance = superToken.balanceOf(alice); - uint256 bobFinalBalance = superToken.balanceOf(bob); - uint256 aliceShouldStream = (NEW_END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; - assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); - assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + + // Total amount streamed should be the same as the original amount planned (should just stream it faster with updated end date). + uint256 aliceShouldStream = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + + assertEq(aliceInitialBalance - superToken.balanceOf(alice), aliceShouldStream, "(sender) wrong final balance"); + assertEq(superToken.balanceOf(bob), bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); } function testExecuteCliffAndFlowRevertClosingTransfer() public { @@ -2370,14 +2380,13 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE); vestingScheduler.executeCliffAndFlow(superToken, alice, bob); - uint32 newEndDate = type(uint32).max - 1234; - + uint32 newEndDate = END_DATE + 1234; // Setting up a batch call. Superfluid Protocol will replace the emtpy context with data about the sender. That's where the sender is retrieved from. ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1); ops[0] = ISuperfluid.Operation({ operationType: BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_APP_ACTION, target: address(vestingScheduler), - data: abi.encodeCall(vestingScheduler.updateVestingSchedule, (superToken, bob, newEndDate, EMPTY_CTX)) + data: abi.encodeCall(vestingScheduler.updateVestingScheduleEndDate, (superToken, bob, newEndDate, EMPTY_CTX)) }); // Act From a219130b6762acdb13c6b2c3559d5a2ce957f42d Mon Sep 17 00:00:00 2001 From: didi Date: Tue, 25 Feb 2025 21:00:44 +0100 Subject: [PATCH 07/59] added support for batch calls using OPERATION_TYPE_ERC2771_FORWARD_CALL --- .../contracts/VestingSchedulerV3.sol | 32 +++++++++++++++---- .../scheduler/test/VestingSchedulerV3.t.sol | 30 +++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 8575261733..0b0ec719bc 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -9,11 +9,12 @@ import { } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; import {SuperAppBase} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBase.sol"; import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol"; +import {IRelayRecipient} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/utils/IRelayRecipient.sol"; import {IVestingSchedulerV3} from "./interface/IVestingSchedulerV3.sol"; import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; -contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { +contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipient { using SuperTokenV1Library for ISuperToken; ISuperfluid public immutable HOST; @@ -86,7 +87,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { _validateAndCreateVestingSchedule( ScheduleCreationParams({ superToken: superToken, - sender: msg.sender, + sender: _msgSender(), receiver: receiver, startDate: _normalizeStartDate(startDate), claimValidityDate: claimValidityDate, @@ -172,7 +173,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { _validateAndCreateVestingSchedule( mapCreateVestingScheduleParams( superToken, - msg.sender, + _msgSender(), receiver, totalAmount, totalDuration, @@ -518,7 +519,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { VestingSchedule memory schedule = agg.schedule; // Ensure that the caller is the sender or the receiver if the vesting schedule requires claiming. - if (msg.sender != agg.sender && msg.sender != agg.receiver) { + if (_msgSender() != agg.sender && _msgSender() != agg.receiver) { revert CannotClaimScheduleOnBehalf(); } @@ -527,7 +528,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { } delete vestingSchedules[agg.id].claimValidityDate; - emit VestingClaimed(agg.superToken, agg.sender, agg.receiver, msg.sender); + emit VestingClaimed(agg.superToken, agg.sender, agg.receiver, _msgSender()); } /// @dev IVestingScheduler.executeCliffAndFlow implementation. @@ -731,7 +732,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { if (msg.sender != address(HOST)) revert HostInvalid(); sender = HOST.decodeCtx(ctx).msgSender; } else { - sender = msg.sender; + sender = _msgSender(); } // This is an invariant and should never happen. assert(sender != address(0)); @@ -745,4 +746,23 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { function _getId(address superToken, address sender, address receiver) private pure returns (bytes32) { return keccak256(abi.encodePacked(superToken, sender, receiver)); } + + /// @dev IRelayRecipient.isTrustedForwarder implementation + function isTrustedForwarder(address forwarder) public view override returns(bool) { + return forwarder == HOST.getERC2771Forwarder(); + } + + /// @dev IRelayRecipient.versionRecipient implementation + function versionRecipient() external override pure returns (string memory) { + return "v1"; + } + + /// @dev gets the relayed sender from calldata as specified by EIP-2771, falling back to msg.sender + function _msgSender() internal virtual view returns (address) { + if(msg.data.length >= 20 && isTrustedForwarder(msg.sender)) { + return address(bytes20(msg.data[msg.data.length - 20:])); + } else { + return msg.sender; + } + } } diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 8dadcdde23..f42111eaf8 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2399,4 +2399,34 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.getVestingSchedule(address(superToken), alice, bob); assertEq(schedule.endDate, newEndDate); } + + function test_use_2771_forward_call() public { + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + _arrangeAllowances(alice, FLOW_RATE); + vm.stopPrank(); + + vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + + uint32 newEndDate = END_DATE + 1234; + ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1); + ops[0] = ISuperfluid.Operation({ + operationType: BatchOperation.OPERATION_TYPE_ERC2771_FORWARD_CALL, + target: address(vestingScheduler), + data: abi.encodeCall(vestingScheduler.updateVestingScheduleEndDate, (superToken, bob, newEndDate, EMPTY_CTX)) + }); + + // Act + vm.prank(alice); + sf.host.batchCall(ops); + vm.stopPrank(); + + // Assert + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertEq(schedule.endDate, newEndDate); + } } From 0a0c227b92ceba6b992cf5c7354920aac7334247 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Wed, 26 Feb 2025 15:13:22 +0100 Subject: [PATCH 08/59] refactor: Optimize VestingSchedulerV3 settlement and tracking mechanisms - Introduce `_settle` method to handle vesting schedule updates and tracking - Remove `totalAmount` from storage and improve vesting calculation logic - Add `lastUpdated` timestamp to track precise vesting progress - Enhance early termination and compensation handling - Commented out unused update methods with TODOs for future implementation --- .../contracts/VestingSchedulerV3.sol | 271 +++++++++++------- .../interface/IVestingSchedulerV3.sol | 2 +- 2 files changed, 175 insertions(+), 98 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 8575261733..f693b4eaec 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -255,10 +255,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { revert TimeWindowInvalid(); } - // Calculate the total amount to be vested over the entire schedule (includes cliff and streamed amount) - uint256 totalAmount = - params.cliffAmount + params.remainderAmount + (params.endDate - cliffAndFlowDate) * uint96(params.flowRate); - bytes32 id = _getId(address(params.superToken), params.sender, params.receiver); if (vestingSchedules[id].endDate != 0) revert ScheduleAlreadyExists(); @@ -269,8 +265,8 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { cliffAmount: params.cliffAmount, remainderAmount: params.remainderAmount, claimValidityDate: params.claimValidityDate, - totalAmount: totalAmount, - alreadyVestedAmount: 0 + alreadyVestedAmount: 0, + lastUpdated: 0 }); emit VestingScheduleCreated( @@ -342,62 +338,87 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { assert(_executeCliffAndFlow(agg)); } - /// @dev IVestingScheduler.updateVestingSchedule implementation. - function updateVestingScheduleAmount( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - bytes memory ctx - ) external returns (bytes memory newCtx) { - newCtx = ctx; - address sender = _getSender(ctx); - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + /// @dev IVestingScheduler.updateVestingScheduleAmount implementation. + /// FIXME : add testing for this function + /// FIXME : updateVestingScheduleFlowRateFromAmount(); + // function updateVestingScheduleAmount( + // ISuperToken superToken, + // address receiver, + // uint256 totalAmount, + // bytes memory ctx + // ) external returns (bytes memory newCtx) { + // newCtx = ctx; + // address sender = _getSender(ctx); + // ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + // VestingSchedule memory schedule = agg.schedule; + + // // Ensure vesting exists + // if (schedule.endDate == 0) revert ScheduleDoesNotExist(); + + // // Dont allow update on schedule that should already have ended + // if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); + + // // Update the total amount to be vested over the entire schedule (includes cliff and streamed amount) + // vestingSchedules[agg.id].totalAmount = totalAmount; + + // // Update the amount already vested and the flow rate if the schedule has already started + // if (schedule.cliffAndFlowDate == 0) { + // // Get the current flow rate and the timestamp of the last flow update + // /// FIXME : change to internal accounting (add ts in storage) + // (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); + + // // Accrue the amount already vested + // vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); + // uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; + + // // Ensure that the new total amount is not less than the amount already vested + // if (totalVestedAmount >= totalAmount) revert InvalidUpdate(); + + // // Calculate the new flow rate and remainder amount + // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + // SafeCast.toInt256(totalAmount - totalVestedAmount) + // / SafeCast.toInt256(schedule.endDate - block.timestamp) + // ); + + // // Update the flow from sender to receiver with the new calculated flow rate + // superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); + // } else { + // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + // SafeCast.toInt256(totalAmount - schedule.cliffAmount) + // / SafeCast.toInt256(schedule.endDate - schedule.cliffAndFlowDate) + // ); + // } + + // /// FIXME : review events parameters + // emit VestingScheduleUpdated( + // superToken, sender, receiver, schedule.endDate, schedule.endDate, schedule.remainderAmount + // ); + // } + + function _settle(ScheduleAggregate memory agg) internal returns (uint256 alreadyVestedAmount) { VestingSchedule memory schedule = agg.schedule; + delete vestingSchedules[agg.id].cliffAmount; - // Ensure vesting exists - if (schedule.endDate == 0) revert ScheduleDoesNotExist(); - - // Dont allow update on schedule that should already have ended - if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); + // Update the timestamp of the last schedule update + vestingSchedules[agg.id].lastUpdated = block.timestamp; - // Update the total amount to be vested over the entire schedule (includes cliff and streamed amount) - vestingSchedules[agg.id].totalAmount = totalAmount; + if (block.timestamp >= schedule.endDate) { + alreadyVestedAmount = _getTotalVestedAmount(schedule); - // Update the amount already vested and the flow rate if the schedule has already started - if (schedule.cliffAndFlowDate == 0) { - // Get the current flow rate and the timestamp of the last flow update - (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); + vestingSchedules[agg.id].alreadyVestedAmount = alreadyVestedAmount; + } else { + uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; // Accrue the amount already vested - vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); - uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; - - // Ensure that the new total amount is not less than the amount already vested - if (totalVestedAmount >= totalAmount) revert InvalidUpdate(); + vestingSchedules[agg.id].alreadyVestedAmount += + ((block.timestamp - actualLastUpdate) * uint96(schedule.flowRate)) + schedule.cliffAmount; - // Calculate the new flow rate and remainder amount - vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - SafeCast.toInt256(totalAmount - totalVestedAmount) - / SafeCast.toInt256(schedule.endDate - block.timestamp) - ); - - // Update the flow from sender to receiver with the new calculated flow rate - superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); - } else { - vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - SafeCast.toInt256(totalAmount - schedule.cliffAmount) - / SafeCast.toInt256(schedule.endDate - schedule.cliffAndFlowDate) - ); + alreadyVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; } - - /// FIXME : review events parameters - emit VestingScheduleUpdated( - superToken, sender, receiver, schedule.endDate, schedule.endDate, schedule.remainderAmount - ); } /// @dev IVestingSchedulerV3.updateVestingScheduleEndDate implementation. - /// FIXME : review this function + /// FIXME : updateVestingScheduleFlowRateFromEndDate(); function updateVestingScheduleEndDate(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) external returns (bytes memory newCtx) @@ -416,35 +437,103 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { // Ensure end date is in the future if (endDate <= block.timestamp) revert TimeWindowInvalid(); + // Ensure the schedule has already started + if (block.timestamp < schedule.cliffAndFlowDate) revert TimeWindowInvalid(); + // Update the schedule end date vestingSchedules[agg.id].endDate = endDate; - // Update the amount already vested and the flow rate if the schedule has already started - if (schedule.cliffAndFlowDate == 0) { - // Get the current flow rate and the timestamp of the last flow update - (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); + uint256 totalVestedAmount = _getTotalVestedAmount(schedule); - // Accrue the amount already vested - vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); - uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; + agg = _getVestingScheduleAggregate(superToken, sender, receiver); + uint256 alreadyVestedAmount = _settle(agg); - // Calculate the new flow rate - vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - SafeCast.toInt256(schedule.totalAmount - totalVestedAmount) - / SafeCast.toInt256((endDate - block.timestamp)) - ); + // Update the vesting flow rate + vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + SafeCast.toInt256(totalVestedAmount - alreadyVestedAmount) / SafeCast.toInt256(endDate - block.timestamp) + ); + + vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( + (totalVestedAmount - alreadyVestedAmount) + / (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * (endDate - block.timestamp)) + ); + if (schedule.cliffAndFlowDate == 0) { // Update the flow from sender to receiver with the new calculated flow rate - superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); - } else { - vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - SafeCast.toInt256(schedule.totalAmount - schedule.cliffAmount) - / SafeCast.toInt256(endDate - schedule.cliffAndFlowDate) - ); + if (newCtx.length != 0) { + newCtx = superToken.flowFromWithCtx(sender, receiver, vestingSchedules[agg.id].flowRate, newCtx); + } else { + superToken.flowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); + } } + // V2 Event emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, 0); - } + // V3 Event + // emit VestingScheduleUpdated([...]); + } + + /// @dev IVestingScheduler.updateVestingScheduleAmountAndEndDate implementation. + /// FIXME : add testing for this function + /// FIXME : updateVestingScheduleFlowRateFromAmountAndEndDate(); + // function updateVestingScheduleAmountAndEndDate( + // ISuperToken superToken, + // address receiver, + // uint256 totalAmount, + // uint32 endDate, + // bytes memory ctx + // ) external returns (bytes memory newCtx) { + // newCtx = ctx; + // address sender = _getSender(ctx); + // ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + // VestingSchedule memory schedule = agg.schedule; + + // // Ensure vesting exists + // if (schedule.endDate == 0) revert ScheduleDoesNotExist(); + + // // Dont allow update on schedule that should already have ended + // if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); + + // // Ensure end date is in the future + // if (endDate <= block.timestamp) revert TimeWindowInvalid(); + + // // Update the total amount to be vested over the entire schedule (includes cliff and streamed amount) + // vestingSchedules[agg.id].totalAmount = totalAmount; + + // // Update the schedule end date + // vestingSchedules[agg.id].endDate = endDate; + + // // Update the amount already vested and the flow rate if the schedule has already started + // if (schedule.cliffAndFlowDate == 0) { + // // Get the current flow rate and the timestamp of the last flow update + // (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); + + // // Accrue the amount already vested + // vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); + // uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; + + // // Ensure that the new total amount is not less than the amount already vested + // if (totalVestedAmount >= totalAmount) revert InvalidUpdate(); + + // // Calculate the new flow rate and remainder amount + // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + // SafeCast.toInt256(totalAmount - totalVestedAmount) / SafeCast.toInt256(endDate - block.timestamp) + // ); + + // // Update the flow from sender to receiver with the new calculated flow rate + // superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); + // } else { + // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( + // SafeCast.toInt256(totalAmount - schedule.cliffAmount) + // / SafeCast.toInt256(endDate - schedule.cliffAndFlowDate) + // ); + // } + + // /// FIXME : review events parameters + // emit VestingScheduleUpdated( + // superToken, sender, receiver, schedule.endDate, schedule.endDate, schedule.remainderAmount + // ); + // } /// @dev IVestingScheduler.deleteVestingSchedule implementation. function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) @@ -534,19 +623,15 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { function _executeCliffAndFlow(ScheduleAggregate memory agg) private returns (bool success) { VestingSchedule memory schedule = agg.schedule; + uint256 alreadyVestedAmount = _settle(agg); + // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. delete vestingSchedules[agg.id].cliffAndFlowDate; - delete vestingSchedules[agg.id].cliffAmount; - - // Compensate for the fact that flow will almost always be executed slightly later than scheduled. - uint256 flowDelayCompensation = (block.timestamp - schedule.cliffAndFlowDate) * uint96(schedule.flowRate); - // If there's cliff or compensation then transfer that amount. - if (schedule.cliffAmount != 0 || flowDelayCompensation != 0) { + // If there's cliff then transfer that amount. + if (alreadyVestedAmount != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. - assert(agg.superToken.transferFrom(agg.sender, agg.receiver, schedule.cliffAmount + flowDelayCompensation)); - - vestingSchedules[agg.id].alreadyVestedAmount += schedule.cliffAmount + flowDelayCompensation; + assert(agg.superToken.transferFrom(agg.sender, agg.receiver, alreadyVestedAmount)); } // Create a flow according to the vesting schedule configuration. @@ -559,7 +644,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { schedule.cliffAndFlowDate, schedule.flowRate, schedule.cliffAmount, - flowDelayCompensation + alreadyVestedAmount ); return true; @@ -597,9 +682,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { return true; } - function _getTotalVestedAmount(VestingSchedule memory schedule) private pure returns (uint256) { - return schedule.cliffAmount + schedule.remainderAmount - + (schedule.endDate - schedule.cliffAndFlowDate) * SafeCast.toUint256(schedule.flowRate); + function _getTotalVestedAmount(VestingSchedule memory schedule) private pure returns (uint256 totalVestedAmount) { + uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; + + totalVestedAmount = schedule.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount + + (schedule.endDate - actualLastUpdate) * SafeCast.toUint256(schedule.flowRate); } /// @dev IVestingScheduler.executeEndVesting implementation. @@ -617,25 +704,15 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { // If vesting is not running, we can't do anything, just emit failing event. if (_isFlowOngoing(superToken, sender, receiver)) { - // Get the current flow rate and the timestamp of the last flow update - (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); - - // Calculate the amount vested since the last flow update - uint256 totalVestedAmount = - schedule.alreadyVestedAmount + (block.timestamp - lastUpdated) * uint96(currentFlowRate); + uint256 alreadyVestedAmount = _settle(agg); + uint256 totalVestedAmount = _getTotalVestedAmount(schedule); // delete first the stream and unlock deposit amount. superToken.deleteFlowFrom(sender, receiver); // Note: we consider the compensation as failed if the stream is still ongoing after the end date. - bool didCompensationFail = true; - uint256 earlyEndCompensation; - - if (schedule.endDate >= block.timestamp) { - earlyEndCompensation = - totalVestedAmount < schedule.totalAmount ? schedule.totalAmount - totalVestedAmount : 0; - didCompensationFail = false; - } + bool didCompensationFail = schedule.endDate < block.timestamp; + uint256 earlyEndCompensation = totalVestedAmount - alreadyVestedAmount; if (earlyEndCompensation != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index bc28a942cb..3d1e3dd512 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -36,8 +36,8 @@ interface IVestingSchedulerV3 { uint256 cliffAmount; uint96 remainderAmount; uint32 claimValidityDate; - uint256 totalAmount; uint256 alreadyVestedAmount; + uint256 lastUpdated; } /** From e7524d8d525ca2f404fbf12eef6fb18d65241ddd Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Wed, 26 Feb 2025 15:13:39 +0100 Subject: [PATCH 09/59] test: Refine VestingSchedulerV3 settlement and testing logic - Update test cases to reflect recent refactoring of vesting schedule mechanisms - Remove commented-out test methods for non-existent schedule updates - Improve settlement calculation by using total amount to vest - Simplify vesting schedule verification in test scenarios --- .../scheduler/test/VestingSchedulerV3.t.sol | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 8dadcdde23..0545207ea5 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -221,8 +221,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { flowRate: flowRate, cliffAmount: cliffAmount, remainderAmount: 0, - totalAmount: cliffAmount + (endDate - cliffAndFlowDate) * uint96(flowRate), - alreadyVestedAmount: 0 + alreadyVestedAmount: 0, + lastUpdated: 0 }); } @@ -263,8 +263,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { cliffAmount: cliffAmount, remainderAmount: remainderAmount, claimValidityDate: claimPeriod == 0 ? 0 : startDate + claimPeriod, - totalAmount: totalAmount, - alreadyVestedAmount: 0 + alreadyVestedAmount: 0, + lastUpdated: 0 }); } @@ -446,16 +446,16 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.updateVestingScheduleEndDate(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); } - function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { - vm.startPrank(alice); - vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); - vestingScheduler.updateVestingScheduleEndDate(superToken, bob, END_DATE, EMPTY_CTX); + // function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { + // vm.startPrank(alice); + // vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + // vestingScheduler.updateVestingScheduleEndDate(superToken, bob, END_DATE, EMPTY_CTX); - newAmount = bound(newAmount, 1, type(uint256).max); - vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); - vestingScheduler.updateVestingScheduleAmount(superToken, bob, newAmount, EMPTY_CTX); - vm.stopPrank(); - } + // newAmount = bound(newAmount, 1, type(uint256).max); + // vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + // vestingScheduler.updateVestingScheduleAmount(superToken, bob, newAmount, EMPTY_CTX); + // vm.stopPrank(); + // } function testDeleteVestingSchedule() public { _createVestingScheduleWithDefaultData(alice, bob); @@ -545,6 +545,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); _createVestingScheduleWithDefaultData(alice, bob); + uint256 totalAmountToVest = CLIFF_TRANSFER_AMOUNT + ((END_DATE - CLIFF_DATE) * uint96(FLOW_RATE)); + vm.prank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); vm.startPrank(admin); @@ -575,9 +577,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(alice, bob); - uint256 adjustedAmountClosing = schedule.totalAmount - - (schedule.alreadyVestedAmount + (block.timestamp - lastUpdated) * uint96(currentFlowRate)); + + uint256 adjustedAmountClosing = totalAmountToVest - schedule.alreadyVestedAmount; vm.expectEmit(true, true, true, true); emit Transfer(alice, bob, adjustedAmountClosing); From c89da6b23a63af77d5a8bfa0a66579b339577fb8 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Wed, 26 Feb 2025 17:37:43 +0100 Subject: [PATCH 10/59] fix: Correct vesting amount calculation and settlement logic - Adjust settlement calculation to subtract cliff amount from already vested amount - Optimize total vested amount calculation by improving parentheses placement - Refactor `_executeCliffAndFlow` and `executeEndVesting` to handle vesting amounts more precisely --- .../scheduler/contracts/VestingSchedulerV3.sol | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index f693b4eaec..271a4ed583 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -623,12 +623,13 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { function _executeCliffAndFlow(ScheduleAggregate memory agg) private returns (bool success) { VestingSchedule memory schedule = agg.schedule; + // Settle the amount already vested uint256 alreadyVestedAmount = _settle(agg); // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. delete vestingSchedules[agg.id].cliffAndFlowDate; - // If there's cliff then transfer that amount. + // Transfer the amount already vested (includes the cliff, if any) if (alreadyVestedAmount != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. assert(agg.superToken.transferFrom(agg.sender, agg.receiver, alreadyVestedAmount)); @@ -644,7 +645,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { schedule.cliffAndFlowDate, schedule.flowRate, schedule.cliffAmount, - alreadyVestedAmount + alreadyVestedAmount - schedule.cliffAmount ); return true; @@ -686,7 +687,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; totalVestedAmount = schedule.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount - + (schedule.endDate - actualLastUpdate) * SafeCast.toUint256(schedule.flowRate); + + ((schedule.endDate - actualLastUpdate) * SafeCast.toUint256(schedule.flowRate)); } /// @dev IVestingScheduler.executeEndVesting implementation. @@ -699,14 +700,14 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ false); + uint256 alreadyVestedAmount = _settle(agg); + uint256 totalVestedAmount = _getTotalVestedAmount(schedule); + // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. delete vestingSchedules[agg.id]; // If vesting is not running, we can't do anything, just emit failing event. if (_isFlowOngoing(superToken, sender, receiver)) { - uint256 alreadyVestedAmount = _settle(agg); - uint256 totalVestedAmount = _getTotalVestedAmount(schedule); - // delete first the stream and unlock deposit amount. superToken.deleteFlowFrom(sender, receiver); From abc7caeb42c4e02fcc267164ab92a8da32b57104 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Wed, 26 Feb 2025 17:37:55 +0100 Subject: [PATCH 11/59] test: Refactor VestingSchedulerV3 test scenarios for precise settlement - Update test cases to improve vesting amount and timing calculations - Simplify test setup and execution flow - Enhance precision in settlement and balance verification - Optimize time warping and event expectation in test methods --- .../scheduler/test/VestingSchedulerV3.t.sol | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 0545207ea5..0e3958ca9a 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -551,10 +551,10 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); vm.startPrank(admin); - uint256 initialTimestamp = block.timestamp + 10 days + 1800; - vm.warp(initialTimestamp); + vm.warp(block.timestamp + CLIFF_DATE + 30 minutes); uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + vm.expectEmit(true, true, true, true); emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); vm.expectEmit(true, true, true, true); @@ -566,19 +566,20 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertTrue(success, "executeVesting should return true"); vm.stopPrank(); - uint32 NEW_END_DATE = END_DATE - 1000; + uint32 NEW_END_DATE = END_DATE - 4 hours; + + vm.warp(block.timestamp + 2 days); vm.prank(alice); vm.expectEmit(true, true, true, true); emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, 0); vestingScheduler.updateVestingScheduleEndDate(superToken, bob, NEW_END_DATE, EMPTY_CTX); - uint256 finalTimestamp = block.timestamp + 10 days - 3600; - vm.warp(finalTimestamp); - IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - uint256 adjustedAmountClosing = totalAmountToVest - schedule.alreadyVestedAmount; + vm.warp(schedule.endDate - 1 hours); + + uint256 adjustedAmountClosing = uint96(schedule.flowRate) * 1 hours + schedule.remainderAmount; vm.expectEmit(true, true, true, true); emit Transfer(alice, bob, adjustedAmountClosing); @@ -653,11 +654,16 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testCannotExecuteEndWithoutStreamRunning() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + // Create Vesting Schedule _createVestingScheduleWithDefaultData(alice, bob); + + // Sender increase allowance to vesting scheduler vm.prank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); - vm.startPrank(admin); - uint256 initialTimestamp = block.timestamp + 10 days + 1800; + + // Move time to 30 minutes after the `cliffAndFlowDate` + uint256 initialTimestamp = block.timestamp + CLIFF_DATE + 30 minutes; vm.warp(initialTimestamp); uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); @@ -670,9 +676,9 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation ); + vm.prank(admin); bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertTrue(success, "executeVesting should return true"); - vm.stopPrank(); vm.startPrank(alice); superToken.deleteFlow(alice, bob); vm.stopPrank(); @@ -691,7 +697,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint256 aliceInitialBalance = superToken.balanceOf(alice); uint256 bobInitialBalance = superToken.balanceOf(bob); - // # Create schedule + // Create schedule _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.startPrank(alice); @@ -724,9 +730,9 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 0, EMPTY_CTX ); - // --- + vm.stopPrank(); - // # Execute start + // Execute start vm.expectEmit(); emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT); @@ -734,30 +740,28 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { emit VestingCliffAndFlowExecuted( superToken, alice, bob, startAndCliffDate, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, uint256(0) ); - vm.stopPrank(); - vm.startPrank(admin); + vm.prank(admin); bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); - vm.stopPrank(); assertTrue(success, "executeVesting should return true"); - // --- - // # Execute end - uint256 finalTimestamp = END_DATE - 3600; - vm.warp(finalTimestamp); + // Execute end + vm.warp(END_DATE - 1 hours); - uint256 timeDiffToEndDate = END_DATE > block.timestamp ? END_DATE - block.timestamp : 0; - uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); + uint256 totalAmount = CLIFF_TRANSFER_AMOUNT + ((END_DATE - startAndCliffDate) * uint96(FLOW_RATE)); + uint256 adjustedAmountClosing = + totalAmount - CLIFF_TRANSFER_AMOUNT - ((block.timestamp - startAndCliffDate) * uint96(FLOW_RATE)); vm.expectEmit(); emit Transfer(alice, bob, adjustedAmountClosing); vm.expectEmit(); emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); - vm.startPrank(admin); + + vm.prank(admin); success = vestingScheduler.executeEndVesting(superToken, alice, bob); - vm.stopPrank(); + assertTrue(success, "executeCloseVesting should return true"); uint256 aliceFinalBalance = superToken.balanceOf(alice); @@ -765,7 +769,6 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint256 aliceShouldStream = (END_DATE - startAndCliffDate) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); - // --- } function test_createScheduleFromAmountAndDuration_reverts() public { From c4afde1bee44a70f5505e7d6d4c249b380f67558 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:06:35 +0100 Subject: [PATCH 12/59] fix: Improve vesting amount calculation and remainder tracking - Refactor `_getTotalVestedAmount` to enhance clarity and precision of vesting calculations - Correct remainder amount calculation by subtracting flow rate duration from total vested amount - Add comments to clarify vesting logic for different schedule stages - Optimize flow amount calculation with improved variable naming --- .../scheduler/contracts/VestingSchedulerV3.sol | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 271a4ed583..dd799d69bb 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -403,10 +403,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { vestingSchedules[agg.id].lastUpdated = block.timestamp; if (block.timestamp >= schedule.endDate) { + // If the schedule end date has passed, settle the total amount vested alreadyVestedAmount = _getTotalVestedAmount(schedule); - vestingSchedules[agg.id].alreadyVestedAmount = alreadyVestedAmount; } else { + // If the schedule end date has not passed, accrue the amount already vested uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; // Accrue the amount already vested @@ -455,7 +456,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( (totalVestedAmount - alreadyVestedAmount) - / (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * (endDate - block.timestamp)) + - (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * (endDate - block.timestamp)) ); if (schedule.cliffAndFlowDate == 0) { @@ -686,8 +687,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { function _getTotalVestedAmount(VestingSchedule memory schedule) private pure returns (uint256 totalVestedAmount) { uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; - totalVestedAmount = schedule.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount - + ((schedule.endDate - actualLastUpdate) * SafeCast.toUint256(schedule.flowRate)); + uint256 currentFlowDuration = schedule.endDate - actualLastUpdate; + uint256 currentFlowAmount = currentFlowDuration * SafeCast.toUint256(schedule.flowRate); + + totalVestedAmount = + schedule.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount + currentFlowAmount; } /// @dev IVestingScheduler.executeEndVesting implementation. From 9973e12bdcf9f018f7591898e9bb666a22740c33 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:06:57 +0100 Subject: [PATCH 13/59] test: Refactor early vesting end test with improved randomization - Replace hardcoded early end time calculation with bounded randomization - Remove unused total amount to vest calculation - Comment out previous early end time calculation method - Improve test scenario randomness and flexibility --- .../scheduler/test/VestingSchedulerV3.t.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 0e3958ca9a..3dc8f1547a 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -545,8 +545,6 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); _createVestingScheduleWithDefaultData(alice, bob); - uint256 totalAmountToVest = CLIFF_TRANSFER_AMOUNT + ((END_DATE - CLIFF_DATE) * uint96(FLOW_RATE)); - vm.prank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); vm.startPrank(admin); @@ -1130,10 +1128,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { if (randomizer % 7 != 0) { // # Test end execution on time. - console.log("Executing end vesting early."); - uint32 randomEarlyEndTime = - (vestingScheduler.END_DATE_VALID_BEFORE() - (vestingScheduler.END_DATE_VALID_BEFORE() / randomizer)); + // uint32 randomEarlyEndTime = + // (vestingScheduler.END_DATE_VALID_BEFORE() - (vestingScheduler.END_DATE_VALID_BEFORE() / randomizer)); + + uint32 randomEarlyEndTime = uint32(bound(randomizer, 1, vestingScheduler.END_DATE_VALID_BEFORE())); + vm.warp($.expectedSchedule.endDate - randomEarlyEndTime); vm.expectEmit(); uint256 earlyEndCompensation = randomEarlyEndTime * SafeCast.toUint256($.expectedSchedule.flowRate) From d7181b49375027020c10b40e2b4c59ed18f071e8 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:00:13 +0100 Subject: [PATCH 14/59] test: fallback to previous early vesting end time calculation method - Restore original early end time calculation using division - Remove bounded randomization approach - Simplify early vesting end time selection logic --- .../scheduler/test/VestingSchedulerV3.t.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 3dc8f1547a..48a671d459 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -1129,10 +1129,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { if (randomizer % 7 != 0) { // # Test end execution on time. console.log("Executing end vesting early."); - // uint32 randomEarlyEndTime = - // (vestingScheduler.END_DATE_VALID_BEFORE() - (vestingScheduler.END_DATE_VALID_BEFORE() / randomizer)); - - uint32 randomEarlyEndTime = uint32(bound(randomizer, 1, vestingScheduler.END_DATE_VALID_BEFORE())); + uint32 randomEarlyEndTime = + (vestingScheduler.END_DATE_VALID_BEFORE() - (vestingScheduler.END_DATE_VALID_BEFORE() / randomizer)); vm.warp($.expectedSchedule.endDate - randomEarlyEndTime); vm.expectEmit(); From 8c7798b32358eb8aa24f51bf593e53b96ca7b646 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:00:36 +0100 Subject: [PATCH 15/59] feat: Implement updateVestingScheduleFlowRateFromAmount and refine schedule update logic - Add implementation for `updateVestingScheduleFlowRateFromAmount` method - Enhance schedule update validation with comprehensive time window checks - Improve settlement logic in `_settle` method - Optimize flow rate and remainder amount calculations during schedule updates - Add comments to clarify update conditions and vesting logic --- .../contracts/VestingSchedulerV3.sol | 149 +++++++++--------- .../interface/IVestingSchedulerV3.sol | 14 ++ 2 files changed, 90 insertions(+), 73 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index dd799d69bb..eb5c107034 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -338,74 +338,18 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { assert(_executeCliffAndFlow(agg)); } - /// @dev IVestingScheduler.updateVestingScheduleAmount implementation. - /// FIXME : add testing for this function - /// FIXME : updateVestingScheduleFlowRateFromAmount(); - // function updateVestingScheduleAmount( - // ISuperToken superToken, - // address receiver, - // uint256 totalAmount, - // bytes memory ctx - // ) external returns (bytes memory newCtx) { - // newCtx = ctx; - // address sender = _getSender(ctx); - // ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - // VestingSchedule memory schedule = agg.schedule; - - // // Ensure vesting exists - // if (schedule.endDate == 0) revert ScheduleDoesNotExist(); - - // // Dont allow update on schedule that should already have ended - // if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); - - // // Update the total amount to be vested over the entire schedule (includes cliff and streamed amount) - // vestingSchedules[agg.id].totalAmount = totalAmount; - - // // Update the amount already vested and the flow rate if the schedule has already started - // if (schedule.cliffAndFlowDate == 0) { - // // Get the current flow rate and the timestamp of the last flow update - // /// FIXME : change to internal accounting (add ts in storage) - // (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); - - // // Accrue the amount already vested - // vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); - // uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; - - // // Ensure that the new total amount is not less than the amount already vested - // if (totalVestedAmount >= totalAmount) revert InvalidUpdate(); - - // // Calculate the new flow rate and remainder amount - // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - // SafeCast.toInt256(totalAmount - totalVestedAmount) - // / SafeCast.toInt256(schedule.endDate - block.timestamp) - // ); - - // // Update the flow from sender to receiver with the new calculated flow rate - // superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); - // } else { - // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - // SafeCast.toInt256(totalAmount - schedule.cliffAmount) - // / SafeCast.toInt256(schedule.endDate - schedule.cliffAndFlowDate) - // ); - // } - - // /// FIXME : review events parameters - // emit VestingScheduleUpdated( - // superToken, sender, receiver, schedule.endDate, schedule.endDate, schedule.remainderAmount - // ); - // } - function _settle(ScheduleAggregate memory agg) internal returns (uint256 alreadyVestedAmount) { VestingSchedule memory schedule = agg.schedule; + + // Delete the cliff amount and account for it in the already vested amount delete vestingSchedules[agg.id].cliffAmount; // Update the timestamp of the last schedule update vestingSchedules[agg.id].lastUpdated = block.timestamp; - if (block.timestamp >= schedule.endDate) { + if (block.timestamp > schedule.endDate) { // If the schedule end date has passed, settle the total amount vested - alreadyVestedAmount = _getTotalVestedAmount(schedule); - vestingSchedules[agg.id].alreadyVestedAmount = alreadyVestedAmount; + vestingSchedules[agg.id].alreadyVestedAmount = _getTotalVestedAmount(schedule); } else { // If the schedule end date has not passed, accrue the amount already vested uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; @@ -413,9 +357,67 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { // Accrue the amount already vested vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - actualLastUpdate) * uint96(schedule.flowRate)) + schedule.cliffAmount; + } + alreadyVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; + } - alreadyVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; + /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. + /// FIXME : add testing for this function + function updateVestingScheduleFlowRateFromAmount( + ISuperToken superToken, + address receiver, + uint256 newTotalAmount, + bytes memory ctx + ) external returns (bytes memory newCtx) { + newCtx = ctx; + address sender = _getSender(ctx); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + // Ensure vesting exists + if (schedule.endDate == 0) revert ScheduleDoesNotExist(); + + /* + Schedule update is not allowed if : + - schedule should already have ended + - cliff and flow date is in the future + */ + if (schedule.endDate < block.timestamp || block.timestamp < schedule.cliffAndFlowDate) { + revert TimeWindowInvalid(); } + + uint256 alreadyVestedAmount = _settle(agg); + + // Ensure that the new total amount is not less than the amount already vested + if (alreadyVestedAmount >= newTotalAmount) revert InvalidUpdate(); + + uint256 timeLeftToVest = schedule.endDate - block.timestamp; + + // Calculate the new flow rate based on the new total amount, the amount already vested and the time left to vest + int96 newFlowRate = SafeCast.toInt96( + SafeCast.toInt256(newTotalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest) + ); + + // Update the vesting flow rate and remainder amount + vestingSchedules[agg.id].flowRate = newFlowRate; + vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( + (newTotalAmount - alreadyVestedAmount) - (SafeCast.toUint256(newFlowRate) * timeLeftToVest) + ); + + // if the schedule is flowing, update the existing flow rate to the new calculated flow rate + if (schedule.cliffAndFlowDate == 0) { + if (newCtx.length != 0) { + newCtx = superToken.flowFromWithCtx(sender, receiver, newFlowRate, newCtx); + } else { + superToken.flowFrom(sender, receiver, newFlowRate); + } + } + + // Emit VestingSchedulerV2 event for backward compatibility + emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, schedule.endDate, 0); + + // Emit VestingSchedulerV3 event for additional data + // emit VestingScheduleUpdated([...]); } /// @dev IVestingSchedulerV3.updateVestingScheduleEndDate implementation. @@ -432,21 +434,21 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { // Ensure vesting exists if (schedule.endDate == 0) revert ScheduleDoesNotExist(); - // Dont allow update on schedule that should already have ended - if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); - - // Ensure end date is in the future - if (endDate <= block.timestamp) revert TimeWindowInvalid(); - - // Ensure the schedule has already started - if (block.timestamp < schedule.cliffAndFlowDate) revert TimeWindowInvalid(); + /* + Schedule update is not allowed if : + - schedule should already have ended + - new end date is in the past + - cliff and flow date is in the future + */ + if ( + schedule.endDate < block.timestamp || endDate <= block.timestamp + || block.timestamp < schedule.cliffAndFlowDate + ) revert TimeWindowInvalid(); // Update the schedule end date vestingSchedules[agg.id].endDate = endDate; uint256 totalVestedAmount = _getTotalVestedAmount(schedule); - - agg = _getVestingScheduleAggregate(superToken, sender, receiver); uint256 alreadyVestedAmount = _settle(agg); // Update the vesting flow rate @@ -468,9 +470,10 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { } } - // V2 Event + // Emit VestingSchedulerV2 event for backward compatibility emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, 0); - // V3 Event + + // Emit VestingSchedulerV3 event for additional data // emit VestingScheduleUpdated([...]); } diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index 3d1e3dd512..d8ccc640e3 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -251,6 +251,20 @@ interface IVestingSchedulerV3 { uint96 remainderAmount ); + /** + * @dev Updates a vesting schedule flow rate based on a new total amount to be vested + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param newTotalAmount The new total amount to be vested + * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + */ + function updateVestingScheduleFlowRateFromAmount( + ISuperToken superToken, + address receiver, + uint256 newTotalAmount, + bytes memory ctx + ) external returns (bytes memory newCtx); + /** * @dev Updates the end date for a vesting schedule which already reached the cliff * @notice When updating, there's no restriction to the end date other than not being in the past From 6fdf37e5b553e07d2aaac4593fbf36d01f5956cf Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:48:09 +0100 Subject: [PATCH 16/59] feat: Add detailed events for vesting schedule updates in VestingSchedulerV3 - Implement new events `VestingScheduleTotalAmountUpdated` and `VestingScheduleEndDateUpdated` - Update event emissions to include remainder amount and flow rate details - Rename `updateVestingScheduleEndDate` to `updateVestingScheduleFlowRateFromEndDate` - Enhance event logging with comprehensive schedule update information --- .../contracts/VestingSchedulerV3.sol | 43 +++++++++++---- .../interface/IVestingSchedulerV3.sol | 53 +++++++++++++++++-- 2 files changed, 83 insertions(+), 13 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index eb5c107034..d111ab65d2 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -404,7 +404,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { (newTotalAmount - alreadyVestedAmount) - (SafeCast.toUint256(newFlowRate) * timeLeftToVest) ); - // if the schedule is flowing, update the existing flow rate to the new calculated flow rate + // If the schedule is flowing, update the existing flow rate to the new calculated flow rate if (schedule.cliffAndFlowDate == 0) { if (newCtx.length != 0) { newCtx = superToken.flowFromWithCtx(sender, receiver, newFlowRate, newCtx); @@ -414,18 +414,30 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { } // Emit VestingSchedulerV2 event for backward compatibility - emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, schedule.endDate, 0); + emit VestingScheduleUpdated( + superToken, sender, receiver, schedule.endDate, schedule.endDate, vestingSchedules[agg.id].remainderAmount + ); // Emit VestingSchedulerV3 event for additional data - // emit VestingScheduleUpdated([...]); + emit VestingScheduleTotalAmountUpdated( + superToken, + sender, + receiver, + schedule.flowRate, + newFlowRate, + _getTotalVestedAmount(schedule), + newTotalAmount, + vestingSchedules[agg.id].remainderAmount + ); } /// @dev IVestingSchedulerV3.updateVestingScheduleEndDate implementation. - /// FIXME : updateVestingScheduleFlowRateFromEndDate(); - function updateVestingScheduleEndDate(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) - external - returns (bytes memory newCtx) - { + function updateVestingScheduleFlowRateFromEndDate( + ISuperToken superToken, + address receiver, + uint32 endDate, + bytes memory ctx + ) external returns (bytes memory newCtx) { newCtx = ctx; address sender = _getSender(ctx); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); @@ -471,10 +483,21 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { } // Emit VestingSchedulerV2 event for backward compatibility - emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, 0); + emit VestingScheduleUpdated( + superToken, sender, receiver, schedule.endDate, endDate, vestingSchedules[agg.id].remainderAmount + ); // Emit VestingSchedulerV3 event for additional data - // emit VestingScheduleUpdated([...]); + emit VestingScheduleEndDateUpdated( + superToken, + sender, + receiver, + schedule.endDate, + endDate, + schedule.flowRate, + vestingSchedules[agg.id].flowRate, + vestingSchedules[agg.id].remainderAmount + ); } /// @dev IVestingScheduler.updateVestingScheduleAmountAndEndDate implementation. diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index d8ccc640e3..fbcab6ba5e 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -251,6 +251,50 @@ interface IVestingSchedulerV3 { uint96 remainderAmount ); + /** + * @dev Event emitted when a vesting schedule's total amount is updated + * @param superToken The superToken being vested + * @param sender The vesting sender + * @param receiver The vesting receiver + * @param previousFlowRate The flow rate before the update + * @param newFlowRate The flow rate after the update + * @param previousTotalAmount The total amount to be vested before the update + * @param newTotalAmount The total amount to be vested after the update + * @param remainderAmount The remainder amount that cannot be streamed + */ + event VestingScheduleTotalAmountUpdated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + int96 previousFlowRate, + int96 newFlowRate, + uint256 previousTotalAmount, + uint256 newTotalAmount, + uint96 remainderAmount + ); + + /** + * @dev Event emitted when a vesting schedule's end date is updated + * @param superToken The superToken being vested + * @param sender The vesting sender + * @param receiver The vesting receiver + * @param oldEndDate The end date before the update + * @param endDate The end date after the update + * @param previousFlowRate The flow rate before the update + * @param newFlowRate The flow rate after the update + * @param remainderAmount The remainder amount that cannot be streamed + */ + event VestingScheduleEndDateUpdated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 oldEndDate, + uint32 endDate, + int96 previousFlowRate, + int96 newFlowRate, + uint96 remainderAmount + ); + /** * @dev Updates a vesting schedule flow rate based on a new total amount to be vested * @param superToken SuperToken to be vested @@ -273,9 +317,12 @@ interface IVestingSchedulerV3 { * @param endDate The timestamp when the stream should stop * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) */ - function updateVestingScheduleEndDate(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) - external - returns (bytes memory newCtx); + function updateVestingScheduleFlowRateFromEndDate( + ISuperToken superToken, + address receiver, + uint32 endDate, + bytes memory ctx + ) external returns (bytes memory newCtx); /** * @dev Event emitted on deletion of a vesting schedule From 3d9e7ea0a7104d213afb0e3ad3c1fda1b93b07c6 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:48:27 +0100 Subject: [PATCH 17/59] test: Update VestingSchedulerV3 test cases for flow rate and end date updates - Modify test methods to use `updateVestingScheduleFlowRateFromEndDate` - Enhance calculation of expected remainder and new flow rate - Update test assertions to verify total vested amount and balance transfers - Refactor batch operation encoding to match new method signature --- .../scheduler/test/VestingSchedulerV3.t.sol | 39 +++++++++++++------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 48a671d459..3232cda227 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -415,7 +415,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.executeCliffAndFlow(superToken, alice, bob); vm.stopPrank(); vm.startPrank(alice); - vestingScheduler.updateVestingScheduleEndDate(superToken, bob, uint32(END_DATE + 1000), EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(END_DATE + 1000), EMPTY_CTX); //assert storage data IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -440,10 +440,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(alice); vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleEndDate(superToken, bob, uint32(initialTimestamp - 1), EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate( + superToken, bob, uint32(initialTimestamp - 1), EMPTY_CTX + ); vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleEndDate(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); } // function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { @@ -542,8 +544,10 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testExecuteCliffAndFlowWithUpdatedEndDate() public { uint256 aliceInitialBalance = superToken.balanceOf(alice); uint256 bobInitialBalance = superToken.balanceOf(bob); + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); _createVestingScheduleWithDefaultData(alice, bob); + uint256 totalAmount = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; vm.prank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); @@ -566,11 +570,23 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint32 NEW_END_DATE = END_DATE - 4 hours; + // Expected remainder = (total amount - vested amount) - (new flow rate * time left to vest) + vm.warp(block.timestamp + 2 days); - vm.prank(alice); + + uint256 timeLeftToVest = NEW_END_DATE - block.timestamp; + uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + int96 newFlowRate = + SafeCast.toInt96(SafeCast.toInt256(totalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest)); + + uint96 expectedRemainder = + SafeCast.toUint96((totalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, 0); - vestingScheduler.updateVestingScheduleEndDate(superToken, bob, NEW_END_DATE, EMPTY_CTX); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, expectedRemainder); + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE, EMPTY_CTX); IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -586,11 +602,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { success = vestingScheduler.executeEndVesting(superToken, alice, bob); assertTrue(success, "executeCloseVesting should return true"); - // Total amount streamed should be the same as the original amount planned (should just stream it faster with updated end date). - uint256 aliceShouldStream = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; - - assertEq(aliceInitialBalance - superToken.balanceOf(alice), aliceShouldStream, "(sender) wrong final balance"); - assertEq(superToken.balanceOf(bob), bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + assertEq(aliceInitialBalance - superToken.balanceOf(alice), totalAmount, "(sender) wrong final balance"); + assertEq(superToken.balanceOf(bob), bobInitialBalance + totalAmount, "(receiver) wrong final balance"); } function testExecuteCliffAndFlowRevertClosingTransfer() public { @@ -2388,7 +2401,9 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ops[0] = ISuperfluid.Operation({ operationType: BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_APP_ACTION, target: address(vestingScheduler), - data: abi.encodeCall(vestingScheduler.updateVestingScheduleEndDate, (superToken, bob, newEndDate, EMPTY_CTX)) + data: abi.encodeCall( + vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate, EMPTY_CTX) + ) }); // Act From 40c1ec93ef2dd2a6de70f0a397becd520ddd374c Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 17:52:08 +0100 Subject: [PATCH 18/59] refactor: Remove commented-out updateVestingScheduleAmountAndEndDate method - Delete unused and commented-out method from VestingSchedulerV3 - Clean up code by removing deprecated implementation with TODO comments - Simplify contract by eliminating unimplemented method --- .../contracts/VestingSchedulerV3.sol | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index d111ab65d2..f8502974fb 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -500,68 +500,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase { ); } - /// @dev IVestingScheduler.updateVestingScheduleAmountAndEndDate implementation. - /// FIXME : add testing for this function - /// FIXME : updateVestingScheduleFlowRateFromAmountAndEndDate(); - // function updateVestingScheduleAmountAndEndDate( - // ISuperToken superToken, - // address receiver, - // uint256 totalAmount, - // uint32 endDate, - // bytes memory ctx - // ) external returns (bytes memory newCtx) { - // newCtx = ctx; - // address sender = _getSender(ctx); - // ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - // VestingSchedule memory schedule = agg.schedule; - - // // Ensure vesting exists - // if (schedule.endDate == 0) revert ScheduleDoesNotExist(); - - // // Dont allow update on schedule that should already have ended - // if (schedule.endDate < block.timestamp) revert TimeWindowInvalid(); - - // // Ensure end date is in the future - // if (endDate <= block.timestamp) revert TimeWindowInvalid(); - - // // Update the total amount to be vested over the entire schedule (includes cliff and streamed amount) - // vestingSchedules[agg.id].totalAmount = totalAmount; - - // // Update the schedule end date - // vestingSchedules[agg.id].endDate = endDate; - - // // Update the amount already vested and the flow rate if the schedule has already started - // if (schedule.cliffAndFlowDate == 0) { - // // Get the current flow rate and the timestamp of the last flow update - // (uint256 lastUpdated, int96 currentFlowRate,,) = superToken.getFlowInfo(sender, receiver); - - // // Accrue the amount already vested - // vestingSchedules[agg.id].alreadyVestedAmount += ((block.timestamp - lastUpdated) * uint96(currentFlowRate)); - // uint256 totalVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; - - // // Ensure that the new total amount is not less than the amount already vested - // if (totalVestedAmount >= totalAmount) revert InvalidUpdate(); - - // // Calculate the new flow rate and remainder amount - // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - // SafeCast.toInt256(totalAmount - totalVestedAmount) / SafeCast.toInt256(endDate - block.timestamp) - // ); - - // // Update the flow from sender to receiver with the new calculated flow rate - // superToken.updateFlowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); - // } else { - // vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - // SafeCast.toInt256(totalAmount - schedule.cliffAmount) - // / SafeCast.toInt256(endDate - schedule.cliffAndFlowDate) - // ); - // } - - // /// FIXME : review events parameters - // emit VestingScheduleUpdated( - // superToken, sender, receiver, schedule.endDate, schedule.endDate, schedule.remainderAmount - // ); - // } - /// @dev IVestingScheduler.deleteVestingSchedule implementation. function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) external From 393e508c19795abf95227cc499b365a67bf5c3d7 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 27 Feb 2025 17:52:20 +0100 Subject: [PATCH 19/59] test: Add comprehensive test cases for VestingSchedulerV3 schedule updates - Implement test scenarios for updating vesting schedules with longer and shorter durations - Add test cases for updating vesting schedule amounts (both larger and smaller) - Uncomment and update previously commented-out test method for non-existent schedule updates - Enhance test coverage for flow rate and remainder calculations during schedule modifications --- .../scheduler/test/VestingSchedulerV3.t.sol | 244 +++++++++++++++++- 1 file changed, 230 insertions(+), 14 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 3232cda227..5b6f904ae3 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -448,16 +448,16 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); } - // function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { - // vm.startPrank(alice); - // vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); - // vestingScheduler.updateVestingScheduleEndDate(superToken, bob, END_DATE, EMPTY_CTX); + function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { + vm.startPrank(alice); + vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE, EMPTY_CTX); - // newAmount = bound(newAmount, 1, type(uint256).max); - // vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); - // vestingScheduler.updateVestingScheduleAmount(superToken, bob, newAmount, EMPTY_CTX); - // vm.stopPrank(); - // } + newAmount = bound(newAmount, 1, type(uint256).max); + vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); + vm.stopPrank(); + } function testDeleteVestingSchedule() public { _createVestingScheduleWithDefaultData(alice, bob); @@ -541,7 +541,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); } - function testExecuteCliffAndFlowWithUpdatedEndDate() public { + function testExecuteCliffAndFlowWithUpdatedEndDate_longerDuration() public { uint256 aliceInitialBalance = superToken.balanceOf(alice); uint256 bobInitialBalance = superToken.balanceOf(bob); @@ -568,9 +568,71 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertTrue(success, "executeVesting should return true"); vm.stopPrank(); - uint32 NEW_END_DATE = END_DATE - 4 hours; + uint32 NEW_END_DATE = END_DATE + 4 hours; + + vm.warp(block.timestamp + 2 days); + + uint256 timeLeftToVest = NEW_END_DATE - block.timestamp; + uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + int96 newFlowRate = + SafeCast.toInt96(SafeCast.toInt256(totalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest)); + + uint96 expectedRemainder = + SafeCast.toUint96((totalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + + vm.expectEmit(true, true, true, true); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, expectedRemainder); + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE, EMPTY_CTX); + + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + uint256 earlyEndDelay = 1 hours; + vm.warp(schedule.endDate - earlyEndDelay); + + uint256 adjustedAmountClosing = uint96(schedule.flowRate) * earlyEndDelay + schedule.remainderAmount; + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, NEW_END_DATE, adjustedAmountClosing, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + + assertEq(aliceInitialBalance - superToken.balanceOf(alice), totalAmount, "(sender) wrong final balance"); + assertEq(superToken.balanceOf(bob), bobInitialBalance + totalAmount, "(receiver) wrong final balance"); + } + + function testExecuteCliffAndFlowWithUpdatedEndDate_shorterDuration() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); + _createVestingScheduleWithDefaultData(alice, bob); + uint256 totalAmount = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; - // Expected remainder = (total amount - vested amount) - (new flow rate * time left to vest) + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + + vm.warp(block.timestamp + CLIFF_DATE + 30 minutes); + + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + vm.stopPrank(); + + uint32 NEW_END_DATE = END_DATE - 4 hours; vm.warp(block.timestamp + 2 days); @@ -591,9 +653,10 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - vm.warp(schedule.endDate - 1 hours); + uint256 earlyEndDelay = 1 hours; + vm.warp(schedule.endDate - earlyEndDelay); - uint256 adjustedAmountClosing = uint96(schedule.flowRate) * 1 hours + schedule.remainderAmount; + uint256 adjustedAmountClosing = uint96(schedule.flowRate) * earlyEndDelay + schedule.remainderAmount; vm.expectEmit(true, true, true, true); emit Transfer(alice, bob, adjustedAmountClosing); @@ -606,6 +669,159 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertEq(superToken.balanceOf(bob), bobInitialBalance + totalAmount, "(receiver) wrong final balance"); } + function testExecuteCliffAndFlowWithUpdatedAmount_largerAmount() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); + _createVestingScheduleWithDefaultData(alice, bob); + uint256 totalAmount = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + + vm.warp(block.timestamp + CLIFF_DATE + 30 minutes); + + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + vm.stopPrank(); + + uint256 newTotalAmount = totalAmount + (totalAmount / 2); + + vm.warp(block.timestamp + 2 days); + + uint256 timeLeftToVest = END_DATE - block.timestamp; + uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + int96 newFlowRate = SafeCast.toInt96( + SafeCast.toInt256(newTotalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest) + ); + + uint96 expectedRemainder = + SafeCast.toUint96((newTotalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + + vm.expectEmit(true, true, true, true); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, END_DATE, expectedRemainder); + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount, EMPTY_CTX); + + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + uint256 earlyEndDelay = 1 hours; + vm.warp(schedule.endDate - earlyEndDelay); + + uint256 adjustedAmountClosing = uint96(schedule.flowRate) * earlyEndDelay + schedule.remainderAmount; + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + + uint256 expectedTotalAmountTransferred = + alreadyVestedAmount + (timeLeftToVest * uint96(newFlowRate)) + schedule.remainderAmount; + + assertEq( + aliceInitialBalance - superToken.balanceOf(alice), + expectedTotalAmountTransferred, + "(sender) wrong final balance" + ); + assertEq( + superToken.balanceOf(bob), + bobInitialBalance + expectedTotalAmountTransferred, + "(receiver) wrong final balance" + ); + } + + function testExecuteCliffAndFlowWithUpdatedAmount_smallerAmount() public { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); + _createVestingScheduleWithDefaultData(alice, bob); + uint256 totalAmount = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + + vm.warp(block.timestamp + CLIFF_DATE + 30 minutes); + + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + vm.stopPrank(); + + uint256 newTotalAmount = totalAmount - (totalAmount / 10000); + + vm.warp(block.timestamp + 2 days); + uint256 timeLeftToVest = END_DATE - block.timestamp; + + uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + + int96 newFlowRate = SafeCast.toInt96( + SafeCast.toInt256(newTotalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest) + ); + + uint96 expectedRemainder = + SafeCast.toUint96((newTotalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + + vm.expectEmit(true, true, true, true); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, END_DATE, expectedRemainder); + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount, EMPTY_CTX); + + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + uint256 earlyEndDelay = 1 hours; + vm.warp(schedule.endDate - earlyEndDelay); + + uint256 adjustedAmountClosing = uint96(schedule.flowRate) * earlyEndDelay + schedule.remainderAmount; + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + + uint256 expectedTotalAmountTransferred = + alreadyVestedAmount + (timeLeftToVest * uint96(newFlowRate)) + schedule.remainderAmount; + + assertEq( + aliceInitialBalance - superToken.balanceOf(alice), + expectedTotalAmountTransferred, + "(sender) wrong final balance" + ); + assertEq( + superToken.balanceOf(bob), + bobInitialBalance + expectedTotalAmountTransferred, + "(receiver) wrong final balance" + ); + } + function testExecuteCliffAndFlowRevertClosingTransfer() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); _createVestingScheduleWithDefaultData(alice, bob); From a11578a78b23a2d4ce6459f229bf15a8b50fc2fa Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:18:49 +0100 Subject: [PATCH 20/59] test: added testing scenarios --- .../scheduler/test/VestingSchedulerV3.t.sol | 329 ++++++++++++++++++ 1 file changed, 329 insertions(+) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 5b6f904ae3..25be97daf8 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2264,6 +2264,57 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { testAssertScheduleDoesNotExist(address(superToken), alice, bob); } + function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_receiverClaim_withUpdatedAmountAfterClaim() + public + { + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + vm.warp(block.timestamp + CLIFF_DATE + 30 minutes); + + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, CLIFF_TRANSFER_AMOUNT + flowDelayCompensation); + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + + vm.prank(bob); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeVesting should return true"); + + // Move time to 1 hour before end of vesting + uint256 finalTimestamp = block.timestamp + 10 days - 1 hours; + vm.warp(finalTimestamp); + + uint256 timeDiffToEndDate = END_DATE > block.timestamp ? END_DATE - block.timestamp : 0; + uint256 adjustedAmountClosing = timeDiffToEndDate * uint96(FLOW_RATE); + + vm.expectEmit(true, true, true, true); + emit Transfer(alice, bob, adjustedAmountClosing); + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, END_DATE, adjustedAmountClosing, false); + + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeCloseVesting should return true"); + + uint256 aliceFinalBalance = superToken.balanceOf(alice); + uint256 bobFinalBalance = superToken.balanceOf(bob); + uint256 aliceShouldStream = (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + + assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); + assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); + + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + function test_executeCliffAndFlow_claimAfterEndDate(uint256 delayAfterEndDate, uint256 claimDate, uint8 randomizer) public { @@ -2632,4 +2683,282 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.getVestingSchedule(address(superToken), alice, bob); assertEq(schedule.endDate, newEndDate); } + + // VestingSchedulerV3 Scenarios : + /* Scenario 1 : + Assuming a 5 month long schedule: + + Define schedule for 1000 USDC (guaranteeing only 200USDC for 1 month) + + One month later, update schedule to 1400 USDC (with four months left and 200USDC already transferred, this means 300 USDC/mo) + + One month later, update schedule to 1100 USDC (with 3 months left, and 500 USDC already transferred, this means 200 USDC/mo) + + At the time of the last month, with the total amount set at 1500 USDC, the stream shall be closed up to ±24hrs early, settling any differences to the expected total + */ + function testVestingSchedulerV3_scenario_notClaimable() public { + // Initial setup + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + // Define constants for the test + uint256 initialTotalAmount = 1000 ether; + uint32 totalDuration = 150 days; // 5 months + + // Set up permissions for Alice + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); // Allow any flow rate + + // Create the initial vesting schedule + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + uint32 startDate = uint32(block.timestamp + 10 days); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + initialTotalAmount, + totalDuration, + startDate, + 0, // No cliff period + 0, // No claim period + EMPTY_CTX + ); + vm.stopPrank(); + + // Get the schedule + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + // Verify initial schedule + assertEq(schedule.endDate - startDate, totalDuration, "Duration should be 5 months"); + + // Warp to cliff date and execute cliff and flow + vm.warp(startDate); + + vm.prank(alice); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeCliffAndFlow should return true"); + + vm.warp(startDate + 30 days); + // Verify Bob received the first month's amount (200 USDC) + uint256 firstMonthAmount = initialTotalAmount / 5; // 200 USDC + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + firstMonthAmount, + firstMonthAmount * 10 / 10_000, + "Bob should have received 200 USDC after first month" + ); + + // Warp to second month and update schedule to 1400 USDC + vm.warp(startDate + 2 * 30 days); + + uint256 secondUpdateAmount = 1400 ether; + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, secondUpdateAmount, EMPTY_CTX); + + // Verify the flow rate has been updated + schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + uint256 remainingMonths = 4; + uint256 remainingAmount = secondUpdateAmount - schedule.alreadyVestedAmount; + int96 expectedFlowRate = + SafeCast.toInt96(SafeCast.toInt256(remainingAmount / (schedule.endDate - block.timestamp))); + assertEq(schedule.flowRate, expectedFlowRate, "Flow rate should be updated for 300 USDC/month"); + + // Warp to third month and update schedule to 1100 USDC + vm.warp(startDate + 3 * 30 days); + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1100 ether, EMPTY_CTX); + + // Verify the flow rate has been updated again + schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + // Calculate new flow rate for remaining 3 months + remainingMonths = 3; + remainingAmount = 1100 ether - schedule.alreadyVestedAmount; + expectedFlowRate = SafeCast.toInt96(SafeCast.toInt256(remainingAmount / (schedule.endDate - block.timestamp))); + assertEq(schedule.flowRate, expectedFlowRate, "Flow rate should be updated for 200 USDC/month"); + + // Warp to last month and update schedule to 1500 USDC + vm.warp(startDate + 4 * 30 days); + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1500 ether, EMPTY_CTX); + + // Warp to 24 hours before end date and execute end vesting + vm.warp(schedule.endDate - 24 hours); + + vm.prank(alice); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeEndVesting should return true"); + + // Verify final balances + assertEq( + aliceInitialBalance - superToken.balanceOf(alice), + 1500 ether, + "Alice should have transferred the full 1500 USDC" + ); + assertEq( + superToken.balanceOf(bob) - bobInitialBalance, 1500 ether, "Bob should have received the full 1500 USDC" + ); + + // Verify schedule no longer exists + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + + /* Scenario 1 : + Assuming a 5 month long schedule that requires the receiver to claim the schedule: + + Define schedule for 1000 USDC (guaranteeing only 200USDC for 1 month) + + One month later, update schedule to 1400 USDC (with four months left and 200USDC meant to be transferred, this means 300 USDC/mo) + The receiver claims the schedule right after the update + + One month later, update schedule to 1100 USDC (with 3 months left, and 500 USDC already transferred, this means 200 USDC/mo) + + At the time of the last month, with the total amount set at 1500 USDC, the stream shall be closed up to ±24hrs early, settling any differences to the expected total + */ + function testVestingSchedulerV3_scenario_withClaim() public { + // Initial setup + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + // Define constants for the test + uint256 initialTotalAmount = 1000 ether; + uint32 totalDuration = 150 days; // 5 months + uint32 claimPeriod = 60 days; // Receiver has 60 days to claim + + // Set up permissions for Alice + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); // Allow any flow rate + + // Create the initial vesting schedule with claim period + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + uint32 startDate = uint32(block.timestamp); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + initialTotalAmount, + totalDuration, + startDate, + 0, // No cliff period + claimPeriod, // Claim period of 60 days + EMPTY_CTX + ); + vm.stopPrank(); + + // Get the schedule + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + // Verify initial schedule + assertEq(schedule.endDate - startDate, totalDuration, "Duration should be 5 months"); + assertEq(schedule.claimValidityDate, startDate + claimPeriod, "Claim validity date should be set correctly"); + + // Warp to first month (30 days) and update schedule to 1400 USDC + vm.warp(startDate + 30 days); + + uint256 updatedTotalAmount = 1400 ether; + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, updatedTotalAmount, EMPTY_CTX); + + // Get the updated schedule + schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + // Calculate expected flow rate after update + int96 expectedFlowRate = SafeCast.toInt96( + SafeCast.toInt256( + (updatedTotalAmount - schedule.alreadyVestedAmount) / (schedule.endDate - block.timestamp) + ) + ); + + // Verify the flow rate has been updated correctly + assertApproxEqAbs( + schedule.flowRate, + expectedFlowRate, + 1e10, // Allow small rounding differences + "Flow rate should be updated correctly" + ); + + // Bob claims the schedule right after the update + vm.prank(bob); + bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(success, "executeCliffAndFlow should return true"); + + // Calculate expected amount received at claim time + // This should be approximately 1 month's worth of the original schedule (200 USDC) + uint256 expectedClaimAmount = initialTotalAmount / 5; // 200 USDC for first month + + // Verify Bob received the correct amount at claim time + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + expectedClaimAmount, + expectedClaimAmount * 10 / 10_000, // Allow 0.1% difference + "Bob should have received ~200 USDC at claim time" + ); + + // Warp to second month (60 days total) + vm.warp(startDate + 60 days); + + // Calculate expected amount after second month + // First month at original rate + second month at updated rate + uint256 expectedTotalAfterTwoMonths = expectedClaimAmount + (SafeCast.toUint256(expectedFlowRate) * 30 days); + + // Verify Bob's balance after second month + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + expectedTotalAfterTwoMonths, + expectedTotalAfterTwoMonths * 10 / 10_000, // Allow 0.1% difference + "Bob should have received ~500 USDC after second month" + ); + + // Update schedule again to 1100 USDC + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1100 ether, EMPTY_CTX); + + // Warp to third month (90 days total) + vm.warp(startDate + 90 days); + + // Calculate expected amount after third month + // First two months + third month at reduced rate + schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + uint256 thirdMonthAmount = (1100 ether - schedule.alreadyVestedAmount) / 3; // ~200 USDC/month + + // Verify Bob's balance after third month + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + expectedTotalAfterTwoMonths + thirdMonthAmount, + (expectedTotalAfterTwoMonths + thirdMonthAmount) * 10 / 10_000, // Allow 0.1% difference + "Bob should have received ~680 USDC after third month" + ); + + // Update schedule one last time to 1500 USDC + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1500 ether, EMPTY_CTX); + + // Warp to 24 hours before end date and execute end vesting + vm.warp(schedule.endDate - 24 hours); + + vm.prank(alice); + success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(success, "executeEndVesting should return true"); + + // Verify final balances + assertApproxEqAbs( + aliceInitialBalance - superToken.balanceOf(alice), + 1500 ether, + 1e16, // Allow small rounding differences + "Alice should have transferred approximately 1500 USDC" + ); + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + 1500 ether, + 1e16, // Allow small rounding differences + "Bob should have received approximately 1500 USDC" + ); + + // Verify schedule no longer exists + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } } From dc7d5319c24e30096a8dcb65d35821b6bf967bf1 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:21:12 +0100 Subject: [PATCH 21/59] refactor: reordre test scripts --- .../scheduler/test/VestingSchedulerV3.t.sol | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 25be97daf8..aeb5fab11e 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2650,40 +2650,6 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.stopPrank(); } - function test_getSender_works_in_a_batch_call() public { - // Create a vesting schedule to update with a batch call that uses the context - vm.startPrank(alice); - vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX - ); - _arrangeAllowances(alice, FLOW_RATE); - vm.stopPrank(); - - vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE); - vestingScheduler.executeCliffAndFlow(superToken, alice, bob); - - uint32 newEndDate = END_DATE + 1234; - // Setting up a batch call. Superfluid Protocol will replace the emtpy context with data about the sender. That's where the sender is retrieved from. - ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1); - ops[0] = ISuperfluid.Operation({ - operationType: BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_APP_ACTION, - target: address(vestingScheduler), - data: abi.encodeCall( - vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate, EMPTY_CTX) - ) - }); - - // Act - vm.prank(alice); - sf.host.batchCall(ops); - vm.stopPrank(); - - // Assert - IVestingSchedulerV3.VestingSchedule memory schedule = - vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - assertEq(schedule.endDate, newEndDate); - } - // VestingSchedulerV3 Scenarios : /* Scenario 1 : Assuming a 5 month long schedule: @@ -2961,4 +2927,38 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Verify schedule no longer exists testAssertScheduleDoesNotExist(address(superToken), alice, bob); } + + function test_getSender_works_in_a_batch_call() public { + // Create a vesting schedule to update with a batch call that uses the context + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + ); + _arrangeAllowances(alice, FLOW_RATE); + vm.stopPrank(); + + vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + + uint32 newEndDate = END_DATE + 1234; + // Setting up a batch call. Superfluid Protocol will replace the emtpy context with data about the sender. That's where the sender is retrieved from. + ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1); + ops[0] = ISuperfluid.Operation({ + operationType: BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_APP_ACTION, + target: address(vestingScheduler), + data: abi.encodeCall( + vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate, EMPTY_CTX) + ) + }); + + // Act + vm.prank(alice); + sf.host.batchCall(ops); + vm.stopPrank(); + + // Assert + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertEq(schedule.endDate, newEndDate); + } } From aaa343bc8088d27410c35e887c3df63c41b6a6d9 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:35:45 +0100 Subject: [PATCH 22/59] test: Update test method to use updateVestingScheduleFlowRateFromEndDate --- .../scheduler/test/VestingSchedulerV3.t.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index d0e9ca82d3..efaad722e5 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2978,7 +2978,9 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ops[0] = ISuperfluid.Operation({ operationType: BatchOperation.OPERATION_TYPE_ERC2771_FORWARD_CALL, target: address(vestingScheduler), - data: abi.encodeCall(vestingScheduler.updateVestingScheduleEndDate, (superToken, bob, newEndDate, EMPTY_CTX)) + data: abi.encodeCall( + vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate, EMPTY_CTX) + ) }); // Act From 9c61580ff2f499147ebfe57a0184db9163618841 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 10:47:43 +0100 Subject: [PATCH 23/59] refactor: updated deploy script and .env.example to support VestingSchedulerV3 deployment --- .../scheduler/.env.example | 3 +- .../scheduler/deploy/deploy.js | 39 +++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/packages/automation-contracts/scheduler/.env.example b/packages/automation-contracts/scheduler/.env.example index 19cb641fc6..595f0c7a62 100644 --- a/packages/automation-contracts/scheduler/.env.example +++ b/packages/automation-contracts/scheduler/.env.example @@ -13,4 +13,5 @@ ETHERSCAN_API_KEY= DEPLOY_FLOW_SCHEDULER= DEPLOY_VESTING_SCHEDULER= -DEPLOY_VESTING_SCHEDULER_V2= \ No newline at end of file +DEPLOY_VESTING_SCHEDULER_V2= +DEPLOY_VESTING_SCHEDULER_V3= diff --git a/packages/automation-contracts/scheduler/deploy/deploy.js b/packages/automation-contracts/scheduler/deploy/deploy.js index a094182a33..84897d20bc 100644 --- a/packages/automation-contracts/scheduler/deploy/deploy.js +++ b/packages/automation-contracts/scheduler/deploy/deploy.js @@ -23,19 +23,24 @@ module.exports = async function ({ deployments, getNamedAccounts }) { const { deploy } = deployments; const { deployer } = await getNamedAccounts(); - console.log(`network: ${hre.network.name}`); console.log(`chainId: ${chainId}`); console.log(`rpc: ${hre.network.config.url}`); console.log(`host: ${host}`); + const deployFlowScheduler = + process.env.DEPLOY_FLOW_SCHEDULER?.toLowerCase() === "true"; + const deployVestingScheduler = + process.env.DEPLOY_VESTING_SCHEDULER?.toLowerCase() === "true"; + const deployVestingSchedulerV2 = + process.env.DEPLOY_VESTING_SCHEDULER_V2?.toLowerCase() === "true"; + const deployVestingSchedulerV3 = + process.env.DEPLOY_VESTING_SCHEDULER_V3?.toLowerCase() === "true"; - const deployFlowScheduler = process.env.DEPLOY_FLOW_SCHEDULER?.toLowerCase() === "true"; - const deployVestingScheduler = process.env.DEPLOY_VESTING_SCHEDULER?.toLowerCase() === "true"; - const deployVestingSchedulerV2 = process.env.DEPLOY_VESTING_SCHEDULER_V2?.toLowerCase() === "true"; console.log(`deployFlowScheduler: ${deployFlowScheduler}`); console.log(`deployVestingScheduler: ${deployVestingScheduler}`); console.log(`deployVestingSchedulerV2: ${deployVestingSchedulerV2}`); + console.log(`deployVestingSchedulerV3: ${deployVestingSchedulerV3}`); if (deployFlowScheduler) { console.log("Deploying FlowScheduler..."); @@ -43,7 +48,7 @@ module.exports = async function ({ deployments, getNamedAccounts }) { from: deployer, args: [host], log: true, - skipIfAlreadyDeployed: false + skipIfAlreadyDeployed: false, }); // wait for 15 seconds to allow etherscan to indexed the contracts @@ -56,14 +61,14 @@ module.exports = async function ({ deployments, getNamedAccounts }) { contract: "contracts/FlowScheduler.sol:FlowScheduler", }); } - + if (deployVestingScheduler) { console.log("Deploying VestingScheduler..."); const VestingScheduler = await deploy("VestingScheduler", { from: deployer, args: [host], log: true, - skipIfAlreadyDeployed: false + skipIfAlreadyDeployed: false, }); // wait for 15 seconds to allow etherscan to indexed the contracts @@ -97,5 +102,25 @@ module.exports = async function ({ deployments, getNamedAccounts }) { }); } + if (deployVestingSchedulerV3) { + console.log("Deploying VestingSchedulerV3..."); + const VestingSchedulerV3 = await deploy("VestingSchedulerV3", { + from: deployer, + args: [host], + log: true, + skipIfAlreadyDeployed: false, + }); + + // wait for 15 seconds to allow etherscan to indexed the contracts + await sleep(15000); + + console.log("Verifying VestingSchedulerV3..."); + await hre.run("verify:verify", { + address: VestingSchedulerV3.address, + constructorArguments: [host], + contract: "contracts/VestingSchedulerV3.sol:VestingSchedulerV3", + }); + } + console.log("Finished."); }; From f20934041318ea3b86ac7e02a8b74b483cebc088 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 12:56:55 +0100 Subject: [PATCH 24/59] test: increase test coverage --- .../scheduler/test/VestingSchedulerV3.t.sol | 79 ++++++++++++++++--- 1 file changed, 68 insertions(+), 11 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index efaad722e5..1d65a9c61a 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -400,7 +400,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { _createVestingScheduleWithDefaultData(alice, bob); } - function testUpdateVestingSchedule() public { + function testUpdateVestingScheduleFlowRateFromEndDate() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.expectEmit(true, true, true, true); emit VestingScheduleCreated( @@ -423,29 +423,86 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertTrue(schedule.endDate == END_DATE + 1000, "schedule.endDate"); } - function test_updateVestingSchedule_invalidEndDate() public { + function test_updateVestingScheduleFlowRateFromEndDate_invalidTimeWindow() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.expectEmit(true, true, true, true); emit VestingScheduleCreated( superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 ); _createVestingScheduleWithDefaultData(alice, bob); - vm.prank(alice); - superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); - vm.startPrank(admin); - uint256 initialTimestamp = block.timestamp + 10 days + 1800; - vm.warp(initialTimestamp); - vestingScheduler.executeCliffAndFlow(superToken, alice, bob); - vm.stopPrank(); vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + uint256 beforeCliffAndFlowDate = CLIFF_DATE - 30 minutes; + vm.warp(beforeCliffAndFlowDate); + + // Schedule update is not allowed if : "the cliff and flow date is in the future" + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE + 1 hours, EMPTY_CTX); + + uint256 afterCliffAndFlowDate = CLIFF_DATE + 30 minutes; + vm.warp(afterCliffAndFlowDate); + // Schedule update is not allowed if : "the new end date is in the past" vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromEndDate( - superToken, bob, uint32(initialTimestamp - 1), EMPTY_CTX + superToken, bob, uint32(afterCliffAndFlowDate - 1), EMPTY_CTX ); + // Schedule update is not allowed if : "the new end date is in right now (block.timestamp)" vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(initialTimestamp), EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate( + superToken, bob, uint32(afterCliffAndFlowDate), EMPTY_CTX + ); + + uint256 afterEndDate = END_DATE + 1 hours; + vm.warp(afterEndDate); + + // Schedule update is not allowed if : "the current end date has passed" + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate( + superToken, bob, uint32(afterEndDate + 1 hours), EMPTY_CTX + ); + + vm.stopPrank(); + } + + function test_updateVestingScheduleFlowRateFromAmount_invalidParameters() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + ); + _createVestingScheduleWithDefaultData(alice, bob); + + uint256 newAmount = CLIFF_TRANSFER_AMOUNT + (END_DATE - CLIFF_DATE) * uint96(FLOW_RATE) + 10 ether; + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + uint256 beforeCliffAndFlowDate = CLIFF_DATE - 30 minutes; + vm.warp(beforeCliffAndFlowDate); + + // Schedule update is not allowed if : "the cliff and flow date is in the future" + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); + + uint256 afterCliffAndFlowDate = CLIFF_DATE + 30 minutes; + vm.warp(afterCliffAndFlowDate); + + // Amount is invalid if it is less than the already vested amount + uint256 invalidNewAmount = CLIFF_TRANSFER_AMOUNT; + vm.expectRevert(IVestingSchedulerV3.InvalidUpdate.selector); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, invalidNewAmount, EMPTY_CTX); + + uint256 afterEndDate = END_DATE + 1 hours; + vm.warp(afterEndDate); + + // Schedule update is not allowed if : "the current end date has passed" + vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); + + vm.stopPrank(); } function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { From 429a4072d28a4adf695ca094e0b47758a9c4c4d9 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 12:57:27 +0100 Subject: [PATCH 25/59] refactor: comments & format --- .../contracts/VestingSchedulerV3.sol | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index d28d3b2b9b..bdbd5049b5 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -363,7 +363,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. - /// FIXME : add testing for this function function updateVestingScheduleFlowRateFromAmount( ISuperToken superToken, address receiver, @@ -380,17 +379,18 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien /* Schedule update is not allowed if : - - schedule should already have ended - - cliff and flow date is in the future + - the schedule end date has passed + - the cliff and flow date is in the future */ - if (schedule.endDate < block.timestamp || block.timestamp < schedule.cliffAndFlowDate) { + if (schedule.endDate <= block.timestamp || block.timestamp < schedule.cliffAndFlowDate) { revert TimeWindowInvalid(); } + // Settle the amount already vested uint256 alreadyVestedAmount = _settle(agg); - // Ensure that the new total amount is not less than the amount already vested - if (alreadyVestedAmount >= newTotalAmount) revert InvalidUpdate(); + // Ensure that the new total amount is larger than the amount already vested + if (newTotalAmount <= alreadyVestedAmount) revert InvalidUpdate(); uint256 timeLeftToVest = schedule.endDate - block.timestamp; @@ -405,7 +405,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien (newTotalAmount - alreadyVestedAmount) - (SafeCast.toUint256(newFlowRate) * timeLeftToVest) ); - // If the schedule is flowing, update the existing flow rate to the new calculated flow rate + // If the schedule is started, update the existing flow rate to the new calculated flow rate if (schedule.cliffAndFlowDate == 0) { if (newCtx.length != 0) { newCtx = superToken.flowFromWithCtx(sender, receiver, newFlowRate, newCtx); @@ -449,12 +449,12 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien /* Schedule update is not allowed if : - - schedule should already have ended - - new end date is in the past - - cliff and flow date is in the future + - the current end date has passed + - the new end date is in the past + - the cliff and flow date is in the future */ if ( - schedule.endDate < block.timestamp || endDate <= block.timestamp + schedule.endDate <= block.timestamp || endDate <= block.timestamp || block.timestamp < schedule.cliffAndFlowDate ) revert TimeWindowInvalid(); @@ -462,20 +462,21 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien vestingSchedules[agg.id].endDate = endDate; uint256 totalVestedAmount = _getTotalVestedAmount(schedule); + + // Settle the amount already vested uint256 alreadyVestedAmount = _settle(agg); - // Update the vesting flow rate + // Update the vesting flow rate and remainder amount vestingSchedules[agg.id].flowRate = SafeCast.toInt96( SafeCast.toInt256(totalVestedAmount - alreadyVestedAmount) / SafeCast.toInt256(endDate - block.timestamp) ); - vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( (totalVestedAmount - alreadyVestedAmount) - (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * (endDate - block.timestamp)) ); + // If the schedule is started, update the existing flow rate to the new calculated flow rate if (schedule.cliffAndFlowDate == 0) { - // Update the flow from sender to receiver with the new calculated flow rate if (newCtx.length != 0) { newCtx = superToken.flowFromWithCtx(sender, receiver, vestingSchedules[agg.id].flowRate, newCtx); } else { @@ -794,18 +795,18 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } /// @dev IRelayRecipient.isTrustedForwarder implementation - function isTrustedForwarder(address forwarder) public view override returns(bool) { + function isTrustedForwarder(address forwarder) public view override returns (bool) { return forwarder == HOST.getERC2771Forwarder(); } /// @dev IRelayRecipient.versionRecipient implementation - function versionRecipient() external override pure returns (string memory) { + function versionRecipient() external pure override returns (string memory) { return "v1"; } /// @dev gets the relayed sender from calldata as specified by EIP-2771, falling back to msg.sender - function _msgSender() internal virtual view returns (address) { - if(msg.data.length >= 20 && isTrustedForwarder(msg.sender)) { + function _msgSender() internal view virtual returns (address) { + if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) { return address(bytes20(msg.data[msg.data.length - 20:])); } else { return msg.sender; From a7222a6b815c7fb3f47b67f478f9c06c8b168dc5 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 14:21:10 +0100 Subject: [PATCH 26/59] deploy: VestingSchedulerV3 on OP Sepolia --- packages/metadata/main/networks/list.cjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/metadata/main/networks/list.cjs b/packages/metadata/main/networks/list.cjs index 08e00d319a..3ba6591163 100644 --- a/packages/metadata/main/networks/list.cjs +++ b/packages/metadata/main/networks/list.cjs @@ -109,6 +109,7 @@ module.exports = "flowScheduler": "0x73B1Ce21d03ad389C2A291B1d1dc4DAFE7B5Dc68", "vestingScheduler": "0x27444c0235a4D921F3106475faeba0B5e7ABDD7a", "vestingSchedulerV2": "0x3aa62b96f44D0f8892BeBBC819DE8e02E9DE69A8", + "vestingSchedulerV3": "0x2dCFbF5BcE0522257E78bF164871770D30634A96", "autowrap": { "manager": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", "wrapStrategy": "0xf232f1fd34CE12e24F4391865c2D6E374D2C34d9" From d8f1cc23e5d56f9ce29adee7c1b1cf6d8e656229 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:36:54 +0100 Subject: [PATCH 27/59] refactor: rearrange calculation for better readibility --- .../contracts/VestingSchedulerV3.sol | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index bdbd5049b5..ddd51c6686 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -392,25 +392,22 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien // Ensure that the new total amount is larger than the amount already vested if (newTotalAmount <= alreadyVestedAmount) revert InvalidUpdate(); + uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; uint256 timeLeftToVest = schedule.endDate - block.timestamp; - // Calculate the new flow rate based on the new total amount, the amount already vested and the time left to vest - int96 newFlowRate = SafeCast.toInt96( - SafeCast.toInt256(newTotalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest) - ); - // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = newFlowRate; + vestingSchedules[agg.id].flowRate = + SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( - (newTotalAmount - alreadyVestedAmount) - (SafeCast.toUint256(newFlowRate) * timeLeftToVest) + amountLeftToVest - (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * timeLeftToVest) ); // If the schedule is started, update the existing flow rate to the new calculated flow rate if (schedule.cliffAndFlowDate == 0) { if (newCtx.length != 0) { - newCtx = superToken.flowFromWithCtx(sender, receiver, newFlowRate, newCtx); + newCtx = superToken.flowFromWithCtx(sender, receiver, vestingSchedules[agg.id].flowRate, newCtx); } else { - superToken.flowFrom(sender, receiver, newFlowRate); + superToken.flowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); } } @@ -425,7 +422,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien sender, receiver, schedule.flowRate, - newFlowRate, + vestingSchedules[agg.id].flowRate, _getTotalVestedAmount(schedule), newTotalAmount, vestingSchedules[agg.id].remainderAmount @@ -461,18 +458,14 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien // Update the schedule end date vestingSchedules[agg.id].endDate = endDate; - uint256 totalVestedAmount = _getTotalVestedAmount(schedule); - - // Settle the amount already vested - uint256 alreadyVestedAmount = _settle(agg); + uint256 amountLeftToVest = _getTotalVestedAmount(schedule) - _settle(agg); + uint256 timeLeftToVest = endDate - block.timestamp; // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = SafeCast.toInt96( - SafeCast.toInt256(totalVestedAmount - alreadyVestedAmount) / SafeCast.toInt256(endDate - block.timestamp) - ); + vestingSchedules[agg.id].flowRate = + SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( - (totalVestedAmount - alreadyVestedAmount) - - (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * (endDate - block.timestamp)) + amountLeftToVest - (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * timeLeftToVest) ); // If the schedule is started, update the existing flow rate to the new calculated flow rate From 576255022c34382de3a2571688929c1eca5c8836 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:42:42 +0100 Subject: [PATCH 28/59] feat: add timestamp validation for cliff and flow date in vesting settlement --- .../scheduler/contracts/VestingSchedulerV3.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index ddd51c6686..da998d3785 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -342,6 +342,9 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien function _settle(ScheduleAggregate memory agg) internal returns (uint256 alreadyVestedAmount) { VestingSchedule memory schedule = agg.schedule; + // Ensure that the cliff and flow date has passed + assert(block.timestamp >= schedule.cliffAndFlowDate); + // Delete the cliff amount and account for it in the already vested amount delete vestingSchedules[agg.id].cliffAmount; From 2546df1188328e50dcbb8c022fc08c93d0980d61 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Fri, 28 Feb 2025 15:48:56 +0100 Subject: [PATCH 29/59] refactor: extract flow rate and remainder amount calculation into helper method --- .../contracts/VestingSchedulerV3.sol | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index da998d3785..f4ee5690f7 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -365,6 +365,21 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien alreadyVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; } + function _updateFlowRateAndRemainderAmount( + ScheduleAggregate memory agg, + uint256 amountLeftToVest, + uint256 timeLeftToVest + ) internal { + // Calculate the new flow rate and remainder amount + int96 newFlowRate = SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); + uint96 newRemainderAmount = + SafeCast.toUint96(amountLeftToVest - (SafeCast.toUint256(newFlowRate) * timeLeftToVest)); + + // Update the flow rate and remainder amount + vestingSchedules[agg.id].flowRate = newFlowRate; + vestingSchedules[agg.id].remainderAmount = newRemainderAmount; + } + /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. function updateVestingScheduleFlowRateFromAmount( ISuperToken superToken, @@ -399,11 +414,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien uint256 timeLeftToVest = schedule.endDate - block.timestamp; // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = - SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); - vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( - amountLeftToVest - (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * timeLeftToVest) - ); + _updateFlowRateAndRemainderAmount(agg, amountLeftToVest, timeLeftToVest); // If the schedule is started, update the existing flow rate to the new calculated flow rate if (schedule.cliffAndFlowDate == 0) { @@ -465,11 +476,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien uint256 timeLeftToVest = endDate - block.timestamp; // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = - SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); - vestingSchedules[agg.id].remainderAmount = SafeCast.toUint96( - amountLeftToVest - (SafeCast.toUint256(vestingSchedules[agg.id].flowRate) * timeLeftToVest) - ); + _updateFlowRateAndRemainderAmount(agg, amountLeftToVest, timeLeftToVest); // If the schedule is started, update the existing flow rate to the new calculated flow rate if (schedule.cliffAndFlowDate == 0) { From 49712b9ea49fa669509abf94582f2ab04c136bdd Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Wed, 5 Mar 2025 16:39:54 +0100 Subject: [PATCH 30/59] refactor: separate accounting data from vesting schedule - review V3 interface (inherits V2 + added new functions) --- .../contracts/VestingSchedulerV3.sol | 121 ++++-- .../interface/IVestingSchedulerV3.sol | 369 +----------------- 2 files changed, 97 insertions(+), 393 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index f4ee5690f7..9e9e51a6be 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -19,6 +19,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien ISuperfluid public immutable HOST; mapping(bytes32 => VestingSchedule) public vestingSchedules; // id = keccak(supertoken, sender, receiver) + mapping(bytes32 => ScheduleAccounting) public accountings; uint32 public constant MIN_VESTING_DURATION = 7 days; uint32 public constant START_DATE_VALID_AFTER = 3 days; @@ -30,6 +31,12 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien address receiver; bytes32 id; VestingSchedule schedule; + ScheduleAccounting accounting; + } + + struct ScheduleAccounting { + uint256 alreadyVestedAmount; + uint256 lastUpdated; } constructor(ISuperfluid host) { @@ -265,9 +272,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien flowRate: params.flowRate, cliffAmount: params.cliffAmount, remainderAmount: params.remainderAmount, - claimValidityDate: params.claimValidityDate, - alreadyVestedAmount: 0, - lastUpdated: 0 + claimValidityDate: params.claimValidityDate }); emit VestingScheduleCreated( @@ -340,29 +345,28 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } function _settle(ScheduleAggregate memory agg) internal returns (uint256 alreadyVestedAmount) { - VestingSchedule memory schedule = agg.schedule; - // Ensure that the cliff and flow date has passed - assert(block.timestamp >= schedule.cliffAndFlowDate); + assert(block.timestamp >= agg.schedule.cliffAndFlowDate); // Delete the cliff amount and account for it in the already vested amount delete vestingSchedules[agg.id].cliffAmount; // Update the timestamp of the last schedule update - vestingSchedules[agg.id].lastUpdated = block.timestamp; + accountings[agg.id].lastUpdated = block.timestamp; - if (block.timestamp > schedule.endDate) { + if (block.timestamp > agg.schedule.endDate) { // If the schedule end date has passed, settle the total amount vested - vestingSchedules[agg.id].alreadyVestedAmount = _getTotalVestedAmount(schedule); + accountings[agg.id].alreadyVestedAmount = _getTotalVestedAmount(agg.schedule, agg.accounting); } else { // If the schedule end date has not passed, accrue the amount already vested - uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; + uint256 actualLastUpdate = + agg.accounting.lastUpdated == 0 ? agg.schedule.cliffAndFlowDate : agg.accounting.lastUpdated; // Accrue the amount already vested - vestingSchedules[agg.id].alreadyVestedAmount += - ((block.timestamp - actualLastUpdate) * uint96(schedule.flowRate)) + schedule.cliffAmount; + accountings[agg.id].alreadyVestedAmount += + ((block.timestamp - actualLastUpdate) * uint96(agg.schedule.flowRate)) + agg.schedule.cliffAmount; } - alreadyVestedAmount = vestingSchedules[agg.id].alreadyVestedAmount; + alreadyVestedAmount = accountings[agg.id].alreadyVestedAmount; } function _updateFlowRateAndRemainderAmount( @@ -408,7 +412,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien uint256 alreadyVestedAmount = _settle(agg); // Ensure that the new total amount is larger than the amount already vested - if (newTotalAmount <= alreadyVestedAmount) revert InvalidUpdate(); + if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; uint256 timeLeftToVest = schedule.endDate - block.timestamp; @@ -437,7 +441,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien receiver, schedule.flowRate, vestingSchedules[agg.id].flowRate, - _getTotalVestedAmount(schedule), + _getTotalVestedAmount(schedule, agg.accounting), newTotalAmount, vestingSchedules[agg.id].remainderAmount ); @@ -472,7 +476,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien // Update the schedule end date vestingSchedules[agg.id].endDate = endDate; - uint256 amountLeftToVest = _getTotalVestedAmount(schedule) - _settle(agg); + uint256 amountLeftToVest = _getTotalVestedAmount(schedule, agg.accounting) - _settle(agg); uint256 timeLeftToVest = endDate - block.timestamp; // Update the vesting flow rate and remainder amount @@ -505,6 +509,37 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien ); } + /// @dev IVestingScheduler.updateVestingSchedule implementation. + function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) + external + returns (bytes memory newCtx) + { + newCtx = ctx; + address sender = _getSender(ctx); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + if (endDate <= block.timestamp) revert TimeWindowInvalid(); + + // Note: Claimable schedules that have not been claimed cannot be updated + + // Only allow an update if 1. vesting exists 2. executeCliffAndFlow() has been called + if (schedule.cliffAndFlowDate != 0 || schedule.endDate == 0) revert ScheduleNotFlowing(); + + vestingSchedules[agg.id].endDate = endDate; + // Note: Nullify the remainder amount when complexity of updates is introduced. + vestingSchedules[agg.id].remainderAmount = 0; + + emit VestingScheduleUpdated( + superToken, + sender, + receiver, + schedule.endDate, + endDate, + 0 // remainderAmount + ); + } + /// @dev IVestingScheduler.deleteVestingSchedule implementation. function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) external @@ -516,7 +551,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien VestingSchedule memory schedule = agg.schedule; if (schedule.endDate != 0) { - delete vestingSchedules[agg.id]; + _deleteVestingSchedule(agg.id); emit VestingScheduleDeleted(superToken, sender, receiver); } else { revert ScheduleDoesNotExist(); @@ -623,10 +658,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien function _executeVestingAsSingleTransfer(ScheduleAggregate memory agg) private returns (bool success) { VestingSchedule memory schedule = agg.schedule; + ScheduleAccounting memory accounting = agg.accounting; - delete vestingSchedules[agg.id]; + _deleteVestingSchedule(agg.id); - uint256 totalVestedAmount = _getTotalVestedAmount(schedule); + uint256 totalVestedAmount = _getTotalVestedAmount(schedule, accounting); // Note: Super Tokens revert, not return false, i.e. we expect always true here. assert(agg.superToken.transferFrom(agg.sender, agg.receiver, totalVestedAmount)); @@ -653,14 +689,18 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien return true; } - function _getTotalVestedAmount(VestingSchedule memory schedule) private pure returns (uint256 totalVestedAmount) { - uint256 actualLastUpdate = schedule.lastUpdated == 0 ? schedule.cliffAndFlowDate : schedule.lastUpdated; + function _getTotalVestedAmount(VestingSchedule memory schedule, ScheduleAccounting memory accounting) + private + pure + returns (uint256 totalVestedAmount) + { + uint256 actualLastUpdate = accounting.lastUpdated == 0 ? schedule.cliffAndFlowDate : accounting.lastUpdated; uint256 currentFlowDuration = schedule.endDate - actualLastUpdate; uint256 currentFlowAmount = currentFlowDuration * SafeCast.toUint256(schedule.flowRate); totalVestedAmount = - schedule.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount + currentFlowAmount; + accounting.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount + currentFlowAmount; } /// @dev IVestingScheduler.executeEndVesting implementation. @@ -670,14 +710,15 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien { ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); VestingSchedule memory schedule = agg.schedule; + ScheduleAccounting memory accounting = agg.accounting; _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ false); uint256 alreadyVestedAmount = _settle(agg); - uint256 totalVestedAmount = _getTotalVestedAmount(schedule); + uint256 totalVestedAmount = _getTotalVestedAmount(schedule, accounting); // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. - delete vestingSchedules[agg.id]; + _deleteVestingSchedule(agg.id); // If vesting is not running, we can't do anything, just emit failing event. if (_isFlowOngoing(superToken, sender, receiver)) { @@ -745,7 +786,8 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien sender: sender, receiver: receiver, id: id, - schedule: vestingSchedules[id] + schedule: vestingSchedules[id], + accounting: accountings[id] }); } @@ -757,8 +799,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien return startDate; } - /// @dev IVestingScheduler.getMaximumNeededTokenAllowance implementation. - function getMaximumNeededTokenAllowance(VestingSchedule memory schedule) external pure override returns (uint256) { + function _getMaximumNeededTokenAllowance(VestingSchedule memory schedule, ScheduleAccounting memory accounting) + internal + pure + returns (uint256) + { uint256 maxFlowDelayCompensationAmount = schedule.cliffAndFlowDate == 0 ? 0 : START_DATE_VALID_AFTER * SafeCast.toUint256(schedule.flowRate); uint256 maxEarlyEndCompensationAmount = @@ -768,7 +813,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien return schedule.cliffAmount + schedule.remainderAmount + maxFlowDelayCompensationAmount + maxEarlyEndCompensationAmount; } else if (schedule.claimValidityDate >= _gteDateToExecuteEndVesting(schedule)) { - return _getTotalVestedAmount(schedule); + return _getTotalVestedAmount(schedule, accounting); } else { return schedule.cliffAmount + schedule.remainderAmount + (schedule.claimValidityDate - schedule.cliffAndFlowDate) * SafeCast.toUint256(schedule.flowRate) @@ -776,6 +821,21 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } } + /// @dev IVestingScheduler.getMaximumNeededTokenAllowance implementation + function getMaximumNeededTokenAllowance(VestingSchedule memory schedule) external pure returns (uint256) { + return _getMaximumNeededTokenAllowance(schedule, ScheduleAccounting({alreadyVestedAmount: 0, lastUpdated: 0})); + } + + function getMaximumNeededTokenAllowance(ISuperToken superToken, address sender, address receiver) + external + view + returns (uint256) + { + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + + return _getMaximumNeededTokenAllowance(agg.schedule, agg.accounting); + } + /// @dev get sender of transaction from Superfluid Context or transaction itself. function _getSender(bytes memory ctx) private view returns (address sender) { if (ctx.length != 0) { @@ -797,6 +857,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien return keccak256(abi.encodePacked(superToken, sender, receiver)); } + function _deleteVestingSchedule(bytes32 id) internal { + delete vestingSchedules[id]; + delete accountings[id]; + } + /// @dev IRelayRecipient.isTrustedForwarder implementation function isTrustedForwarder(address forwarder) public view override returns (bool) { return forwarder == HOST.getERC2771Forwarder(); diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index fbcab6ba5e..c396def229 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -2,254 +2,11 @@ pragma solidity ^0.8.0; import {ISuperToken} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; +import {IVestingSchedulerV2} from "./IVestingSchedulerV2.sol"; -interface IVestingSchedulerV3 { - error TimeWindowInvalid(); - error AccountInvalid(); - error ZeroAddress(); - error HostInvalid(); - error FlowRateInvalid(); - error CliffInvalid(); - error ScheduleAlreadyExists(); - error ScheduleDoesNotExist(); - error ScheduleNotFlowing(); - error CannotClaimScheduleOnBehalf(); - error AlreadyExecuted(); - error ScheduleNotClaimed(); - error InvalidUpdate(); - - /** - * @dev Vesting configuration provided by user. - * @param cliffAndFlowDate Date of flow start and cliff execution (if a cliff was specified) - * @param endDate End date of the vesting - * @param flowRate Flow rate of the stream in tokens per second - * @param cliffAmount Amount to be transferred at the cliff date - * @param remainderAmount Amount to be transferred at the end to account for rounding errors - * @param claimValidityDate Date before which the claimable schedule must be claimed (0 if not claimable) - * @param totalAmount Total amount to be vested over the entire schedule (includes cliff and streamed amount) - * @param alreadyVestedAmount Amount that has already been vested - */ - struct VestingSchedule { - uint32 cliffAndFlowDate; - uint32 endDate; - int96 flowRate; - uint256 cliffAmount; - uint96 remainderAmount; - uint32 claimValidityDate; - uint256 alreadyVestedAmount; - uint256 lastUpdated; - } - - /** - * @dev Parameters used to create vesting schedules - * @param superToken SuperToken to be vested - * @param receiver Vesting receiver - * @param startDate Timestamp when the vesting should start - * @param claimValidityDate Date before which the claimable schedule must be claimed - * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff - * @param flowRate The flowRate for the stream - * @param cliffAmount The amount to be transferred at the cliff - * @param endDate The timestamp when the stream should stop. - * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" - * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) - */ - struct ScheduleCreationParams { - ISuperToken superToken; - address sender; - address receiver; - uint32 startDate; - uint32 claimValidityDate; - uint32 cliffDate; - int96 flowRate; - uint256 cliffAmount; - uint32 endDate; - uint96 remainderAmount; - } - - /** - * @dev Event emitted on creation of a new vesting schedule - * @param superToken SuperToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - * @param startDate Timestamp when the vesting starts - * @param claimValidityDate Date before which the claimable schedule must be claimed - * @param cliffDate Timestamp of the cliff - * @param flowRate The flowRate for the stream - * @param endDate The timestamp when the stream should stop - * @param cliffAmount The amount to be transferred at the cliff - * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" - */ - event VestingScheduleCreated( - ISuperToken indexed superToken, - address indexed sender, - address indexed receiver, - uint32 startDate, - uint32 cliffDate, - int96 flowRate, - uint32 endDate, - uint256 cliffAmount, - uint32 claimValidityDate, - uint96 remainderAmount - ); - - /** - * @dev Creates a new vesting schedule - * @dev If a non-zero cliffDate is set, the startDate has no effect other than being logged in an event. - * @dev If cliffDate is set to zero, the startDate becomes the cliff (transfer cliffAmount and start stream). - * @param superToken SuperToken to be vested - * @param receiver Vesting receiver - * @param startDate Timestamp when the vesting should start - * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff - * @param flowRate The flowRate for the stream - * @param cliffAmount The amount to be transferred at the cliff - * @param endDate The timestamp when the stream should stop. - * @param claimValidityDate Date before which the claimable schedule must be claimed - * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) - */ - function createVestingSchedule( - ISuperToken superToken, - address receiver, - uint32 startDate, - uint32 cliffDate, - int96 flowRate, - uint256 cliffAmount, - uint32 endDate, - uint32 claimValidityDate, - bytes memory ctx - ) external returns (bytes memory newCtx); - - /** - * @dev See IVestingScheduler.createVestingSchedule overload for more details. - */ - function createVestingSchedule( - ISuperToken superToken, - address receiver, - uint32 startDate, - uint32 cliffDate, - int96 flowRate, - uint256 cliffAmount, - uint32 endDate, - bytes memory ctx - ) external returns (bytes memory newCtx); - - /** - * @dev See IVestingScheduler.createVestingSchedule overload for more details. - */ - function createVestingSchedule( - ISuperToken superToken, - address receiver, - uint32 startDate, - uint32 cliffDate, - int96 flowRate, - uint256 cliffAmount, - uint32 endDate, - uint32 claimValidityDate - ) external; - - /** - * @dev Creates a new vesting schedule - * @dev The function makes it more intuitive to create a vesting schedule compared to the original function. - * @dev The function calculates the endDate, cliffDate, cliffAmount, flowRate, etc, based on the input arguments. - * @param superToken SuperToken to be vested - * @param receiver Vesting receiver - * @param totalAmount The total amount to be vested - * @param totalDuration The total duration of the vestingß - * @param startDate Timestamp when the vesting should start - * @param cliffPeriod The cliff period of the vesting - * @param claimPeriod The claim availability period - * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) - */ - function createVestingScheduleFromAmountAndDuration( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - uint32 totalDuration, - uint32 startDate, - uint32 cliffPeriod, - uint32 claimPeriod, - bytes memory ctx - ) external returns (bytes memory newCtx); - - /** - * @dev See IVestingScheduler.createVestingScheduleFromAmountAndDuration overload for more details. - */ - function createVestingScheduleFromAmountAndDuration( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - uint32 totalDuration, - uint32 startDate, - uint32 cliffPeriod, - uint32 claimPeriod - ) external; - - /** - * @dev Returns all relevant information related to a new vesting schedule creation - * @dev based on the amounts and durations. - * @param superToken SuperToken to be vested - * @param receiver Vesting receiver - * @param totalAmount The total amount to be vested - * @param totalDuration The total duration of the vestingß - * @param startDate Timestamp when the vesting should start - * @param cliffPeriod The cliff period of the vesting - * @param claimPeriod The claim availability period - */ - function mapCreateVestingScheduleParams( - ISuperToken superToken, - address sender, - address receiver, - uint256 totalAmount, - uint32 totalDuration, - uint32 startDate, - uint32 cliffPeriod, - uint32 claimPeriod - ) external returns (ScheduleCreationParams memory params); - - /** - * @dev Creates a new vesting schedule - * @dev The function calculates the endDate, cliffDate, cliffAmount, flowRate, etc, based on the input arguments. - * @dev The function creates the vesting schedule with start date set to current timestamp, - * @dev and executes the start (i.e. creation of the flow) immediately. - * @param superToken SuperToken to be vested - * @param receiver Vesting receiver - * @param totalAmount The total amount to be vested - * @param totalDuration The total duration of the vesting - * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) - */ - function createAndExecuteVestingScheduleFromAmountAndDuration( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - uint32 totalDuration, - bytes memory ctx - ) external returns (bytes memory newCtx); - - /** - * @dev See IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. - */ - function createAndExecuteVestingScheduleFromAmountAndDuration( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - uint32 totalDuration - ) external; - - /** - * @dev Event emitted on update of a vesting schedule - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - * @param oldEndDate Old timestamp when the stream should stop - * @param endDate New timestamp when the stream should stop - */ - event VestingScheduleUpdated( - ISuperToken indexed superToken, - address indexed sender, - address indexed receiver, - uint32 oldEndDate, - uint32 endDate, - uint96 remainderAmount - ); +interface IVestingSchedulerV3 is IVestingSchedulerV2 { + // FIXME Add comments + error InvalidNewTotalAmount(); /** * @dev Event emitted when a vesting schedule's total amount is updated @@ -323,122 +80,4 @@ interface IVestingSchedulerV3 { uint32 endDate, bytes memory ctx ) external returns (bytes memory newCtx); - - /** - * @dev Event emitted on deletion of a vesting schedule - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - */ - event VestingScheduleDeleted(ISuperToken indexed superToken, address indexed sender, address indexed receiver); - - /** - * @dev Event emitted on end of a vesting that failed because there was no running stream - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - * @param endDate The timestamp when the stream should stop - */ - event VestingEndFailed( - ISuperToken indexed superToken, address indexed sender, address indexed receiver, uint32 endDate - ); - - /** - * @dev Deletes a vesting schedule - * @param superToken The superToken to be vested - * @param receiver Vesting receiver - * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) - */ - function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) - external - returns (bytes memory newCtx); - - /** - * @dev Emitted when the cliff of a scheduled vesting is executed - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - * @param cliffAndFlowDate The timestamp when the stream should start - * @param flowRate The flowRate for the stream - * @param cliffAmount The amount you would like to transfer at the startDate when you start streaming - * @param flowDelayCompensation Adjusted amount transferred to receiver. (elapse time from config and tx timestamp) - */ - event VestingCliffAndFlowExecuted( - ISuperToken indexed superToken, - address indexed sender, - address indexed receiver, - uint32 cliffAndFlowDate, - int96 flowRate, - uint256 cliffAmount, - uint256 flowDelayCompensation - ); - - /** - * @dev Emitted when a claimable vesting schedule is claimed - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - * @param claimer Account that claimed the vesting (can only be sender or receiver) - */ - event VestingClaimed( - ISuperToken indexed superToken, address indexed sender, address indexed receiver, address claimer - ); - - /** - * @dev Executes a cliff (transfer and stream start) - * @notice Intended to be invoked by a backend service - * @param superToken SuperToken to be streamed - * @param sender Account who will be send the stream - * @param receiver Account who will be receiving the stream - */ - function executeCliffAndFlow(ISuperToken superToken, address sender, address receiver) - external - returns (bool success); - - /** - * @dev Emitted when the end of a scheduled vesting is executed - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - * @param endDate The timestamp when the stream should stop - * @param earlyEndCompensation adjusted close amount transferred to receiver. - * @param didCompensationFail adjusted close amount transfer fail. - */ - event VestingEndExecuted( - ISuperToken indexed superToken, - address indexed sender, - address indexed receiver, - uint32 endDate, - uint256 earlyEndCompensation, - bool didCompensationFail - ); - - /** - * @dev Executes the end of a vesting (stop stream) - * @notice Intended to be invoked by a backend service - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - */ - function executeEndVesting(ISuperToken superToken, address sender, address receiver) - external - returns (bool success); - - /** - * @dev Gets data currently stored for a vesting schedule - * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - */ - function getVestingSchedule(address superToken, address sender, address receiver) - external - view - returns (VestingSchedule memory); - - /** - * @dev Estimates the maximum possible ERC-20 token allowance needed for the vesting schedule - * @dev to work properly under all circumstances. - * @param vestingSchedule A vesting schedule (doesn't have to exist) - */ - function getMaximumNeededTokenAllowance(VestingSchedule memory vestingSchedule) external returns (uint256); } From fefb340171e9c222a44bd29faa1b30cf6f9d4bee Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Wed, 5 Mar 2025 16:40:02 +0100 Subject: [PATCH 31/59] test: update test cases to use IVestingSchedulerV2 interface --- .../scheduler/test/VestingSchedulerV3.t.sol | 152 ++++++++++-------- 1 file changed, 83 insertions(+), 69 deletions(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 1d65a9c61a..ae2119dc27 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -8,6 +8,7 @@ import { BatchOperation, ISuperApp } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; +import {IVestingSchedulerV2} from "./../contracts/interface/IVestingSchedulerV2.sol"; import {IVestingSchedulerV3} from "./../contracts/interface/IVestingSchedulerV3.sol"; import {VestingSchedulerV3} from "./../contracts/VestingSchedulerV3.sol"; import {FoundrySuperfluidTester} from @@ -214,15 +215,13 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint32 cliffAndFlowDate = cliffDate == 0 ? startDate : cliffDate; - expectedSchedule = IVestingSchedulerV3.VestingSchedule({ + expectedSchedule = IVestingSchedulerV2.VestingSchedule({ cliffAndFlowDate: cliffAndFlowDate, endDate: endDate, claimValidityDate: 0, flowRate: flowRate, cliffAmount: cliffAmount, - remainderAmount: 0, - alreadyVestedAmount: 0, - lastUpdated: 0 + remainderAmount: 0 }); } @@ -256,15 +255,13 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint96 remainderAmount = SafeCast.toUint96(totalAmount - SafeCast.toUint256(flowRate) * totalDuration); - expectedSchedule = IVestingSchedulerV3.VestingSchedule({ + expectedSchedule = IVestingSchedulerV2.VestingSchedule({ cliffAndFlowDate: cliffAndFlowDate, endDate: endDate, flowRate: flowRate, cliffAmount: cliffAmount, remainderAmount: remainderAmount, - claimValidityDate: claimPeriod == 0 ? 0 : startDate + claimPeriod, - alreadyVestedAmount: 0, - lastUpdated: 0 + claimValidityDate: claimPeriod == 0 ? 0 : startDate + claimPeriod }); } @@ -314,7 +311,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testCannotCreateVestingScheduleWithWrongData() public { vm.startPrank(alice); // revert with superToken = 0 - vm.expectRevert(IVestingSchedulerV3.ZeroAddress.selector); + vm.expectRevert(IVestingSchedulerV2.ZeroAddress.selector); vestingScheduler.createVestingSchedule( ISuperToken(address(0)), bob, @@ -328,67 +325,67 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with receivers = sender - vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.AccountInvalid.selector); vestingScheduler.createVestingSchedule( superToken, alice, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX ); // revert with receivers = address(0) - vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.AccountInvalid.selector); vestingScheduler.createVestingSchedule( superToken, address(0), START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX ); // revert with flowRate = 0 - vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.FlowRateInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, START_DATE, CLIFF_DATE, 0, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX ); // revert with cliffDate = 0 but cliffAmount != 0 - vm.expectRevert(IVestingSchedulerV3.CliffInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.CliffInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, 0, 0, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX ); // revert with startDate < block.timestamp && cliffDate = 0 - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, uint32(block.timestamp - 1), 0, FLOW_RATE, 0, END_DATE, 0, EMPTY_CTX ); // revert with endDate = 0 - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, 0, 0, EMPTY_CTX ); // revert with cliffAndFlowDate < block.timestamp - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, 0, uint32(block.timestamp) - 1, FLOW_RATE, 0, END_DATE, 0, EMPTY_CTX ); // revert with cliffAndFlowDate >= endDate - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0, EMPTY_CTX ); // revert with cliffAndFlowDate + startDateValidFor >= endDate - endDateValidBefore - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0, EMPTY_CTX ); // revert with startDate > cliffDate - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, CLIFF_DATE + 1, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX ); // revert with vesting duration < 7 days - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE + 2 days, 0, EMPTY_CTX ); @@ -396,7 +393,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testCannotCreateVestingScheduleIfDataExist() public { _createVestingScheduleWithDefaultData(alice, bob); - vm.expectRevert(IVestingSchedulerV3.ScheduleAlreadyExists.selector); + vm.expectRevert(IVestingSchedulerV2.ScheduleAlreadyExists.selector); _createVestingScheduleWithDefaultData(alice, bob); } @@ -437,20 +434,20 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(beforeCliffAndFlowDate); // Schedule update is not allowed if : "the cliff and flow date is in the future" - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE + 1 hours, EMPTY_CTX); uint256 afterCliffAndFlowDate = CLIFF_DATE + 30 minutes; vm.warp(afterCliffAndFlowDate); // Schedule update is not allowed if : "the new end date is in the past" - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromEndDate( superToken, bob, uint32(afterCliffAndFlowDate - 1), EMPTY_CTX ); // Schedule update is not allowed if : "the new end date is in right now (block.timestamp)" - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromEndDate( superToken, bob, uint32(afterCliffAndFlowDate), EMPTY_CTX ); @@ -459,7 +456,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(afterEndDate); // Schedule update is not allowed if : "the current end date has passed" - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromEndDate( superToken, bob, uint32(afterEndDate + 1 hours), EMPTY_CTX ); @@ -484,7 +481,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(beforeCliffAndFlowDate); // Schedule update is not allowed if : "the cliff and flow date is in the future" - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); uint256 afterCliffAndFlowDate = CLIFF_DATE + 30 minutes; @@ -492,14 +489,14 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Amount is invalid if it is less than the already vested amount uint256 invalidNewAmount = CLIFF_TRANSFER_AMOUNT; - vm.expectRevert(IVestingSchedulerV3.InvalidUpdate.selector); + vm.expectRevert(IVestingSchedulerV3.InvalidNewTotalAmount.selector); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, invalidNewAmount, EMPTY_CTX); uint256 afterEndDate = END_DATE + 1 hours; vm.warp(afterEndDate); // Schedule update is not allowed if : "the current end date has passed" - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); vm.stopPrank(); @@ -507,11 +504,11 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { vm.startPrank(alice); - vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vm.expectRevert(IVestingSchedulerV2.ScheduleDoesNotExist.selector); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE, EMPTY_CTX); newAmount = bound(newAmount, 1, type(uint256).max); - vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vm.expectRevert(IVestingSchedulerV2.ScheduleDoesNotExist.selector); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); vm.stopPrank(); } @@ -527,7 +524,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testCannotDeleteVestingScheduleIfDataDontExist() public { vm.startPrank(alice); - vm.expectRevert(IVestingSchedulerV3.ScheduleDoesNotExist.selector); + vm.expectRevert(IVestingSchedulerV2.ScheduleDoesNotExist.selector); vestingScheduler.deleteVestingSchedule(superToken, bob, EMPTY_CTX); } @@ -566,7 +563,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertEq(aliceInitialBalance - aliceFinalBalance, aliceShouldStream, "(sender) wrong final balance"); assertEq(bobFinalBalance, bobInitialBalance + aliceShouldStream, "(receiver) wrong final balance"); - vm.expectRevert(IVestingSchedulerV3.AlreadyExecuted.selector); + vm.expectRevert(IVestingSchedulerV2.AlreadyExecuted.selector); success = vestingScheduler.executeEndVesting(superToken, alice, bob); } @@ -922,7 +919,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.prank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); vm.startPrank(admin); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.executeEndVesting(superToken, alice, bob); } @@ -932,7 +929,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.prank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); vm.startPrank(admin); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.executeCliffAndFlow(superToken, alice, bob); } @@ -1059,7 +1056,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); - vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.FlowRateInvalid.selector); vestingScheduler.createVestingScheduleFromAmountAndDuration( superToken, bob, @@ -1072,7 +1069,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); console.log("Revert with cliff and start in history."); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingScheduleFromAmountAndDuration( superToken, bob, @@ -1111,7 +1108,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); console.log("Revert with start date in history."); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingScheduleFromAmountAndDuration( superToken, bob, @@ -1307,7 +1304,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Intermediary `mapCreateVestingScheduleParams` test assertAreScheduleCreationParamsEqual( - IVestingSchedulerV3.ScheduleCreationParams( + IVestingSchedulerV2.ScheduleCreationParams( superToken, alice, bob, @@ -1564,7 +1561,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Intermediary `mapCreateVestingScheduleParams` test assertAreScheduleCreationParamsEqual( - IVestingSchedulerV3.ScheduleCreationParams( + IVestingSchedulerV2.ScheduleCreationParams( superToken, alice, bob, @@ -1603,7 +1600,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.stopPrank(); // Assert - IVestingSchedulerV3.VestingSchedule memory actualSchedule = + IVestingSchedulerV2.VestingSchedule memory actualSchedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); assertEq( actualSchedule.cliffAndFlowDate, @@ -1876,7 +1873,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function test_createClaimableVestingSchedule_wrongData() public { vm.startPrank(alice); // revert with superToken = 0 - vm.expectRevert(IVestingSchedulerV3.ZeroAddress.selector); + vm.expectRevert(IVestingSchedulerV2.ZeroAddress.selector); vestingScheduler.createVestingSchedule( ISuperToken(address(0)), bob, @@ -1890,7 +1887,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with receivers = sender - vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.AccountInvalid.selector); vestingScheduler.createVestingSchedule( superToken, alice, @@ -1904,7 +1901,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with receivers = address(0) - vm.expectRevert(IVestingSchedulerV3.AccountInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.AccountInvalid.selector); vestingScheduler.createVestingSchedule( superToken, address(0), @@ -1918,37 +1915,37 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with flowRate = 0 - vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.FlowRateInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, START_DATE, CLIFF_DATE, 0, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX ); // revert with cliffDate = 0 but cliffAmount != 0 - vm.expectRevert(IVestingSchedulerV3.CliffInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.CliffInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, 0, 0, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX ); // revert with startDate < block.timestamp && cliffDate = 0 - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, uint32(block.timestamp - 1), 0, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX ); // revert with endDate = 0 - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, 0, CLAIM_VALIDITY_DATE, EMPTY_CTX ); // revert with cliffAndFlowDate < block.timestamp - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, 0, uint32(block.timestamp) - 1, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX ); // revert with cliffAndFlowDate >= endDate - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, @@ -1962,7 +1959,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with cliffAndFlowDate + startDateValidFor >= endDate - endDateValidBefore - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, @@ -1976,7 +1973,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with startDate > cliffDate - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, @@ -1990,7 +1987,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with vesting duration < 7 days - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, @@ -2004,7 +2001,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); // revert with invalid claim validity date (before schedule/cliff start) - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( superToken, bob, @@ -2033,7 +2030,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); vm.stopPrank(); - vm.expectRevert(IVestingSchedulerV3.ScheduleAlreadyExists.selector); + vm.expectRevert(IVestingSchedulerV2.ScheduleAlreadyExists.selector); vm.startPrank(alice); vestingScheduler.createVestingSchedule( @@ -2217,7 +2214,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); - vm.expectRevert(IVestingSchedulerV3.FlowRateInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.FlowRateInvalid.selector); vestingScheduler.createVestingScheduleFromAmountAndDuration( superToken, bob, @@ -2230,7 +2227,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); console.log("Revert with cliff and start in history."); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingScheduleFromAmountAndDuration( superToken, bob, @@ -2269,7 +2266,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); console.log("Revert with start date in history."); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingScheduleFromAmountAndDuration( superToken, bob, @@ -2464,7 +2461,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint256 initialTimestamp = block.timestamp + 10 days + 1800; vm.warp(initialTimestamp); vm.prank(_claimer); - vm.expectRevert(IVestingSchedulerV3.CannotClaimScheduleOnBehalf.selector); + vm.expectRevert(IVestingSchedulerV2.CannotClaimScheduleOnBehalf.selector); bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertEq(success, false); } @@ -2478,7 +2475,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(startTimestamp - 1); vm.prank(bob); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertEq(success, false); } @@ -2491,7 +2488,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(CLAIM_VALIDITY_DATE + 1); vm.prank(bob); - vm.expectRevert(IVestingSchedulerV3.TimeWindowInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertEq(success, false); } @@ -2506,7 +2503,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(bob); bool success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertEq(success, true); - vm.expectRevert(IVestingSchedulerV3.AlreadyExecuted.selector); + vm.expectRevert(IVestingSchedulerV2.AlreadyExecuted.selector); success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertEq(success, false); vm.stopPrank(); @@ -2597,7 +2594,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function test_executeEndVesting_scheduleNotClaimed() public { _createClaimableVestingScheduleWithDefaultData(alice, bob); - vm.expectRevert(IVestingSchedulerV3.ScheduleNotClaimed.selector); + vm.expectRevert(IVestingSchedulerV2.ScheduleNotClaimed.selector); vestingScheduler.executeEndVesting(superToken, alice, bob); } @@ -2698,7 +2695,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { } function test_getSender_throws_when_invalid_host() public { - vm.expectRevert(IVestingSchedulerV3.HostInvalid.selector); + vm.expectRevert(IVestingSchedulerV2.HostInvalid.selector); vm.startPrank(alice); vestingScheduler.createVestingSchedule( @@ -2782,7 +2779,11 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Verify the flow rate has been updated schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); uint256 remainingMonths = 4; - uint256 remainingAmount = secondUpdateAmount - schedule.alreadyVestedAmount; + + (uint256 alreadyVestedAmount,) = + vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); + + uint256 remainingAmount = secondUpdateAmount - alreadyVestedAmount; int96 expectedFlowRate = SafeCast.toInt96(SafeCast.toInt256(remainingAmount / (schedule.endDate - block.timestamp))); assertEq(schedule.flowRate, expectedFlowRate, "Flow rate should be updated for 300 USDC/month"); @@ -2796,9 +2797,11 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Verify the flow rate has been updated again schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + (alreadyVestedAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); + // Calculate new flow rate for remaining 3 months remainingMonths = 3; - remainingAmount = 1100 ether - schedule.alreadyVestedAmount; + remainingAmount = 1100 ether - alreadyVestedAmount; expectedFlowRate = SafeCast.toInt96(SafeCast.toInt256(remainingAmount / (schedule.endDate - block.timestamp))); assertEq(schedule.flowRate, expectedFlowRate, "Flow rate should be updated for 200 USDC/month"); @@ -2889,11 +2892,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Get the updated schedule schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + (uint256 alreadyVestedAmount,) = + vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); + // Calculate expected flow rate after update int96 expectedFlowRate = SafeCast.toInt96( - SafeCast.toInt256( - (updatedTotalAmount - schedule.alreadyVestedAmount) / (schedule.endDate - block.timestamp) - ) + SafeCast.toInt256((updatedTotalAmount - alreadyVestedAmount) / (schedule.endDate - block.timestamp)) ); // Verify the flow rate has been updated correctly @@ -2943,10 +2947,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Warp to third month (90 days total) vm.warp(startDate + 90 days); + (alreadyVestedAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); + // Calculate expected amount after third month // First two months + third month at reduced rate schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - uint256 thirdMonthAmount = (1100 ether - schedule.alreadyVestedAmount) / 3; // ~200 USDC/month + uint256 thirdMonthAmount = (1100 ether - alreadyVestedAmount) / 3; // ~200 USDC/month // Verify Bob's balance after third month assertApproxEqAbs( @@ -3050,4 +3056,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.getVestingSchedule(address(superToken), alice, bob); assertEq(schedule.endDate, newEndDate); } + + function _helperGetScheduleId(address superToken, address sender, address receiver) + private + pure + returns (bytes32) + { + return keccak256(abi.encodePacked(superToken, sender, receiver)); + } } From 2930060937a82e878682fc688ba36ea15c4f9435 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 6 Mar 2025 09:57:12 +0100 Subject: [PATCH 32/59] refactor: improve flow rate and remainder amount calculation methods - Extract flow rate and remainder amount calculation into separate pure methods - Add new `_updateVestingFlowRate` method to handle flow updates with context - Simplify flow rate update logic in `updateVestingScheduleFlowRateFromAmount` and `updateVestingScheduleEndDate` - Add debug event for remainder amount calculation - Minor validation improvements for end date updates --- .../contracts/VestingSchedulerV3.sol | 132 ++++++++++-------- 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 9e9e51a6be..384bc738c7 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -369,19 +369,36 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien alreadyVestedAmount = accountings[agg.id].alreadyVestedAmount; } - function _updateFlowRateAndRemainderAmount( - ScheduleAggregate memory agg, - uint256 amountLeftToVest, - uint256 timeLeftToVest - ) internal { - // Calculate the new flow rate and remainder amount - int96 newFlowRate = SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); - uint96 newRemainderAmount = - SafeCast.toUint96(amountLeftToVest - (SafeCast.toUint256(newFlowRate) * timeLeftToVest)); - - // Update the flow rate and remainder amount - vestingSchedules[agg.id].flowRate = newFlowRate; - vestingSchedules[agg.id].remainderAmount = newRemainderAmount; + function _calculateFlowRate(uint256 amountLeftToVest, uint256 timeLeftToVest) + internal + pure + returns (int96 flowRate) + { + // Calculate the new flow rate + flowRate = SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); + } + + function _calculateRemainderAmount(uint256 amountLeftToVest, uint256 timeLeftToVest, int96 flowRate) + internal + pure + returns (uint96 remainderAmount) + { + // Calculate the remainder amount + remainderAmount = SafeCast.toUint96(amountLeftToVest - (SafeCast.toUint256(flowRate) * timeLeftToVest)); + } + + function _updateVestingFlowRate( + ISuperToken superToken, + address sender, + address receiver, + int96 newFlowRate, + bytes memory ctx + ) internal returns (bytes memory newCtx) { + if (ctx.length != 0) { + newCtx = superToken.flowFromWithCtx(sender, receiver, newFlowRate, ctx); + } else { + superToken.flowFrom(sender, receiver, newFlowRate); + } } /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. @@ -394,17 +411,16 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien newCtx = ctx; address sender = _getSender(ctx); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; // Ensure vesting exists - if (schedule.endDate == 0) revert ScheduleDoesNotExist(); + if (agg.schedule.endDate == 0) revert ScheduleDoesNotExist(); /* Schedule update is not allowed if : - the schedule end date has passed - the cliff and flow date is in the future */ - if (schedule.endDate <= block.timestamp || block.timestamp < schedule.cliffAndFlowDate) { + if (agg.schedule.endDate <= block.timestamp || block.timestamp < agg.schedule.cliffAndFlowDate) { revert TimeWindowInvalid(); } @@ -415,23 +431,28 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; - uint256 timeLeftToVest = schedule.endDate - block.timestamp; + uint256 timeLeftToVest = agg.schedule.endDate - block.timestamp; + + int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); // Update the vesting flow rate and remainder amount - _updateFlowRateAndRemainderAmount(agg, amountLeftToVest, timeLeftToVest); + vestingSchedules[agg.id].flowRate = newFlowRate; + vestingSchedules[agg.id].remainderAmount = + _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); // If the schedule is started, update the existing flow rate to the new calculated flow rate - if (schedule.cliffAndFlowDate == 0) { - if (newCtx.length != 0) { - newCtx = superToken.flowFromWithCtx(sender, receiver, vestingSchedules[agg.id].flowRate, newCtx); - } else { - superToken.flowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); - } + if (agg.schedule.cliffAndFlowDate == 0) { + newCtx = _updateVestingFlowRate(superToken, sender, receiver, newFlowRate, newCtx); } // Emit VestingSchedulerV2 event for backward compatibility emit VestingScheduleUpdated( - superToken, sender, receiver, schedule.endDate, schedule.endDate, vestingSchedules[agg.id].remainderAmount + superToken, + sender, + receiver, + agg.schedule.endDate, + agg.schedule.endDate, + vestingSchedules[agg.id].remainderAmount ); // Emit VestingSchedulerV3 event for additional data @@ -439,9 +460,9 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien superToken, sender, receiver, - schedule.flowRate, - vestingSchedules[agg.id].flowRate, - _getTotalVestedAmount(schedule, agg.accounting), + agg.schedule.flowRate, + newFlowRate, + _getTotalVestedAmount(agg.schedule, agg.accounting), newTotalAmount, vestingSchedules[agg.id].remainderAmount ); @@ -457,10 +478,9 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien newCtx = ctx; address sender = _getSender(ctx); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; // Ensure vesting exists - if (schedule.endDate == 0) revert ScheduleDoesNotExist(); + if (agg.schedule.endDate == 0) revert ScheduleDoesNotExist(); /* Schedule update is not allowed if : @@ -469,31 +489,31 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien - the cliff and flow date is in the future */ if ( - schedule.endDate <= block.timestamp || endDate <= block.timestamp - || block.timestamp < schedule.cliffAndFlowDate + agg.schedule.endDate <= block.timestamp || endDate <= block.timestamp + || block.timestamp < agg.schedule.cliffAndFlowDate ) revert TimeWindowInvalid(); // Update the schedule end date vestingSchedules[agg.id].endDate = endDate; - uint256 amountLeftToVest = _getTotalVestedAmount(schedule, agg.accounting) - _settle(agg); + uint256 amountLeftToVest = _getTotalVestedAmount(agg.schedule, agg.accounting) - _settle(agg); uint256 timeLeftToVest = endDate - block.timestamp; + int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); + // Update the vesting flow rate and remainder amount - _updateFlowRateAndRemainderAmount(agg, amountLeftToVest, timeLeftToVest); + vestingSchedules[agg.id].flowRate = newFlowRate; + vestingSchedules[agg.id].remainderAmount = + _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); // If the schedule is started, update the existing flow rate to the new calculated flow rate - if (schedule.cliffAndFlowDate == 0) { - if (newCtx.length != 0) { - newCtx = superToken.flowFromWithCtx(sender, receiver, vestingSchedules[agg.id].flowRate, newCtx); - } else { - superToken.flowFrom(sender, receiver, vestingSchedules[agg.id].flowRate); - } + if (agg.schedule.cliffAndFlowDate == 0) { + newCtx = _updateVestingFlowRate(superToken, sender, receiver, newFlowRate, newCtx); } // Emit VestingSchedulerV2 event for backward compatibility emit VestingScheduleUpdated( - superToken, sender, receiver, schedule.endDate, endDate, vestingSchedules[agg.id].remainderAmount + superToken, sender, receiver, agg.schedule.endDate, endDate, vestingSchedules[agg.id].remainderAmount ); // Emit VestingSchedulerV3 event for additional data @@ -501,10 +521,10 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien superToken, sender, receiver, - schedule.endDate, + agg.schedule.endDate, endDate, - schedule.flowRate, - vestingSchedules[agg.id].flowRate, + agg.schedule.flowRate, + newFlowRate, vestingSchedules[agg.id].remainderAmount ); } @@ -519,7 +539,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); VestingSchedule memory schedule = agg.schedule; - if (endDate <= block.timestamp) revert TimeWindowInvalid(); + if (endDate < block.timestamp) revert TimeWindowInvalid(); // Note: Claimable schedules that have not been claimed cannot be updated @@ -527,19 +547,21 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien if (schedule.cliffAndFlowDate != 0 || schedule.endDate == 0) revert ScheduleNotFlowing(); vestingSchedules[agg.id].endDate = endDate; - // Note: Nullify the remainder amount when complexity of updates is introduced. - vestingSchedules[agg.id].remainderAmount = 0; - emit VestingScheduleUpdated( - superToken, - sender, - receiver, - schedule.endDate, - endDate, - 0 // remainderAmount - ); + uint256 amountLeftToVest = _getTotalVestedAmount(vestingSchedules[agg.id], agg.accounting) - _settle(agg); + uint256 timeLeftToVest = endDate - block.timestamp; + + emit DEBUG(amountLeftToVest, timeLeftToVest, schedule.flowRate); + + uint96 newRemainderAmount = _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, schedule.flowRate); + // Update the vesting remainder amount + vestingSchedules[agg.id].remainderAmount = newRemainderAmount; + + emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, newRemainderAmount); } + event DEBUG(uint256 amountLeftToVest, uint256 timeLeftToVest, int96 flowRate); + /// @dev IVestingScheduler.deleteVestingSchedule implementation. function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) external From 0a5f065d3f99c8c2a7f207402e3a38ab834ba7b7 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Thu, 6 Mar 2025 09:57:25 +0100 Subject: [PATCH 33/59] test: add test for updating vesting schedule end date - Implement `testUpdateVestingSchedule` to verify end date modification - Check that cliff and flow date is reset after update - Validate updated end date in vesting schedule storage --- .../scheduler/test/VestingSchedulerV3.t.sol | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index ae2119dc27..b5bfc85f60 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -14,7 +14,6 @@ import {VestingSchedulerV3} from "./../contracts/VestingSchedulerV3.sol"; import {FoundrySuperfluidTester} from "@superfluid-finance/ethereum-contracts/test/foundry/FoundrySuperfluidTester.t.sol"; import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol"; -import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import "forge-std/console.sol"; @@ -513,6 +512,29 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.stopPrank(); } + function testUpdateVestingSchedule() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + ); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + vm.stopPrank(); + vm.startPrank(alice); + vestingScheduler.updateVestingSchedule(superToken, bob, END_DATE + 1000, EMPTY_CTX); + //assert storage data + IVestingSchedulerV2.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + assertTrue(schedule.cliffAndFlowDate == 0, "schedule.cliffAndFlowDate"); + assertTrue(schedule.endDate == END_DATE + 1000, "schedule.endDate"); + } + function testDeleteVestingSchedule() public { _createVestingScheduleWithDefaultData(alice, bob); vm.startPrank(alice); From ef01806c880c62b23787ca35ee49acce7247ffbc Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:30:46 +0100 Subject: [PATCH 34/59] refactor: remove logging event --- .../scheduler/contracts/VestingSchedulerV3.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 384bc738c7..d3cda6b9d5 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -551,8 +551,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien uint256 amountLeftToVest = _getTotalVestedAmount(vestingSchedules[agg.id], agg.accounting) - _settle(agg); uint256 timeLeftToVest = endDate - block.timestamp; - emit DEBUG(amountLeftToVest, timeLeftToVest, schedule.flowRate); - uint96 newRemainderAmount = _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, schedule.flowRate); // Update the vesting remainder amount vestingSchedules[agg.id].remainderAmount = newRemainderAmount; @@ -560,8 +558,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, newRemainderAmount); } - event DEBUG(uint256 amountLeftToVest, uint256 timeLeftToVest, int96 flowRate); - /// @dev IVestingScheduler.deleteVestingSchedule implementation. function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) external From 42e04f25fc88b3bc32f214a36f7dd5ec477e432a Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:06:59 +0100 Subject: [PATCH 35/59] feat: added overloaded `createVestingScheduleFromAmountAndDuration` for non-linear cliff schedule creation --- .../contracts/VestingSchedulerV3.sol | 137 +++++++++++++++--- .../interface/IVestingSchedulerV3.sol | 53 +++++++ 2 files changed, 173 insertions(+), 17 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index d3cda6b9d5..fa45468385 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -191,6 +191,68 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien ); } + /// FIXME : add in the interface V3 + /// @dev IVestingScheduler.createVestingScheduleFromAmountAndDuration implementation. + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + uint256 cliffAmount, + bytes memory ctx + ) external returns (bytes memory newCtx) { + newCtx = ctx; + address sender = _getSender(ctx); + + if (cliffPeriod != 0 && cliffAmount == 0) revert CliffInvalid(); + + _validateAndCreateVestingSchedule( + mapCreateVestingScheduleParams( + superToken, + sender, + receiver, + totalAmount, + totalDuration, + _normalizeStartDate(startDate), + cliffPeriod, + claimPeriod, + cliffAmount + ) + ); + } + + /// @dev IVestingScheduler.createVestingScheduleFromAmountAndDuration implementation. + /// FIXME : add in the interface V3 + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + uint256 cliffAmount + ) external { + if (cliffPeriod != 0 && cliffAmount == 0) revert CliffInvalid(); + + _validateAndCreateVestingSchedule( + mapCreateVestingScheduleParams( + superToken, + _msgSender(), + receiver, + totalAmount, + totalDuration, + _normalizeStartDate(startDate), + cliffPeriod, + claimPeriod, + cliffAmount + ) + ); + } + /// @dev IVestingScheduler.mapCreateVestingScheduleParams implementation. function mapCreateVestingScheduleParams( ISuperToken superToken, @@ -202,27 +264,68 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien uint32 cliffPeriod, uint32 claimPeriod ) public pure override returns (ScheduleCreationParams memory params) { - uint32 claimValidityDate = claimPeriod != 0 ? startDate + claimPeriod : 0; + return mapCreateVestingScheduleParams( + superToken, sender, receiver, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, 0 + ); + } + /// @dev IVestingScheduler.mapCreateVestingScheduleParams implementation. + function mapCreateVestingScheduleParams( + ISuperToken superToken, + address sender, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + uint256 cliffAmount + ) public pure override returns (ScheduleCreationParams memory params) { + uint32 claimValidityDate = claimPeriod != 0 ? startDate + claimPeriod : 0; uint32 endDate = startDate + totalDuration; - int96 flowRate = SafeCast.toInt96(SafeCast.toInt256(totalAmount / totalDuration)); - uint96 remainderAmount = SafeCast.toUint96(totalAmount - (SafeCast.toUint256(flowRate) * totalDuration)); - if (cliffPeriod == 0) { - params = ScheduleCreationParams({ - superToken: superToken, - sender: sender, - receiver: receiver, - startDate: startDate, - claimValidityDate: claimValidityDate, - cliffDate: 0, - flowRate: flowRate, - cliffAmount: 0, - endDate: endDate, - remainderAmount: remainderAmount - }); + if (cliffAmount == 0) { + int96 flowRate = SafeCast.toInt96(SafeCast.toInt256(totalAmount / totalDuration)); + uint96 remainderAmount = SafeCast.toUint96(totalAmount - (SafeCast.toUint256(flowRate) * totalDuration)); + + if (cliffPeriod == 0) { + // No Cliff + params = ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: startDate, + claimValidityDate: claimValidityDate, + cliffDate: 0, + flowRate: flowRate, + cliffAmount: 0, + endDate: endDate, + remainderAmount: remainderAmount + }); + } else { + // Linear Default Cliff (calculated based on the overall vesting flow rate) + cliffAmount = SafeMath.mul(cliffPeriod, SafeCast.toUint256(flowRate)); + params = ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: startDate, + claimValidityDate: claimValidityDate, + cliffDate: startDate + cliffPeriod, + flowRate: flowRate, + cliffAmount: cliffAmount, + endDate: endDate, + remainderAmount: remainderAmount + }); + } } else { - uint256 cliffAmount = SafeMath.mul(cliffPeriod, SafeCast.toUint256(flowRate)); + // Non-Linear Cliff (user defined cliff amount) + int96 flowRate = + SafeCast.toInt96(SafeCast.toInt256(totalAmount - cliffAmount / totalDuration - cliffPeriod)); + uint96 remainderAmount = SafeCast.toUint96( + totalAmount - cliffAmount - (SafeCast.toUint256(flowRate) * totalDuration - cliffPeriod) + ); + params = ScheduleCreationParams({ superToken: superToken, sender: sender, diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index c396def229..3f0bddc50f 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -52,6 +52,59 @@ interface IVestingSchedulerV3 is IVestingSchedulerV2 { uint96 remainderAmount ); + /** + * @dev See IVestingSchedulerV2.createVestingScheduleFromAmountAndDuration overload for more details. + */ + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + uint256 cliffAmount, + bytes memory ctx + ) external returns (bytes memory newCtx); + + /** + * @dev See IVestingSchedulerV2.createVestingScheduleFromAmountAndDuration overload for more details. + */ + function createVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + uint256 cliffAmount + ) external; + + /** + * @dev Returns all relevant information related to a new vesting schedule creation + * @dev based on the amounts and durations. + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param totalAmount The total amount to be vested + * @param totalDuration The total duration of the vestingß + * @param startDate Timestamp when the vesting should start + * @param cliffPeriod The cliff period of the vesting + * @param claimPeriod The claim availability period + * @param cliffAmount The cliff amount of the vesting + */ + function mapCreateVestingScheduleParams( + ISuperToken superToken, + address sender, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + uint256 cliffAmount + ) external pure returns (ScheduleCreationParams memory params); + /** * @dev Updates a vesting schedule flow rate based on a new total amount to be vested * @param superToken SuperToken to be vested From 4be6390f8ca5a9e59a26157769fa09ac7f926cbb Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:08:09 +0100 Subject: [PATCH 36/59] refactor: remove note --- .../scheduler/contracts/VestingSchedulerV3.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index fa45468385..7cc8015d67 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -191,7 +191,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien ); } - /// FIXME : add in the interface V3 /// @dev IVestingScheduler.createVestingScheduleFromAmountAndDuration implementation. function createVestingScheduleFromAmountAndDuration( ISuperToken superToken, @@ -225,7 +224,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } /// @dev IVestingScheduler.createVestingScheduleFromAmountAndDuration implementation. - /// FIXME : add in the interface V3 function createVestingScheduleFromAmountAndDuration( ISuperToken superToken, address receiver, From 47191b875aa88f5c269701350108eba99405b8a6 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:47:47 +0100 Subject: [PATCH 37/59] test: added case for new overloaded function (non linear cliff) --- .../scheduler/contracts/VestingSchedulerV3.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 7cc8015d67..f406ebdda7 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -319,9 +319,9 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } else { // Non-Linear Cliff (user defined cliff amount) int96 flowRate = - SafeCast.toInt96(SafeCast.toInt256(totalAmount - cliffAmount / totalDuration - cliffPeriod)); + SafeCast.toInt96(SafeCast.toInt256((totalAmount - cliffAmount) / (totalDuration - cliffPeriod))); uint96 remainderAmount = SafeCast.toUint96( - totalAmount - cliffAmount - (SafeCast.toUint256(flowRate) * totalDuration - cliffPeriod) + (totalAmount - cliffAmount) - (SafeCast.toUint256(flowRate) * (totalDuration - cliffPeriod)) ); params = ScheduleCreationParams({ From e3406c8f0b9e652bd30b3f031b8f902983180121 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:48:10 +0100 Subject: [PATCH 38/59] test: added case for new overloaded function (non linear cliff) --- .../scheduler/test/VestingSchedulerV3.t.sol | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index b5bfc85f60..7fa437f358 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -1231,6 +1231,52 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.stopPrank(); } + function test_createVestingScheduleFromAmountAndDuration_nonLinearCliff(uint8 randomizer) public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.stopPrank(); + + uint32 startDate = uint32(block.timestamp); + uint256 totalVestedAmount = 200_000_000; // a value perfectly divisible + uint256 cliffAmount = 150_000_000; // Cliff account of 75% of the total amount + uint32 vestingDuration = 1 weeks + 1 days; + uint32 cliffPeriod = 1 days; + + int96 expectedFlowRate = + SafeCast.toInt96(SafeCast.toInt256((totalVestedAmount - cliffAmount) / (vestingDuration - cliffPeriod))); + uint96 expectedRemainderAmount = SafeCast.toUint96( + (totalVestedAmount - cliffAmount) - (SafeCast.toUint256(expectedFlowRate) * (vestingDuration - cliffPeriod)) + ); + vm.expectEmit(); + emit VestingScheduleCreated( + superToken, + alice, + bob, + startDate, + startDate + cliffPeriod, // expected cliff date + expectedFlowRate, + startDate + vestingDuration, // expected end date + cliffAmount, + 0, + expectedRemainderAmount + ); + + vm.startPrank(alice); + bool useCtx = randomizer % 2 == 0; + if (useCtx) { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0, cliffAmount, EMPTY_CTX + ); + } else { + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0, cliffAmount + ); + } + vm.stopPrank(); + } + struct BigTestData { uint256 beforeSenderBalance; uint256 beforeReceiverBalance; From 7a4fd33a0f3df803e1096114115bbd50a6c9c904 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 25 Mar 2025 10:49:49 +0100 Subject: [PATCH 39/59] deploy: update vestingSchedulerV3 address in network configuration --- packages/metadata/main/networks/list.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/metadata/main/networks/list.cjs b/packages/metadata/main/networks/list.cjs index 3ba6591163..4417bc3f1e 100644 --- a/packages/metadata/main/networks/list.cjs +++ b/packages/metadata/main/networks/list.cjs @@ -109,7 +109,7 @@ module.exports = "flowScheduler": "0x73B1Ce21d03ad389C2A291B1d1dc4DAFE7B5Dc68", "vestingScheduler": "0x27444c0235a4D921F3106475faeba0B5e7ABDD7a", "vestingSchedulerV2": "0x3aa62b96f44D0f8892BeBBC819DE8e02E9DE69A8", - "vestingSchedulerV3": "0x2dCFbF5BcE0522257E78bF164871770D30634A96", + "vestingSchedulerV3": "0x4fEc5B896AF3AFFeE74fC6F25c476fF53aAEfCe1", "autowrap": { "manager": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", "wrapStrategy": "0xf232f1fd34CE12e24F4391865c2D6E374D2C34d9" From 7be6c05ae73f724a26456da8044ca0e87c54c5e5 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 1 Apr 2025 14:33:45 +0200 Subject: [PATCH 40/59] feat: add external function `getTotalVestedAmount` --- .../scheduler/contracts/VestingSchedulerV3.sol | 13 +++++++++++++ .../contracts/interface/IVestingSchedulerV3.sol | 12 ++++++++++++ .../scheduler/test/VestingSchedulerV3.t.sol | 6 ++++++ 3 files changed, 31 insertions(+) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index f406ebdda7..37f316308d 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -822,6 +822,19 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien accounting.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount + currentFlowAmount; } + /// @inheritdoc IVestingSchedulerV3 + function getTotalVestedAmount(ISuperToken superToken, address sender, address receiver) + external + view + returns (uint256 totalVestedAmount) + { + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + ScheduleAccounting memory accounting = agg.accounting; + + return _getTotalVestedAmount(schedule, accounting); + } + /// @dev IVestingScheduler.executeEndVesting implementation. function executeEndVesting(ISuperToken superToken, address sender, address receiver) external diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index 3f0bddc50f..ce7898f9fc 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -133,4 +133,16 @@ interface IVestingSchedulerV3 is IVestingSchedulerV2 { uint32 endDate, bytes memory ctx ) external returns (bytes memory newCtx); + + /** + * @dev Returns the total amount of vested tokens for a given vesting schedule + * @param superToken The superToken being vested + * @param sender The vesting sender + * @param receiver The vesting receiver + * @return totalVestedAmount The total amount of vested tokens + */ + function getTotalVestedAmount(ISuperToken superToken, address sender, address receiver) + external + view + returns (uint256 totalVestedAmount); } diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 7fa437f358..6087683d19 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2957,6 +2957,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, updatedTotalAmount, EMPTY_CTX); + assertEq(vestingScheduler.getTotalVestedAmount(superToken, alice, bob), updatedTotalAmount); + // Get the updated schedule schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -3012,6 +3014,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1100 ether, EMPTY_CTX); + assertEq(vestingScheduler.getTotalVestedAmount(superToken, alice, bob), 1100 ether); + // Warp to third month (90 days total) vm.warp(startDate + 90 days); @@ -3034,6 +3038,8 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1500 ether, EMPTY_CTX); + assertEq(vestingScheduler.getTotalVestedAmount(superToken, alice, bob), 1500 ether); + // Warp to 24 hours before end date and execute end vesting vm.warp(schedule.endDate - 24 hours); From 757b578bec31f0e0c4480fcb3ccb8f2e44e713dc Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:26:51 +0200 Subject: [PATCH 41/59] feat: add `updateVestingScheduleFlowRateFromAmountAndEndDate` function + remove `ctx` from other update functions --- .../contracts/VestingSchedulerV3.sol | 109 +++++++++++++++--- .../interface/IVestingSchedulerV3.sol | 28 +++-- .../scheduler/test/VestingSchedulerV3.t.sol | 56 ++++----- 3 files changed, 132 insertions(+), 61 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 37f316308d..70039198d6 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -502,15 +502,96 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } } - /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. - function updateVestingScheduleFlowRateFromAmount( + function _updateVestingFlowRate(ISuperToken superToken, address sender, address receiver, int96 newFlowRate) + internal + { + superToken.flowFrom(sender, receiver, newFlowRate); + } + + /// @inheritdoc IVestingSchedulerV3 + function updateVestingScheduleFlowRateFromAmountAndEndDate( ISuperToken superToken, address receiver, uint256 newTotalAmount, - bytes memory ctx - ) external returns (bytes memory newCtx) { - newCtx = ctx; - address sender = _getSender(ctx); + uint32 newEndDate + ) external { + address sender = _msgSender(); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + + // Ensure vesting exists + if (agg.schedule.endDate == 0) revert ScheduleDoesNotExist(); + + /* + Schedule update is not allowed if : + - the current end date has passed + - the new end date is in the past + - the cliff and flow date is in the future + */ + if ( + agg.schedule.endDate <= block.timestamp || newEndDate <= block.timestamp + || block.timestamp < agg.schedule.cliffAndFlowDate + ) revert TimeWindowInvalid(); + + // Update the schedule end date + vestingSchedules[agg.id].endDate = newEndDate; + + // Settle the amount already vested + uint256 alreadyVestedAmount = _settle(agg); + + // Ensure that the new total amount is larger than the amount already vested + if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); + + uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; + uint256 timeLeftToVest = newEndDate - block.timestamp; + + int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); + + // Update the vesting flow rate and remainder amount + vestingSchedules[agg.id].flowRate = newFlowRate; + vestingSchedules[agg.id].remainderAmount = + _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); + + // If the schedule is started, update the existing flow rate to the new calculated flow rate + if (agg.schedule.cliffAndFlowDate == 0) { + _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); + } + + // Emit VestingSchedulerV2 event for backward compatibility + emit VestingScheduleUpdated( + superToken, sender, receiver, agg.schedule.endDate, newEndDate, vestingSchedules[agg.id].remainderAmount + ); + + // Emit VestingSchedulerV3 event for additional data + emit VestingScheduleEndDateUpdated( + superToken, + sender, + receiver, + agg.schedule.endDate, + newEndDate, + agg.schedule.flowRate, + newFlowRate, + vestingSchedules[agg.id].remainderAmount + ); + + // Emit VestingSchedulerV3 event for additional data + emit VestingScheduleTotalAmountUpdated( + superToken, + sender, + receiver, + agg.schedule.flowRate, + newFlowRate, + _getTotalVestedAmount(agg.schedule, agg.accounting), + newTotalAmount, + vestingSchedules[agg.id].remainderAmount + ); + } + + /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. + function updateVestingScheduleFlowRateFromAmount(ISuperToken superToken, address receiver, uint256 newTotalAmount) + external + { + address sender = _msgSender(); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); // Ensure vesting exists @@ -543,7 +624,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien // If the schedule is started, update the existing flow rate to the new calculated flow rate if (agg.schedule.cliffAndFlowDate == 0) { - newCtx = _updateVestingFlowRate(superToken, sender, receiver, newFlowRate, newCtx); + _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); } // Emit VestingSchedulerV2 event for backward compatibility @@ -570,14 +651,10 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } /// @dev IVestingSchedulerV3.updateVestingScheduleEndDate implementation. - function updateVestingScheduleFlowRateFromEndDate( - ISuperToken superToken, - address receiver, - uint32 endDate, - bytes memory ctx - ) external returns (bytes memory newCtx) { - newCtx = ctx; - address sender = _getSender(ctx); + function updateVestingScheduleFlowRateFromEndDate(ISuperToken superToken, address receiver, uint32 endDate) + external + { + address sender = _msgSender(); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); // Ensure vesting exists @@ -609,7 +686,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien // If the schedule is started, update the existing flow rate to the new calculated flow rate if (agg.schedule.cliffAndFlowDate == 0) { - newCtx = _updateVestingFlowRate(superToken, sender, receiver, newFlowRate, newCtx); + _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); } // Emit VestingSchedulerV2 event for backward compatibility diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index ce7898f9fc..53ae8848c6 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -106,18 +106,27 @@ interface IVestingSchedulerV3 is IVestingSchedulerV2 { ) external pure returns (ScheduleCreationParams memory params); /** - * @dev Updates a vesting schedule flow rate based on a new total amount to be vested + * @dev Updates a vesting schedule flow rate based on a new total amount to be vested and a new end date * @param superToken SuperToken to be vested * @param receiver Vesting receiver * @param newTotalAmount The new total amount to be vested - * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) + * @param newEndDate The new end date */ - function updateVestingScheduleFlowRateFromAmount( + function updateVestingScheduleFlowRateFromAmountAndEndDate( ISuperToken superToken, address receiver, uint256 newTotalAmount, - bytes memory ctx - ) external returns (bytes memory newCtx); + uint32 newEndDate + ) external; + + /** + * @dev Updates a vesting schedule flow rate based on a new total amount to be vested + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param newTotalAmount The new total amount to be vested + */ + function updateVestingScheduleFlowRateFromAmount(ISuperToken superToken, address receiver, uint256 newTotalAmount) + external; /** * @dev Updates the end date for a vesting schedule which already reached the cliff @@ -125,14 +134,9 @@ interface IVestingSchedulerV3 is IVestingSchedulerV2 { * @param superToken SuperToken to be vested * @param receiver Vesting receiver * @param endDate The timestamp when the stream should stop - * @param ctx Superfluid context used when batching operations. (or bytes(0) if not SF batching) */ - function updateVestingScheduleFlowRateFromEndDate( - ISuperToken superToken, - address receiver, - uint32 endDate, - bytes memory ctx - ) external returns (bytes memory newCtx); + function updateVestingScheduleFlowRateFromEndDate(ISuperToken superToken, address receiver, uint32 endDate) + external; /** * @dev Returns the total amount of vested tokens for a given vesting schedule diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 6087683d19..f21c5c4904 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -411,7 +411,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.executeCliffAndFlow(superToken, alice, bob); vm.stopPrank(); vm.startPrank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(END_DATE + 1000), EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(END_DATE + 1000)); //assert storage data IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -434,31 +434,25 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Schedule update is not allowed if : "the cliff and flow date is in the future" vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE + 1 hours, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE + 1 hours); uint256 afterCliffAndFlowDate = CLIFF_DATE + 30 minutes; vm.warp(afterCliffAndFlowDate); // Schedule update is not allowed if : "the new end date is in the past" vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate( - superToken, bob, uint32(afterCliffAndFlowDate - 1), EMPTY_CTX - ); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterCliffAndFlowDate - 1)); // Schedule update is not allowed if : "the new end date is in right now (block.timestamp)" vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate( - superToken, bob, uint32(afterCliffAndFlowDate), EMPTY_CTX - ); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterCliffAndFlowDate)); uint256 afterEndDate = END_DATE + 1 hours; vm.warp(afterEndDate); // Schedule update is not allowed if : "the current end date has passed" vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate( - superToken, bob, uint32(afterEndDate + 1 hours), EMPTY_CTX - ); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterEndDate + 1 hours)); vm.stopPrank(); } @@ -481,7 +475,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Schedule update is not allowed if : "the cliff and flow date is in the future" vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount); uint256 afterCliffAndFlowDate = CLIFF_DATE + 30 minutes; vm.warp(afterCliffAndFlowDate); @@ -489,14 +483,14 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Amount is invalid if it is less than the already vested amount uint256 invalidNewAmount = CLIFF_TRANSFER_AMOUNT; vm.expectRevert(IVestingSchedulerV3.InvalidNewTotalAmount.selector); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, invalidNewAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, invalidNewAmount); uint256 afterEndDate = END_DATE + 1 hours; vm.warp(afterEndDate); // Schedule update is not allowed if : "the current end date has passed" vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount); vm.stopPrank(); } @@ -504,11 +498,11 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testCannotUpdateVestingScheduleIfDataDontExist(uint256 newAmount) public { vm.startPrank(alice); vm.expectRevert(IVestingSchedulerV2.ScheduleDoesNotExist.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE); newAmount = bound(newAmount, 1, type(uint256).max); vm.expectRevert(IVestingSchedulerV2.ScheduleDoesNotExist.selector); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newAmount); vm.stopPrank(); } @@ -527,7 +521,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingScheduler.executeCliffAndFlow(superToken, alice, bob); vm.stopPrank(); vm.startPrank(alice); - vestingScheduler.updateVestingSchedule(superToken, bob, END_DATE + 1000, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE + 1000); //assert storage data IVestingSchedulerV2.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -660,7 +654,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, expectedRemainder); vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE); IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -724,7 +718,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, expectedRemainder); vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE); IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -789,7 +783,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, END_DATE, expectedRemainder); vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount); IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -866,7 +860,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, END_DATE, expectedRemainder); vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount); IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -2842,7 +2836,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint256 secondUpdateAmount = 1400 ether; vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, secondUpdateAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, secondUpdateAmount); // Verify the flow rate has been updated schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -2860,7 +2854,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(startDate + 3 * 30 days); vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1100 ether, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1100 ether); // Verify the flow rate has been updated again schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); @@ -2877,7 +2871,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(startDate + 4 * 30 days); vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1500 ether, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1500 ether); // Warp to 24 hours before end date and execute end vesting vm.warp(schedule.endDate - 24 hours); @@ -2955,7 +2949,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint256 updatedTotalAmount = 1400 ether; vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, updatedTotalAmount, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, updatedTotalAmount); assertEq(vestingScheduler.getTotalVestedAmount(superToken, alice, bob), updatedTotalAmount); @@ -3012,7 +3006,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Update schedule again to 1100 USDC vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1100 ether, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1100 ether); assertEq(vestingScheduler.getTotalVestedAmount(superToken, alice, bob), 1100 ether); @@ -3036,7 +3030,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Update schedule one last time to 1500 USDC vm.prank(alice); - vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1500 ether, EMPTY_CTX); + vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, 1500 ether); assertEq(vestingScheduler.getTotalVestedAmount(superToken, alice, bob), 1500 ether); @@ -3083,9 +3077,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ops[0] = ISuperfluid.Operation({ operationType: BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_APP_ACTION, target: address(vestingScheduler), - data: abi.encodeCall( - vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate, EMPTY_CTX) - ) + data: abi.encodeCall(vestingScheduler.updateVestingSchedule, (superToken, bob, newEndDate, EMPTY_CTX)) }); // Act @@ -3115,9 +3107,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ops[0] = ISuperfluid.Operation({ operationType: BatchOperation.OPERATION_TYPE_ERC2771_FORWARD_CALL, target: address(vestingScheduler), - data: abi.encodeCall( - vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate, EMPTY_CTX) - ) + data: abi.encodeCall(vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate)) }); // Act From aad945d0c79d96890d117c5d64d35846627907ed Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 1 Apr 2025 16:38:40 +0200 Subject: [PATCH 42/59] test: added test for `updateVestingScheduleFlowRateFromAmountAndEndDate` --- .../scheduler/test/VestingSchedulerV3.t.sol | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index f21c5c4904..31081e1778 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -396,6 +396,37 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { _createVestingScheduleWithDefaultData(alice, bob); } + function testUdateVestingScheduleFlowRateFromAmountAndEndDate() public { + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); + vm.expectEmit(true, true, true, true); + emit VestingScheduleCreated( + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + ); + _createVestingScheduleWithDefaultData(alice, bob); + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + vm.startPrank(admin); + uint256 initialTimestamp = block.timestamp + 10 days + 1800; + vm.warp(initialTimestamp); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + vm.stopPrank(); + vm.startPrank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( + superToken, bob, CLIFF_TRANSFER_AMOUNT * 2, uint32(END_DATE + 1000) + ); + + IVestingSchedulerV3.VestingSchedule memory schedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + int96 expectedFlowRate = SafeCast.toInt96( + SafeCast.toInt256((CLIFF_TRANSFER_AMOUNT * 2 - CLIFF_TRANSFER_AMOUNT) / (END_DATE + 1000 - block.timestamp)) + ); + //assert storage data + assertTrue(schedule.cliffAndFlowDate == 0, "schedule.cliffAndFlowDate"); + assertApproxEqAbs(schedule.flowRate, expectedFlowRate, 1e10, "schedule.flowRate"); + assertTrue(schedule.endDate == END_DATE + 1000, "schedule.endDate"); + } + function testUpdateVestingScheduleFlowRateFromEndDate() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.expectEmit(true, true, true, true); From 232fed6002bbef686e68391d9c4392ba6f2a1642 Mon Sep 17 00:00:00 2001 From: Pilou <76021631+0xPilou@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:24:15 +0200 Subject: [PATCH 43/59] feat: large refactoring - remove ctx based function - reformat interface and contract - added natspec comments --- .../contracts/VestingSchedulerV3.sol | 1184 ++++++++--------- .../interface/IVestingSchedulerV3.sol | 433 +++++- .../scheduler/test/VestingSchedulerV3.t.sol | 375 ++---- 3 files changed, 1062 insertions(+), 930 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 70039198d6..35cb9916f8 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -2,29 +2,40 @@ // solhint-disable not-rely-on-time pragma solidity ^0.8.0; +/// @dev OpenZeppelin Imports +import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; +import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; + +/// @dev Superfluid Protocol Imports import { ISuperfluid, - ISuperToken, - SuperAppDefinitions + ISuperToken } from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; -import {SuperAppBase} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBase.sol"; import {SuperTokenV1Library} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperTokenV1Library.sol"; import {IRelayRecipient} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/utils/IRelayRecipient.sol"; -import {IVestingSchedulerV3} from "./interface/IVestingSchedulerV3.sol"; -import {SafeMath} from "@openzeppelin/contracts/utils/math/SafeMath.sol"; -import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol"; - -contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipient { - using SuperTokenV1Library for ISuperToken; - - ISuperfluid public immutable HOST; - mapping(bytes32 => VestingSchedule) public vestingSchedules; // id = keccak(supertoken, sender, receiver) - mapping(bytes32 => ScheduleAccounting) public accountings; - uint32 public constant MIN_VESTING_DURATION = 7 days; - uint32 public constant START_DATE_VALID_AFTER = 3 days; - uint32 public constant END_DATE_VALID_BEFORE = 1 days; +/// @dev Automation Contracts Imports +import {IVestingSchedulerV3} from "./interface/IVestingSchedulerV3.sol"; +using SuperTokenV1Library for ISuperToken; + +contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { + // ____ __ __ + // / __ \____ _/ /_____ _/ /___ ______ ___ _____ + // / / / / __ `/ __/ __ `/ __/ / / / __ \/ _ \/ ___/ + // / /_/ / /_/ / /_/ /_/ / /_/ /_/ / /_/ / __(__ ) + // /_____/\__,_/\__/\__,_/\__/\__, / .___/\___/____/ + // /____/_/ + + /** + * @notice Aggregate struct containing all schedule-related data + * @param superToken The SuperToken being vested + * @param sender The vesting sender + * @param receiver The vesting receiver + * @param id The unique identifier for this schedule + * @param schedule The vesting schedule details + * @param accounting The accounting details for this schedule + */ struct ScheduleAggregate { ISuperToken superToken; address sender; @@ -34,53 +45,69 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien ScheduleAccounting accounting; } + /** + * @notice Struct containing accounting details for a schedule + * @param alreadyVestedAmount The amount already vested + * @param lastUpdated The timestamp of the last update + */ struct ScheduleAccounting { uint256 alreadyVestedAmount; uint256 lastUpdated; } + // ____ __ __ __ _____ __ __ + // / _/___ ___ ____ ___ __ __/ /_____ _/ /_ / /__ / ___// /_____ _/ /____ _____ + // / // __ `__ \/ __ `__ \/ / / / __/ __ `/ __ \/ / _ \ \__ \/ __/ __ `/ __/ _ \/ ___/ + // _/ // / / / / / / / / / / /_/ / /_/ /_/ / /_/ / / __/ ___/ / /_/ /_/ / /_/ __(__ ) + // /___/_/ /_/ /_/_/ /_/ /_/\__,_/\__/\__,_/_.___/_/\___/ /____/\__/\__,_/\__/\___/____/ + + /// @notice The Superfluid host contract. + ISuperfluid public immutable HOST; + + /// @notice The minimum vesting duration. + uint32 public constant MIN_VESTING_DURATION = 7 days; + + /// @notice Delay after the start date after which the vesting cannot be executed. + uint32 public constant START_DATE_VALID_AFTER = 3 days; + + /// @notice Delay before the end date before which the vesting cannot be terminated. + uint32 public constant END_DATE_VALID_BEFORE = 1 days; + + // _____ __ __ + // / ___// /_____ _/ /____ _____ + // \__ \/ __/ __ `/ __/ _ \/ ___/ + // ___/ / /_/ /_/ / /_/ __(__ ) + // /____/\__/\__,_/\__/\___/____/ + + /// @notice The vesting schedules. + /// @dev id = keccak(supertoken, sender, receiver) + mapping(bytes32 vestingId => VestingSchedule) public vestingSchedules; + + /// @notice The vesting schedule accounting. + /// @dev id = keccak(supertoken, sender, receiver) + mapping(bytes32 vestingId => ScheduleAccounting) public accountings; + + // ______ __ __ + // / ____/___ ____ _____/ /________ _______/ /_____ _____ + // / / / __ \/ __ \/ ___/ __/ ___/ / / / ___/ __/ __ \/ ___/ + // / /___/ /_/ / / / (__ ) /_/ / / /_/ / /__/ /_/ /_/ / / + // \____/\____/_/ /_/____/\__/_/ \__,_/\___/\__/\____/_/ + + /** + * @notice VestingSchedulerV3 contract constructor + * @param host The Superfluid host contract + */ constructor(ISuperfluid host) { - // Superfluid SuperApp registration. This is a dumb SuperApp, only for front-end tx batch calls. - uint256 configWord = SuperAppDefinitions.APP_LEVEL_FINAL | SuperAppDefinitions.BEFORE_AGREEMENT_CREATED_NOOP - | SuperAppDefinitions.AFTER_AGREEMENT_CREATED_NOOP | SuperAppDefinitions.BEFORE_AGREEMENT_UPDATED_NOOP - | SuperAppDefinitions.AFTER_AGREEMENT_UPDATED_NOOP | SuperAppDefinitions.BEFORE_AGREEMENT_TERMINATED_NOOP - | SuperAppDefinitions.AFTER_AGREEMENT_TERMINATED_NOOP; - host.registerApp(configWord); HOST = host; } - /// @dev IVestingScheduler.createVestingSchedule implementation. - function createVestingSchedule( - ISuperToken superToken, - address receiver, - uint32 startDate, - uint32 cliffDate, - int96 flowRate, - uint256 cliffAmount, - uint32 endDate, - uint32 claimValidityDate, - bytes memory ctx - ) external returns (bytes memory newCtx) { - newCtx = ctx; - address sender = _getSender(ctx); - - _validateAndCreateVestingSchedule( - ScheduleCreationParams({ - superToken: superToken, - sender: sender, - receiver: receiver, - startDate: _normalizeStartDate(startDate), - claimValidityDate: claimValidityDate, - cliffDate: cliffDate, - flowRate: flowRate, - cliffAmount: cliffAmount, - endDate: endDate, - remainderAmount: 0 - }) - ); - } + // ______ __ __ ______ __ _ + // / ____/ __/ /____ _________ ____ _/ / / ____/_ ______ _____/ /_(_)___ ____ _____ + // / __/ | |/_/ __/ _ \/ ___/ __ \/ __ `/ / / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ + // / /____> params.cliffDate) revert TimeWindowInvalid(); - if (params.cliffDate == 0 && params.cliffAmount != 0) revert CliffInvalid(); + // Ensure that the new total amount is larger than the amount already vested + if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); - uint32 cliffAndFlowDate = params.cliffDate == 0 ? params.startDate : params.cliffDate; - // Note: Vesting Scheduler V2 allows cliff and flow to be in the schedule creation block, V1 didn't. - if ( - cliffAndFlowDate < block.timestamp || cliffAndFlowDate >= params.endDate - || cliffAndFlowDate + START_DATE_VALID_AFTER >= params.endDate - END_DATE_VALID_BEFORE - || params.endDate - cliffAndFlowDate < MIN_VESTING_DURATION - ) revert TimeWindowInvalid(); + uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; + uint256 timeLeftToVest = newEndDate - block.timestamp; - // Note : claimable schedule created with a claim validity date equal to 0 is considered regular schedule - if (params.claimValidityDate != 0 && params.claimValidityDate < cliffAndFlowDate) { - revert TimeWindowInvalid(); - } + int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); - bytes32 id = _getId(address(params.superToken), params.sender, params.receiver); - if (vestingSchedules[id].endDate != 0) revert ScheduleAlreadyExists(); + // Update the vesting flow rate and remainder amount + vestingSchedules[agg.id].flowRate = newFlowRate; + vestingSchedules[agg.id].remainderAmount = + _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); - vestingSchedules[id] = VestingSchedule({ - cliffAndFlowDate: cliffAndFlowDate, - endDate: params.endDate, - flowRate: params.flowRate, - cliffAmount: params.cliffAmount, - remainderAmount: params.remainderAmount, - claimValidityDate: params.claimValidityDate - }); + // If the schedule is started, update the existing flow rate to the new calculated flow rate + if (agg.schedule.cliffAndFlowDate == 0) { + _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); + } - emit VestingScheduleCreated( - params.superToken, - params.sender, - params.receiver, - params.startDate, - params.cliffDate, - params.flowRate, - params.endDate, - params.cliffAmount, - params.claimValidityDate, - params.remainderAmount - ); - } - - /// @dev IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. - function createAndExecuteVestingScheduleFromAmountAndDuration( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - uint32 totalDuration, - bytes memory ctx - ) external returns (bytes memory newCtx) { - newCtx = _validateAndCreateAndExecuteVestingScheduleFromAmountAndDuration( - superToken, receiver, totalAmount, totalDuration, ctx + // Emit VestingSchedulerV2 event for backward compatibility + emit VestingScheduleUpdated( + superToken, sender, receiver, agg.schedule.endDate, newEndDate, vestingSchedules[agg.id].remainderAmount ); - } - /// @dev IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. - function createAndExecuteVestingScheduleFromAmountAndDuration( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - uint32 totalDuration - ) external { - _validateAndCreateAndExecuteVestingScheduleFromAmountAndDuration( - superToken, receiver, totalAmount, totalDuration, bytes("") + // Emit VestingSchedulerV3 event for additional data + emit VestingScheduleEndDateUpdated( + superToken, + sender, + receiver, + agg.schedule.endDate, + newEndDate, + agg.schedule.flowRate, + newFlowRate, + vestingSchedules[agg.id].remainderAmount ); - } - /// @dev IVestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration. - function _validateAndCreateAndExecuteVestingScheduleFromAmountAndDuration( - ISuperToken superToken, - address receiver, - uint256 totalAmount, - uint32 totalDuration, - bytes memory ctx - ) private returns (bytes memory newCtx) { - newCtx = ctx; - address sender = _getSender(ctx); - - _validateAndCreateVestingSchedule( - mapCreateVestingScheduleParams( - superToken, - sender, - receiver, - totalAmount, - totalDuration, - _normalizeStartDate(0), - 0, // cliffPeriod - 0 // claimValidityDate - ) + // Emit VestingSchedulerV3 event for additional data + emit VestingScheduleTotalAmountUpdated( + superToken, + sender, + receiver, + agg.schedule.flowRate, + newFlowRate, + _getTotalVestedAmount(agg.schedule, agg.accounting), + newTotalAmount, + vestingSchedules[agg.id].remainderAmount ); - - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - - _validateBeforeCliffAndFlow(agg.schedule, /* disableClaimCheck: */ false); - assert(_executeCliffAndFlow(agg)); - } - - function _settle(ScheduleAggregate memory agg) internal returns (uint256 alreadyVestedAmount) { - // Ensure that the cliff and flow date has passed - assert(block.timestamp >= agg.schedule.cliffAndFlowDate); - - // Delete the cliff amount and account for it in the already vested amount - delete vestingSchedules[agg.id].cliffAmount; - - // Update the timestamp of the last schedule update - accountings[agg.id].lastUpdated = block.timestamp; - - if (block.timestamp > agg.schedule.endDate) { - // If the schedule end date has passed, settle the total amount vested - accountings[agg.id].alreadyVestedAmount = _getTotalVestedAmount(agg.schedule, agg.accounting); - } else { - // If the schedule end date has not passed, accrue the amount already vested - uint256 actualLastUpdate = - agg.accounting.lastUpdated == 0 ? agg.schedule.cliffAndFlowDate : agg.accounting.lastUpdated; - - // Accrue the amount already vested - accountings[agg.id].alreadyVestedAmount += - ((block.timestamp - actualLastUpdate) * uint96(agg.schedule.flowRate)) + agg.schedule.cliffAmount; - } - alreadyVestedAmount = accountings[agg.id].alreadyVestedAmount; - } - - function _calculateFlowRate(uint256 amountLeftToVest, uint256 timeLeftToVest) - internal - pure - returns (int96 flowRate) - { - // Calculate the new flow rate - flowRate = SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); } - function _calculateRemainderAmount(uint256 amountLeftToVest, uint256 timeLeftToVest, int96 flowRate) - internal - pure - returns (uint96 remainderAmount) - { - // Calculate the remainder amount - remainderAmount = SafeCast.toUint96(amountLeftToVest - (SafeCast.toUint256(flowRate) * timeLeftToVest)); - } - - function _updateVestingFlowRate( - ISuperToken superToken, - address sender, - address receiver, - int96 newFlowRate, - bytes memory ctx - ) internal returns (bytes memory newCtx) { - if (ctx.length != 0) { - newCtx = superToken.flowFromWithCtx(sender, receiver, newFlowRate, ctx); - } else { - superToken.flowFrom(sender, receiver, newFlowRate); - } - } - - function _updateVestingFlowRate(ISuperToken superToken, address sender, address receiver, int96 newFlowRate) - internal + /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. + function updateVestingScheduleFlowRateFromAmount(ISuperToken superToken, address receiver, uint256 newTotalAmount) + external { - superToken.flowFrom(sender, receiver, newFlowRate); - } - - /// @inheritdoc IVestingSchedulerV3 - function updateVestingScheduleFlowRateFromAmountAndEndDate( - ISuperToken superToken, - address receiver, - uint256 newTotalAmount, - uint32 newEndDate - ) external { address sender = _msgSender(); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); // Ensure vesting exists @@ -523,17 +315,12 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien /* Schedule update is not allowed if : - - the current end date has passed - - the new end date is in the past + - the schedule end date has passed - the cliff and flow date is in the future */ - if ( - agg.schedule.endDate <= block.timestamp || newEndDate <= block.timestamp - || block.timestamp < agg.schedule.cliffAndFlowDate - ) revert TimeWindowInvalid(); - - // Update the schedule end date - vestingSchedules[agg.id].endDate = newEndDate; + if (agg.schedule.endDate <= block.timestamp || block.timestamp < agg.schedule.cliffAndFlowDate) { + revert TimeWindowInvalid(); + } // Settle the amount already vested uint256 alreadyVestedAmount = _settle(agg); @@ -542,7 +329,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; - uint256 timeLeftToVest = newEndDate - block.timestamp; + uint256 timeLeftToVest = agg.schedule.endDate - block.timestamp; int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); @@ -558,18 +345,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien // Emit VestingSchedulerV2 event for backward compatibility emit VestingScheduleUpdated( - superToken, sender, receiver, agg.schedule.endDate, newEndDate, vestingSchedules[agg.id].remainderAmount - ); - - // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleEndDateUpdated( superToken, sender, receiver, agg.schedule.endDate, - newEndDate, - agg.schedule.flowRate, - newFlowRate, + agg.schedule.endDate, vestingSchedules[agg.id].remainderAmount ); @@ -586,12 +366,11 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien ); } - /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. - function updateVestingScheduleFlowRateFromAmount(ISuperToken superToken, address receiver, uint256 newTotalAmount) + /// @inheritdoc IVestingSchedulerV3 + function updateVestingScheduleFlowRateFromEndDate(ISuperToken superToken, address receiver, uint32 endDate) external { address sender = _msgSender(); - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); // Ensure vesting exists @@ -599,21 +378,20 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien /* Schedule update is not allowed if : - - the schedule end date has passed + - the current end date has passed + - the new end date is in the past - the cliff and flow date is in the future */ - if (agg.schedule.endDate <= block.timestamp || block.timestamp < agg.schedule.cliffAndFlowDate) { - revert TimeWindowInvalid(); - } - - // Settle the amount already vested - uint256 alreadyVestedAmount = _settle(agg); + if ( + agg.schedule.endDate <= block.timestamp || endDate <= block.timestamp + || block.timestamp < agg.schedule.cliffAndFlowDate + ) revert TimeWindowInvalid(); - // Ensure that the new total amount is larger than the amount already vested - if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); + // Update the schedule end date + vestingSchedules[agg.id].endDate = endDate; - uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; - uint256 timeLeftToVest = agg.schedule.endDate - block.timestamp; + uint256 amountLeftToVest = _getTotalVestedAmount(agg.schedule, agg.accounting) - _settle(agg); + uint256 timeLeftToVest = endDate - block.timestamp; int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); @@ -629,152 +407,400 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien // Emit VestingSchedulerV2 event for backward compatibility emit VestingScheduleUpdated( - superToken, - sender, - receiver, - agg.schedule.endDate, - agg.schedule.endDate, - vestingSchedules[agg.id].remainderAmount + superToken, sender, receiver, agg.schedule.endDate, endDate, vestingSchedules[agg.id].remainderAmount ); // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleTotalAmountUpdated( + emit VestingScheduleEndDateUpdated( superToken, sender, receiver, + agg.schedule.endDate, + endDate, agg.schedule.flowRate, newFlowRate, - _getTotalVestedAmount(agg.schedule, agg.accounting), - newTotalAmount, vestingSchedules[agg.id].remainderAmount ); } - /// @dev IVestingSchedulerV3.updateVestingScheduleEndDate implementation. - function updateVestingScheduleFlowRateFromEndDate(ISuperToken superToken, address receiver, uint32 endDate) + /// @inheritdoc IVestingSchedulerV3 + function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate) external { + address sender = _msgSender(); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + if (endDate < block.timestamp) revert TimeWindowInvalid(); + + // Note: Claimable schedules that have not been claimed cannot be updated + + // Only allow an update if 1. vesting exists 2. executeCliffAndFlow() has been called + if (schedule.cliffAndFlowDate != 0 || schedule.endDate == 0) revert ScheduleNotFlowing(); + + vestingSchedules[agg.id].endDate = endDate; + + uint256 amountLeftToVest = _getTotalVestedAmount(vestingSchedules[agg.id], agg.accounting) - _settle(agg); + uint256 timeLeftToVest = endDate - block.timestamp; + + uint96 newRemainderAmount = _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, schedule.flowRate); + // Update the vesting remainder amount + vestingSchedules[agg.id].remainderAmount = newRemainderAmount; + + emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, newRemainderAmount); + } + + /// @inheritdoc IVestingSchedulerV3 + function deleteVestingSchedule(ISuperToken superToken, address receiver) external { + address sender = _msgSender(); + + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + if (schedule.endDate != 0) { + _deleteVestingSchedule(agg.id); + emit VestingScheduleDeleted(superToken, sender, receiver); + } else { + revert ScheduleDoesNotExist(); + } + } + + /// @inheritdoc IVestingSchedulerV3 + function executeCliffAndFlow(ISuperToken superToken, address sender, address receiver) external + returns (bool success) { - address sender = _msgSender(); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + + if (schedule.claimValidityDate != 0) { + _validateAndClaim(agg); + _validateBeforeCliffAndFlow(schedule, /* disableClaimCheck: */ true); + if (block.timestamp >= _gteDateToExecuteEndVesting(schedule)) { + _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ true); + success = _executeVestingAsSingleTransfer(agg); + } else { + success = _executeCliffAndFlow(agg); + } + } else { + _validateBeforeCliffAndFlow(schedule, /* disableClaimCheck: */ false); + success = _executeCliffAndFlow(agg); + } + } + + /// @inheritdoc IVestingSchedulerV3 + function executeEndVesting(ISuperToken superToken, address sender, address receiver) + external + returns (bool success) + { + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + ScheduleAccounting memory accounting = agg.accounting; + + _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ false); + + uint256 alreadyVestedAmount = _settle(agg); + uint256 totalVestedAmount = _getTotalVestedAmount(schedule, accounting); + + // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. + _deleteVestingSchedule(agg.id); + + // If vesting is not running, we can't do anything, just emit failing event. + if (_isFlowOngoing(superToken, sender, receiver)) { + // delete first the stream and unlock deposit amount. + superToken.deleteFlowFrom(sender, receiver); + + // Note: we consider the compensation as failed if the stream is still ongoing after the end date. + bool didCompensationFail = schedule.endDate < block.timestamp; + uint256 earlyEndCompensation = totalVestedAmount - alreadyVestedAmount; + + if (earlyEndCompensation != 0) { + // Note: Super Tokens revert, not return false, i.e. we expect always true here. + assert(superToken.transferFrom(sender, receiver, earlyEndCompensation)); + } + + emit VestingEndExecuted( + superToken, sender, receiver, schedule.endDate, earlyEndCompensation, didCompensationFail + ); + } else { + emit VestingEndFailed(superToken, sender, receiver, schedule.endDate); + } + + success = true; + } + + // _ ___ ______ __ _ + // | | / (_)__ _ __ / ____/_ ______ _____/ /_(_)___ ____ _____ + // | | / / / _ \ | /| / / / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ + // | |/ / / __/ |/ |/ / / __/ / /_/ / / / / /__/ /_/ / /_/ / / / (__ ) + // |___/_/\___/|__/|__/ /_/ \__,_/_/ /_/\___/\__/_/\____/_/ /_/____/ + + /// @inheritdoc IVestingSchedulerV3 + function mapCreateVestingScheduleParams( + ISuperToken superToken, + address sender, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod + ) public pure override returns (ScheduleCreationParams memory params) { + return mapCreateVestingScheduleParams( + superToken, sender, receiver, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, 0 + ); + } + + /// @inheritdoc IVestingSchedulerV3 + function mapCreateVestingScheduleParams( + ISuperToken superToken, + address sender, + address receiver, + uint256 totalAmount, + uint32 totalDuration, + uint32 startDate, + uint32 cliffPeriod, + uint32 claimPeriod, + uint256 cliffAmount + ) public pure override returns (ScheduleCreationParams memory params) { + uint32 claimValidityDate = claimPeriod != 0 ? startDate + claimPeriod : 0; + uint32 endDate = startDate + totalDuration; + + if (cliffAmount == 0) { + int96 flowRate = SafeCast.toInt96(SafeCast.toInt256(totalAmount / totalDuration)); + uint96 remainderAmount = SafeCast.toUint96(totalAmount - (SafeCast.toUint256(flowRate) * totalDuration)); + + if (cliffPeriod == 0) { + // No Cliff + params = ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: startDate, + claimValidityDate: claimValidityDate, + cliffDate: 0, + flowRate: flowRate, + cliffAmount: 0, + endDate: endDate, + remainderAmount: remainderAmount + }); + } else { + // Linear Default Cliff (calculated based on the overall vesting flow rate) + cliffAmount = SafeMath.mul(cliffPeriod, SafeCast.toUint256(flowRate)); + params = ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: startDate, + claimValidityDate: claimValidityDate, + cliffDate: startDate + cliffPeriod, + flowRate: flowRate, + cliffAmount: cliffAmount, + endDate: endDate, + remainderAmount: remainderAmount + }); + } + } else { + // Non-Linear Cliff (user defined cliff amount) + int96 flowRate = + SafeCast.toInt96(SafeCast.toInt256((totalAmount - cliffAmount) / (totalDuration - cliffPeriod))); + uint96 remainderAmount = SafeCast.toUint96( + (totalAmount - cliffAmount) - (SafeCast.toUint256(flowRate) * (totalDuration - cliffPeriod)) + ); + + params = ScheduleCreationParams({ + superToken: superToken, + sender: sender, + receiver: receiver, + startDate: startDate, + claimValidityDate: claimValidityDate, + cliffDate: startDate + cliffPeriod, + flowRate: flowRate, + cliffAmount: cliffAmount, + endDate: endDate, + remainderAmount: remainderAmount + }); + } + } + + /// @inheritdoc IVestingSchedulerV3 + function getTotalVestedAmount(ISuperToken superToken, address sender, address receiver) + external + view + returns (uint256 totalVestedAmount) + { + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + VestingSchedule memory schedule = agg.schedule; + ScheduleAccounting memory accounting = agg.accounting; + + totalVestedAmount = _getTotalVestedAmount(schedule, accounting); + } + + /// @inheritdoc IVestingSchedulerV3 + function getVestingSchedule(address superToken, address sender, address receiver) + external + view + returns (VestingSchedule memory schedule) + { + schedule = vestingSchedules[_getId(address(superToken), sender, receiver)]; + } + + /// @inheritdoc IVestingSchedulerV3 + function getMaximumNeededTokenAllowance(VestingSchedule memory schedule) + external + pure + returns (uint256 maxNeededAllowance) + { + maxNeededAllowance = + _getMaximumNeededTokenAllowance(schedule, ScheduleAccounting({alreadyVestedAmount: 0, lastUpdated: 0})); + } + + /// @inheritdoc IVestingSchedulerV3 + function getMaximumNeededTokenAllowance(ISuperToken superToken, address sender, address receiver) + external + view + returns (uint256 maxNeededAllowance) + { + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + + maxNeededAllowance = _getMaximumNeededTokenAllowance(agg.schedule, agg.accounting); + } + + /// @inheritdoc IRelayRecipient + function isTrustedForwarder(address forwarder) public view override returns (bool isForwarderTrusted) { + isForwarderTrusted = forwarder == HOST.getERC2771Forwarder(); + } + + /// @inheritdoc IRelayRecipient + function versionRecipient() external pure override returns (string memory version) { + version = "v1"; + } + + // ____ __ __ ______ __ _ + // / _/___ / /____ _________ ____ _/ / / ____/_ ______ _____/ /_(_)___ ____ _____ + // / // __ \/ __/ _ \/ ___/ __ \/ __ `/ / / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ + // _/ // / / / /_/ __/ / / / / / /_/ / / / __/ / /_/ / / / / /__/ /_/ / /_/ / / / (__ ) + // /___/_/ /_/\__/\___/_/ /_/ /_/\__,_/_/ /_/ \__,_/_/ /_/\___/\__/_/\____/_/ /_/____/ + + function _validateAndCreateVestingSchedule(ScheduleCreationParams memory params) private { + if (params.startDate < block.timestamp) revert TimeWindowInvalid(); + if (params.endDate <= END_DATE_VALID_BEFORE) revert TimeWindowInvalid(); - // Ensure vesting exists - if (agg.schedule.endDate == 0) revert ScheduleDoesNotExist(); + if (params.receiver == address(0) || params.receiver == params.sender) revert AccountInvalid(); + if (address(params.superToken) == address(0)) revert ZeroAddress(); + if (params.flowRate <= 0) revert FlowRateInvalid(); + if (params.cliffDate != 0 && params.startDate > params.cliffDate) revert TimeWindowInvalid(); + if (params.cliffDate == 0 && params.cliffAmount != 0) revert CliffInvalid(); - /* - Schedule update is not allowed if : - - the current end date has passed - - the new end date is in the past - - the cliff and flow date is in the future - */ + uint32 cliffAndFlowDate = params.cliffDate == 0 ? params.startDate : params.cliffDate; if ( - agg.schedule.endDate <= block.timestamp || endDate <= block.timestamp - || block.timestamp < agg.schedule.cliffAndFlowDate + cliffAndFlowDate < block.timestamp || cliffAndFlowDate >= params.endDate + || cliffAndFlowDate + START_DATE_VALID_AFTER >= params.endDate - END_DATE_VALID_BEFORE + || params.endDate - cliffAndFlowDate < MIN_VESTING_DURATION ) revert TimeWindowInvalid(); - // Update the schedule end date - vestingSchedules[agg.id].endDate = endDate; - - uint256 amountLeftToVest = _getTotalVestedAmount(agg.schedule, agg.accounting) - _settle(agg); - uint256 timeLeftToVest = endDate - block.timestamp; - - int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); + if (params.claimValidityDate != 0 && params.claimValidityDate < cliffAndFlowDate) { + revert TimeWindowInvalid(); + } - // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = newFlowRate; - vestingSchedules[agg.id].remainderAmount = - _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); + bytes32 id = _getId(address(params.superToken), params.sender, params.receiver); + if (vestingSchedules[id].endDate != 0) revert ScheduleAlreadyExists(); - // If the schedule is started, update the existing flow rate to the new calculated flow rate - if (agg.schedule.cliffAndFlowDate == 0) { - _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); - } + vestingSchedules[id] = VestingSchedule({ + cliffAndFlowDate: cliffAndFlowDate, + endDate: params.endDate, + flowRate: params.flowRate, + cliffAmount: params.cliffAmount, + remainderAmount: params.remainderAmount, + claimValidityDate: params.claimValidityDate + }); - // Emit VestingSchedulerV2 event for backward compatibility - emit VestingScheduleUpdated( - superToken, sender, receiver, agg.schedule.endDate, endDate, vestingSchedules[agg.id].remainderAmount + emit VestingScheduleCreated( + params.superToken, + params.sender, + params.receiver, + params.startDate, + params.cliffDate, + params.flowRate, + params.endDate, + params.cliffAmount, + params.claimValidityDate, + params.remainderAmount ); + } - // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleEndDateUpdated( - superToken, - sender, - receiver, - agg.schedule.endDate, - endDate, - agg.schedule.flowRate, - newFlowRate, - vestingSchedules[agg.id].remainderAmount + function _validateAndCreateAndExecuteVestingScheduleFromAmountAndDuration( + ISuperToken superToken, + address receiver, + uint256 totalAmount, + uint32 totalDuration + ) internal { + address sender = _msgSender(); + + _validateAndCreateVestingSchedule( + mapCreateVestingScheduleParams( + superToken, + sender, + receiver, + totalAmount, + totalDuration, + _normalizeStartDate(0), + 0, // cliffPeriod + 0 // claimValidityDate + ) ); - } - /// @dev IVestingScheduler.updateVestingSchedule implementation. - function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate, bytes memory ctx) - external - returns (bytes memory newCtx) - { - newCtx = ctx; - address sender = _getSender(ctx); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; - if (endDate < block.timestamp) revert TimeWindowInvalid(); - - // Note: Claimable schedules that have not been claimed cannot be updated + _validateBeforeCliffAndFlow(agg.schedule, /* disableClaimCheck: */ false); + assert(_executeCliffAndFlow(agg)); + } - // Only allow an update if 1. vesting exists 2. executeCliffAndFlow() has been called - if (schedule.cliffAndFlowDate != 0 || schedule.endDate == 0) revert ScheduleNotFlowing(); + function _settle(ScheduleAggregate memory agg) private returns (uint256 alreadyVestedAmount) { + // Ensure that the cliff and flow date has passed + assert(block.timestamp >= agg.schedule.cliffAndFlowDate); - vestingSchedules[agg.id].endDate = endDate; + // Delete the cliff amount and account for it in the already vested amount + delete vestingSchedules[agg.id].cliffAmount; - uint256 amountLeftToVest = _getTotalVestedAmount(vestingSchedules[agg.id], agg.accounting) - _settle(agg); - uint256 timeLeftToVest = endDate - block.timestamp; + // Update the timestamp of the last schedule update + accountings[agg.id].lastUpdated = block.timestamp; - uint96 newRemainderAmount = _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, schedule.flowRate); - // Update the vesting remainder amount - vestingSchedules[agg.id].remainderAmount = newRemainderAmount; + if (block.timestamp > agg.schedule.endDate) { + // If the schedule end date has passed, settle the total amount vested + accountings[agg.id].alreadyVestedAmount = _getTotalVestedAmount(agg.schedule, agg.accounting); + } else { + // If the schedule end date has not passed, accrue the amount already vested + uint256 actualLastUpdate = + agg.accounting.lastUpdated == 0 ? agg.schedule.cliffAndFlowDate : agg.accounting.lastUpdated; - emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, newRemainderAmount); + // Accrue the amount already vested + accountings[agg.id].alreadyVestedAmount += + ((block.timestamp - actualLastUpdate) * uint96(agg.schedule.flowRate)) + agg.schedule.cliffAmount; + } + alreadyVestedAmount = accountings[agg.id].alreadyVestedAmount; } - /// @dev IVestingScheduler.deleteVestingSchedule implementation. - function deleteVestingSchedule(ISuperToken superToken, address receiver, bytes memory ctx) - external - returns (bytes memory newCtx) + function _calculateFlowRate(uint256 amountLeftToVest, uint256 timeLeftToVest) + private + pure + returns (int96 flowRate) { - newCtx = ctx; - address sender = _getSender(ctx); - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; - - if (schedule.endDate != 0) { - _deleteVestingSchedule(agg.id); - emit VestingScheduleDeleted(superToken, sender, receiver); - } else { - revert ScheduleDoesNotExist(); - } + // Calculate the new flow rate + flowRate = SafeCast.toInt96(SafeCast.toInt256(amountLeftToVest) / SafeCast.toInt256(timeLeftToVest)); } - /// @dev IVestingScheduler.executeCliffAndFlow implementation. - function executeCliffAndFlow(ISuperToken superToken, address sender, address receiver) - external - returns (bool success) + function _calculateRemainderAmount(uint256 amountLeftToVest, uint256 timeLeftToVest, int96 flowRate) + private + pure + returns (uint96 remainderAmount) { - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; + // Calculate the remainder amount + remainderAmount = SafeCast.toUint96(amountLeftToVest - (SafeCast.toUint256(flowRate) * timeLeftToVest)); + } - if (schedule.claimValidityDate != 0) { - _validateAndClaim(agg); - _validateBeforeCliffAndFlow(schedule, /* disableClaimCheck: */ true); - if (block.timestamp >= _gteDateToExecuteEndVesting(schedule)) { - _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ true); - success = _executeVestingAsSingleTransfer(agg); - } else { - success = _executeCliffAndFlow(agg); - } - } else { - _validateBeforeCliffAndFlow(schedule, /* disableClaimCheck: */ false); - success = _executeCliffAndFlow(agg); - } + function _updateVestingFlowRate(ISuperToken superToken, address sender, address receiver, int96 newFlowRate) + private + { + superToken.flowFrom(sender, receiver, newFlowRate); } function _validateBeforeCliffAndFlow(VestingSchedule memory schedule, bool disableClaimCheck) private view { @@ -792,15 +818,15 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } } - function _lteDateToExecuteCliffAndFlow(VestingSchedule memory schedule) private pure returns (uint32) { + function _lteDateToExecuteCliffAndFlow(VestingSchedule memory schedule) private pure returns (uint32 date) { if (schedule.cliffAndFlowDate == 0) { revert AlreadyExecuted(); } if (schedule.claimValidityDate != 0) { - return schedule.claimValidityDate; + date = schedule.claimValidityDate; } else { - return schedule.cliffAndFlowDate + START_DATE_VALID_AFTER; + date = schedule.cliffAndFlowDate + START_DATE_VALID_AFTER; } } @@ -849,7 +875,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien alreadyVestedAmount - schedule.cliffAmount ); - return true; + success = true; } function _executeVestingAsSingleTransfer(ScheduleAggregate memory agg) private returns (bool success) { @@ -881,8 +907,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien 0, // Early end compensation false // Did end fail ); - - return true; + success = true; } function _getTotalVestedAmount(VestingSchedule memory schedule, ScheduleAccounting memory accounting) @@ -899,60 +924,6 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien accounting.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount + currentFlowAmount; } - /// @inheritdoc IVestingSchedulerV3 - function getTotalVestedAmount(ISuperToken superToken, address sender, address receiver) - external - view - returns (uint256 totalVestedAmount) - { - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; - ScheduleAccounting memory accounting = agg.accounting; - - return _getTotalVestedAmount(schedule, accounting); - } - - /// @dev IVestingScheduler.executeEndVesting implementation. - function executeEndVesting(ISuperToken superToken, address sender, address receiver) - external - returns (bool success) - { - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; - ScheduleAccounting memory accounting = agg.accounting; - - _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ false); - - uint256 alreadyVestedAmount = _settle(agg); - uint256 totalVestedAmount = _getTotalVestedAmount(schedule, accounting); - - // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. - _deleteVestingSchedule(agg.id); - - // If vesting is not running, we can't do anything, just emit failing event. - if (_isFlowOngoing(superToken, sender, receiver)) { - // delete first the stream and unlock deposit amount. - superToken.deleteFlowFrom(sender, receiver); - - // Note: we consider the compensation as failed if the stream is still ongoing after the end date. - bool didCompensationFail = schedule.endDate < block.timestamp; - uint256 earlyEndCompensation = totalVestedAmount - alreadyVestedAmount; - - if (earlyEndCompensation != 0) { - // Note: Super Tokens revert, not return false, i.e. we expect always true here. - assert(superToken.transferFrom(sender, receiver, earlyEndCompensation)); - } - - emit VestingEndExecuted( - superToken, sender, receiver, schedule.endDate, earlyEndCompensation, didCompensationFail - ); - } else { - emit VestingEndFailed(superToken, sender, receiver, schedule.endDate); - } - - return true; - } - function _validateBeforeEndVesting(VestingSchedule memory schedule, bool disableClaimCheck) private view { if (schedule.endDate == 0) { revert AlreadyExecuted(); @@ -967,30 +938,20 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien } } - function _gteDateToExecuteEndVesting(VestingSchedule memory schedule) private pure returns (uint32) { + function _gteDateToExecuteEndVesting(VestingSchedule memory schedule) private pure returns (uint32 date) { if (schedule.endDate == 0) { revert AlreadyExecuted(); } - - return schedule.endDate - END_DATE_VALID_BEFORE; - } - - /// @dev IVestingScheduler.getVestingSchedule implementation. - function getVestingSchedule(address superToken, address sender, address receiver) - external - view - returns (VestingSchedule memory) - { - return vestingSchedules[_getId(address(superToken), sender, receiver)]; + date = schedule.endDate - END_DATE_VALID_BEFORE; } function _getVestingScheduleAggregate(ISuperToken superToken, address sender, address receiver) private view - returns (ScheduleAggregate memory) + returns (ScheduleAggregate memory agg) { bytes32 id = _getId(address(superToken), sender, receiver); - return ScheduleAggregate({ + agg = ScheduleAggregate({ superToken: superToken, sender: sender, receiver: receiver, @@ -1000,18 +961,19 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien }); } - function _normalizeStartDate(uint32 startDate) private view returns (uint32) { + function _normalizeStartDate(uint32 startDate) private view returns (uint32 normalizedStartDate) { // Default to current block timestamp if no start date is provided. if (startDate == 0) { - return uint32(block.timestamp); + normalizedStartDate = uint32(block.timestamp); + } else { + normalizedStartDate = startDate; } - return startDate; } function _getMaximumNeededTokenAllowance(VestingSchedule memory schedule, ScheduleAccounting memory accounting) - internal + private pure - returns (uint256) + returns (uint256 maxNeededAllowance) { uint256 maxFlowDelayCompensationAmount = schedule.cliffAndFlowDate == 0 ? 0 : START_DATE_VALID_AFTER * SafeCast.toUint256(schedule.flowRate); @@ -1019,74 +981,44 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, SuperAppBase, IRelayRecipien schedule.endDate == 0 ? 0 : END_DATE_VALID_BEFORE * SafeCast.toUint256(schedule.flowRate); if (schedule.claimValidityDate == 0) { - return schedule.cliffAmount + schedule.remainderAmount + maxFlowDelayCompensationAmount + maxNeededAllowance = schedule.cliffAmount + schedule.remainderAmount + maxFlowDelayCompensationAmount + maxEarlyEndCompensationAmount; } else if (schedule.claimValidityDate >= _gteDateToExecuteEndVesting(schedule)) { - return _getTotalVestedAmount(schedule, accounting); + maxNeededAllowance = _getTotalVestedAmount(schedule, accounting); } else { - return schedule.cliffAmount + schedule.remainderAmount + maxNeededAllowance = schedule.cliffAmount + schedule.remainderAmount + (schedule.claimValidityDate - schedule.cliffAndFlowDate) * SafeCast.toUint256(schedule.flowRate) + maxEarlyEndCompensationAmount; } } - /// @dev IVestingScheduler.getMaximumNeededTokenAllowance implementation - function getMaximumNeededTokenAllowance(VestingSchedule memory schedule) external pure returns (uint256) { - return _getMaximumNeededTokenAllowance(schedule, ScheduleAccounting({alreadyVestedAmount: 0, lastUpdated: 0})); - } - - function getMaximumNeededTokenAllowance(ISuperToken superToken, address sender, address receiver) - external + function _isFlowOngoing(ISuperToken superToken, address sender, address receiver) + private view - returns (uint256) + returns (bool isFlowOngoing) { - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - - return _getMaximumNeededTokenAllowance(agg.schedule, agg.accounting); - } - - /// @dev get sender of transaction from Superfluid Context or transaction itself. - function _getSender(bytes memory ctx) private view returns (address sender) { - if (ctx.length != 0) { - if (msg.sender != address(HOST)) revert HostInvalid(); - sender = HOST.decodeCtx(ctx).msgSender; - } else { - sender = _msgSender(); - } - // This is an invariant and should never happen. - assert(sender != address(0)); - } - - /// @dev get flowRate of stream - function _isFlowOngoing(ISuperToken superToken, address sender, address receiver) private view returns (bool) { - return superToken.getFlowRate(sender, receiver) != 0; + isFlowOngoing = superToken.getFlowRate(sender, receiver) != 0; } - function _getId(address superToken, address sender, address receiver) private pure returns (bytes32) { - return keccak256(abi.encodePacked(superToken, sender, receiver)); + function _getId(address superToken, address sender, address receiver) + private + pure + returns (bytes32 vestingScheduleId) + { + vestingScheduleId = keccak256(abi.encodePacked(superToken, sender, receiver)); } - function _deleteVestingSchedule(bytes32 id) internal { + function _deleteVestingSchedule(bytes32 id) private { delete vestingSchedules[id]; delete accountings[id]; } - /// @dev IRelayRecipient.isTrustedForwarder implementation - function isTrustedForwarder(address forwarder) public view override returns (bool) { - return forwarder == HOST.getERC2771Forwarder(); - } - - /// @dev IRelayRecipient.versionRecipient implementation - function versionRecipient() external pure override returns (string memory) { - return "v1"; - } - /// @dev gets the relayed sender from calldata as specified by EIP-2771, falling back to msg.sender - function _msgSender() internal view virtual returns (address) { + function _msgSender() internal view virtual returns (address sender) { if (msg.data.length >= 20 && isTrustedForwarder(msg.sender)) { - return address(bytes20(msg.data[msg.data.length - 20:])); + sender = address(bytes20(msg.data[msg.data.length - 20:])); } else { - return msg.sender; + sender = msg.sender; } } } diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index 53ae8848c6..53426aea0f 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -2,12 +2,132 @@ pragma solidity ^0.8.0; import {ISuperToken} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; -import {IVestingSchedulerV2} from "./IVestingSchedulerV2.sol"; -interface IVestingSchedulerV3 is IVestingSchedulerV2 { - // FIXME Add comments +interface IVestingSchedulerV3 { + // ______ __ ______ + // / ____/_ _______/ /_____ ____ ___ / ____/_____________ __________ + // / / / / / / ___/ __/ __ \/ __ `__ \ / __/ / ___/ ___/ __ \/ ___/ ___/ + // / /___/ /_/ (__ ) /_/ /_/ / / / / / / / /___/ / / / / /_/ / / (__ ) + // \____/\__,_/____/\__/\____/_/ /_/ /_/ /_____/_/ /_/ \____/_/ /____/ + + /// @notice Thrown when the new total amount is less than or equal to the amount already vested error InvalidNewTotalAmount(); + /// @notice Thrown when the time window for an operation is invalid + error TimeWindowInvalid(); + + /// @notice Thrown when an account is invalid (e.g. sender = receiver) + error AccountInvalid(); + + /// @notice Thrown when a required address parameter is the zero address + error ZeroAddress(); + + /// @notice Thrown when a flow rate is invalid (e.g. zero or negative) + error FlowRateInvalid(); + + /// @notice Thrown when cliff parameters are invalid + error CliffInvalid(); + + /// @notice Thrown when trying to create a schedule that already exists + error ScheduleAlreadyExists(); + + /// @notice Thrown when trying to operate on a non-existent schedule + error ScheduleDoesNotExist(); + + /// @notice Thrown when trying to operate on a schedule that is not flowing + error ScheduleNotFlowing(); + + /// @notice Thrown when trying to claim a schedule on behalf of another account + error CannotClaimScheduleOnBehalf(); + + /// @notice Thrown when trying to execute an already executed operation + error AlreadyExecuted(); + + /// @notice Thrown when trying to operate on an unclaimed schedule + error ScheduleNotClaimed(); + + // ____ __ __ + // / __ \____ _/ /_____ _/ /___ ______ ___ _____ + // / / / / __ `/ __/ __ `/ __/ / / / __ \/ _ \/ ___/ + // / /_/ / /_/ / /_/ /_/ / /_/ /_/ / /_/ / __(__ ) + // /_____/\__,_/\__/\__,_/\__/\__, / .___/\___/____/ + // /____/_/ + + /** + * @dev Vesting Schedule Parameters + * @param cliffAndFlowDate Date of flow start and cliff execution (if a cliff was specified) + * @param endDate End date of the vesting + * @param flowRate For the stream + * @param cliffAmount Amount to be transferred at the cliff + * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" + * @param claimValidityDate Date before which the claimable schedule must be claimed + */ + struct VestingSchedule { + uint32 cliffAndFlowDate; + uint32 endDate; + int96 flowRate; + uint256 cliffAmount; + uint96 remainderAmount; + uint32 claimValidityDate; + } + + /** + * @dev Parameters used to create vesting schedules + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + * @param startDate Timestamp when the vesting should start + * @param claimValidityDate Date before which the claimable schedule must be claimed + * @param cliffDate Timestamp of cliff execution - if 0, startDate acts as cliff + * @param flowRate The flowRate for the stream + * @param cliffAmount The amount to be transferred at the cliff + * @param endDate The timestamp when the stream should stop. + * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" + */ + struct ScheduleCreationParams { + ISuperToken superToken; + address sender; + address receiver; + uint32 startDate; + uint32 claimValidityDate; + uint32 cliffDate; + int96 flowRate; + uint256 cliffAmount; + uint32 endDate; + uint96 remainderAmount; + } + + // ______ __ + // / ____/ _____ ____ / /______ + // / __/ | | / / _ \/ __ \/ __/ ___/ + // / /___ | |/ / __/ / / / /_(__ ) + // /_____/ |___/\___/_/ /_/\__/____/ + + /** + * @dev Event emitted on creation of a new vesting schedule + * @param superToken SuperToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param startDate Timestamp when the vesting starts + * @param cliffDate Timestamp of the cliff + * @param flowRate The flowRate for the stream + * @param endDate The timestamp when the stream should stop + * @param cliffAmount The amount to be transferred at the cliff + * @param claimValidityDate Date before which the claimable schedule must be claimed + * @param remainderAmount Amount transferred during early end to achieve an accurate "total vested amount" + */ + event VestingScheduleCreated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 startDate, + uint32 cliffDate, + int96 flowRate, + uint32 endDate, + uint256 cliffAmount, + uint32 claimValidityDate, + uint96 remainderAmount + ); + /** * @dev Event emitted when a vesting schedule's total amount is updated * @param superToken The superToken being vested @@ -53,22 +173,154 @@ interface IVestingSchedulerV3 is IVestingSchedulerV2 { ); /** - * @dev See IVestingSchedulerV2.createVestingScheduleFromAmountAndDuration overload for more details. + * @dev Event emitted on update of a vesting schedule + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param oldEndDate Old timestamp when the stream should stop + * @param endDate New timestamp when the stream should stop + * @param remainderAmount The remainder amount that cannot be streamed */ - function createVestingScheduleFromAmountAndDuration( + event VestingScheduleUpdated( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 oldEndDate, + uint32 endDate, + uint96 remainderAmount + ); + + /** + * @dev Emitted when the end of a scheduled vesting is executed + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param endDate The timestamp when the stream should stop + * @param earlyEndCompensation adjusted close amount transferred to receiver. + * @param didCompensationFail adjusted close amount transfer fail. + */ + event VestingEndExecuted( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 endDate, + uint256 earlyEndCompensation, + bool didCompensationFail + ); + + /** + * @dev Emitted when the cliff of a scheduled vesting is executed + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param cliffAndFlowDate The timestamp when the stream should start + * @param flowRate The flowRate for the stream + * @param cliffAmount The amount you would like to transfer at the startDate when you start streaming + * @param flowDelayCompensation Adjusted amount transferred to receiver. (elapse time from config and tx timestamp) + */ + event VestingCliffAndFlowExecuted( + ISuperToken indexed superToken, + address indexed sender, + address indexed receiver, + uint32 cliffAndFlowDate, + int96 flowRate, + uint256 cliffAmount, + uint256 flowDelayCompensation + ); + + /** + * @dev Emitted when a claimable vesting schedule is claimed + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param claimer Account that claimed the vesting (can only be sender or receiver) + */ + event VestingClaimed( + ISuperToken indexed superToken, address indexed sender, address indexed receiver, address claimer + ); + + /** + * @dev Event emitted on deletion of a vesting schedule + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + */ + event VestingScheduleDeleted(ISuperToken indexed superToken, address indexed sender, address indexed receiver); + + /** + * @dev Event emitted on end of a vesting that failed because there was no running stream + * @param superToken The superToken to be vested + * @param sender Vesting sender + * @param receiver Vesting receiver + * @param endDate The timestamp when the stream should stop + */ + event VestingEndFailed( + ISuperToken indexed superToken, address indexed sender, address indexed receiver, uint32 endDate + ); + + // ______ __ __ ______ __ _ + // / ____/ __/ /____ _________ ____ _/ / / ____/_ ______ _____/ /_(_)___ ____ _____ + // / __/ | |/_/ __/ _ \/ ___/ __ \/ __ `/ / / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ + // / /____> = endDate vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0, EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0 ); // revert with cliffAndFlowDate + startDateValidFor >= endDate - endDateValidBefore vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0, EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, 0 ); // revert with startDate > cliffDate vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, CLIFF_DATE + 1, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + superToken, bob, CLIFF_DATE + 1, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0 ); // revert with vesting duration < 7 days vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE + 2 days, 0, EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE + 2 days, 0 ); } @@ -554,7 +538,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(alice); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, END_DATE + 1000); //assert storage data - IVestingSchedulerV2.VestingSchedule memory schedule = + IVestingSchedulerV3.VestingSchedule memory schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); assertTrue(schedule.cliffAndFlowDate == 0, "schedule.cliffAndFlowDate"); assertTrue(schedule.endDate == END_DATE + 1000, "schedule.endDate"); @@ -565,14 +549,14 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(alice); vm.expectEmit(true, true, true, true); emit VestingScheduleDeleted(superToken, alice, bob); - vestingScheduler.deleteVestingSchedule(superToken, bob, EMPTY_CTX); + vestingScheduler.deleteVestingSchedule(superToken, bob); testAssertScheduleDoesNotExist(address(superToken), alice, bob); } function testCannotDeleteVestingScheduleIfDataDontExist() public { vm.startPrank(alice); vm.expectRevert(IVestingSchedulerV2.ScheduleDoesNotExist.selector); - vestingScheduler.deleteVestingSchedule(superToken, bob, EMPTY_CTX); + vestingScheduler.deleteVestingSchedule(superToken, bob); } function testExecuteCliffAndFlowWithCliffAmount() public { @@ -619,9 +603,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint256 bobInitialBalance = superToken.balanceOf(bob); _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.startPrank(alice); - vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, 0, END_DATE, 0, EMPTY_CTX - ); + vestingScheduler.createVestingSchedule(superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, 0, END_DATE, 0); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); vm.stopPrank(); vm.startPrank(admin); @@ -1048,15 +1030,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); vestingScheduler.createVestingSchedule( - superToken, - bob, - startAndCliffDate, - startAndCliffDate, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - 0, - EMPTY_CTX + superToken, bob, startAndCliffDate, startAndCliffDate, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0 ); vm.stopPrank(); @@ -1111,8 +1085,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp), // startDate 604800, // cliffPeriod - 0, // claimPeriod - EMPTY_CTX + 0 // claimPeriod ); console.log("Revert with cliff and start in history."); @@ -1124,8 +1097,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp - 1), // startDate 0, // cliffPeriod - 0, // claimPeriod - EMPTY_CTX + 0 // claimPeriod ); console.log("Revert with overflow."); @@ -1137,8 +1109,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp), // startDate 0, // cliffPeriod - 0, // claimPeriod - EMPTY_CTX + 0 // claimPeriod ); console.log("Revert with underflow/overflow."); @@ -1150,8 +1121,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { type(uint32).max, // duration uint32(block.timestamp), // startDate 0, // cliffPeriod - 0, // claimPeriod - EMPTY_CTX + 0 // claimPeriod ); console.log("Revert with start date in history."); @@ -1163,8 +1133,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp - 1), // startDate 604800, // cliffPeriod - 0, // claimPeriod - EMPTY_CTX + 0 // claimPeriod ); } @@ -1194,8 +1163,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vestingDuration, startDate, 0, // cliffPeriod - 0, // claimPeriod - EMPTY_CTX + 0 // claimPeriod ); } else { vestingScheduler.createVestingScheduleFromAmountAndDuration( @@ -1211,7 +1179,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.stopPrank(); } - function testNewFunctionScheduleCreationWithCliff(uint8 randomizer) public { + function testNewFunctionScheduleCreationWithCliff() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.startPrank(alice); @@ -1243,20 +1211,14 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); vm.startPrank(alice); - bool useCtx = randomizer % 2 == 0; - if (useCtx) { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0, EMPTY_CTX - ); - } else { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0 - ); - } + + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0 + ); vm.stopPrank(); } - function test_createVestingScheduleFromAmountAndDuration_nonLinearCliff(uint8 randomizer) public { + function test_createVestingScheduleFromAmountAndDuration_nonLinearCliff() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.startPrank(alice); @@ -1289,16 +1251,10 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); vm.startPrank(alice); - bool useCtx = randomizer % 2 == 0; - if (useCtx) { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0, cliffAmount, EMPTY_CTX - ); - } else { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0, cliffAmount - ); - } + + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, 0, cliffAmount + ); vm.stopPrank(); } @@ -1397,7 +1353,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Intermediary `mapCreateVestingScheduleParams` test assertAreScheduleCreationParamsEqual( - IVestingSchedulerV2.ScheduleCreationParams( + IVestingSchedulerV3.ScheduleCreationParams( superToken, alice, bob, @@ -1430,17 +1386,10 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Act vm.startPrank(alice); - if (randomizer % 3 == 0) { - console.log("Using the overload without superfluid context."); - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod - ); - } else { - console.log("Using the overload with superfluid context."); - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX - ); - } + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod + ); + vm.stopPrank(); // Assert @@ -1654,7 +1603,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Intermediary `mapCreateVestingScheduleParams` test assertAreScheduleCreationParamsEqual( - IVestingSchedulerV2.ScheduleCreationParams( + IVestingSchedulerV3.ScheduleCreationParams( superToken, alice, bob, @@ -1688,12 +1637,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Act vm.startPrank(alice); vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod ); vm.stopPrank(); // Assert - IVestingSchedulerV2.VestingSchedule memory actualSchedule = + IVestingSchedulerV3.VestingSchedule memory actualSchedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); assertEq( actualSchedule.cliffAndFlowDate, @@ -1805,7 +1754,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { emit VestingCliffAndFlowExecuted(superToken, alice, bob, uint32(block.timestamp), flowRate, 0, 0); vestingScheduler.createAndExecuteVestingScheduleFromAmountAndDuration( - superToken, bob, _totalAmount, _totalDuration, EMPTY_CTX + superToken, bob, _totalAmount, _totalDuration ); vm.stopPrank(); @@ -1868,15 +1817,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(alice); vestingScheduler.createVestingSchedule( - superToken, - bob, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); vm.stopPrank(); @@ -1908,15 +1849,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(alice); vestingScheduler.createVestingSchedule( - superToken, - bob, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); vm.stopPrank(); @@ -1975,22 +1908,13 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + CLAIM_VALIDITY_DATE ); // revert with receivers = sender vm.expectRevert(IVestingSchedulerV2.AccountInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, - alice, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, alice, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); // revert with receivers = address(0) @@ -2003,80 +1927,55 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + CLAIM_VALIDITY_DATE ); // revert with flowRate = 0 vm.expectRevert(IVestingSchedulerV2.FlowRateInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, 0, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, 0, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); // revert with cliffDate = 0 but cliffAmount != 0 vm.expectRevert(IVestingSchedulerV2.CliffInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, 0, 0, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + superToken, bob, 0, 0, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); // revert with startDate < block.timestamp && cliffDate = 0 vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, uint32(block.timestamp - 1), 0, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + superToken, bob, uint32(block.timestamp - 1), 0, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE ); // revert with endDate = 0 vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, 0, CLAIM_VALIDITY_DATE, EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, 0, CLAIM_VALIDITY_DATE ); // revert with cliffAndFlowDate < block.timestamp vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, bob, 0, uint32(block.timestamp) - 1, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE, EMPTY_CTX + superToken, bob, 0, uint32(block.timestamp) - 1, FLOW_RATE, 0, END_DATE, CLAIM_VALIDITY_DATE ); // revert with cliffAndFlowDate >= endDate vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, - bob, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - CLIFF_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, CLAIM_VALIDITY_DATE ); // revert with cliffAndFlowDate + startDateValidFor >= endDate - endDateValidBefore vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, - bob, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - CLIFF_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE, CLAIM_VALIDITY_DATE ); // revert with startDate > cliffDate vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, - bob, - CLIFF_DATE + 1, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, bob, CLIFF_DATE + 1, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); // revert with vesting duration < 7 days @@ -2089,37 +1988,20 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { FLOW_RATE, CLIFF_TRANSFER_AMOUNT, CLIFF_DATE + 2 days, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + CLAIM_VALIDITY_DATE ); // revert with invalid claim validity date (before schedule/cliff start) vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.createVestingSchedule( - superToken, - bob, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - CLIFF_DATE - 1, - EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLIFF_DATE - 1 ); } function test_createClaimableVestingSchedule_dataExists() public { vm.startPrank(alice); vestingScheduler.createVestingSchedule( - superToken, - bob, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); vm.stopPrank(); @@ -2127,20 +2009,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.startPrank(alice); vestingScheduler.createVestingSchedule( - superToken, - bob, - START_DATE, - CLIFF_DATE, - FLOW_RATE, - CLIFF_TRANSFER_AMOUNT, - END_DATE, - CLAIM_VALIDITY_DATE, - EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, CLAIM_VALIDITY_DATE ); vm.stopPrank(); } - function test_createClaimableVestingScheduleFromAmountAndDuration_withoutCliff(uint8 randomizer) public { + function test_createClaimableVestingScheduleFromAmountAndDuration_withoutCliff() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.startPrank(alice); @@ -2159,29 +2033,16 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { superToken, alice, bob, startDate, 0, expectedFlowRate, expectedEndDate, 0, startDate + claimPeriod, 0 ); vm.startPrank(alice); - bool useCtx = randomizer % 2 == 0; - if (useCtx) { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, - bob, - totalVestedAmount, - vestingDuration, - startDate, - 0, // cliffPeriod - claimPeriod, - EMPTY_CTX - ); - } else { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, - bob, - totalVestedAmount, - vestingDuration, - startDate, - 0, // cliffPeriod - claimPeriod - ); - } + + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + totalVestedAmount, + vestingDuration, + startDate, + 0, // cliffPeriod + claimPeriod + ); vm.stopPrank(); } @@ -2220,7 +2081,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.stopPrank(); } - function test_createClaimableVestingScheduleFromAmountAndDuration_withCliff(uint8 randomizer) public { + function test_createClaimableVestingScheduleFromAmountAndDuration_withCliff() public { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.startPrank(alice); @@ -2250,16 +2111,9 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ); vm.startPrank(alice); - bool useCtx = randomizer % 2 == 0; - if (useCtx) { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX - ); - } else { - vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, claimPeriod - ); - } + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, bob, totalVestedAmount, vestingDuration, startDate, cliffPeriod, claimPeriod + ); vm.stopPrank(); } @@ -2315,8 +2169,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp), // startDate 604800, // cliffPeriod - 15 days, // claimPeriod - EMPTY_CTX + 15 days // claimPeriod ); console.log("Revert with cliff and start in history."); @@ -2328,8 +2181,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp - 1), // startDate 0, // cliffPeriod - 15 days, // claimPeriod - EMPTY_CTX + 15 days // claimPeriod ); console.log("Revert with overflow."); @@ -2341,8 +2193,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp), // startDate 0, // cliffPeriod - 15 days, // claimPeriod - EMPTY_CTX + 15 days // claimPeriod ); console.log("Revert with underflow/overflow."); @@ -2354,8 +2205,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { type(uint32).max, // duration uint32(block.timestamp), // startDate 0, // cliffPeriod - 15 days, // claimPeriod - EMPTY_CTX + 15 days // claimPeriod ); console.log("Revert with start date in history."); @@ -2367,8 +2217,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 1209600, // duration uint32(block.timestamp - 1), // startDate 604800, // cliffPeriod - 15 days, // claimPeriod - EMPTY_CTX + 15 days // claimPeriod ); } @@ -2653,7 +2502,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Act vm.startPrank(alice); vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, 0, EMPTY_CTX + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, 0 ); vm.stopPrank(); @@ -2751,7 +2600,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Act vm.startPrank(alice); vestingScheduler.createVestingScheduleFromAmountAndDuration( - superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod, EMPTY_CTX + superToken, bob, totalAmount, totalDuration, startDate, cliffPeriod, claimPeriod ); vm.stopPrank(); @@ -2787,16 +2636,6 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { testAssertScheduleDoesNotExist(address(superToken), alice, bob); } - function test_getSender_throws_when_invalid_host() public { - vm.expectRevert(IVestingSchedulerV2.HostInvalid.selector); - - vm.startPrank(alice); - vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, NON_EMPTY_CTX - ); - vm.stopPrank(); - } - // VestingSchedulerV3 Scenarios : /* Scenario 1 : Assuming a 5 month long schedule: @@ -2833,8 +2672,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { totalDuration, startDate, 0, // No cliff period - 0, // No claim period - EMPTY_CTX + 0 // No claim period ); vm.stopPrank(); @@ -2962,8 +2800,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { totalDuration, startDate, 0, // No cliff period - claimPeriod, // Claim period of 60 days - EMPTY_CTX + claimPeriod // Claim period of 60 days ); vm.stopPrank(); @@ -3090,42 +2927,10 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { testAssertScheduleDoesNotExist(address(superToken), alice, bob); } - function test_getSender_works_in_a_batch_call() public { - // Create a vesting schedule to update with a batch call that uses the context - vm.startPrank(alice); - vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX - ); - _arrangeAllowances(alice, FLOW_RATE); - vm.stopPrank(); - - vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE); - vestingScheduler.executeCliffAndFlow(superToken, alice, bob); - - uint32 newEndDate = END_DATE + 1234; - // Setting up a batch call. Superfluid Protocol will replace the emtpy context with data about the sender. That's where the sender is retrieved from. - ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1); - ops[0] = ISuperfluid.Operation({ - operationType: BatchOperation.OPERATION_TYPE_SUPERFLUID_CALL_APP_ACTION, - target: address(vestingScheduler), - data: abi.encodeCall(vestingScheduler.updateVestingSchedule, (superToken, bob, newEndDate, EMPTY_CTX)) - }); - - // Act - vm.prank(alice); - sf.host.batchCall(ops); - vm.stopPrank(); - - // Assert - IVestingSchedulerV3.VestingSchedule memory schedule = - vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - assertEq(schedule.endDate, newEndDate); - } - function test_use_2771_forward_call() public { vm.startPrank(alice); vestingScheduler.createVestingSchedule( - superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0, EMPTY_CTX + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0 ); _arrangeAllowances(alice, FLOW_RATE); vm.stopPrank(); From b68038048b2b16573800979e84d2b10b176016f2 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Tue, 8 Apr 2025 19:00:02 +0300 Subject: [PATCH 44/59] add another test with multiple updates --- .../scheduler/test/VestingSchedulerV3.t.sol | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index c7a45daf3d..99012a4c4e 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2927,6 +2927,164 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { testAssertScheduleDoesNotExist(address(superToken), alice, bob); } + struct VestingTestState { + uint256 initialAmount; + uint256 secondAmount; + uint256 thirdAmount; + uint256 finalAmount; + uint32 totalDuration; + uint32 durationExtension1; + uint32 durationExtension2; + uint32 startDate; + uint32 firstNewEndDate; + uint32 secondNewEndDate; + uint32 finalEndDate; + uint256 aliceInitialBalance; + uint256 aliceFinalBalance; + uint256 bobInitialBalance; + uint256 bobFinalBalance; + uint256 retrievedTotalAmount; + bool success; + } + + function testFuzz_UpdateVestingSchedule_WithAmountAndEndDate( + uint256 initialAmount, + uint256 secondAmount, + uint256 thirdAmount, + uint256 finalAmount, + uint32 totalDuration, + uint32 durationExtension1, + uint32 durationExtension2, + uint8 randomizer + ) public { + VestingTestState memory state; + + // Bound inputs to reasonable values + state.initialAmount = bound(initialAmount, 500 ether, 2000 ether); + + state.secondAmount = bound(secondAmount, state.initialAmount - 10 ether, state.initialAmount + 10 ether); + state.thirdAmount = bound(thirdAmount, state.secondAmount - 10 ether, state.secondAmount + 10 ether); + state.finalAmount = bound(finalAmount, state.thirdAmount - 10 ether, state.thirdAmount + 10 ether); + + // Ensure reasonable durations + state.totalDuration = uint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION() + 7 days, 180 days)); + state.durationExtension1 = uint32(bound(durationExtension1, 1 days, 3 days)); + state.durationExtension2 = uint32(bound(durationExtension2, 1 days, 3 days)); + + // Capture initial balances + state.aliceInitialBalance = superToken.balanceOf(alice); + state.bobInitialBalance = superToken.balanceOf(bob); + + // Setup + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + // Create initial vesting schedule + state.startDate = uint32(block.timestamp); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + state.initialAmount, + state.totalDuration, + state.startDate, + 0, // No cliff period + 0 // No claim period + ); + vm.stopPrank(); + + // Verify initial total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.initialAmount, "Initial total amount should match"); + + // Execute cliff and flow to start vesting + vm.warp(state.startDate); + vm.prank(alice); + state.success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(state.success, "executeCliffAndFlow should succeed"); + + console.log("First update - 25% into vesting"); + vm.warp(state.startDate + state.totalDuration/4); + + console.log("Update to secondAmount and extend duration"); + state.firstNewEndDate = state.startDate + state.totalDuration + state.durationExtension1; + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( + superToken, + bob, + state.secondAmount, + state.firstNewEndDate + ); + + // Verify updated total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.secondAmount, "Total amount after first update should match secondAmount"); + + console.log("Warp to 50% of original duration"); + vm.warp(state.startDate + state.totalDuration / 2); + + // Second update + state.secondNewEndDate = state.firstNewEndDate - state.durationExtension2; + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( + superToken, + bob, + state.thirdAmount, + state.secondNewEndDate + ); + + // Verify updated total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.thirdAmount, "Total amount after second update should match thirdAmount"); + + console.log("Warp to 75% of original duration"); + vm.warp(state.startDate + (state.totalDuration * 3)/4); + + console.log("Final update"); + state.finalEndDate = state.secondNewEndDate - 1 days; + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( + superToken, + bob, + state.finalAmount, + state.finalEndDate + ); + + // Verify updated total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.finalAmount, "Total amount after final update should match finalAmount"); + + // Warp to 12 hours before end and execute end vesting + vm.warp(state.finalEndDate - 12 hours); + + vm.prank(alice); + state.success = vestingScheduler.executeEndVesting(superToken, alice, bob); + assertTrue(state.success, "executeEndVesting should succeed"); + + // Verify final balances + state.aliceFinalBalance = superToken.balanceOf(alice); + state.bobFinalBalance = superToken.balanceOf(bob); + + assertEq( + state.aliceInitialBalance - state.aliceFinalBalance, + state.finalAmount, + "Alice should have transferred exactly finalAmount" + ); + + assertEq( + state.bobFinalBalance - state.bobInitialBalance, + state.finalAmount, + "Bob should have received exactly finalAmount" + ); + + // Verify schedule no longer exists + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + function test_use_2771_forward_call() public { vm.startPrank(alice); vestingScheduler.createVestingSchedule( From 9221df9a00722ddecb8c3d5d0b895f4ce2a0b54e Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Wed, 9 Apr 2025 10:42:50 +0300 Subject: [PATCH 45/59] add test w multiple updates and single claim transfer --- .../scheduler/test/VestingSchedulerV3.t.sol | 134 +++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 99012a4c4e..0128356ce9 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2947,7 +2947,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { bool success; } - function testFuzz_UpdateVestingSchedule_WithAmountAndEndDate( + function testVestingSchedulerV3_UpdateVestingSchedule_WithAmountAndEndDate( uint256 initialAmount, uint256 secondAmount, uint256 thirdAmount, @@ -3085,6 +3085,138 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { testAssertScheduleDoesNotExist(address(superToken), alice, bob); } + function testVestingSchedulerV3_UpdateVestingSchedule_WithAmountAndEndDate_AndClaimPeriodExecutedAsSingleTransfer( + uint256 initialAmount, + uint256 secondAmount, + uint256 thirdAmount, + uint256 finalAmount, + uint32 totalDuration, + uint32 durationExtension1, + uint32 durationExtension2, + uint8 randomizer + ) public { + VestingTestState memory state; + + // Bound inputs to reasonable values + state.initialAmount = bound(initialAmount, 500 ether, 2000 ether); + + state.secondAmount = bound(secondAmount, state.initialAmount - 10 ether, state.initialAmount + 10 ether); + state.thirdAmount = bound(thirdAmount, state.secondAmount - 10 ether, state.secondAmount + 10 ether); + state.finalAmount = bound(finalAmount, state.thirdAmount - 10 ether, state.thirdAmount + 10 ether); + + // Ensure reasonable durations + state.totalDuration = uint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION() + 7 days, 180 days)); + state.durationExtension1 = uint32(bound(durationExtension1, 1 days, 3 days)); + state.durationExtension2 = uint32(bound(durationExtension2, 1 days, 3 days)); + + // Capture initial balances + state.aliceInitialBalance = superToken.balanceOf(alice); + state.bobInitialBalance = superToken.balanceOf(bob); + + // Setup + _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); + + vm.startPrank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + // Create initial vesting schedule + state.startDate = uint32(block.timestamp); + vestingScheduler.createVestingScheduleFromAmountAndDuration( + superToken, + bob, + state.initialAmount, + state.totalDuration, + state.startDate, + 0, // No cliff period + state.totalDuration + 7 days + ); + vm.stopPrank(); + + // Verify initial total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.initialAmount, "Initial total amount should match"); + + console.log("First update - 25% into vesting"); + vm.warp(state.startDate + state.totalDuration/4); + + console.log("Update to secondAmount and extend duration"); + state.firstNewEndDate = state.startDate + state.totalDuration + state.durationExtension1; + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( + superToken, + bob, + state.secondAmount, + state.firstNewEndDate + ); + + // Verify updated total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.secondAmount, "Total amount after first update should match secondAmount"); + + console.log("Warp to 50% of original duration"); + vm.warp(state.startDate + state.totalDuration / 2); + + // Second update + state.secondNewEndDate = state.firstNewEndDate - state.durationExtension2; + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( + superToken, + bob, + state.thirdAmount, + state.secondNewEndDate + ); + + // Verify updated total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.thirdAmount, "Total amount after second update should match thirdAmount"); + + console.log("Warp to 75% of original duration"); + vm.warp(state.startDate + (state.totalDuration * 3)/4); + + console.log("Final update"); + state.finalEndDate = state.secondNewEndDate - 1 days; + + vm.prank(alice); + vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( + superToken, + bob, + state.finalAmount, + state.finalEndDate + ); + + // Verify updated total amount + state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); + assertEq(state.retrievedTotalAmount, state.finalAmount, "Total amount after final update should match finalAmount"); + + // Warp to 12 hours before end and execute end vesting + vm.warp(state.finalEndDate + 1 days); + + vm.prank(alice); + state.success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + assertTrue(state.success, "executeCliffAndFlow should succeed"); + + // Verify final balances + state.aliceFinalBalance = superToken.balanceOf(alice); + state.bobFinalBalance = superToken.balanceOf(bob); + + assertEq( + state.aliceInitialBalance - state.aliceFinalBalance, + state.finalAmount, + "Alice should have transferred exactly finalAmount" + ); + + assertEq( + state.bobFinalBalance - state.bobInitialBalance, + state.finalAmount, + "Bob should have received exactly finalAmount" + ); + + // Verify schedule no longer exists + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + function test_use_2771_forward_call() public { vm.startPrank(alice); vestingScheduler.createVestingSchedule( From 2cbe7817d7107e0077a9232fdcc0de15e32f0ff2 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Wed, 9 Apr 2025 14:58:03 +0300 Subject: [PATCH 46/59] refactor update functions into single core logic --- .../contracts/VestingSchedulerV3.sol | 273 +++++++----------- 1 file changed, 102 insertions(+), 171 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 35cb9916f8..f4e938ca3f 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -224,6 +224,12 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { ); } + struct UpdateVestingScheduleParams { + uint32 newEndDate; + uint256 newTotalAmount; + int96 newFlowRate; + } + /// @inheritdoc IVestingSchedulerV3 function updateVestingScheduleFlowRateFromAmountAndEndDate( ISuperToken superToken, @@ -234,147 +240,61 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { address sender = _msgSender(); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - // Ensure vesting exists - if (agg.schedule.endDate == 0) revert ScheduleDoesNotExist(); - - /* - Schedule update is not allowed if : - - the current end date has passed - - the new end date is in the past - - the cliff and flow date is in the future - */ - if ( - agg.schedule.endDate <= block.timestamp || newEndDate <= block.timestamp - || block.timestamp < agg.schedule.cliffAndFlowDate - ) revert TimeWindowInvalid(); - - // Update the schedule end date - vestingSchedules[agg.id].endDate = newEndDate; - - // Settle the amount already vested - uint256 alreadyVestedAmount = _settle(agg); - - // Ensure that the new total amount is larger than the amount already vested - if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); - - uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; - uint256 timeLeftToVest = newEndDate - block.timestamp; - - int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); - - // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = newFlowRate; - vestingSchedules[agg.id].remainderAmount = - _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); - - // If the schedule is started, update the existing flow rate to the new calculated flow rate - if (agg.schedule.cliffAndFlowDate == 0) { - _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); - } - - // Emit VestingSchedulerV2 event for backward compatibility - emit VestingScheduleUpdated( - superToken, sender, receiver, agg.schedule.endDate, newEndDate, vestingSchedules[agg.id].remainderAmount - ); + _updateVestingSchedule(agg, UpdateVestingScheduleParams({ + newEndDate: newEndDate, + newTotalAmount: newTotalAmount, + newFlowRate: 0 // Note: 0 means it will be re-calculated. + })); + } - // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleEndDateUpdated( - superToken, - sender, - receiver, - agg.schedule.endDate, - newEndDate, - agg.schedule.flowRate, - newFlowRate, - vestingSchedules[agg.id].remainderAmount - ); + function updateVestingScheduleFlowRateFromAmount( + ISuperToken superToken, + address receiver, + uint256 newTotalAmount + ) external { + address sender = _msgSender(); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleTotalAmountUpdated( - superToken, - sender, - receiver, - agg.schedule.flowRate, - newFlowRate, - _getTotalVestedAmount(agg.schedule, agg.accounting), - newTotalAmount, - vestingSchedules[agg.id].remainderAmount - ); + uint32 currentEndDate = agg.schedule.endDate; + _updateVestingSchedule(agg, UpdateVestingScheduleParams({ + newEndDate: currentEndDate, + newTotalAmount: newTotalAmount, + newFlowRate: 0 // Note: 0 means it will be re-calculated. + })); } - /// @dev IVestingScheduler.updateVestingScheduleFlowRateFromAmount implementation. - function updateVestingScheduleFlowRateFromAmount(ISuperToken superToken, address receiver, uint256 newTotalAmount) + /// @inheritdoc IVestingSchedulerV3 + function updateVestingScheduleFlowRateFromEndDate(ISuperToken superToken, address receiver, uint32 newEndDate) external { address sender = _msgSender(); - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - // Ensure vesting exists - if (agg.schedule.endDate == 0) revert ScheduleDoesNotExist(); - - /* - Schedule update is not allowed if : - - the schedule end date has passed - - the cliff and flow date is in the future - */ - if (agg.schedule.endDate <= block.timestamp || block.timestamp < agg.schedule.cliffAndFlowDate) { - revert TimeWindowInvalid(); - } - - // Settle the amount already vested - uint256 alreadyVestedAmount = _settle(agg); - - // Ensure that the new total amount is larger than the amount already vested - if (newTotalAmount <= alreadyVestedAmount) revert InvalidNewTotalAmount(); - - uint256 amountLeftToVest = newTotalAmount - alreadyVestedAmount; - uint256 timeLeftToVest = agg.schedule.endDate - block.timestamp; - - int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); - - // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = newFlowRate; - vestingSchedules[agg.id].remainderAmount = - _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); - - // If the schedule is started, update the existing flow rate to the new calculated flow rate - if (agg.schedule.cliffAndFlowDate == 0) { - _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); - } - - // Emit VestingSchedulerV2 event for backward compatibility - emit VestingScheduleUpdated( - superToken, - sender, - receiver, - agg.schedule.endDate, - agg.schedule.endDate, - vestingSchedules[agg.id].remainderAmount - ); - - // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleTotalAmountUpdated( - superToken, - sender, - receiver, - agg.schedule.flowRate, - newFlowRate, - _getTotalVestedAmount(agg.schedule, agg.accounting), - newTotalAmount, - vestingSchedules[agg.id].remainderAmount - ); + uint256 currentTotalAmount = _getTotalVestedAmount(vestingSchedules[agg.id], agg.accounting); + _updateVestingSchedule(agg, UpdateVestingScheduleParams({ + newEndDate: newEndDate, + newTotalAmount: currentTotalAmount, + newFlowRate: 0 // Note: 0 means it will be re-calculated. + })); } /// @inheritdoc IVestingSchedulerV3 - function updateVestingScheduleFlowRateFromEndDate(ISuperToken superToken, address receiver, uint32 endDate) - external - { + function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 newEndDate) external { address sender = _msgSender(); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - // Ensure vesting exists - if (agg.schedule.endDate == 0) revert ScheduleDoesNotExist(); + int96 currentFlowRate = agg.schedule.flowRate; + _updateVestingSchedule(agg, UpdateVestingScheduleParams({ + newEndDate: newEndDate, + newTotalAmount: 0, // Note: 0 means it will be re-calculated. + newFlowRate: currentFlowRate + })); + } + + function _updateVestingSchedule(ScheduleAggregate memory agg, UpdateVestingScheduleParams memory update) private { + + if (agg.schedule.endDate == 0) + revert ScheduleDoesNotExist(); /* Schedule update is not allowed if : @@ -383,69 +303,80 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { - the cliff and flow date is in the future */ if ( - agg.schedule.endDate <= block.timestamp || endDate <= block.timestamp - || block.timestamp < agg.schedule.cliffAndFlowDate - ) revert TimeWindowInvalid(); + agg.schedule.endDate <= block.timestamp || + update.newEndDate <= block.timestamp || + block.timestamp < agg.schedule.cliffAndFlowDate + ) + revert TimeWindowInvalid(); - // Update the schedule end date - vestingSchedules[agg.id].endDate = endDate; + vestingSchedules[agg.id].endDate = update.newEndDate; - uint256 amountLeftToVest = _getTotalVestedAmount(agg.schedule, agg.accounting) - _settle(agg); - uint256 timeLeftToVest = endDate - block.timestamp; + // Settle the amount already vested + uint256 alreadyVestedAmount = _settle(agg); + uint256 timeLeftToVest = update.newEndDate - block.timestamp; + + if (update.newTotalAmount == 0 && update.newFlowRate != 0) { + update.newTotalAmount = alreadyVestedAmount + (SafeCast.toUint256(update.newFlowRate) * timeLeftToVest); + } - int96 newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); + // Ensure that the new total amount is larger than the amount already vested + if (update.newTotalAmount < alreadyVestedAmount) + revert InvalidNewTotalAmount(); - // Update the vesting flow rate and remainder amount - vestingSchedules[agg.id].flowRate = newFlowRate; - vestingSchedules[agg.id].remainderAmount = - _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, newFlowRate); + uint256 amountLeftToVest = update.newTotalAmount - alreadyVestedAmount; + + if (update.newFlowRate == 0) { + update.newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); + } + + if (update.newFlowRate != vestingSchedules[agg.id].flowRate) { + vestingSchedules[agg.id].flowRate = update.newFlowRate; - // If the schedule is started, update the existing flow rate to the new calculated flow rate - if (agg.schedule.cliffAndFlowDate == 0) { - _updateVestingFlowRate(superToken, sender, receiver, newFlowRate); + // If the schedule is started, update the existing flow rate to the new calculated flow rate + if (agg.schedule.cliffAndFlowDate == 0) { + _updateVestingFlowRate(agg.superToken, agg.sender, agg.receiver, update.newFlowRate); + } } + vestingSchedules[agg.id].remainderAmount = + _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, update.newFlowRate); + // Emit VestingSchedulerV2 event for backward compatibility emit VestingScheduleUpdated( - superToken, sender, receiver, agg.schedule.endDate, endDate, vestingSchedules[agg.id].remainderAmount + agg.superToken, + agg.sender, + agg.receiver, + agg.schedule.endDate, // maybe remove this + update.newEndDate, + vestingSchedules[agg.id].remainderAmount + // flow rate + // total amount + // settled amount ); // Emit VestingSchedulerV3 event for additional data emit VestingScheduleEndDateUpdated( - superToken, - sender, - receiver, + agg.superToken, + agg.sender, + agg.receiver, agg.schedule.endDate, - endDate, + update.newEndDate, agg.schedule.flowRate, - newFlowRate, + update.newFlowRate, vestingSchedules[agg.id].remainderAmount ); - } - - /// @inheritdoc IVestingSchedulerV3 - function updateVestingSchedule(ISuperToken superToken, address receiver, uint32 endDate) external { - address sender = _msgSender(); - ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); - VestingSchedule memory schedule = agg.schedule; - if (endDate < block.timestamp) revert TimeWindowInvalid(); - - // Note: Claimable schedules that have not been claimed cannot be updated - - // Only allow an update if 1. vesting exists 2. executeCliffAndFlow() has been called - if (schedule.cliffAndFlowDate != 0 || schedule.endDate == 0) revert ScheduleNotFlowing(); - - vestingSchedules[agg.id].endDate = endDate; - - uint256 amountLeftToVest = _getTotalVestedAmount(vestingSchedules[agg.id], agg.accounting) - _settle(agg); - uint256 timeLeftToVest = endDate - block.timestamp; - - uint96 newRemainderAmount = _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, schedule.flowRate); - // Update the vesting remainder amount - vestingSchedules[agg.id].remainderAmount = newRemainderAmount; - - emit VestingScheduleUpdated(superToken, sender, receiver, schedule.endDate, endDate, newRemainderAmount); + // Emit VestingSchedulerV3 event for additional data + emit VestingScheduleTotalAmountUpdated( + agg.superToken, + agg.sender, + agg.receiver, + agg.schedule.flowRate, + update.newFlowRate, + _getTotalVestedAmount(agg.schedule, agg.accounting), + update.newTotalAmount, + vestingSchedules[agg.id].remainderAmount + ); } /// @inheritdoc IVestingSchedulerV3 From 1fb1559003d3cb091e8f7ac6a681cf037146e2f6 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Wed, 9 Apr 2025 15:19:39 +0300 Subject: [PATCH 47/59] refactor to a single update event --- .../contracts/VestingSchedulerV3.sol | 30 +--------- .../interface/IVestingSchedulerV3.sol | 60 ++++--------------- .../scheduler/test/VestingSchedulerV3.t.sol | 14 +++-- 3 files changed, 20 insertions(+), 84 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index f4e938ca3f..732ef1f738 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -341,41 +341,15 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { vestingSchedules[agg.id].remainderAmount = _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, update.newFlowRate); - // Emit VestingSchedulerV2 event for backward compatibility emit VestingScheduleUpdated( agg.superToken, agg.sender, agg.receiver, - agg.schedule.endDate, // maybe remove this update.newEndDate, - vestingSchedules[agg.id].remainderAmount - // flow rate - // total amount - // settled amount - ); - - // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleEndDateUpdated( - agg.superToken, - agg.sender, - agg.receiver, - agg.schedule.endDate, - update.newEndDate, - agg.schedule.flowRate, - update.newFlowRate, - vestingSchedules[agg.id].remainderAmount - ); - - // Emit VestingSchedulerV3 event for additional data - emit VestingScheduleTotalAmountUpdated( - agg.superToken, - agg.sender, - agg.receiver, - agg.schedule.flowRate, + vestingSchedules[agg.id].remainderAmount, update.newFlowRate, - _getTotalVestedAmount(agg.schedule, agg.accounting), update.newTotalAmount, - vestingSchedules[agg.id].remainderAmount + alreadyVestedAmount ); } diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index 53426aea0f..8c8749b4a0 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -128,66 +128,26 @@ interface IVestingSchedulerV3 { uint96 remainderAmount ); - /** - * @dev Event emitted when a vesting schedule's total amount is updated - * @param superToken The superToken being vested - * @param sender The vesting sender - * @param receiver The vesting receiver - * @param previousFlowRate The flow rate before the update - * @param newFlowRate The flow rate after the update - * @param previousTotalAmount The total amount to be vested before the update - * @param newTotalAmount The total amount to be vested after the update - * @param remainderAmount The remainder amount that cannot be streamed - */ - event VestingScheduleTotalAmountUpdated( - ISuperToken indexed superToken, - address indexed sender, - address indexed receiver, - int96 previousFlowRate, - int96 newFlowRate, - uint256 previousTotalAmount, - uint256 newTotalAmount, - uint96 remainderAmount - ); - - /** - * @dev Event emitted when a vesting schedule's end date is updated - * @param superToken The superToken being vested - * @param sender The vesting sender - * @param receiver The vesting receiver - * @param oldEndDate The end date before the update - * @param endDate The end date after the update - * @param previousFlowRate The flow rate before the update - * @param newFlowRate The flow rate after the update - * @param remainderAmount The remainder amount that cannot be streamed - */ - event VestingScheduleEndDateUpdated( - ISuperToken indexed superToken, - address indexed sender, - address indexed receiver, - uint32 oldEndDate, - uint32 endDate, - int96 previousFlowRate, - int96 newFlowRate, - uint96 remainderAmount - ); - /** * @dev Event emitted on update of a vesting schedule * @param superToken The superToken to be vested - * @param sender Vesting sender - * @param receiver Vesting receiver - * @param oldEndDate Old timestamp when the stream should stop + * @param sender Vesting sender - the account that created and funds the vesting schedule + * @param receiver Vesting receiver - the account that receives the vested tokens * @param endDate New timestamp when the stream should stop - * @param remainderAmount The remainder amount that cannot be streamed + * @param remainderAmount The remainder amount that cannot be streamed due to flow rate precision + * @param flowRate The new flow rate for the updated vesting schedule + * @param totalAmount The total amount to be vested over the entire schedule + * @param alreadyVestedAmount The amount that has already been vested up to the update */ event VestingScheduleUpdated( ISuperToken indexed superToken, address indexed sender, address indexed receiver, - uint32 oldEndDate, uint32 endDate, - uint96 remainderAmount + uint96 remainderAmount, + int96 flowRate, + uint256 totalAmount, + uint256 alreadyVestedAmount ); /** diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 0128356ce9..dbb217bfb3 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -39,9 +39,11 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { ISuperToken indexed superToken, address indexed sender, address indexed receiver, - uint32 oldEndDate, uint32 endDate, - uint96 remainderAmount + uint96 remainderAmount, + int96 flowRate, + uint256 totalAmount, + uint256 alreadyVestedAmount ); event VestingScheduleDeleted(ISuperToken indexed superToken, address indexed sender, address indexed receiver); @@ -664,7 +666,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { SafeCast.toUint96((totalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, expectedRemainder); + emit VestingScheduleUpdated(superToken, alice, bob, NEW_END_DATE, expectedRemainder, newFlowRate, totalAmount, alreadyVestedAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE); @@ -728,7 +730,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { SafeCast.toUint96((totalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, NEW_END_DATE, expectedRemainder); + emit VestingScheduleUpdated(superToken, alice, bob, NEW_END_DATE, expectedRemainder, newFlowRate, totalAmount, alreadyVestedAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE); @@ -793,7 +795,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { SafeCast.toUint96((newTotalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, END_DATE, expectedRemainder); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, expectedRemainder, newFlowRate, newTotalAmount, alreadyVestedAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount); @@ -870,7 +872,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { SafeCast.toUint96((newTotalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, END_DATE, expectedRemainder); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, expectedRemainder, newFlowRate, newTotalAmount, alreadyVestedAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount); From 9bf083341babd5d00c34a262f7346630259313cf Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Wed, 9 Apr 2025 15:30:15 +0300 Subject: [PATCH 48/59] rename to settledAmount and settledDate --- .../contracts/VestingSchedulerV3.sol | 52 +++++++++--------- .../interface/IVestingSchedulerV3.sol | 4 +- .../scheduler/test/VestingSchedulerV3.t.sol | 54 +++++++++---------- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 732ef1f738..3eb04f0c2e 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -47,12 +47,12 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { /** * @notice Struct containing accounting details for a schedule - * @param alreadyVestedAmount The amount already vested - * @param lastUpdated The timestamp of the last update + * @param settledAmount The amount already vested/settled + * @param settledDate The timestamp of the last settling */ struct ScheduleAccounting { - uint256 alreadyVestedAmount; - uint256 lastUpdated; + uint256 settledAmount; + uint256 settledDate; } // ____ __ __ __ _____ __ __ @@ -312,18 +312,18 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { vestingSchedules[agg.id].endDate = update.newEndDate; // Settle the amount already vested - uint256 alreadyVestedAmount = _settle(agg); + uint256 settledAmount = _settle(agg); uint256 timeLeftToVest = update.newEndDate - block.timestamp; if (update.newTotalAmount == 0 && update.newFlowRate != 0) { - update.newTotalAmount = alreadyVestedAmount + (SafeCast.toUint256(update.newFlowRate) * timeLeftToVest); + update.newTotalAmount = settledAmount + (SafeCast.toUint256(update.newFlowRate) * timeLeftToVest); } // Ensure that the new total amount is larger than the amount already vested - if (update.newTotalAmount < alreadyVestedAmount) + if (update.newTotalAmount < settledAmount) revert InvalidNewTotalAmount(); - uint256 amountLeftToVest = update.newTotalAmount - alreadyVestedAmount; + uint256 amountLeftToVest = update.newTotalAmount - settledAmount; if (update.newFlowRate == 0) { update.newFlowRate = _calculateFlowRate(amountLeftToVest, timeLeftToVest); @@ -349,7 +349,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { vestingSchedules[agg.id].remainderAmount, update.newFlowRate, update.newTotalAmount, - alreadyVestedAmount + settledAmount ); } @@ -402,7 +402,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { _validateBeforeEndVesting(schedule, /* disableClaimCheck: */ false); - uint256 alreadyVestedAmount = _settle(agg); + uint256 settledAmount = _settle(agg); uint256 totalVestedAmount = _getTotalVestedAmount(schedule, accounting); // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. @@ -415,7 +415,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { // Note: we consider the compensation as failed if the stream is still ongoing after the end date. bool didCompensationFail = schedule.endDate < block.timestamp; - uint256 earlyEndCompensation = totalVestedAmount - alreadyVestedAmount; + uint256 earlyEndCompensation = totalVestedAmount - settledAmount; if (earlyEndCompensation != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. @@ -555,7 +555,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { returns (uint256 maxNeededAllowance) { maxNeededAllowance = - _getMaximumNeededTokenAllowance(schedule, ScheduleAccounting({alreadyVestedAmount: 0, lastUpdated: 0})); + _getMaximumNeededTokenAllowance(schedule, ScheduleAccounting({settledAmount: 0, settledDate: 0})); } /// @inheritdoc IVestingSchedulerV3 @@ -659,7 +659,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { assert(_executeCliffAndFlow(agg)); } - function _settle(ScheduleAggregate memory agg) private returns (uint256 alreadyVestedAmount) { + function _settle(ScheduleAggregate memory agg) private returns (uint256 settledAmount) { // Ensure that the cliff and flow date has passed assert(block.timestamp >= agg.schedule.cliffAndFlowDate); @@ -667,21 +667,21 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { delete vestingSchedules[agg.id].cliffAmount; // Update the timestamp of the last schedule update - accountings[agg.id].lastUpdated = block.timestamp; + accountings[agg.id].settledDate = block.timestamp; if (block.timestamp > agg.schedule.endDate) { // If the schedule end date has passed, settle the total amount vested - accountings[agg.id].alreadyVestedAmount = _getTotalVestedAmount(agg.schedule, agg.accounting); + accountings[agg.id].settledAmount = _getTotalVestedAmount(agg.schedule, agg.accounting); } else { // If the schedule end date has not passed, accrue the amount already vested - uint256 actualLastUpdate = - agg.accounting.lastUpdated == 0 ? agg.schedule.cliffAndFlowDate : agg.accounting.lastUpdated; + uint256 settledDate = + agg.accounting.settledDate == 0 ? agg.schedule.cliffAndFlowDate : agg.accounting.settledDate; // Accrue the amount already vested - accountings[agg.id].alreadyVestedAmount += - ((block.timestamp - actualLastUpdate) * uint96(agg.schedule.flowRate)) + agg.schedule.cliffAmount; + accountings[agg.id].settledAmount += + ((block.timestamp - settledDate) * uint96(agg.schedule.flowRate)) + agg.schedule.cliffAmount; } - alreadyVestedAmount = accountings[agg.id].alreadyVestedAmount; + settledAmount = accountings[agg.id].settledAmount; } function _calculateFlowRate(uint256 amountLeftToVest, uint256 timeLeftToVest) @@ -756,15 +756,15 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { VestingSchedule memory schedule = agg.schedule; // Settle the amount already vested - uint256 alreadyVestedAmount = _settle(agg); + uint256 settledAmount = _settle(agg); // Invalidate configuration straight away -- avoid any chance of re-execution or re-entry. delete vestingSchedules[agg.id].cliffAndFlowDate; // Transfer the amount already vested (includes the cliff, if any) - if (alreadyVestedAmount != 0) { + if (settledAmount != 0) { // Note: Super Tokens revert, not return false, i.e. we expect always true here. - assert(agg.superToken.transferFrom(agg.sender, agg.receiver, alreadyVestedAmount)); + assert(agg.superToken.transferFrom(agg.sender, agg.receiver, settledAmount)); } // Create a flow according to the vesting schedule configuration. @@ -777,7 +777,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { schedule.cliffAndFlowDate, schedule.flowRate, schedule.cliffAmount, - alreadyVestedAmount - schedule.cliffAmount + settledAmount - schedule.cliffAmount ); success = true; @@ -820,13 +820,13 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { pure returns (uint256 totalVestedAmount) { - uint256 actualLastUpdate = accounting.lastUpdated == 0 ? schedule.cliffAndFlowDate : accounting.lastUpdated; + uint256 actualLastUpdate = accounting.settledDate == 0 ? schedule.cliffAndFlowDate : accounting.settledDate; uint256 currentFlowDuration = schedule.endDate - actualLastUpdate; uint256 currentFlowAmount = currentFlowDuration * SafeCast.toUint256(schedule.flowRate); totalVestedAmount = - accounting.alreadyVestedAmount + schedule.cliffAmount + schedule.remainderAmount + currentFlowAmount; + accounting.settledAmount + schedule.cliffAmount + schedule.remainderAmount + currentFlowAmount; } function _validateBeforeEndVesting(VestingSchedule memory schedule, bool disableClaimCheck) private view { diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index 8c8749b4a0..a09cf4da75 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -137,7 +137,7 @@ interface IVestingSchedulerV3 { * @param remainderAmount The remainder amount that cannot be streamed due to flow rate precision * @param flowRate The new flow rate for the updated vesting schedule * @param totalAmount The total amount to be vested over the entire schedule - * @param alreadyVestedAmount The amount that has already been vested up to the update + * @param settledAmount The amount that has already been vested up to the update */ event VestingScheduleUpdated( ISuperToken indexed superToken, @@ -147,7 +147,7 @@ interface IVestingSchedulerV3 { uint96 remainderAmount, int96 flowRate, uint256 totalAmount, - uint256 alreadyVestedAmount + uint256 settledAmount ); /** diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index dbb217bfb3..6908061a9c 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -43,7 +43,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { uint96 remainderAmount, int96 flowRate, uint256 totalAmount, - uint256 alreadyVestedAmount + uint256 settledAmount ); event VestingScheduleDeleted(ISuperToken indexed superToken, address indexed sender, address indexed receiver); @@ -658,15 +658,15 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(block.timestamp + 2 days); uint256 timeLeftToVest = NEW_END_DATE - block.timestamp; - uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + uint256 settledAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; int96 newFlowRate = - SafeCast.toInt96(SafeCast.toInt256(totalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest)); + SafeCast.toInt96(SafeCast.toInt256(totalAmount - settledAmount) / SafeCast.toInt256(timeLeftToVest)); uint96 expectedRemainder = - SafeCast.toUint96((totalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + SafeCast.toUint96((totalAmount - settledAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, NEW_END_DATE, expectedRemainder, newFlowRate, totalAmount, alreadyVestedAmount); + emit VestingScheduleUpdated(superToken, alice, bob, NEW_END_DATE, expectedRemainder, newFlowRate, totalAmount, settledAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE); @@ -722,15 +722,15 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(block.timestamp + 2 days); uint256 timeLeftToVest = NEW_END_DATE - block.timestamp; - uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + uint256 settledAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; int96 newFlowRate = - SafeCast.toInt96(SafeCast.toInt256(totalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest)); + SafeCast.toInt96(SafeCast.toInt256(totalAmount - settledAmount) / SafeCast.toInt256(timeLeftToVest)); uint96 expectedRemainder = - SafeCast.toUint96((totalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + SafeCast.toUint96((totalAmount - settledAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, NEW_END_DATE, expectedRemainder, newFlowRate, totalAmount, alreadyVestedAmount); + emit VestingScheduleUpdated(superToken, alice, bob, NEW_END_DATE, expectedRemainder, newFlowRate, totalAmount, settledAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, NEW_END_DATE); @@ -786,16 +786,16 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(block.timestamp + 2 days); uint256 timeLeftToVest = END_DATE - block.timestamp; - uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + uint256 settledAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; int96 newFlowRate = SafeCast.toInt96( - SafeCast.toInt256(newTotalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest) + SafeCast.toInt256(newTotalAmount - settledAmount) / SafeCast.toInt256(timeLeftToVest) ); uint96 expectedRemainder = - SafeCast.toUint96((newTotalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + SafeCast.toUint96((newTotalAmount - settledAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, expectedRemainder, newFlowRate, newTotalAmount, alreadyVestedAmount); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, expectedRemainder, newFlowRate, newTotalAmount, settledAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount); @@ -816,7 +816,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertTrue(success, "executeCloseVesting should return true"); uint256 expectedTotalAmountTransferred = - alreadyVestedAmount + (timeLeftToVest * uint96(newFlowRate)) + schedule.remainderAmount; + settledAmount + (timeLeftToVest * uint96(newFlowRate)) + schedule.remainderAmount; assertEq( aliceInitialBalance - superToken.balanceOf(alice), @@ -862,17 +862,17 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.warp(block.timestamp + 2 days); uint256 timeLeftToVest = END_DATE - block.timestamp; - uint256 alreadyVestedAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; + uint256 settledAmount = (block.timestamp - CLIFF_DATE) * uint96(FLOW_RATE) + CLIFF_TRANSFER_AMOUNT; int96 newFlowRate = SafeCast.toInt96( - SafeCast.toInt256(newTotalAmount - alreadyVestedAmount) / SafeCast.toInt256(timeLeftToVest) + SafeCast.toInt256(newTotalAmount - settledAmount) / SafeCast.toInt256(timeLeftToVest) ); uint96 expectedRemainder = - SafeCast.toUint96((newTotalAmount - alreadyVestedAmount) - (uint96(newFlowRate) * timeLeftToVest)); + SafeCast.toUint96((newTotalAmount - settledAmount) - (uint96(newFlowRate) * timeLeftToVest)); vm.expectEmit(true, true, true, true); - emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, expectedRemainder, newFlowRate, newTotalAmount, alreadyVestedAmount); + emit VestingScheduleUpdated(superToken, alice, bob, END_DATE, expectedRemainder, newFlowRate, newTotalAmount, settledAmount); vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmount(superToken, bob, newTotalAmount); @@ -893,7 +893,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertTrue(success, "executeCloseVesting should return true"); uint256 expectedTotalAmountTransferred = - alreadyVestedAmount + (timeLeftToVest * uint96(newFlowRate)) + schedule.remainderAmount; + settledAmount + (timeLeftToVest * uint96(newFlowRate)) + schedule.remainderAmount; assertEq( aliceInitialBalance - superToken.balanceOf(alice), @@ -2713,10 +2713,10 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); uint256 remainingMonths = 4; - (uint256 alreadyVestedAmount,) = + (uint256 settledAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); - uint256 remainingAmount = secondUpdateAmount - alreadyVestedAmount; + uint256 remainingAmount = secondUpdateAmount - settledAmount; int96 expectedFlowRate = SafeCast.toInt96(SafeCast.toInt256(remainingAmount / (schedule.endDate - block.timestamp))); assertEq(schedule.flowRate, expectedFlowRate, "Flow rate should be updated for 300 USDC/month"); @@ -2730,11 +2730,11 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Verify the flow rate has been updated again schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - (alreadyVestedAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); + (settledAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); // Calculate new flow rate for remaining 3 months remainingMonths = 3; - remainingAmount = 1100 ether - alreadyVestedAmount; + remainingAmount = 1100 ether - settledAmount; expectedFlowRate = SafeCast.toInt96(SafeCast.toInt256(remainingAmount / (schedule.endDate - block.timestamp))); assertEq(schedule.flowRate, expectedFlowRate, "Flow rate should be updated for 200 USDC/month"); @@ -2826,12 +2826,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Get the updated schedule schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - (uint256 alreadyVestedAmount,) = + (uint256 settledAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); // Calculate expected flow rate after update int96 expectedFlowRate = SafeCast.toInt96( - SafeCast.toInt256((updatedTotalAmount - alreadyVestedAmount) / (schedule.endDate - block.timestamp)) + SafeCast.toInt256((updatedTotalAmount - settledAmount) / (schedule.endDate - block.timestamp)) ); // Verify the flow rate has been updated correctly @@ -2883,12 +2883,12 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { // Warp to third month (90 days total) vm.warp(startDate + 90 days); - (alreadyVestedAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); + (settledAmount,) = vestingScheduler.accountings(_helperGetScheduleId(address(superToken), alice, bob)); // Calculate expected amount after third month // First two months + third month at reduced rate schedule = vestingScheduler.getVestingSchedule(address(superToken), alice, bob); - uint256 thirdMonthAmount = (1100 ether - alreadyVestedAmount) / 3; // ~200 USDC/month + uint256 thirdMonthAmount = (1100 ether - settledAmount) / 3; // ~200 USDC/month // Verify Bob's balance after third month assertApproxEqAbs( From 6adb0224f7a11e3d6312eba86a8f4956883646f4 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Thu, 24 Apr 2025 12:47:21 +0300 Subject: [PATCH 49/59] add endVestingScheduleNow function - allows to cleanly end vesting schedule immediately - added condition not to allow vesting schedule updates when claim period has ended - allowed updating vesting schedule end date to current block timestamp - made executeCliffAndFlow and executeEndVesting public instead of just external --- .../contracts/VestingSchedulerV3.sol | 49 ++- .../interface/IVestingSchedulerV3.sol | 10 + .../scheduler/test/VestingSchedulerV3.t.sol | 364 +++++++++++++----- 3 files changed, 319 insertions(+), 104 deletions(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 3eb04f0c2e..a83e945b40 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -248,8 +248,8 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { } function updateVestingScheduleFlowRateFromAmount( - ISuperToken superToken, - address receiver, + ISuperToken superToken, + address receiver, uint256 newTotalAmount ) external { address sender = _msgSender(); @@ -303,9 +303,10 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { - the cliff and flow date is in the future */ if ( - agg.schedule.endDate <= block.timestamp || - update.newEndDate <= block.timestamp || - block.timestamp < agg.schedule.cliffAndFlowDate + agg.schedule.endDate < block.timestamp || + update.newEndDate < block.timestamp || + block.timestamp < agg.schedule.cliffAndFlowDate || + (agg.schedule.claimValidityDate != 0 && block.timestamp > agg.schedule.claimValidityDate) ) revert TimeWindowInvalid(); @@ -314,7 +315,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { // Settle the amount already vested uint256 settledAmount = _settle(agg); uint256 timeLeftToVest = update.newEndDate - block.timestamp; - + if (update.newTotalAmount == 0 && update.newFlowRate != 0) { update.newTotalAmount = settledAmount + (SafeCast.toUint256(update.newFlowRate) * timeLeftToVest); } @@ -342,10 +343,10 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { _calculateRemainderAmount(amountLeftToVest, timeLeftToVest, update.newFlowRate); emit VestingScheduleUpdated( - agg.superToken, - agg.sender, - agg.receiver, - update.newEndDate, + agg.superToken, + agg.sender, + agg.receiver, + update.newEndDate, vestingSchedules[agg.id].remainderAmount, update.newFlowRate, update.newTotalAmount, @@ -370,7 +371,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { /// @inheritdoc IVestingSchedulerV3 function executeCliffAndFlow(ISuperToken superToken, address sender, address receiver) - external + public returns (bool success) { ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); @@ -393,7 +394,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { /// @inheritdoc IVestingSchedulerV3 function executeEndVesting(ISuperToken superToken, address sender, address receiver) - external + public returns (bool success) { ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); @@ -432,6 +433,30 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { success = true; } + function endVestingScheduleNow(ISuperToken superToken, address receiver) external { + address sender = _msgSender(); + ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); + + // Execute cliff and flow if not yet executed. + // The flow will end up streaming 0 as it will be deleted immediately. + if (agg.schedule.claimValidityDate == 0) { + if (agg.schedule.cliffAndFlowDate != 0) { + assert(executeCliffAndFlow(superToken, sender, receiver)); + } + } + + _updateVestingSchedule(agg, UpdateVestingScheduleParams({ + newEndDate: uint32(block.timestamp), + newTotalAmount: 0, // Note: 0 means it will be re-calculated. + newFlowRate: agg.schedule.flowRate + })); + + // Execute end vesting if not claimable. + if (agg.schedule.claimValidityDate == 0) { + assert(executeEndVesting(superToken, sender, receiver)); + } + } + // _ ___ ______ __ _ // | | / (_)__ _ __ / ____/_ ______ _____/ /_(_)___ ____ _____ // | | / / / _ \ | /| / / / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ diff --git a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol index a09cf4da75..45ac074e26 100644 --- a/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/interface/IVestingSchedulerV3.sol @@ -404,6 +404,16 @@ interface IVestingSchedulerV3 { function updateVestingScheduleFlowRateFromEndDate(ISuperToken superToken, address receiver, uint32 endDate) external; + /** + * @dev Updates vesting schedule to the current block and executes end (if not claimable) immediately, + * @dev and/or executes cliff and flow (if not claimable and cliff and flow not yet executed). + * @notice When ending, the remaining amount will be transferred to the receiver + * @param superToken SuperToken to be vested + * @param receiver Vesting receiver + */ + function endVestingScheduleNow(ISuperToken superToken, address receiver) + external; + // _ ___ ______ __ _ // | | / (_)__ _ __ / ____/_ ______ _____/ /_(_)___ ____ _____ // | | / / / _ \ | /| / / / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/ diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 6908061a9c..8b712841bd 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -440,9 +440,9 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); vm.expectEmit(true, true, true, true); emit VestingScheduleCreated( - superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, 0, 0 + superToken, alice, bob, START_DATE, CLIFF_DATE, FLOW_RATE, END_DATE, CLIFF_TRANSFER_AMOUNT, CLAIM_VALIDITY_DATE, 0 ); - _createVestingScheduleWithDefaultData(alice, bob); + _createClaimableVestingScheduleWithDefaultData(alice, bob); vm.startPrank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); @@ -460,16 +460,18 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterCliffAndFlowDate - 1)); - // Schedule update is not allowed if : "the new end date is in right now (block.timestamp)" - vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterCliffAndFlowDate)); - uint256 afterEndDate = END_DATE + 1 hours; vm.warp(afterEndDate); // Schedule update is not allowed if : "the current end date has passed" vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); - vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterEndDate + 1 hours)); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterEndDate)); + + // Schedule update is not allowed if : "the current claim validity date has passed" + uint256 afterClaimValidityDate = CLAIM_VALIDITY_DATE + 1 hours; + vm.warp(afterClaimValidityDate); + vm.expectRevert(IVestingSchedulerV2.TimeWindowInvalid.selector); + vestingScheduler.updateVestingScheduleFlowRateFromEndDate(superToken, bob, uint32(afterClaimValidityDate)); vm.stopPrank(); } @@ -2397,6 +2399,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { } function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_cannotClaimOnBehalf(address _claimer) public { + vm.assume(isTrustedForwarder(_claimer) == false); vm.assume(_claimer != address(0) && _claimer != alice && _claimer != bob); _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); _createClaimableVestingScheduleWithDefaultData(alice, bob); @@ -2952,37 +2955,36 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testVestingSchedulerV3_UpdateVestingSchedule_WithAmountAndEndDate( uint256 initialAmount, uint256 secondAmount, - uint256 thirdAmount, + uint256 thirdAmount, uint256 finalAmount, uint32 totalDuration, uint32 durationExtension1, - uint32 durationExtension2, - uint8 randomizer + uint32 durationExtension2 ) public { VestingTestState memory state; - + // Bound inputs to reasonable values state.initialAmount = bound(initialAmount, 500 ether, 2000 ether); - + state.secondAmount = bound(secondAmount, state.initialAmount - 10 ether, state.initialAmount + 10 ether); state.thirdAmount = bound(thirdAmount, state.secondAmount - 10 ether, state.secondAmount + 10 ether); state.finalAmount = bound(finalAmount, state.thirdAmount - 10 ether, state.thirdAmount + 10 ether); - + // Ensure reasonable durations state.totalDuration = uint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION() + 7 days, 180 days)); state.durationExtension1 = uint32(bound(durationExtension1, 1 days, 3 days)); state.durationExtension2 = uint32(bound(durationExtension2, 1 days, 3 days)); - + // Capture initial balances state.aliceInitialBalance = superToken.balanceOf(alice); state.bobInitialBalance = superToken.balanceOf(bob); - + // Setup _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); - + vm.startPrank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); - + // Create initial vesting schedule state.startDate = uint32(block.timestamp); vestingScheduler.createVestingScheduleFromAmountAndDuration( @@ -2995,94 +2997,94 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { 0 // No claim period ); vm.stopPrank(); - + // Verify initial total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.initialAmount, "Initial total amount should match"); - + // Execute cliff and flow to start vesting vm.warp(state.startDate); vm.prank(alice); state.success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertTrue(state.success, "executeCliffAndFlow should succeed"); - + console.log("First update - 25% into vesting"); vm.warp(state.startDate + state.totalDuration/4); - + console.log("Update to secondAmount and extend duration"); state.firstNewEndDate = state.startDate + state.totalDuration + state.durationExtension1; - + vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( - superToken, - bob, - state.secondAmount, + superToken, + bob, + state.secondAmount, state.firstNewEndDate ); - + // Verify updated total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.secondAmount, "Total amount after first update should match secondAmount"); - + console.log("Warp to 50% of original duration"); vm.warp(state.startDate + state.totalDuration / 2); - + // Second update state.secondNewEndDate = state.firstNewEndDate - state.durationExtension2; - + vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( - superToken, - bob, - state.thirdAmount, + superToken, + bob, + state.thirdAmount, state.secondNewEndDate ); - + // Verify updated total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.thirdAmount, "Total amount after second update should match thirdAmount"); - + console.log("Warp to 75% of original duration"); vm.warp(state.startDate + (state.totalDuration * 3)/4); - + console.log("Final update"); state.finalEndDate = state.secondNewEndDate - 1 days; - + vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( - superToken, - bob, - state.finalAmount, + superToken, + bob, + state.finalAmount, state.finalEndDate ); - + // Verify updated total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.finalAmount, "Total amount after final update should match finalAmount"); - + // Warp to 12 hours before end and execute end vesting vm.warp(state.finalEndDate - 12 hours); - + vm.prank(alice); state.success = vestingScheduler.executeEndVesting(superToken, alice, bob); assertTrue(state.success, "executeEndVesting should succeed"); - + // Verify final balances state.aliceFinalBalance = superToken.balanceOf(alice); state.bobFinalBalance = superToken.balanceOf(bob); - + assertEq( - state.aliceInitialBalance - state.aliceFinalBalance, - state.finalAmount, + state.aliceInitialBalance - state.aliceFinalBalance, + state.finalAmount, "Alice should have transferred exactly finalAmount" ); - + assertEq( - state.bobFinalBalance - state.bobInitialBalance, - state.finalAmount, + state.bobFinalBalance - state.bobInitialBalance, + state.finalAmount, "Bob should have received exactly finalAmount" ); - + // Verify schedule no longer exists testAssertScheduleDoesNotExist(address(superToken), alice, bob); } @@ -3090,37 +3092,36 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { function testVestingSchedulerV3_UpdateVestingSchedule_WithAmountAndEndDate_AndClaimPeriodExecutedAsSingleTransfer( uint256 initialAmount, uint256 secondAmount, - uint256 thirdAmount, + uint256 thirdAmount, uint256 finalAmount, uint32 totalDuration, uint32 durationExtension1, - uint32 durationExtension2, - uint8 randomizer + uint32 durationExtension2 ) public { VestingTestState memory state; - + // Bound inputs to reasonable values state.initialAmount = bound(initialAmount, 500 ether, 2000 ether); - + state.secondAmount = bound(secondAmount, state.initialAmount - 10 ether, state.initialAmount + 10 ether); state.thirdAmount = bound(thirdAmount, state.secondAmount - 10 ether, state.secondAmount + 10 ether); state.finalAmount = bound(finalAmount, state.thirdAmount - 10 ether, state.thirdAmount + 10 ether); - + // Ensure reasonable durations state.totalDuration = uint32(bound(totalDuration, vestingScheduler.MIN_VESTING_DURATION() + 7 days, 180 days)); state.durationExtension1 = uint32(bound(durationExtension1, 1 days, 3 days)); state.durationExtension2 = uint32(bound(durationExtension2, 1 days, 3 days)); - + // Capture initial balances state.aliceInitialBalance = superToken.balanceOf(alice); state.bobInitialBalance = superToken.balanceOf(bob); - + // Setup _setACL_AUTHORIZE_FULL_CONTROL(alice, type(int96).max); - + vm.startPrank(alice); superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); - + // Create initial vesting schedule state.startDate = uint32(block.timestamp); vestingScheduler.createVestingScheduleFromAmountAndDuration( @@ -3133,92 +3134,245 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { state.totalDuration + 7 days ); vm.stopPrank(); - + // Verify initial total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.initialAmount, "Initial total amount should match"); - + console.log("First update - 25% into vesting"); vm.warp(state.startDate + state.totalDuration/4); - + console.log("Update to secondAmount and extend duration"); state.firstNewEndDate = state.startDate + state.totalDuration + state.durationExtension1; - + vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( - superToken, - bob, - state.secondAmount, + superToken, + bob, + state.secondAmount, state.firstNewEndDate ); - + // Verify updated total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.secondAmount, "Total amount after first update should match secondAmount"); - + console.log("Warp to 50% of original duration"); vm.warp(state.startDate + state.totalDuration / 2); - + // Second update state.secondNewEndDate = state.firstNewEndDate - state.durationExtension2; - + vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( - superToken, - bob, - state.thirdAmount, + superToken, + bob, + state.thirdAmount, state.secondNewEndDate ); - + // Verify updated total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.thirdAmount, "Total amount after second update should match thirdAmount"); - + console.log("Warp to 75% of original duration"); vm.warp(state.startDate + (state.totalDuration * 3)/4); - + console.log("Final update"); state.finalEndDate = state.secondNewEndDate - 1 days; - + vm.prank(alice); vestingScheduler.updateVestingScheduleFlowRateFromAmountAndEndDate( - superToken, - bob, - state.finalAmount, + superToken, + bob, + state.finalAmount, state.finalEndDate ); - + // Verify updated total amount state.retrievedTotalAmount = vestingScheduler.getTotalVestedAmount(superToken, alice, bob); assertEq(state.retrievedTotalAmount, state.finalAmount, "Total amount after final update should match finalAmount"); - + // Warp to 12 hours before end and execute end vesting vm.warp(state.finalEndDate + 1 days); - + vm.prank(alice); state.success = vestingScheduler.executeCliffAndFlow(superToken, alice, bob); assertTrue(state.success, "executeCliffAndFlow should succeed"); - + // Verify final balances state.aliceFinalBalance = superToken.balanceOf(alice); state.bobFinalBalance = superToken.balanceOf(bob); - + assertEq( - state.aliceInitialBalance - state.aliceFinalBalance, - state.finalAmount, + state.aliceInitialBalance - state.aliceFinalBalance, + state.finalAmount, "Alice should have transferred exactly finalAmount" ); - + assertEq( - state.bobFinalBalance - state.bobInitialBalance, - state.finalAmount, + state.bobFinalBalance - state.bobInitialBalance, + state.finalAmount, "Bob should have received exactly finalAmount" ); - + // Verify schedule no longer exists testAssertScheduleDoesNotExist(address(superToken), alice, bob); } + function testEndVestingScheduleNow_Claimable() public { + // Setup + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createClaimableVestingScheduleWithDefaultData(alice, bob); + + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + // Warp to midway through vesting but before claiming + uint256 midwayTime = CLIFF_DATE + (CLAIM_VALIDITY_DATE - CLIFF_DATE) / 2; + vm.assume(midwayTime < CLAIM_VALIDITY_DATE); + vm.warp(midwayTime); + + // End vesting now + vm.prank(alice); + vestingScheduler.endVestingScheduleNow(superToken, bob); + + // Verify schedule was updated but still exists + IVestingSchedulerV3.VestingSchedule memory updatedSchedule = + vestingScheduler.getVestingSchedule(address(superToken), alice, bob); + + assertEq(updatedSchedule.endDate, uint32(block.timestamp), "End date should be updated to current timestamp"); + + // Now claim the schedule + vm.prank(bob); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + + // Verify the full amount was transferred + uint256 totalAmount = CLIFF_TRANSFER_AMOUNT + (END_DATE - CLIFF_DATE) * uint256(uint96(FLOW_RATE)); + + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + totalAmount, + 1e16, // Allow small rounding differences + "Bob should have received the full amount" + ); + + assertApproxEqAbs( + aliceInitialBalance - superToken.balanceOf(alice), + totalAmount, + 1e16, // Allow small rounding differences + "Alice's balance should have decreased by the full amount" + ); + + // Verify schedule no longer exists + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + } + + function testEndVestingScheduleNow_NonClaimable() public { + // Setup + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + // Warp to after cliff date and execute cliff and flow + vm.warp(CLIFF_DATE + 1 days); + vm.prank(admin); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + + // Warp to midway through vesting + uint256 midwayTime = CLIFF_DATE + (END_DATE - CLIFF_DATE) / 2; + vm.warp(midwayTime); + + // Calculate expected vested amount + uint256 expectedVestedAmount = CLIFF_TRANSFER_AMOUNT + + (block.timestamp - CLIFF_DATE) * uint256(uint96(FLOW_RATE)); + + // End vesting now + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, uint32(block.timestamp), 0, false); + + vm.prank(alice); + vestingScheduler.endVestingScheduleNow(superToken, bob); + + // Verify schedule no longer exists + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + + // Verify correct amounts were transferred + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + expectedVestedAmount, + 1e16, // Allow small rounding differences + "Bob should have received the expected vested amount" + ); + + assertApproxEqAbs( + aliceInitialBalance - superToken.balanceOf(alice), + expectedVestedAmount, + 1e16, // Allow small rounding differences + "Alice's balance should have decreased by the expected vested amount" + ); + } + + function testEndVestingScheduleNow_NonClaimable_WithoutCliffAndFlowExecuted() public { + // Setup + uint256 aliceInitialBalance = superToken.balanceOf(alice); + uint256 bobInitialBalance = superToken.balanceOf(bob); + + _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); + _createVestingScheduleWithDefaultData(alice, bob); + + vm.prank(alice); + superToken.increaseAllowance(address(vestingScheduler), type(uint256).max); + + // Warp to after cliff date and execute cliff and flow + vm.warp(CLIFF_DATE + 1 days); + + // Calculate expected vested amount + uint256 expectedVestedAmount = CLIFF_TRANSFER_AMOUNT + + (block.timestamp - CLIFF_DATE) * uint256(uint96(FLOW_RATE)); + + uint256 flowDelayCompensation = (block.timestamp - CLIFF_DATE) * uint256(uint96(FLOW_RATE)); + + // End vesting now + vm.expectEmit(true, true, true, true); + emit VestingCliffAndFlowExecuted( + superToken, alice, bob, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, flowDelayCompensation + ); + + // End vesting now + vm.expectEmit(true, true, true, true); + emit VestingEndExecuted(superToken, alice, bob, uint32(block.timestamp), 0, false); + + vm.prank(alice); + vestingScheduler.endVestingScheduleNow(superToken, bob); + + // Verify schedule no longer exists + testAssertScheduleDoesNotExist(address(superToken), alice, bob); + + // Verify correct amounts were transferred + assertApproxEqAbs( + superToken.balanceOf(bob) - bobInitialBalance, + expectedVestedAmount, + 1e16, // Allow small rounding differences + "Bob should have received the expected vested amount" + ); + + assertApproxEqAbs( + aliceInitialBalance - superToken.balanceOf(alice), + expectedVestedAmount, + 1e16, // Allow small rounding differences + "Alice's balance should have decreased by the expected vested amount" + ); + } + function test_use_2771_forward_call() public { vm.startPrank(alice); vestingScheduler.createVestingSchedule( @@ -3249,6 +3403,32 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { assertEq(schedule.endDate, newEndDate); } + function test_use_2771_forward_call_revert() public { + vm.startPrank(alice); + vestingScheduler.createVestingSchedule( + superToken, bob, START_DATE, CLIFF_DATE, FLOW_RATE, CLIFF_TRANSFER_AMOUNT, END_DATE, 0 + ); + _arrangeAllowances(alice, FLOW_RATE); + vm.stopPrank(); + + vm.warp(CLIFF_DATE != 0 ? CLIFF_DATE : START_DATE); + vestingScheduler.executeCliffAndFlow(superToken, alice, bob); + + uint32 newEndDate = END_DATE + 1234; + ISuperfluid.Operation[] memory ops = new ISuperfluid.Operation[](1); + ops[0] = ISuperfluid.Operation({ + operationType: BatchOperation.OPERATION_TYPE_ERC2771_FORWARD_CALL, + target: address(vestingScheduler), + data: abi.encodeCall(vestingScheduler.updateVestingScheduleFlowRateFromEndDate, (superToken, bob, newEndDate)) + }); + + // Act & Assert + vm.prank(bob); // Not the sender + vm.expectRevert(IVestingSchedulerV2.ScheduleDoesNotExist.selector); + sf.host.batchCall(ops); + vm.stopPrank(); + } + function _helperGetScheduleId(address superToken, address sender, address receiver) private pure From f1b20c9e8953d5f7a57b4113ad00afd234edd1cb Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Thu, 24 Apr 2025 13:14:27 +0300 Subject: [PATCH 50/59] add inheritdoc --- .../scheduler/contracts/VestingSchedulerV3.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index a83e945b40..11d59467d8 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -432,7 +432,8 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { success = true; } - + + /// @inheritdoc IVestingSchedulerV3 function endVestingScheduleNow(ISuperToken superToken, address receiver) external { address sender = _msgSender(); ScheduleAggregate memory agg = _getVestingScheduleAggregate(superToken, sender, receiver); From cee760ce22381444acf2c61609b8b578fb9259e8 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Thu, 24 Apr 2025 15:36:45 +0300 Subject: [PATCH 51/59] add contract-level natspec --- .../contracts/VestingSchedulerV3.sol | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol index 11d59467d8..4170be4fc2 100644 --- a/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol +++ b/packages/automation-contracts/scheduler/contracts/VestingSchedulerV3.sol @@ -19,6 +19,23 @@ import {IVestingSchedulerV3} from "./interface/IVestingSchedulerV3.sol"; using SuperTokenV1Library for ISuperToken; +/** + * @title Superfluid Vesting Scheduler (V3) + * @author Superfluid + * @notice Use precise time and amount based vesting schedules using Super Tokens and real-time continuous streaming. + * Optional features include: + * - Vesting cliffs + * - Receiver claiming + * - Updating schedules (increasing/decreasing vested amount, increasing/decreasing duration) + * @dev All token amounts are in wei; flow rates are wei per second; + * timestamps are Unix‐epoch seconds; durations/periods are in seconds. + * The contract uses ERC-20 allowance and Superfluid ACL flow operator permissions + * to automate the vesting on behalf of the sender. + * The contract is designed to be used with an off-chain automation to execute the vesting start and end. + * The start and end executions are permisionless. + * Execution delays are handled with token transfer compensations, but watch out for complete expiries! + * @custom:metadata The official addresses and subgraphs can be found from @superfluid-finance/metadata package. + */ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { // ____ __ __ // / __ \____ _/ /_____ _/ /___ ______ ___ _____ @@ -432,7 +449,7 @@ contract VestingSchedulerV3 is IVestingSchedulerV3, IRelayRecipient { success = true; } - + /// @inheritdoc IVestingSchedulerV3 function endVestingScheduleNow(ISuperToken superToken, address receiver) external { address sender = _msgSender(); From b35a057c35f87f4d2cc843ae63cb18246a241508 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Thu, 24 Apr 2025 15:44:42 +0300 Subject: [PATCH 52/59] remove address from metadata --- packages/metadata/main/networks/list.cjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/metadata/main/networks/list.cjs b/packages/metadata/main/networks/list.cjs index 4417bc3f1e..08e00d319a 100644 --- a/packages/metadata/main/networks/list.cjs +++ b/packages/metadata/main/networks/list.cjs @@ -109,7 +109,6 @@ module.exports = "flowScheduler": "0x73B1Ce21d03ad389C2A291B1d1dc4DAFE7B5Dc68", "vestingScheduler": "0x27444c0235a4D921F3106475faeba0B5e7ABDD7a", "vestingSchedulerV2": "0x3aa62b96f44D0f8892BeBBC819DE8e02E9DE69A8", - "vestingSchedulerV3": "0x4fEc5B896AF3AFFeE74fC6F25c476fF53aAEfCe1", "autowrap": { "manager": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", "wrapStrategy": "0xf232f1fd34CE12e24F4391865c2D6E374D2C34d9" From 63f5a1b754a039db7d75fc9a40163f2368434b06 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Thu, 24 Apr 2025 16:48:36 +0300 Subject: [PATCH 53/59] update metadata --- packages/automation-contracts/autowrap/package.json | 2 +- packages/automation-contracts/scheduler/package.json | 2 +- packages/ethereum-contracts/package.json | 2 +- packages/js-sdk/package.json | 2 +- packages/metadata/main/networks/list.cjs | 2 ++ packages/metadata/module/networks/list.d.ts | 1 + packages/metadata/module/networks/list.js | 2 ++ packages/metadata/networks.json | 2 ++ packages/metadata/package.json | 2 +- packages/sdk-core/package.json | 2 +- packages/subgraph/package.json | 2 +- 11 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/automation-contracts/autowrap/package.json b/packages/automation-contracts/autowrap/package.json index 4ff352eb98..f67209e93e 100644 --- a/packages/automation-contracts/autowrap/package.json +++ b/packages/automation-contracts/autowrap/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@openzeppelin/contracts": "^4.9.6", "@superfluid-finance/ethereum-contracts": "^1.12.1", - "@superfluid-finance/metadata": "^1.5.2" + "@superfluid-finance/metadata": "^1.6.0" }, "license": "MIT", "scripts": { diff --git a/packages/automation-contracts/scheduler/package.json b/packages/automation-contracts/scheduler/package.json index a12b64129b..c2c6ecf6b3 100644 --- a/packages/automation-contracts/scheduler/package.json +++ b/packages/automation-contracts/scheduler/package.json @@ -5,7 +5,7 @@ "devDependencies": { "@openzeppelin/contracts": "^4.9.6", "@superfluid-finance/ethereum-contracts": "^1.12.1", - "@superfluid-finance/metadata": "^1.5.2" + "@superfluid-finance/metadata": "^1.6.0" }, "license": "MIT", "scripts": { diff --git a/packages/ethereum-contracts/package.json b/packages/ethereum-contracts/package.json index eff4ccd362..04be34089e 100644 --- a/packages/ethereum-contracts/package.json +++ b/packages/ethereum-contracts/package.json @@ -18,7 +18,7 @@ "@safe-global/safe-service-client": "^2.0.3", "@safe-global/safe-web3-lib": "^1.9.4", "@superfluid-finance/js-sdk": "^0.6.3", - "@superfluid-finance/metadata": "^1.5.2", + "@superfluid-finance/metadata": "^1.6.0", "async": "^3.2.6", "csv-writer": "^1.6.0", "ethers": "^5.7.2", diff --git a/packages/js-sdk/package.json b/packages/js-sdk/package.json index 02c193b73e..36edba1e1b 100644 --- a/packages/js-sdk/package.json +++ b/packages/js-sdk/package.json @@ -7,7 +7,7 @@ "path": false }, "dependencies": { - "@superfluid-finance/metadata": "^1.5.2", + "@superfluid-finance/metadata": "^1.6.0", "@truffle/contract": "4.6.31", "auto-bind": "4.0.0", "node-fetch": "2.7.0" diff --git a/packages/metadata/main/networks/list.cjs b/packages/metadata/main/networks/list.cjs index 08e00d319a..b30f2cd63d 100644 --- a/packages/metadata/main/networks/list.cjs +++ b/packages/metadata/main/networks/list.cjs @@ -109,6 +109,7 @@ module.exports = "flowScheduler": "0x73B1Ce21d03ad389C2A291B1d1dc4DAFE7B5Dc68", "vestingScheduler": "0x27444c0235a4D921F3106475faeba0B5e7ABDD7a", "vestingSchedulerV2": "0x3aa62b96f44D0f8892BeBBC819DE8e02E9DE69A8", + "vestingSchedulerV3": "0x4F4BC2ca9A7CA26AfcFabc6A2A381c104927D72C", "autowrap": { "manager": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", "wrapStrategy": "0xf232f1fd34CE12e24F4391865c2D6E374D2C34d9" @@ -359,6 +360,7 @@ module.exports = "flowScheduler": "0x55c8fc400833eEa791087cF343Ff2409A39DeBcC", "vestingScheduler": "0x65377d4dfE9c01639A41952B5083D58964782892", "vestingSchedulerV2": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", + "vestingSchedulerV3": "0x5aB84e4B3a5F418c95B77DbdecFAF18D0Fd3b3E4", "autowrap": { "manager": "0x1fA76f2Cd0C3fe6c399A80111408d9C42C0CAC23", "wrapStrategy": "0x0Cf060a501c0040e9CCC708eFE94079F501c6Bb4" diff --git a/packages/metadata/module/networks/list.d.ts b/packages/metadata/module/networks/list.d.ts index e7bd38c80c..ba83bf6a47 100644 --- a/packages/metadata/module/networks/list.d.ts +++ b/packages/metadata/module/networks/list.d.ts @@ -16,6 +16,7 @@ interface ContractAddresses { readonly toga?: string; readonly vestingScheduler?: string; readonly vestingSchedulerV2?: string; + readonly vestingSchedulerV3?: string; readonly flowScheduler?: string; readonly batchLiquidator?: string; readonly superSpreader?: string; diff --git a/packages/metadata/module/networks/list.js b/packages/metadata/module/networks/list.js index 050bea99e8..5c85385431 100644 --- a/packages/metadata/module/networks/list.js +++ b/packages/metadata/module/networks/list.js @@ -109,6 +109,7 @@ export default "flowScheduler": "0x73B1Ce21d03ad389C2A291B1d1dc4DAFE7B5Dc68", "vestingScheduler": "0x27444c0235a4D921F3106475faeba0B5e7ABDD7a", "vestingSchedulerV2": "0x3aa62b96f44D0f8892BeBBC819DE8e02E9DE69A8", + "vestingSchedulerV3": "0x4F4BC2ca9A7CA26AfcFabc6A2A381c104927D72C", "autowrap": { "manager": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", "wrapStrategy": "0xf232f1fd34CE12e24F4391865c2D6E374D2C34d9" @@ -359,6 +360,7 @@ export default "flowScheduler": "0x55c8fc400833eEa791087cF343Ff2409A39DeBcC", "vestingScheduler": "0x65377d4dfE9c01639A41952B5083D58964782892", "vestingSchedulerV2": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", + "vestingSchedulerV3": "0x5aB84e4B3a5F418c95B77DbdecFAF18D0Fd3b3E4", "autowrap": { "manager": "0x1fA76f2Cd0C3fe6c399A80111408d9C42C0CAC23", "wrapStrategy": "0x0Cf060a501c0040e9CCC708eFE94079F501c6Bb4" diff --git a/packages/metadata/networks.json b/packages/metadata/networks.json index d4576df194..568474b968 100644 --- a/packages/metadata/networks.json +++ b/packages/metadata/networks.json @@ -107,6 +107,7 @@ "flowScheduler": "0x73B1Ce21d03ad389C2A291B1d1dc4DAFE7B5Dc68", "vestingScheduler": "0x27444c0235a4D921F3106475faeba0B5e7ABDD7a", "vestingSchedulerV2": "0x3aa62b96f44D0f8892BeBBC819DE8e02E9DE69A8", + "vestingSchedulerV3": "0x4F4BC2ca9A7CA26AfcFabc6A2A381c104927D72C", "autowrap": { "manager": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", "wrapStrategy": "0xf232f1fd34CE12e24F4391865c2D6E374D2C34d9" @@ -357,6 +358,7 @@ "flowScheduler": "0x55c8fc400833eEa791087cF343Ff2409A39DeBcC", "vestingScheduler": "0x65377d4dfE9c01639A41952B5083D58964782892", "vestingSchedulerV2": "0xe567b32C10B0dB72d9490eB1B9A409C5ADed192C", + "vestingSchedulerV3": "0x5aB84e4B3a5F418c95B77DbdecFAF18D0Fd3b3E4", "autowrap": { "manager": "0x1fA76f2Cd0C3fe6c399A80111408d9C42C0CAC23", "wrapStrategy": "0x0Cf060a501c0040e9CCC708eFE94079F501c6Bb4" diff --git a/packages/metadata/package.json b/packages/metadata/package.json index 904ed0084a..9420bedf4e 100644 --- a/packages/metadata/package.json +++ b/packages/metadata/package.json @@ -1,7 +1,7 @@ { "name": "@superfluid-finance/metadata", "description": "Superfluid Metadata", - "version": "1.5.2", + "version": "1.6.0", "author": "Superfluid", "bugs": "https://github.com/superfluid-finance/protocol-monorepo/issues", "homepage": "https://github.com/superfluid-finance/protocol-monorepo/tree/dev/packages/metadata#readme", diff --git a/packages/sdk-core/package.json b/packages/sdk-core/package.json index 15b1f1ca62..5ac7f5f434 100644 --- a/packages/sdk-core/package.json +++ b/packages/sdk-core/package.json @@ -5,7 +5,7 @@ "bugs": "https://github.com/superfluid-finance/protocol-monorepo/issues", "dependencies": { "@superfluid-finance/ethereum-contracts": "1.12.1", - "@superfluid-finance/metadata": "^1.5.2", + "@superfluid-finance/metadata": "^1.6.0", "graphql-request": "6.1.0", "lodash": "4.17.21", "tsify": "5.0.4" diff --git a/packages/subgraph/package.json b/packages/subgraph/package.json index 9ae9af6f61..aec3d1ff57 100644 --- a/packages/subgraph/package.json +++ b/packages/subgraph/package.json @@ -9,7 +9,7 @@ "mustache": "4.2.0" }, "devDependencies": { - "@superfluid-finance/metadata": "^1.5.2", + "@superfluid-finance/metadata": "^1.6.0", "coingecko-api": "^1.0.10", "graphql": "^16.9.0", "graphql-request": "^6.1.0", From 68a380e55569a0d7afce3394f02c0e8934fd32b2 Mon Sep 17 00:00:00 2001 From: didi Date: Thu, 24 Apr 2025 15:55:06 +0200 Subject: [PATCH 54/59] v3 deployments --- .../scheduler/hardhat.config.js | 30 ++++++++++++++++++- packages/metadata/main/networks/list.cjs | 6 ++++ packages/metadata/module/networks/list.js | 6 ++++ packages/metadata/networks.json | 6 ++++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/hardhat.config.js b/packages/automation-contracts/scheduler/hardhat.config.js index 8336b37f79..1caf0f1273 100644 --- a/packages/automation-contracts/scheduler/hardhat.config.js +++ b/packages/automation-contracts/scheduler/hardhat.config.js @@ -25,6 +25,20 @@ module.exports = { url: "http://127.0.0.1:8545/", chainId: 31337, }, + ethereum: { + url: process.env.ETHEREUM_URL || "", + accounts: + process.env.PRIVATE_KEY !== undefined + ? [process.env.PRIVATE_KEY] + : [], + }, + gnosis: { + url: process.env.GNOSIS_URL || "", + accounts: + process.env.PRIVATE_KEY !== undefined + ? [process.env.PRIVATE_KEY] + : [], + }, polygon: { url: process.env.POLYGON_URL || "", accounts: @@ -32,6 +46,13 @@ module.exports = { ? [process.env.PRIVATE_KEY] : [], }, + avalanche: { + url: process.env.AVALANCHE_URL || "", + accounts: + process.env.PRIVATE_KEY !== undefined + ? [process.env.PRIVATE_KEY] + : [], + }, bsc: { url: process.env.BSC_URL || "", accounts: @@ -46,6 +67,13 @@ module.exports = { ? [process.env.PRIVATE_KEY] : [], }, + arbitrum: { + url: process.env.ARBITRUM_URL, + accounts: + process.env.PRIVATE_KEY !== undefined + ? [process.env.PRIVATE_KEY] + : [], + }, opsepolia: { url: process.env.OPSEPOLIA_URL || "", accounts: @@ -53,7 +81,7 @@ module.exports = { ? [process.env.PRIVATE_KEY] : [], }, - "base-mainnet": { + base: { url: process.env.BASE_URL || "", accounts: process.env.PRIVATE_KEY !== undefined diff --git a/packages/metadata/main/networks/list.cjs b/packages/metadata/main/networks/list.cjs index b30f2cd63d..e3272f679b 100644 --- a/packages/metadata/main/networks/list.cjs +++ b/packages/metadata/main/networks/list.cjs @@ -238,6 +238,7 @@ module.exports = "batchLiquidator": "0x96C3C2d23d143301cF363a02cB7fe3596d2834d7", "flowScheduler": "0x9cC7fc484fF588926149577e9330fA5b2cA74336", "vestingScheduler": "0x0170FFCC75d178d426EBad5b1a31451d00Ddbd0D", + "vestingSchedulerV3": "0x625F04c9B91ECdfbeb7021271749212388F12c11", "wrapManager": "0x7a2899D179a8F205C8EDAd2e52954cA5f6d48D1A", "autowrap": { "manager": "0x8082e58681350876aFe8f52d3Bf8672034A03Db0", @@ -299,6 +300,7 @@ module.exports = "batchLiquidator": "0xA7afDc46999076C295cfC6812dd73d103cF64e19", "flowScheduler": "0x55F7758dd99d5e185f4CC08d4Ad95B71f598264D", "vestingScheduler": "0xcFE6382B33F2AdaFbE46e6A26A88E0182ae32b0c", + "vestingSchedulerV3": "0x488913833474bbD9B11f844FdC2f0897FAc0Ca43", "autowrap": { "manager": "0x2581c27E7f6D6AF452E63fCe884EDE3EDd716b32", "wrapStrategy": "0xb4afa36BAd8c76976Dc77a21c9Ad711EF720eE4b" @@ -421,6 +423,7 @@ module.exports = "batchLiquidator": "0x9224413b9177E6c1D5721B4a4D1D00eC84B07Ce7", "flowScheduler": "0x3fA8B653F9abf91428800C0ba0F8D145a71F97A1", "vestingScheduler": "0x55c8fc400833eEa791087cF343Ff2409A39DeBcC", + "vestingSchedulerV3": "0xc3069bDE869912E3d9B965F35D7764Fc92BccE67", "autowrap": { "manager": "0xf01825eAFAe5CD1Dab5593EFAF218efC8968D272", "wrapStrategy": "0x342076aA957B0ec8bC1d3893af719b288eA31e61" @@ -481,6 +484,7 @@ module.exports = "batchLiquidator": "0x3b387638a5d33aE8772715642A21345f23Af824c", "flowScheduler": "0xF7AfF590E9DE493D7ACb421Fca7f1E35C1ad4Ce5", "vestingScheduler": "0x3fA8B653F9abf91428800C0ba0F8D145a71F97A1", + "vestingSchedulerV3": "0xB84C98d9B51D0e32114C60C500e17eA79dfd0dAf", "autowrap": { "manager": "0x8082e58681350876aFe8f52d3Bf8672034A03Db0", "wrapStrategy": "0x51FBAbD31A615E14b1bC12E9d887f60997264a4E" @@ -541,6 +545,7 @@ module.exports = "batchLiquidator": "0x27636F8E129cdd4ccA0F30E2b4C116DDaC773bE5", "flowScheduler": "0x2f9e2A2A59405682d4F86779275CF5525AD7eC2B", "vestingScheduler": "0x9B91c27f78376383003C6A12Ad12B341d016C5b9", + "vestingSchedulerV3": "0xa032265Ee9dE740D36Af6eb90cf18775577B1Ef3", "autowrap": { "manager": "0x2AcdD61ac1EFFe1535109449c31889bdE8d7f325", "wrapStrategy": "0x9e308cb079ae130790F604b1030cDf386670f199" @@ -700,6 +705,7 @@ module.exports = "flowScheduler": "0xC72CEd15204d02183c83fEbb918b183E400811Ee", "vestingScheduler": "0xDF92D0E6Bcb9385FDe99aD21Ff5e47Fb47E3c6b2", "vestingSchedulerV2": "0x7b77A34b8B76B66E97a5Ae01aD052205d5cbe257", + "vestingSchedulerV3": "0x6Bf35A170056eDf9aEba159dce4a640cfCef9312", "autowrap": { "manager": "0x5D0acD0864Ad07ba4E1E0474AE69Da87482e14A9", "wrapStrategy": "0xB29005319B0caB24cF6D4d24e8420E54BB29Cb0d" diff --git a/packages/metadata/module/networks/list.js b/packages/metadata/module/networks/list.js index 5c85385431..092920459c 100644 --- a/packages/metadata/module/networks/list.js +++ b/packages/metadata/module/networks/list.js @@ -238,6 +238,7 @@ export default "batchLiquidator": "0x96C3C2d23d143301cF363a02cB7fe3596d2834d7", "flowScheduler": "0x9cC7fc484fF588926149577e9330fA5b2cA74336", "vestingScheduler": "0x0170FFCC75d178d426EBad5b1a31451d00Ddbd0D", + "vestingSchedulerV3": "0x625F04c9B91ECdfbeb7021271749212388F12c11", "wrapManager": "0x7a2899D179a8F205C8EDAd2e52954cA5f6d48D1A", "autowrap": { "manager": "0x8082e58681350876aFe8f52d3Bf8672034A03Db0", @@ -299,6 +300,7 @@ export default "batchLiquidator": "0xA7afDc46999076C295cfC6812dd73d103cF64e19", "flowScheduler": "0x55F7758dd99d5e185f4CC08d4Ad95B71f598264D", "vestingScheduler": "0xcFE6382B33F2AdaFbE46e6A26A88E0182ae32b0c", + "vestingSchedulerV3": "0x488913833474bbD9B11f844FdC2f0897FAc0Ca43", "autowrap": { "manager": "0x2581c27E7f6D6AF452E63fCe884EDE3EDd716b32", "wrapStrategy": "0xb4afa36BAd8c76976Dc77a21c9Ad711EF720eE4b" @@ -421,6 +423,7 @@ export default "batchLiquidator": "0x9224413b9177E6c1D5721B4a4D1D00eC84B07Ce7", "flowScheduler": "0x3fA8B653F9abf91428800C0ba0F8D145a71F97A1", "vestingScheduler": "0x55c8fc400833eEa791087cF343Ff2409A39DeBcC", + "vestingSchedulerV3": "0xc3069bDE869912E3d9B965F35D7764Fc92BccE67", "autowrap": { "manager": "0xf01825eAFAe5CD1Dab5593EFAF218efC8968D272", "wrapStrategy": "0x342076aA957B0ec8bC1d3893af719b288eA31e61" @@ -481,6 +484,7 @@ export default "batchLiquidator": "0x3b387638a5d33aE8772715642A21345f23Af824c", "flowScheduler": "0xF7AfF590E9DE493D7ACb421Fca7f1E35C1ad4Ce5", "vestingScheduler": "0x3fA8B653F9abf91428800C0ba0F8D145a71F97A1", + "vestingSchedulerV3": "0xB84C98d9B51D0e32114C60C500e17eA79dfd0dAf", "autowrap": { "manager": "0x8082e58681350876aFe8f52d3Bf8672034A03Db0", "wrapStrategy": "0x51FBAbD31A615E14b1bC12E9d887f60997264a4E" @@ -541,6 +545,7 @@ export default "batchLiquidator": "0x27636F8E129cdd4ccA0F30E2b4C116DDaC773bE5", "flowScheduler": "0x2f9e2A2A59405682d4F86779275CF5525AD7eC2B", "vestingScheduler": "0x9B91c27f78376383003C6A12Ad12B341d016C5b9", + "vestingSchedulerV3": "0xa032265Ee9dE740D36Af6eb90cf18775577B1Ef3", "autowrap": { "manager": "0x2AcdD61ac1EFFe1535109449c31889bdE8d7f325", "wrapStrategy": "0x9e308cb079ae130790F604b1030cDf386670f199" @@ -700,6 +705,7 @@ export default "flowScheduler": "0xC72CEd15204d02183c83fEbb918b183E400811Ee", "vestingScheduler": "0xDF92D0E6Bcb9385FDe99aD21Ff5e47Fb47E3c6b2", "vestingSchedulerV2": "0x7b77A34b8B76B66E97a5Ae01aD052205d5cbe257", + "vestingSchedulerV3": "0x6Bf35A170056eDf9aEba159dce4a640cfCef9312", "autowrap": { "manager": "0x5D0acD0864Ad07ba4E1E0474AE69Da87482e14A9", "wrapStrategy": "0xB29005319B0caB24cF6D4d24e8420E54BB29Cb0d" diff --git a/packages/metadata/networks.json b/packages/metadata/networks.json index 568474b968..c59ed11c1f 100644 --- a/packages/metadata/networks.json +++ b/packages/metadata/networks.json @@ -236,6 +236,7 @@ "batchLiquidator": "0x96C3C2d23d143301cF363a02cB7fe3596d2834d7", "flowScheduler": "0x9cC7fc484fF588926149577e9330fA5b2cA74336", "vestingScheduler": "0x0170FFCC75d178d426EBad5b1a31451d00Ddbd0D", + "vestingSchedulerV3": "0x625F04c9B91ECdfbeb7021271749212388F12c11", "wrapManager": "0x7a2899D179a8F205C8EDAd2e52954cA5f6d48D1A", "autowrap": { "manager": "0x8082e58681350876aFe8f52d3Bf8672034A03Db0", @@ -297,6 +298,7 @@ "batchLiquidator": "0xA7afDc46999076C295cfC6812dd73d103cF64e19", "flowScheduler": "0x55F7758dd99d5e185f4CC08d4Ad95B71f598264D", "vestingScheduler": "0xcFE6382B33F2AdaFbE46e6A26A88E0182ae32b0c", + "vestingSchedulerV3": "0x488913833474bbD9B11f844FdC2f0897FAc0Ca43", "autowrap": { "manager": "0x2581c27E7f6D6AF452E63fCe884EDE3EDd716b32", "wrapStrategy": "0xb4afa36BAd8c76976Dc77a21c9Ad711EF720eE4b" @@ -419,6 +421,7 @@ "batchLiquidator": "0x9224413b9177E6c1D5721B4a4D1D00eC84B07Ce7", "flowScheduler": "0x3fA8B653F9abf91428800C0ba0F8D145a71F97A1", "vestingScheduler": "0x55c8fc400833eEa791087cF343Ff2409A39DeBcC", + "vestingSchedulerV3": "0xc3069bDE869912E3d9B965F35D7764Fc92BccE67", "autowrap": { "manager": "0xf01825eAFAe5CD1Dab5593EFAF218efC8968D272", "wrapStrategy": "0x342076aA957B0ec8bC1d3893af719b288eA31e61" @@ -479,6 +482,7 @@ "batchLiquidator": "0x3b387638a5d33aE8772715642A21345f23Af824c", "flowScheduler": "0xF7AfF590E9DE493D7ACb421Fca7f1E35C1ad4Ce5", "vestingScheduler": "0x3fA8B653F9abf91428800C0ba0F8D145a71F97A1", + "vestingSchedulerV3": "0xB84C98d9B51D0e32114C60C500e17eA79dfd0dAf", "autowrap": { "manager": "0x8082e58681350876aFe8f52d3Bf8672034A03Db0", "wrapStrategy": "0x51FBAbD31A615E14b1bC12E9d887f60997264a4E" @@ -539,6 +543,7 @@ "batchLiquidator": "0x27636F8E129cdd4ccA0F30E2b4C116DDaC773bE5", "flowScheduler": "0x2f9e2A2A59405682d4F86779275CF5525AD7eC2B", "vestingScheduler": "0x9B91c27f78376383003C6A12Ad12B341d016C5b9", + "vestingSchedulerV3": "0xa032265Ee9dE740D36Af6eb90cf18775577B1Ef3", "autowrap": { "manager": "0x2AcdD61ac1EFFe1535109449c31889bdE8d7f325", "wrapStrategy": "0x9e308cb079ae130790F604b1030cDf386670f199" @@ -698,6 +703,7 @@ "flowScheduler": "0xC72CEd15204d02183c83fEbb918b183E400811Ee", "vestingScheduler": "0xDF92D0E6Bcb9385FDe99aD21Ff5e47Fb47E3c6b2", "vestingSchedulerV2": "0x7b77A34b8B76B66E97a5Ae01aD052205d5cbe257", + "vestingSchedulerV3": "0x6Bf35A170056eDf9aEba159dce4a640cfCef9312", "autowrap": { "manager": "0x5D0acD0864Ad07ba4E1E0474AE69Da87482e14A9", "wrapStrategy": "0xB29005319B0caB24cF6D4d24e8420E54BB29Cb0d" From 13991dfed21a0ad9dcf817114ab006aa3e2d533d Mon Sep 17 00:00:00 2001 From: didi Date: Thu, 24 Apr 2025 17:50:04 +0200 Subject: [PATCH 55/59] added eth-mainnet --- packages/automation-contracts/scheduler/hardhat.config.js | 2 +- packages/metadata/main/networks/list.cjs | 1 + packages/metadata/module/networks/list.js | 1 + packages/metadata/networks.json | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/hardhat.config.js b/packages/automation-contracts/scheduler/hardhat.config.js index 1caf0f1273..8db0889983 100644 --- a/packages/automation-contracts/scheduler/hardhat.config.js +++ b/packages/automation-contracts/scheduler/hardhat.config.js @@ -61,7 +61,7 @@ module.exports = { : [], }, optimism: { - url: "https://mainnet.optimism.io", + url: process.env.OPTIMISM_URL || "https://mainnet.optimism.io", accounts: process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] diff --git a/packages/metadata/main/networks/list.cjs b/packages/metadata/main/networks/list.cjs index e3272f679b..86f8f4e37f 100644 --- a/packages/metadata/main/networks/list.cjs +++ b/packages/metadata/main/networks/list.cjs @@ -606,6 +606,7 @@ module.exports = "batchLiquidator": "0x42B709822F18595443c308c1BE5E63CbFEf06481", "flowScheduler": "0xAA0cD305eD020137E302CeCede7b18c0A05aCCDA", "vestingScheduler": "0x39D5cBBa9adEBc25085a3918d36D5325546C001B", + "vestingSchedulerV3": "0xbeEDf563D41dcb3e1b7e0B0f7a86685Fd73Ce84C", "autowrap": { "manager": "0x30aE282CF477E2eF28B14d0125aCEAd57Fe1d7a1", "wrapStrategy": "0x1D65c6d3AD39d454Ea8F682c49aE7744706eA96d" diff --git a/packages/metadata/module/networks/list.js b/packages/metadata/module/networks/list.js index 092920459c..d67285e5e2 100644 --- a/packages/metadata/module/networks/list.js +++ b/packages/metadata/module/networks/list.js @@ -606,6 +606,7 @@ export default "batchLiquidator": "0x42B709822F18595443c308c1BE5E63CbFEf06481", "flowScheduler": "0xAA0cD305eD020137E302CeCede7b18c0A05aCCDA", "vestingScheduler": "0x39D5cBBa9adEBc25085a3918d36D5325546C001B", + "vestingSchedulerV3": "0xbeEDf563D41dcb3e1b7e0B0f7a86685Fd73Ce84C", "autowrap": { "manager": "0x30aE282CF477E2eF28B14d0125aCEAd57Fe1d7a1", "wrapStrategy": "0x1D65c6d3AD39d454Ea8F682c49aE7744706eA96d" diff --git a/packages/metadata/networks.json b/packages/metadata/networks.json index c59ed11c1f..7522b187ef 100644 --- a/packages/metadata/networks.json +++ b/packages/metadata/networks.json @@ -604,6 +604,7 @@ "batchLiquidator": "0x42B709822F18595443c308c1BE5E63CbFEf06481", "flowScheduler": "0xAA0cD305eD020137E302CeCede7b18c0A05aCCDA", "vestingScheduler": "0x39D5cBBa9adEBc25085a3918d36D5325546C001B", + "vestingSchedulerV3": "0xbeEDf563D41dcb3e1b7e0B0f7a86685Fd73Ce84C", "autowrap": { "manager": "0x30aE282CF477E2eF28B14d0125aCEAd57Fe1d7a1", "wrapStrategy": "0x1D65c6d3AD39d454Ea8F682c49aE7744706eA96d" From ce36bf40dbc55d7becd5bafd1e6e0721264c7b1c Mon Sep 17 00:00:00 2001 From: didi Date: Fri, 25 Apr 2025 12:34:44 +0200 Subject: [PATCH 56/59] include grapqhl in root devDependencies to fix yarn install getting stuck --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b2305f51f9..dc0dc21352 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ "typechain": "^8.3.2", "typedoc": "^0.26.6", "typescript": "^5.5.4", - "web3": "^1.10.4" + "web3": "^1.10.4", + "graphql": "^16.9.0" }, "license": "MIT", "npmClient": "yarn", From 43aeabd8176f1a68a3d0e49cef55bad50e9408c3 Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Wed, 7 May 2025 10:05:15 +0300 Subject: [PATCH 57/59] yarn deduplicate --- yarn.lock | 2213 +++-------------------------------------------------- 1 file changed, 124 insertions(+), 2089 deletions(-) diff --git a/yarn.lock b/yarn.lock index 584c5423e4..5176939285 100644 --- a/yarn.lock +++ b/yarn.lock @@ -153,30 +153,7 @@ dependencies: node-fetch "^2.6.1" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" - integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== - dependencies: - "@babel/highlight" "^7.22.10" - chalk "^2.4.2" - -"@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/code-frame@^7.22.13": - version "7.22.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" - integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== - dependencies: - "@babel/highlight" "^7.22.13" - chalk "^2.4.2" - -"@babel/code-frame@^7.24.7": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== @@ -184,64 +161,12 @@ "@babel/highlight" "^7.24.7" picocolors "^1.0.0" -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" - integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== - -"@babel/compat-data@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" - integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== - -"@babel/compat-data@^7.25.2": +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.25.2": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.4.tgz#7d2a80ce229890edcf4cc259d4d696cb4dae2fcb" integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ== -"@babel/core@^7.14.0": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" - integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-module-transforms" "^7.21.2" - "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.3" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.3" - "@babel/types" "^7.21.3" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - -"@babel/core@^7.22.9": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" - integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" - "@babel/helper-compilation-targets" "^7.22.10" - "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.10" - "@babel/parser" "^7.22.10" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.1" - -"@babel/core@^7.23.9": +"@babel/core@^7.14.0", "@babel/core@^7.22.9", "@babel/core@^7.23.9": version "7.25.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77" integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA== @@ -262,37 +187,7 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" - integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== - dependencies: - "@babel/types" "^7.21.3" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" - integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== - dependencies: - "@babel/types" "^7.22.10" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.0.tgz#df5c386e2218be505b34837acbcb874d7a983420" - integrity sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g== - dependencies: - "@babel/types" "^7.23.0" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.25.0", "@babel/generator@^7.25.4": +"@babel/generator@^7.14.0", "@babel/generator@^7.18.13", "@babel/generator@^7.25.0", "@babel/generator@^7.25.4": version "7.25.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.5.tgz#b31cf05b3fe8c32d206b6dad03bb0aacbde73450" integrity sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w== @@ -309,29 +204,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" - -"@babel/helper-compilation-targets@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" - integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== - dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.5" - browserslist "^4.21.9" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-compilation-targets@^7.25.2": +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.25.2": version "7.25.2" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c" integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw== @@ -369,29 +242,11 @@ semver "^6.1.2" "@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== - "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - -"@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== @@ -399,13 +254,6 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.23.0" -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - "@babel/helper-member-expression-to-functions@^7.20.7", "@babel/helper-member-expression-to-functions@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5" @@ -413,21 +261,7 @@ dependencies: "@babel/types" "^7.21.0" -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-imports@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" - integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-imports@^7.24.7": +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== @@ -435,32 +269,7 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/helper-module-transforms@^7.21.2": - version "7.21.2" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2" - integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.20.2" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.2" - "@babel/types" "^7.21.2" - -"@babel/helper-module-transforms@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" - integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.5" - -"@babel/helper-module-transforms@^7.25.2": +"@babel/helper-module-transforms@^7.21.2", "@babel/helper-module-transforms@^7.25.2": version "7.25.2" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6" integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ== @@ -477,12 +286,7 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" - integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== - -"@babel/helper-plugin-utils@^7.22.5": +"@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== @@ -499,21 +303,7 @@ "@babel/traverse" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/helper-simple-access@^7.20.2": - version "7.20.2" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz#0ab452687fe0c2cfb1e2b9e0015de07fc2d62dd9" - integrity sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA== - dependencies: - "@babel/types" "^7.20.2" - -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-simple-access@^7.24.7": +"@babel/helper-simple-access@^7.20.2", "@babel/helper-simple-access@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== @@ -529,87 +319,27 @@ "@babel/types" "^7.20.0" "@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.19.4": - version "7.19.4" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63" - integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw== - -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - "@babel/helper-string-parser@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== -"@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - -"@babel/helper-validator-identifier@^7.22.20": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" - integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== - -"@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== - "@babel/helper-validator-identifier@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== -"@babel/helper-validator-option@^7.18.6": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== - -"@babel/helper-validator-option@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" - integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== - "@babel/helper-validator-option@^7.24.8": version "7.24.8" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== -"@babel/helpers@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e" - integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.0" - "@babel/types" "^7.21.0" - -"@babel/helpers@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" - integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== - dependencies: - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" - "@babel/helpers@^7.25.0": version "7.25.0" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.25.0.tgz#e69beb7841cb93a6505531ede34f34e6a073650a" @@ -618,24 +348,6 @@ "@babel/template" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/highlight@^7.18.6", "@babel/highlight@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" - integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== - dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@babel/highlight@^7.22.13": - version "7.22.20" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.20.tgz#4ca92b71d80554b01427815e06f2df965b9c1f54" - integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== - dependencies: - "@babel/helper-validator-identifier" "^7.22.20" - chalk "^2.4.2" - js-tokens "^4.0.0" - "@babel/highlight@^7.24.7": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" @@ -646,22 +358,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" - integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== - -"@babel/parser@^7.22.10", "@babel/parser@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" - integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== - -"@babel/parser@^7.22.15", "@babel/parser@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.0.tgz#da950e622420bf96ca0d0f2909cdddac3acd8719" - integrity sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw== - -"@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.4": +"@babel/parser@^7.14.0", "@babel/parser@^7.16.8", "@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.4": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.4.tgz#af4f2df7d02440286b7de57b1c21acfb2a6f257a" integrity sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA== @@ -901,34 +598,7 @@ dependencies: regenerator-runtime "^0.13.11" -"@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8" - integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.20.7" - "@babel/types" "^7.20.7" - -"@babel/template@^7.22.15": - version "7.22.15" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" - integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/parser" "^7.22.15" - "@babel/types" "^7.22.15" - -"@babel/template@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== - dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/template@^7.25.0": +"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.22.15", "@babel/template@^7.25.0": version "7.25.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== @@ -937,23 +607,7 @@ "@babel/parser" "^7.25.0" "@babel/types" "^7.25.0" -"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.22.10": - version "7.23.2" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.2.tgz#329c7a06735e144a506bdb2cad0268b7f46f4ad8" - integrity sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw== - dependencies: - "@babel/code-frame" "^7.22.13" - "@babel/generator" "^7.23.0" - "@babel/helper-environment-visitor" "^7.22.20" - "@babel/helper-function-name" "^7.23.0" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.23.0" - "@babel/types" "^7.23.0" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2": +"@babel/traverse@^7.14.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.20.7", "@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.4.tgz#648678046990f2957407e3086e97044f13c3e18e" integrity sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg== @@ -966,34 +620,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" - integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== - dependencies: - "@babel/helper-string-parser" "^7.19.4" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.10", "@babel/types@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" - integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - to-fast-properties "^2.0.0" - -"@babel/types@^7.22.15", "@babel/types@^7.23.0": - version "7.23.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.0.tgz#8c1f020c9df0e737e4e247c0619f58c68458aaeb" - integrity sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.20" - to-fast-properties "^2.0.0" - -"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.4": +"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.13", "@babel/types@^7.18.6", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.4": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.4.tgz#6bcb46c72fdf1012a209d016c07f769e10adcb5f" integrity sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ== @@ -1105,14 +732,7 @@ resolved "https://registry.yarnpkg.com/@ensdomains/resolver/-/resolver-0.2.4.tgz#c10fe28bf5efbf49bff4666d909aed0265efbc89" integrity sha512-bvaTH34PMCbv6anRa9I/0zjLJgY4EuznbEMgbV77JBCQ9KNC46rzi0avuxpOfu+xDjPEtSFGqVEOr5GlUSGudA== -"@eslint-community/eslint-utils@^4.2.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" - integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/eslint-utils@^4.4.0": +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== @@ -1124,26 +744,6 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== -"@eslint-community/regexpp@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" - integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== - -"@eslint/eslintrc@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" - integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.5.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - "@eslint/eslintrc@^2.1.4": version "2.1.4" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" @@ -1159,11 +759,6 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" - integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== - "@eslint/js@8.57.0": version "8.57.0" resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" @@ -2268,25 +1863,11 @@ debug "^4.3.1" minimatch "^3.0.5" -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.5" - "@humanwhocodes/module-importer@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - "@humanwhocodes/object-schema@^2.0.2": version "2.0.3" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" @@ -2380,16 +1961,7 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/gen-mapping@^0.3.5": +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.5": version "0.3.5" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== @@ -2398,22 +1970,12 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/resolve-uri@3.1.0", "@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/set-array@^1.2.1": +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== @@ -2426,12 +1988,7 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -2444,23 +2001,7 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - -"@jridgewell/trace-mapping@^0.3.20": - version "0.3.22" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" - integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25", "@jridgewell/trace-mapping@^0.3.9": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== @@ -2906,14 +2447,7 @@ treeverse "^3.0.0" walk-up-path "^3.0.1" -"@npmcli/fs@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" - integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== - dependencies: - semver "^7.3.5" - -"@npmcli/fs@^3.1.1": +"@npmcli/fs@^3.1.0", "@npmcli/fs@^3.1.1": version "3.1.1" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.1.tgz#59cdaa5adca95d135fc00f2bb53f5771575ce726" integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== @@ -2935,15 +2469,7 @@ semver "^7.3.5" which "^4.0.0" -"@npmcli/installed-package-contents@^2.0.1": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz#bfd817eccd9e8df200919e73f57f9e3d9e4f9e33" - integrity sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ== - dependencies: - npm-bundled "^3.0.0" - npm-normalize-package-bin "^3.0.0" - -"@npmcli/installed-package-contents@^2.1.0": +"@npmcli/installed-package-contents@^2.0.1", "@npmcli/installed-package-contents@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz#63048e5f6e40947a3a88dcbcb4fd9b76fdd37c17" integrity sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w== @@ -3587,14 +3113,7 @@ web3-utils "^1.8.1" zksync-web3 "^0.14.3" -"@safe-global/safe-deployments@^1.20.2": - version "1.23.0" - resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.23.0.tgz#2a5650c98941a2925eef62d5c4b5569b6ad76069" - integrity sha512-8kyARY3DHZrAnig3LaM6AKoQtSvkhnKpyZ3jsxNCdnNb38DVuMXWcKA63UdZMWSnKJfkieVXGhof2Kt5cUHTEw== - dependencies: - semver "^7.3.7" - -"@safe-global/safe-deployments@^1.25.0": +"@safe-global/safe-deployments@^1.20.2", "@safe-global/safe-deployments@^1.25.0": version "1.26.0" resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.26.0.tgz#b83615b3b5a66e736e08f8ecf2801ed988e9e007" integrity sha512-Tw89O4/paT19ieMoiWQbqRApb0Bef/DxweS9rxodXAM5EQModkbyFXGZca+YxXE67sLvWjLr2jJUOxwze8mhGw== @@ -3852,7 +3371,7 @@ dependencies: defer-to-connect "^2.0.1" -"@truffle/abi-utils@^0.3.0", "@truffle/abi-utils@^0.3.9": +"@truffle/abi-utils@^0.3.0": version "0.3.9" resolved "https://registry.yarnpkg.com/@truffle/abi-utils/-/abi-utils-0.3.9.tgz#c476f5cfe01072b513b3e93fd7bea05cf7bd9d96" integrity sha512-G5dqgwRHx5zwlXjz3QT8OJVfB2cOqWwD6DwKso0KttUt/zejhCjnkKq72rSgyeLMkz7wBB9ERLOsupLBILM8MA== @@ -3870,12 +3389,7 @@ fast-check "3.1.1" web3-utils "1.10.0" -"@truffle/blockchain-utils@^0.1.3", "@truffle/blockchain-utils@^0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.1.7.tgz#cf7923a3ae5b591ae4c2a5ee45994a310ccaf1ee" - integrity sha512-1nibqGjEHC7KAyDThEFvbm2+EO8zAHee/VjCtxkYBE3ySwP50joh0QCEBjy7K/9z+icpMoDucfxmgaKToBFUgQ== - -"@truffle/blockchain-utils@^0.1.9": +"@truffle/blockchain-utils@^0.1.3", "@truffle/blockchain-utils@^0.1.9": version "0.1.9" resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.1.9.tgz#d9b55bd23a134578e4217bae55a6dfbbb038d6dc" integrity sha512-RHfumgbIVo68Rv9ofDYfynjnYZIfP/f1vZy4RoqkfYAO+fqfc58PDRzB1WAGq2U6GPuOnipOJxQhnqNnffORZg== @@ -3887,22 +3401,6 @@ dependencies: cbor "^5.2.0" -"@truffle/codec@^0.14.16": - version "0.14.16" - resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.14.16.tgz#eebfb169e46709d6cee53f069a53fc1dff410eaa" - integrity sha512-a9UY3n/FnkKN3Q4zOuMFOOcLWb80mdknj+voim4vvXYtJm1aAZQZE5sG9aLnMBTl4TiGLzUtfNDVYY7WgWgDag== - dependencies: - "@truffle/abi-utils" "^0.3.9" - "@truffle/compile-common" "^0.9.4" - big.js "^6.0.3" - bn.js "^5.1.3" - cbor "^5.2.0" - debug "^4.3.1" - lodash "^4.17.21" - semver "7.3.7" - utf8 "^3.0.0" - web3-utils "1.8.2" - "@truffle/codec@^0.17.3": version "0.17.3" resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.17.3.tgz#94057e56e1a947594b35eba498d96915df3861d2" @@ -3919,15 +3417,7 @@ utf8 "^3.0.0" web3-utils "1.10.0" -"@truffle/compile-common@^0.9.4": - version "0.9.4" - resolved "https://registry.yarnpkg.com/@truffle/compile-common/-/compile-common-0.9.4.tgz#064208cda70491692b538f717809bb904a122c20" - integrity sha512-mnqJB/hLiPHNf+WKwt/2MH6lv34xSG/SFCib7+ckAklutUqVLeFo8EwQxinuHNkU7LY0C+YgZXhK1WTCO5YRJQ== - dependencies: - "@truffle/error" "^0.2.0" - colors "1.4.0" - -"@truffle/compile-common@^0.9.8": +"@truffle/compile-common@^0.9.4", "@truffle/compile-common@^0.9.8": version "0.9.8" resolved "https://registry.yarnpkg.com/@truffle/compile-common/-/compile-common-0.9.8.tgz#f91507c895852289a17bf401eefebc293c4c69f0" integrity sha512-DTpiyo32t/YhLI1spn84D3MHYHrnoVqO+Gp7ZHrYNwDs86mAxtNiH5lsVzSb8cPgiqlvNsRCU9nm9R0YmKMTBQ== @@ -3957,21 +3447,7 @@ semver "7.3.7" solc "0.8.19" -"@truffle/config@^1.3.54": - version "1.3.54" - resolved "https://registry.yarnpkg.com/@truffle/config/-/config-1.3.54.tgz#2bc72c08b5efd43e526406591132ef1e2faec860" - integrity sha512-sCFIRqBkxanuYueMQalp4q/1+wxYq5IdAZSJFUXK5FbvhDGU437bl1MuMxGDxhjztf0ZN49YsELAjYMVzOGpUQ== - dependencies: - "@truffle/error" "^0.2.0" - "@truffle/events" "^0.1.22" - "@truffle/provider" "^0.3.7" - conf "^10.1.2" - debug "^4.3.1" - find-up "^2.1.0" - lodash "^4.17.21" - original-require "^1.0.1" - -"@truffle/config@^1.3.61": +"@truffle/config@^1.3.54", "@truffle/config@^1.3.61": version "1.3.61" resolved "https://registry.yarnpkg.com/@truffle/config/-/config-1.3.61.tgz#912d11eb03720b6b8cb79979aa56b85215075aec" integrity sha512-L4uyG47V+k0NrSoVJ9D+hp2jcMstihW1QlNuXiu5g3mU24BjrozlJT34DFkczh/TtRceLjdrQJKA8WJCMICutw== @@ -3985,15 +3461,7 @@ lodash "^4.17.21" original-require "^1.0.1" -"@truffle/contract-schema@^3.3.1", "@truffle/contract-schema@^3.4.13", "@truffle/contract-schema@^3.4.7": - version "3.4.13" - resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.13.tgz#48447673f29380830f5821e8139ceefbbd545aac" - integrity sha512-emG7upuryYFrsPDbHqeASPWXL824M1tinhQwSPG0phSoa3g+RX9fUNNN/VPmF3tSkXLWUMhRnb7ehxnaCuRbZg== - dependencies: - ajv "^6.10.0" - debug "^4.3.1" - -"@truffle/contract-schema@^3.4.16": +"@truffle/contract-schema@^3.3.1", "@truffle/contract-schema@^3.4.16", "@truffle/contract-schema@^3.4.7": version "3.4.16" resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.4.16.tgz#c529c3f230db407b2f03290373b20b7366f2d37e" integrity sha512-g0WNYR/J327DqtJPI70ubS19K1Fth/1wxt2jFqLsPmz5cGZVjCwuhiie+LfBde4/Mc9QR8G+L3wtmT5cyoBxAg== @@ -4009,7 +3477,7 @@ debug "^4.3.1" glob "^7.1.6" -"@truffle/contract@4.6.31": +"@truffle/contract@4.6.31", "@truffle/contract@^4.0.35", "@truffle/contract@^4.6.18": version "4.6.31" resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.6.31.tgz#75cb059689ce73b365675d9650718908c01b6b58" integrity sha512-s+oHDpXASnZosiCdzu+X1Tx5mUJUs1L1CYXIcgRmzMghzqJkaUFmR6NpNo7nJYliYbO+O9/aW8oCKqQ7rCHfmQ== @@ -4029,41 +3497,6 @@ web3-eth-abi "1.10.0" web3-utils "1.10.0" -"@truffle/contract@^4.0.35", "@truffle/contract@^4.6.18": - version "4.6.18" - resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.6.18.tgz#096f82dbc05060acc9ed0bd8bb5811f497b8e3ad" - integrity sha512-x49EWZI16VMdYV8pH2LYM1AMFM3xAZ6ZFT2dG9Y71nIDZHdh+HKdlPSL40CqFtzpeoEk9UQoSJL99D/DXtpaog== - dependencies: - "@ensdomains/ensjs" "^2.1.0" - "@truffle/blockchain-utils" "^0.1.7" - "@truffle/contract-schema" "^3.4.13" - "@truffle/debug-utils" "^6.0.47" - "@truffle/error" "^0.2.0" - "@truffle/interface-adapter" "^0.5.31" - bignumber.js "^7.2.1" - debug "^4.3.1" - ethers "^4.0.32" - web3 "1.8.2" - web3-core-helpers "1.8.2" - web3-core-promievent "1.8.2" - web3-eth-abi "1.8.2" - web3-utils "1.8.2" - -"@truffle/dashboard-message-bus-client@^0.1.10": - version "0.1.10" - resolved "https://registry.yarnpkg.com/@truffle/dashboard-message-bus-client/-/dashboard-message-bus-client-0.1.10.tgz#bd1cef19956f06716d55a327b8ea6f983e41f0b0" - integrity sha512-r9GpdR96T8xzk2Z3Qq5lowixT6hQwDZ9F3D3oNjOv2AOwBrC7dGkt1Ra1FQRsABn4K7LUVvnjjn6rALlsatAdw== - dependencies: - "@truffle/dashboard-message-bus-common" "^0.1.5" - "@truffle/promise-tracker" "^0.1.5" - axios "1.2.4" - debug "^4.3.1" - delay "^5.0.0" - isomorphic-ws "^4.0.1" - node-abort-controller "^3.0.1" - tiny-typed-emitter "^2.1.0" - ws "^7.2.0" - "@truffle/dashboard-message-bus-client@^0.1.12": version "0.1.12" resolved "https://registry.yarnpkg.com/@truffle/dashboard-message-bus-client/-/dashboard-message-bus-client-0.1.12.tgz#160bf0ae888efee8a45425232d296630f83fe6af" @@ -4079,11 +3512,6 @@ tiny-typed-emitter "^2.1.0" ws "^7.2.0" -"@truffle/dashboard-message-bus-common@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@truffle/dashboard-message-bus-common/-/dashboard-message-bus-common-0.1.5.tgz#180c8c421b2836b7bd109944e7956f427236d8ab" - integrity sha512-F4RfXi7ymNA3HFOlaujRJxAb3I8ciErCRQq+MZVaqjSPF9LSw23IizZsGpLaY43K2bGoBSxyNQRZWxsUEBujPQ== - "@truffle/dashboard-message-bus-common@^0.1.7": version "0.1.7" resolved "https://registry.yarnpkg.com/@truffle/dashboard-message-bus-common/-/dashboard-message-bus-common-0.1.7.tgz#dc7b76e18845788429929a97a416c1e6e37580dc" @@ -4121,24 +3549,12 @@ pouchdb-find "^7.0.0" web3-utils "1.10.0" -"@truffle/debug-utils@^6.0.22", "@truffle/debug-utils@^6.0.47": - version "6.0.47" - resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-6.0.47.tgz#975f95be8d8a8eb11b35c7a8255b0a12ceaa51ba" - integrity sha512-bUjdzLPdEKtoUCDzaXkrOoi+PbyAJlMBzGequBK8tirT7xL9bCP2Pd/WxvnmRd7AnfroxGNvXwVXWTItW5SMWQ== +"@truffle/debug-utils@^6.0.22", "@truffle/debug-utils@^6.0.57": + version "6.0.57" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-6.0.57.tgz#4e9a1051221c5f467daa398b0ca638d8b6408a82" + integrity sha512-Q6oI7zLaeNLB69ixjwZk2UZEWBY6b2OD1sjLMGDKBGR7GaHYiw96GLR2PFgPH1uwEeLmV4N78LYaQCrDsHbNeA== dependencies: - "@truffle/codec" "^0.14.16" - "@trufflesuite/chromafi" "^3.0.0" - bn.js "^5.1.3" - chalk "^2.4.2" - debug "^4.3.1" - highlightjs-solidity "^2.0.6" - -"@truffle/debug-utils@^6.0.57": - version "6.0.57" - resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-6.0.57.tgz#4e9a1051221c5f467daa398b0ca638d8b6408a82" - integrity sha512-Q6oI7zLaeNLB69ixjwZk2UZEWBY6b2OD1sjLMGDKBGR7GaHYiw96GLR2PFgPH1uwEeLmV4N78LYaQCrDsHbNeA== - dependencies: - "@truffle/codec" "^0.17.3" + "@truffle/codec" "^0.17.3" "@trufflesuite/chromafi" "^3.0.0" bn.js "^5.1.3" chalk "^2.4.2" @@ -4171,27 +3587,11 @@ resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.1.1.tgz#e52026ac8ca7180d83443dca73c03e07ace2a301" integrity sha512-sE7c9IHIGdbK4YayH4BC8i8qMjoAOeg6nUXUDZZp8wlU21/EMpaG+CLx+KqcIPyR+GSWIW3Dm0PXkr2nlggFDA== -"@truffle/error@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.2.0.tgz#65de6f03f5c041f883cc87677eecf8231428f1ab" - integrity sha512-Fe0/z4WWb7IP2gBnv3l6zqP87Y0kSMs7oiSLakKJq17q3GUunrHSdioKuNspdggxkXIBhEQLhi8C+LJdwmHKWQ== - "@truffle/error@^0.2.2": version "0.2.2" resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.2.2.tgz#1b4c4237c14dda792f20bd4f19ff4e4585b47796" integrity sha512-TqbzJ0O8DHh34cu8gDujnYl4dUl6o2DE4PR6iokbybvnIm/L2xl6+Gv1VC+YJS45xfH83Yo3/Zyg/9Oq8/xZWg== -"@truffle/events@^0.1.22": - version "0.1.22" - resolved "https://registry.yarnpkg.com/@truffle/events/-/events-0.1.22.tgz#007617de4d20093e8c257a63ddc1193a21a9c5b8" - integrity sha512-WBEfaQ5zagS3J1M66J8wQ8N1As/EnBjLQsRlCCFs3/KbmeWhsoalVZ5Effhe0Vxd+e+k7lvwbloQBdS6roc+wg== - dependencies: - "@truffle/dashboard-message-bus-client" "^0.1.10" - "@truffle/spinners" "^0.2.3" - debug "^4.3.1" - emittery "^0.4.1" - web3-utils "1.8.2" - "@truffle/events@^0.1.25": version "0.1.25" resolved "https://registry.yarnpkg.com/@truffle/events/-/events-0.1.25.tgz#52d4ae968273c267edfcb5c8e2d3b90c7f1f6b89" @@ -4235,16 +3635,7 @@ keccak "3.0.2" secp256k1 "4.0.3" -"@truffle/interface-adapter@^0.5.16", "@truffle/interface-adapter@^0.5.31": - version "0.5.31" - resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.31.tgz#9e62e9ed1c1c07d50d9e1dcffd6ef24efc1230e7" - integrity sha512-f5mOqbptQUUgHhBrBvWie4EUAUqHLN/wCBjFoP2N/QNcyvwGfdC3TSck9kjwIIFIgYgQQyAxQDGBQcjHryvxzg== - dependencies: - bn.js "^5.1.3" - ethers "^4.0.32" - web3 "1.8.2" - -"@truffle/interface-adapter@^0.5.37": +"@truffle/interface-adapter@^0.5.16", "@truffle/interface-adapter@^0.5.37": version "0.5.37" resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.37.tgz#95d249c1912d2baaa63c54e8a138d3f476a1181a" integrity sha512-lPH9MDgU+7sNDlJSClwyOwPCfuOimqsCx0HfGkznL3mcFRymc1pukAR1k17zn7ErHqBwJjiKAZ6Ri72KkS+IWw== @@ -4262,11 +3653,6 @@ "@truffle/expect" "^0.1.5" debug "^4.3.1" -"@truffle/promise-tracker@^0.1.5": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@truffle/promise-tracker/-/promise-tracker-0.1.5.tgz#df68df14d45a32bda6237ab85169d1808e4b3928" - integrity sha512-wZx8eeu/6rcwwkmRF0Y832/NSQR9A9u6pyhTozv+j77jklnd/KZvu2JlACaAjP30eL5SOtSrSOzAMcSh/trJjg== - "@truffle/promise-tracker@^0.1.7": version "0.1.7" resolved "https://registry.yarnpkg.com/@truffle/promise-tracker/-/promise-tracker-0.1.7.tgz#edc5e5940656439db7b1956bd4838d12dd4b9ecf" @@ -4282,16 +3668,6 @@ debug "^4.3.1" web3 "1.10.0" -"@truffle/provider@^0.3.7": - version "0.3.7" - resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.3.7.tgz#fc217d34e84cb3f19f77fa41eef94c82b51bef36" - integrity sha512-OF4JZe3oIR9epWMMbJgCnJJCnu1Ce6IeLk8lCAuNtSlZ46gGj7INEDCXwB5KrgydUC5KDnGp4knHWnQfk5YWXg== - dependencies: - "@truffle/error" "^0.2.0" - "@truffle/interface-adapter" "^0.5.31" - debug "^4.3.1" - web3 "1.8.2" - "@truffle/provisioner@^0.2.77": version "0.2.77" resolved "https://registry.yarnpkg.com/@truffle/provisioner/-/provisioner-0.2.77.tgz#5e2a8f13d587ecb171034673345b48b6974fb986" @@ -4330,13 +3706,6 @@ node-interval-tree "^1.3.3" web3-utils "1.10.0" -"@truffle/spinners@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@truffle/spinners/-/spinners-0.2.3.tgz#5689d06ef16855a9f6b70dc0cd40188de54450ec" - integrity sha512-YnaQ+oBRQ1I1+/P18i8oSW4orUYi6vwpZQxauEZ5X0L8atjKq+RWdiNaza6J6L+KOLunXM4+pWxnNzuUmxlJZw== - dependencies: - "@trufflesuite/spinnies" "^0.1.1" - "@truffle/spinners@^0.2.5": version "0.2.5" resolved "https://registry.yarnpkg.com/@truffle/spinners/-/spinners-0.2.5.tgz#fe3bb3451768f5353085551b8fe6285d354705ef" @@ -4600,12 +3969,7 @@ "@types/react" "*" hoist-non-react-statics "^3.3.0" -"@types/http-cache-semantics@*": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== - -"@types/http-cache-semantics@^4.0.2": +"@types/http-cache-semantics@*", "@types/http-cache-semantics@^4.0.2": version "4.0.4" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== @@ -4677,12 +4041,7 @@ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== -"@types/node@*": - version "18.15.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.5.tgz#3af577099a99c61479149b716183e70b5239324a" - integrity sha512-Ark2WDjjZO7GmvsyFFf81MXuGTA/d6oP38anyxWOL6EREyBKAxKoFHwBhaZxCfLRLpO8JgVXwqOwSwa7jRcjew== - -"@types/node@>=13.7.0": +"@types/node@*", "@types/node@>=13.7.0": version "18.16.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.0.tgz#4668bc392bb6938637b47e98b1f2ed5426f33316" integrity sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ== @@ -5111,11 +4470,6 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== -"@whatwg-node/events@^0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.2.tgz#7b7107268d2982fc7b7aff5ee6803c64018f84dd" - integrity sha512-WKj/lI4QjnLuPrim0cfO7i+HsDSXHxNv1y0CrJhdntuO3hxWZmnXCwNDnwOvry11OjRin6cgWNF+j/9Pn8TN4w== - "@whatwg-node/events@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" @@ -5126,18 +4480,7 @@ resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.1.1.tgz#0ca718508249419587e130da26d40e29d99b5356" integrity sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w== -"@whatwg-node/fetch@^0.8.0": - version "0.8.3" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.3.tgz#41968bd9450f04e9eb60af636430bb1763882031" - integrity sha512-qpacTdzO8z36pa3HpEXEE2PoCXS7sn3LNqr7IjDETEPWJWS0RTJMD49fL1ZKdjQrfHZNBQCRwVNOcT1RZv/xVg== - dependencies: - "@peculiar/webcrypto" "^1.4.0" - "@whatwg-node/node-fetch" "^0.3.3" - busboy "^1.6.0" - urlpattern-polyfill "^6.0.2" - web-streams-polyfill "^3.2.1" - -"@whatwg-node/fetch@^0.8.4": +"@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.4": version "0.8.8" resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.8.tgz#48c6ad0c6b7951a73e812f09dd22d75e9fa18cae" integrity sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg== @@ -5156,17 +4499,6 @@ "@whatwg-node/node-fetch" "^0.4.8" urlpattern-polyfill "^9.0.0" -"@whatwg-node/node-fetch@^0.3.3": - version "0.3.4" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.4.tgz#4beb88579c53ebd870e28d0f2f0376191b9fd6c3" - integrity sha512-gP1MN6DiHVbhkLWH1eCELhE2ZtLRxb+HRKu4eYze1Tijxz0uT1T2kk3lseZp94txzxCfbxGFU0jsWkxNdH3EXA== - dependencies: - "@whatwg-node/events" "^0.0.2" - busboy "^1.6.0" - fast-querystring "^1.1.1" - fast-url-parser "^1.1.3" - tslib "^2.3.1" - "@whatwg-node/node-fetch@^0.3.6": version "0.3.6" resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.6.tgz#e28816955f359916e2d830b68a64493124faa6d0" @@ -5381,12 +4713,7 @@ acorn@^7.0.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - -acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: version "8.10.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== @@ -5413,14 +4740,7 @@ agent-base@6: dependencies: debug "4" -agent-base@^7.0.2, agent-base@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" - integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg== - dependencies: - debug "^4.3.4" - -agent-base@^7.1.1: +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== @@ -5457,17 +4777,7 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.3: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - -ajv@^8.17.1: +ajv@^8.0.0, ajv@^8.0.1, ajv@^8.17.1, ajv@^8.6.3: version "8.17.1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -5979,12 +5289,7 @@ async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0: dependencies: lodash "^4.17.14" -async@^3.2.3, async@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== - -async@^3.2.6: +async@^3.2.3, async@^3.2.4, async@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== @@ -6344,7 +5649,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: +braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -6500,37 +5805,7 @@ browserify@^17.0.1: vm-browserify "^1.0.0" xtend "^4.0.0" -browserslist@^4.21.10: - version "4.22.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" - integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== - dependencies: - caniuse-lite "^1.0.30001580" - electron-to-chromium "^1.4.648" - node-releases "^2.0.14" - update-browserslist-db "^1.0.13" - -browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== - dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" - -browserslist@^4.21.9: - version "4.21.10" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" - integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== - dependencies: - caniuse-lite "^1.0.30001517" - electron-to-chromium "^1.4.477" - node-releases "^2.0.13" - update-browserslist-db "^1.0.11" - -browserslist@^4.23.1: +browserslist@^4.21.10, browserslist@^4.21.5, browserslist@^4.23.1: version "4.23.3" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== @@ -6644,13 +5919,6 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== -builtins@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" - integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== - dependencies: - semver "^7.0.0" - busboy@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -6742,24 +6010,7 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -call-bind@^1.0.4, call-bind@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" - integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== - dependencies: - function-bind "^1.1.2" - get-intrinsic "^1.2.1" - set-function-length "^1.1.1" - -call-bind@^1.0.7: +call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== @@ -6820,21 +6071,6 @@ camelcase@^6.0.0, camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001449: - version "1.0.30001469" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001469.tgz#3dd505430c8522fdc9f94b4a19518e330f5c945a" - integrity sha512-Rcp7221ScNqQPP3W+lVOYDyjdR6dC+neEQCttoNr5bAyz54AboB4iwpnWgyi8P4YUsPybVzT4LgWiBbI3drL4g== - -caniuse-lite@^1.0.30001517: - version "1.0.30001522" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz#44b87a406c901269adcdb834713e23582dd71856" - integrity sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg== - -caniuse-lite@^1.0.30001580: - version "1.0.30001585" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz#0b4e848d84919c783b2a41c13f7de8ce96744401" - integrity sha512-yr2BWR1yLXQ8fMpdS/4ZZXpseBgE7o4g41x3a6AJOqZuOi+iE/WdJYAuZ6Y95i4Ohd2Y+9MzIWRR+uGABH4s3Q== - caniuse-lite@^1.0.30001646: version "1.0.30001653" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz#b8af452f8f33b1c77f122780a4aecebea0caca56" @@ -7096,7 +6332,7 @@ cheerio@^1.0.0-rc.2: parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" -chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2: +chokidar@3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -7111,7 +6347,7 @@ chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" -chokidar@^3.5.3: +chokidar@^3.4.0, chokidar@^3.5.2, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -7246,17 +6482,7 @@ cli-spinners@2.6.1: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== -cli-spinners@^2.2.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a" - integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw== - -cli-spinners@^2.5.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.0.tgz#5881d0ad96381e117bbe07ad91f2008fe6ffd8db" - integrity sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g== - -cli-spinners@^2.9.2: +cli-spinners@^2.2.0, cli-spinners@^2.5.0, cli-spinners@^2.9.2: version "2.9.2" resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== @@ -7477,12 +6703,7 @@ commander@12.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== -commander@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1" - integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA== - -commander@^10.0.1: +commander@^10.0.0, commander@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== @@ -7794,17 +7015,7 @@ cosmiconfig@9.0.0: js-yaml "^4.1.0" parse-json "^5.2.0" -cosmiconfig@^8.0.0: - version "8.1.3" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.3.tgz#0e614a118fcc2d9e5afc2f87d53cd09931015689" - integrity sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw== - dependencies: - import-fresh "^3.2.1" - js-yaml "^4.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - -cosmiconfig@^8.1.0, cosmiconfig@^8.1.3, cosmiconfig@^8.2.0: +cosmiconfig@^8.0.0, cosmiconfig@^8.1.0, cosmiconfig@^8.1.3, cosmiconfig@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.2.0.tgz#f7d17c56a590856cd1e7cee98734dca272b0d8fd" integrity sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ== @@ -8031,7 +7242,14 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@4.3.4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + +debug@4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -8045,13 +7263,6 @@ debug@^3.1.0, debug@^3.2.6, debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.3.5: - version "4.3.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" - integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== - dependencies: - ms "2.1.2" - decamelize-keys@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" @@ -8145,16 +7356,7 @@ deferred-leveldown@~5.3.0: abstract-leveldown "~6.2.1" inherits "^2.0.3" -define-data-property@^1.0.1, define-data-property@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" - integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== - dependencies: - get-intrinsic "^1.2.1" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - -define-data-property@^1.1.4: +define-data-property@^1.0.1, define-data-property@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== @@ -8270,12 +7472,7 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== -diff@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" - integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== - -diff@^5.2.0: +diff@^5.0.0, diff@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== @@ -8433,12 +7630,7 @@ dotenv-expand@~11.0.6: dependencies: dotenv "^16.4.4" -dotenv@^16.0.0: - version "16.0.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" - integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== - -dotenv@^16.4.4, dotenv@^16.4.5, dotenv@~16.4.5: +dotenv@^16.0.0, dotenv@^16.4.4, dotenv@^16.4.5, dotenv@~16.4.5: version "16.4.5" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== @@ -8509,21 +7701,6 @@ electron-fetch@^1.7.2: dependencies: encoding "^0.1.13" -electron-to-chromium@^1.4.284: - version "1.4.335" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.335.tgz#69c08baa608bbb58e290d83320190fa82c835efe" - integrity sha512-l/eowQqTnrq3gu+WSrdfkhfNHnPgYqlKAwxz7MTOj6mom19vpEDHNXl6dxDxyTiYuhemydprKr/HCrHfgk+OfQ== - -electron-to-chromium@^1.4.477: - version "1.4.498" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.498.tgz#cef35341123f62a35ba7084e439c911d25e0d81b" - integrity sha512-4LODxAzKGVy7CJyhhN5mebwe7U2L29P+0G+HUriHnabm0d7LSff8Yn7t+Wq+2/9ze2Fu1dhX7mww090xfv7qXQ== - -electron-to-chromium@^1.4.648: - version "1.4.660" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.660.tgz#80be71d08c1224980e645904ab9155f3fa54a1ea" - integrity sha512-1BqvQG0BBQrAA7FVL2EMrb5A1sVyXF3auwJneXjGWa1TpN+g0C4KbUsYWePz6OZ0mXZfXGy+RmQDELJWwE8v/Q== - electron-to-chromium@^1.5.4: version "1.5.13" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6" @@ -8621,14 +7798,14 @@ enhanced-resolve@^5.17.1: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@2.3.6, enquirer@^2.3.0, enquirer@^2.3.6, enquirer@~2.3.6: +enquirer@2.3.6, enquirer@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" -enquirer@2.4.1: +enquirer@2.4.1, enquirer@^2.3.0, enquirer@^2.3.6: version "2.4.1" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== @@ -8646,16 +7823,11 @@ env-paths@^2.2.0, env-paths@^2.2.1: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== -envinfo@7.13.0: +envinfo@7.13.0, envinfo@^7.7.3: version "7.13.0" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.13.0.tgz#81fbb81e5da35d74e814941aeab7c325a606fb31" integrity sha512-cvcaMr7KqXVh4nyzGTVqTum+gAiL265x5jUWQIDLq//zOGbW+gSW/C+OWLleY/rs9Qole6AZLMXPbtIFQbqu+Q== -envinfo@^7.7.3: - version "7.8.1" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" - integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== - err-code@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" @@ -8680,47 +7852,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== - dependencies: - array-buffer-byte-length "^1.0.0" - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.3" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" - -es-abstract@^1.22.1: +es-abstract@^1.20.4, es-abstract@^1.22.1: version "1.22.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== @@ -8851,12 +7983,7 @@ es6-symbol@^3.1.1, es6-symbol@^3.1.3: d "^1.0.1" ext "^1.1.2" -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escalade@^3.1.2: +escalade@^3.1.1, escalade@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== @@ -8956,15 +8083,7 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-scope@^7.2.2: +eslint-scope@^7.1.1, eslint-scope@^7.2.2: version "7.2.2" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== @@ -8972,22 +8091,12 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint-visitor-keys@^3.4.1: - version "3.4.2" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz#8c2095440eca8c933bedcadf16fefa44dbe9ba5f" - integrity sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw== - -eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.57.0: +eslint@^8.57.0, eslint@^8.7.0: version "8.57.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -9031,52 +8140,6 @@ eslint@^8.57.0: strip-ansi "^6.0.1" text-table "^0.2.0" -eslint@^8.7.0: - version "8.36.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" - integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.1" - "@eslint/js" "8.36.0" - "@humanwhocodes/config-array" "^0.11.8" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-visitor-keys "^3.3.0" - espree "^9.5.0" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - esniff@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" @@ -9096,15 +8159,6 @@ espree@^9.3.1, espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -espree@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" - integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" - esprima@2.7.x, esprima@^2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" @@ -9750,29 +8804,7 @@ fast-fifo@^1.0.0: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.2.0.tgz#2ee038da2468e8623066dee96958b0c1763aa55a" integrity sha512-NcvQXt7Cky1cNau15FWy64IjuO8X0JijhTBBrJj1YlxlDfRkJXNaK9RFUjwpfDPzMdv7wB38jr53l9tkNLxnWg== -fast-glob@^3.0.3: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.2.9: - version "3.3.1" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" - integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.3.2: +fast-glob@^3.0.3, fast-glob@^3.2.9, fast-glob@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== @@ -10130,16 +9162,7 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.1.0: - version "11.1.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" - integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs-extra@^11.2.0: +fs-extra@^11.1.0, fs-extra@^11.2.0: version "11.2.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== @@ -10219,26 +9242,11 @@ fsu@^1.1.1: resolved "https://registry.yarnpkg.com/fsu/-/fsu-1.1.1.tgz#bd36d3579907c59d85b257a75b836aa9e0c31834" integrity sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function-bind@^1.1.2: +function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" @@ -10254,7 +9262,7 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -10339,36 +9347,7 @@ get-installed-path@^4.0.8: dependencies: global-modules "1.0.0" -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-proto "^1.0.1" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" - integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== - dependencies: - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - -get-intrinsic@^1.2.4: +get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== @@ -10549,18 +9528,7 @@ glob@9.3.5, glob@^9.2.0: minipass "^4.2.4" path-scurry "^1.6.1" -glob@^10.2.2: - version "10.3.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" - integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.0.3" - minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" - -glob@^10.3.10, glob@^10.3.7: +glob@^10.2.2, glob@^10.3.10, glob@^10.3.7: version "10.4.5" resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== @@ -10882,12 +9850,7 @@ graphql@^15.3.0: resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== -graphql@^16.6.0: - version "16.8.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" - integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== - -graphql@^16.9.0: +graphql@^16.6.0, graphql@^16.9.0: version "16.9.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f" integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw== @@ -10899,19 +9862,7 @@ gzip-size@^6.0.0: dependencies: duplexer "^0.1.2" -handlebars@^4.0.1: - version "4.7.7" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" - integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== - dependencies: - minimist "^1.2.5" - neo-async "^2.6.0" - source-map "^0.6.1" - wordwrap "^1.0.0" - optionalDependencies: - uglify-js "^3.1.4" - -handlebars@^4.7.7: +handlebars@^4.0.1, handlebars@^4.7.7: version "4.7.8" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ== @@ -11047,14 +9998,7 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-property-descriptors@^1.0.2: +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== @@ -11312,15 +10256,7 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -https-proxy-agent@^7.0.0: - version "7.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.1.tgz#0277e28f13a07d45c663633841e20a40aaafe0ab" - integrity sha512-Eun8zV0kcYS1g19r78osiQLEFIRspRUDd9tIfBCTBPBeMieF/EsJNL8VI3xOIdYRDEkjQnqOYPsZ2DsWsVsFwQ== - dependencies: - agent-base "^7.0.2" - debug "4" - -https-proxy-agent@^7.0.1: +https-proxy-agent@^7.0.0, https-proxy-agent@^7.0.1: version "7.0.5" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== @@ -11381,12 +10317,7 @@ ignore-walk@^6.0.4: dependencies: minimatch "^9.0.0" -ignore@^5.0.4, ignore@^5.0.5, ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== - -ignore@^5.3.1: +ignore@^5.0.4, ignore@^5.0.5, ignore@^5.1.1, ignore@^5.2.0, ignore@^5.2.4, ignore@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== @@ -11416,7 +10347,7 @@ immutable@~3.7.6: resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" integrity sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw== -import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -11500,28 +10431,7 @@ inline-source-map@~0.6.0: dependencies: source-map "~0.5.3" -inquirer@^8.0.0: - version "8.2.5" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.5.tgz#d8654a7542c35a9b9e069d27e2df4858784d54f8" - integrity sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.1" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.21" - mute-stream "0.0.8" - ora "^5.4.1" - run-async "^2.4.0" - rxjs "^7.5.5" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - wrap-ansi "^7.0.0" - -inquirer@^8.2.4: +inquirer@^8.0.0, inquirer@^8.2.4: version "8.2.6" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== @@ -11792,14 +10702,7 @@ is-ci@3.0.1: dependencies: ci-info "^3.2.0" -is-core-module@^2.13.0, is-core-module@^2.5.0, is-core-module@^2.9.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== - dependencies: - has "^1.0.3" - -is-core-module@^2.13.1: +is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0: version "2.13.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== @@ -12043,18 +10946,7 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.10, is-typed-array@^1.1.3, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -is-typed-array@^1.1.12: +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.3, is-typed-array@^1.1.9: version "1.1.12" resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== @@ -12301,15 +11193,6 @@ iterall@^1.2.2: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== -jackspeak@^2.0.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.0.tgz#aa228a94de830f31d4e4f0184427ce91c4ff1493" - integrity sha512-uKmsITSsF4rUWQHzqaRUuyAir3fZfW3f202Ee34lz/gZCi970CPZwyQXLGNgWJvvZbvFyzeyGq0+4fcG/mBKZg== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" @@ -12347,17 +11230,7 @@ jayson@4.0.0: uuid "^8.3.2" ws "^7.4.5" -"jest-diff@>=29.4.3 < 30": - version "29.6.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.3.tgz#365c6b037ea8e67d2f2af68bc018fc18d44311f0" - integrity sha512-3sw+AdWnwH9sSNohMRKA7JiYUJSRr/WS6+sEFfBuhxU5V5GlEVKfvUn8JuMHE0wqKowemR1C2aHy8VtXbaV8dQ== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.6.3" - jest-get-type "^29.6.3" - pretty-format "^29.6.3" - -jest-diff@^29.4.1: +"jest-diff@>=29.4.3 < 30", jest-diff@^29.4.1: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a" integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== @@ -12391,11 +11264,6 @@ jose@^4.11.4: resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.5.tgz#6475d0f467ecd3c630a1b5dadd2735a7288df706" integrity sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg== -js-sdsl@^4.1.4: - version "4.4.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" - integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== - js-sha3@0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.5.tgz#baf0c0e8c54ad5903447df96ade7a4a1bca79a4a" @@ -12461,12 +11329,7 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== -json-parse-even-better-errors@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz#2cb2ee33069a78870a0c7e3da560026b89669cf7" - integrity sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA== - -json-parse-even-better-errors@^3.0.2: +json-parse-even-better-errors@^3.0.0, json-parse-even-better-errors@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz#b43d35e89c0f3be6b5fbbe9dc6c82467b30c28da" integrity sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ== @@ -12668,14 +11531,7 @@ keccak@^3.0.0, keccak@^3.0.2: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keyv@^4.0.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" - integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== - dependencies: - json-buffer "3.0.1" - -keyv@^4.5.3: +keyv@^4.0.0, keyv@^4.5.3: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== @@ -13406,11 +12262,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -"lru-cache@^9.1.1 || ^10.0.0": - version "10.0.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a" - integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g== - lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -13715,14 +12566,14 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@9.0.3, minimatch@^9.0.0, minimatch@^9.0.1: +minimatch@9.0.3: version "9.0.3" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== dependencies: brace-expansion "^2.0.1" -minimatch@9.0.5, minimatch@^9.0.4, minimatch@^9.0.5: +minimatch@9.0.5, minimatch@^9.0.0, minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== @@ -13818,7 +12669,7 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minipass@^4.0.0, minipass@^4.2.4: +minipass@^4.2.4: version "4.2.8" resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.8.tgz#f0010f64393ecfc1d1ccb5f582bcaf45f48e1a3a" integrity sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ== @@ -13828,12 +12679,7 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.3.tgz#05ea638da44e475037ed94d1c7efcc76a25e1974" - integrity sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg== - -minipass@^7.0.2, minipass@^7.0.4, minipass@^7.1.2: +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4, minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== @@ -13873,10 +12719,10 @@ mkdirp-promise@^5.0.1: dependencies: mkdirp "*" -mkdirp@*: - version "2.1.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.5.tgz#78d7eaf15e069ba7b6b47d76dd94cfadf7a4062f" - integrity sha512-jbjfql+shJtAPrFoKxHOXip4xS+kul9W3OzfzzrqueWK2QMGon2bFH2opl6W9EagBThjEz+iysyi/swOoVfB/w== +mkdirp@*, mkdirp@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" + integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== mkdirp@0.5.x, mkdirp@^0.5.1, mkdirp@^0.5.5: version "0.5.6" @@ -13890,11 +12736,6 @@ mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-3.0.1.tgz#e44e4c5607fb279c168241713cc6e0fea9adcb50" - integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== - mnemonist@^0.38.0: version "0.38.5" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" @@ -13929,34 +12770,7 @@ mocha@10.1.0: yargs-parser "20.2.4" yargs-unparser "2.0.0" -mocha@^10.0.0, mocha@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" - integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== - dependencies: - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.3" - debug "4.3.4" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.2.0" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "5.0.1" - ms "2.1.3" - nanoid "3.3.3" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - workerpool "6.2.1" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -mocha@^10.7.3: +mocha@^10.0.0, mocha@^10.2.0, mocha@^10.7.3: version "10.7.3" resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== @@ -14221,7 +13035,7 @@ negotiator@0.6.3, negotiator@^0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -neo-async@^2.6.0, neo-async@^2.6.2: +neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -14282,34 +13096,13 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-fetch@2.7.0, node-fetch@^2.6.12: +node-fetch@2.7.0, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.12, node-fetch@^2.6.6, node-fetch@^2.6.7, node-fetch@^2.6.8: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" -node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.8: - version "2.6.9" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.9.tgz#7c7f744b5cc6eb5fd404e0c7a9fec630a55657e6" - integrity sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^2.6.6: - version "2.6.11" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.11.tgz#cde7fc71deef3131ef80a738919f999e6edfff25" - integrity sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w== - dependencies: - whatwg-url "^5.0.0" - -node-fetch@^2.6.7: - version "2.6.13" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.13.tgz#a20acbbec73c2e09f9007de5cda17104122e0010" - integrity sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA== - dependencies: - whatwg-url "^5.0.0" - node-gyp-build@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" @@ -14381,26 +13174,11 @@ node-preload@^0.2.1: dependencies: process-on-spawn "^1.0.0" -node-releases@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" - integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== - -node-releases@^2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" - integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== - node-releases@^2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -node-releases@^2.0.8: - version "2.0.10" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" - integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== - nodemon@^3.1.4: version "3.1.4" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.4.tgz#c34dcd8eb46a05723ccde60cbdd25addcc8725e4" @@ -14514,14 +13292,7 @@ npm-bundled@^3.0.0: dependencies: npm-normalize-package-bin "^3.0.0" -npm-install-checks@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.2.0.tgz#fae55b9967b03ac309695ec96629492d5cedf371" - integrity sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g== - dependencies: - semver "^7.1.1" - -npm-install-checks@^6.2.0: +npm-install-checks@^6.0.0, npm-install-checks@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-6.3.0.tgz#046552d8920e801fa9f919cad569545d60e826fe" integrity sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw== @@ -14726,11 +13497,6 @@ object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.3, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - object-inspect@^1.13.1: version "1.13.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" @@ -14848,18 +13614,6 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -15382,15 +14136,7 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" -path-scurry@^1.10.1, path-scurry@^1.6.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" - integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== - dependencies: - lru-cache "^9.1.1 || ^10.0.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - -path-scurry@^1.11.1: +path-scurry@^1.11.1, path-scurry@^1.6.1: version "1.11.1" resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== @@ -15450,12 +14196,7 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picocolors@^1.0.1: +picocolors@^1.0.0, picocolors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== @@ -15779,15 +14520,6 @@ prettier@^3.0.1, prettier@^3.3.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== -pretty-format@^29.6.3: - version "29.6.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.3.tgz#d432bb4f1ca6f9463410c3fb25a0ba88e594ace7" - integrity sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw== - dependencies: - "@jest/schemas" "^29.6.3" - ansi-styles "^5.0.0" - react-is "^18.0.0" - pretty-format@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812" @@ -16025,20 +14757,13 @@ pvutils@^1.1.3: resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== -qs@6.13.0: +qs@6.13.0, qs@^6.4.0, qs@^6.9.4: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: side-channel "^1.0.6" -qs@^6.4.0, qs@^6.9.4: - version "6.11.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.1.tgz#6c29dff97f0c0060765911ba65cbc9764186109f" - integrity sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ== - dependencies: - side-channel "^1.0.4" - qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -16408,15 +15133,6 @@ regenerator-runtime@^0.13.11: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - regexp.prototype.flags@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" @@ -16541,12 +15257,7 @@ reselect-tree@^1.3.7: json-pointer "^0.6.1" reselect "^4.0.0" -reselect@^4.0.0: - version "4.1.7" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.7.tgz#56480d9ff3d3188970ee2b76527bd94a95567a42" - integrity sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A== - -reselect@^4.1.8: +reselect@^4.0.0, reselect@^4.1.8: version "4.1.8" resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ== @@ -16593,25 +15304,7 @@ resolve@1.17.0: dependencies: path-parse "^1.0.6" -resolve@^1.1.4, resolve@^1.1.6, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.4.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.10.0: - version "1.22.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" - integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.20.0, resolve@^1.22.4: +resolve@^1.1.4, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.4, resolve@^1.4.0: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -16815,16 +15508,7 @@ scheduler@^0.23.0: dependencies: loose-envify "^1.1.0" -schema-utils@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" - integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== - dependencies: - "@types/json-schema" "^7.0.8" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -schema-utils@^3.2.0: +schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -16867,7 +15551,7 @@ semaphore@>=1.0.1, semaphore@^1.0.3: resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== -"semver@2 || 3 || 4 || 5", semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -16893,33 +15577,16 @@ semver@7.4.0: dependencies: lru-cache "^6.0.0" -semver@7.6.3, semver@^7.3.6, semver@^7.6.0: +semver@7.6.3, semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: version "7.6.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== -semver@^5.3.0, semver@^5.5.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.3.1: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^6.1.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.0.0, semver@^7.1.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: - version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" - integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== - dependencies: - lru-cache "^6.0.0" - semver@~5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" @@ -16968,14 +15635,7 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== - dependencies: - randombytes "^2.1.0" - -serialize-javascript@^6.0.2: +serialize-javascript@^6.0.1, serialize-javascript@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== @@ -17008,16 +15668,6 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-function-length@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" - integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== - dependencies: - define-data-property "^1.1.1" - get-intrinsic "^1.2.1" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - set-function-length@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" @@ -17139,16 +15789,7 @@ shiki@^1.9.1: "@shikijs/core" "1.14.1" "@types/hast" "^3.0.4" -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -side-channel@^1.0.6: +side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== @@ -17517,14 +16158,7 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^10.0.0: - version "10.0.5" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.5.tgz#e49efcd6e36385196cb515d3a2ad6c3f0265ef8c" - integrity sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A== - dependencies: - minipass "^7.0.3" - -ssri@^10.0.6: +ssri@^10.0.0, ssri@^10.0.6: version "10.0.6" resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== @@ -17668,15 +16302,6 @@ string.prototype.padend@^3.0.0: define-properties "^1.1.4" es-abstract "^1.20.4" -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trim@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" @@ -17686,15 +16311,6 @@ string.prototype.trim@^1.2.8: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trimend@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" @@ -17704,15 +16320,6 @@ string.prototype.trimend@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string.prototype.trimstart@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" @@ -17824,7 +16431,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -18058,7 +16665,7 @@ tar-stream@~2.2.0: inherits "^2.0.3" readable-stream "^3.1.1" -tar@6.2.1, tar@^6.2.1: +tar@6.2.1, tar@^6.1.0, tar@^6.1.11, tar@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== @@ -18083,30 +16690,6 @@ tar@^4.0.2: safe-buffer "^5.2.1" yallist "^3.1.1" -tar@^6.1.0: - version "6.1.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" - integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^4.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - -tar@^6.1.11: - version "6.1.15" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.15.tgz#c9738b0b98845a3b344d334b8fa3041aaba53a69" - integrity sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - tar@^7.4.0: version "7.4.3" resolved "https://registry.yarnpkg.com/tar/-/tar-7.4.3.tgz#88bbe9286a3fcd900e94592cda7a22b192e80571" @@ -18393,12 +16976,7 @@ truffle@^5.11.5: optionalDependencies: "@truffle/db" "^2.0.36" -ts-api-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d" - integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A== - -ts-api-utils@^1.3.0: +ts-api-utils@^1.0.1, ts-api-utils@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== @@ -18424,26 +17002,7 @@ ts-log@^2.2.3: resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.5.tgz#aef3252f1143d11047e2cb6f7cfaac7408d96623" integrity sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA== -ts-node@^10.9.1: - version "10.9.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" - integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== - dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" - -ts-node@^10.9.2: +ts-node@^10.9.1, ts-node@^10.9.2: version "10.9.2" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== @@ -18513,12 +17072,7 @@ tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.3.1, tslib@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== - -tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.2, tslib@~2.6.0: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.2, tslib@~2.6.0: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -18753,12 +17307,7 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -"typescript@>=3 < 6": - version "5.1.6" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274" - integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA== - -typescript@^5.2.2, typescript@^5.5.4: +"typescript@>=3 < 6", typescript@^5.2.2, typescript@^5.5.4: version "5.5.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== @@ -18916,30 +17465,6 @@ upath@2.0.1: resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b" integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w== -update-browserslist-db@^1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3" - integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -update-browserslist-db@^1.0.13: - version "1.0.13" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" - integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - update-browserslist-db@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" @@ -19002,13 +17527,6 @@ url@~0.11.0: punycode "1.3.2" querystring "0.2.0" -urlpattern-polyfill@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-6.0.2.tgz#a193fe773459865a2a5c93b246bb794b13d07256" - integrity sha512-5vZjFlH9ofROmuWmXM9yj2wljYKgWstGwe8YTyiqM7hVum/g9LyCizPZtb3UqsuppVwety9QJmfc42VggLpTgg== - dependencies: - braces "^3.0.2" - urlpattern-polyfill@^8.0.0: version "8.0.2" resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5" @@ -19121,18 +17639,11 @@ validate-npm-package-license@3.0.4, validate-npm-package-license@^3.0.1, validat spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validate-npm-package-name@5.0.1: +validate-npm-package-name@5.0.1, validate-npm-package-name@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8" integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ== -validate-npm-package-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz#f16afd48318e6f90a1ec101377fa0384cfc8c713" - integrity sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ== - dependencies: - builtins "^5.0.0" - validator@^13.6.0: version "13.9.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" @@ -19234,15 +17745,6 @@ web3-bzz@1.10.0: got "12.1.0" swarm-js "^0.1.40" -web3-bzz@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.10.1.tgz#29edb8e91e806a4cf69de0735b4cdc18b21fb4f0" - integrity sha512-0T2BTYm9mLPpnRJuXSS7PA39dTXCPj6a3/Qdee84Plm6WsSIl4aZooJ4YUMnlII8HjyzwiIzjnH7AEZrBcBu9w== - dependencies: - "@types/node" "^12.12.6" - got "12.1.0" - swarm-js "^0.1.40" - web3-bzz@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.10.4.tgz#dcc787970767d9004c73d11d0eeef774ce16b880" @@ -19252,15 +17754,6 @@ web3-bzz@1.10.4: got "12.1.0" swarm-js "^0.1.40" -web3-bzz@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.8.2.tgz#67ea1c775874056250eece551ded22905ed08784" - integrity sha512-1EEnxjPnFnvNWw3XeeKuTR8PBxYd0+XWzvaLK7OJC/Go9O8llLGxrxICbKV+8cgIE0sDRBxiYx02X+6OhoAQ9w== - dependencies: - "@types/node" "^12.12.6" - got "12.1.0" - swarm-js "^0.1.40" - web3-core-helpers@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.0.tgz#1016534c51a5df77ed4f94d1fcce31de4af37fad" @@ -19269,14 +17762,6 @@ web3-core-helpers@1.10.0: web3-eth-iban "1.10.0" web3-utils "1.10.0" -web3-core-helpers@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.1.tgz#dd597adc758efe03b380f1423a4da3de1757530b" - integrity sha512-lgOgdiIyIIXxIVjEHjT8PC2CsjFvvBXfVF0Xq5SiRcPKj47B2F7uur0gPoPc6e6+kjo49qEqLlx6eZKOkCAR1A== - dependencies: - web3-eth-iban "1.10.1" - web3-utils "1.10.1" - web3-core-helpers@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.10.4.tgz#bd2b4140df2016d5dd3bb2b925fc29ad8678677c" @@ -19285,14 +17770,6 @@ web3-core-helpers@1.10.4: web3-eth-iban "1.10.4" web3-utils "1.10.4" -web3-core-helpers@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.8.2.tgz#82066560f8085e6c7b93bcc8e88b441289ea9f9f" - integrity sha512-6B1eLlq9JFrfealZBomd1fmlq1o4A09vrCVQSa51ANoib/jllT3atZrRDr0zt1rfI7TSZTZBXdN/aTdeN99DWw== - dependencies: - web3-eth-iban "1.8.2" - web3-utils "1.8.2" - web3-core-method@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.0.tgz#82668197fa086e8cc8066742e35a9d72535e3412" @@ -19304,17 +17781,6 @@ web3-core-method@1.10.0: web3-core-subscriptions "1.10.0" web3-utils "1.10.0" -web3-core-method@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.1.tgz#decd9a11d95c199960477b297a45b8f135ac7770" - integrity sha512-QEqgMsagp6vs0GOlI4QHzZcsvzJs+Zp1Eo8uOZgosYoRfusklzfPmX4OYg4H6XyenCavgvmAkxw0g8y8hlLHiQ== - dependencies: - "@ethersproject/transactions" "^5.6.2" - web3-core-helpers "1.10.1" - web3-core-promievent "1.10.1" - web3-core-subscriptions "1.10.1" - web3-utils "1.10.1" - web3-core-method@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.10.4.tgz#566b52f006d3cbb13b21b72b8d2108999bf5d6bf" @@ -19326,17 +17792,6 @@ web3-core-method@1.10.4: web3-core-subscriptions "1.10.4" web3-utils "1.10.4" -web3-core-method@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.8.2.tgz#ba5ec68084e903f0516415010477618be017eac2" - integrity sha512-1qnr5mw5wVyULzLOrk4B+ryO3gfGjGd/fx8NR+J2xCGLf1e6OSjxT9vbfuQ3fErk/NjSTWWreieYWLMhaogcRA== - dependencies: - "@ethersproject/transactions" "^5.6.2" - web3-core-helpers "1.8.2" - web3-core-promievent "1.8.2" - web3-core-subscriptions "1.8.2" - web3-utils "1.8.2" - web3-core-promievent@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.0.tgz#cbb5b3a76b888df45ed3a8d4d8d4f54ccb66a37b" @@ -19344,13 +17799,6 @@ web3-core-promievent@1.10.0: dependencies: eventemitter3 "4.0.4" -web3-core-promievent@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.1.tgz#d20b1328d1ff8881acb8cf4b5749bb2218798b0e" - integrity sha512-ggInbRxkx0n0FVMU5GXx9pbTwq7rfF2DJ6J6AafifOC0P0269TbHfFKMlU7B5K5i6/VQxrsY9fBPf6am9DmQuw== - dependencies: - eventemitter3 "4.0.4" - web3-core-promievent@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.10.4.tgz#629b970b7934430b03c5033c79f3bb3893027e22" @@ -19358,13 +17806,6 @@ web3-core-promievent@1.10.4: dependencies: eventemitter3 "4.0.4" -web3-core-promievent@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.8.2.tgz#e670d6b4453632e6ecfd9ad82da44f77ac1585c9" - integrity sha512-nvkJWDVgoOSsolJldN33tKW6bKKRJX3MCPDYMwP5SUFOA/mCzDEoI88N0JFofDTXkh1k7gOqp1pvwi9heuaxGg== - dependencies: - eventemitter3 "4.0.4" - web3-core-requestmanager@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.0.tgz#4b34f6e05837e67c70ff6f6993652afc0d54c340" @@ -19376,17 +17817,6 @@ web3-core-requestmanager@1.10.0: web3-providers-ipc "1.10.0" web3-providers-ws "1.10.0" -web3-core-requestmanager@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.1.tgz#f0c765ae811c1c0b24c7c8ede8e9a254bc520fce" - integrity sha512-hBHuKbh8PGrSs4vTg2EA7xM+BIDVOrmOZnK4I+KeWw8zZr6bmhhk8xkmtKo2/0fADAkvVqMiJwuZcpRr3DILnw== - dependencies: - util "^0.12.5" - web3-core-helpers "1.10.1" - web3-providers-http "1.10.1" - web3-providers-ipc "1.10.1" - web3-providers-ws "1.10.1" - web3-core-requestmanager@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.10.4.tgz#eb1f147e6b9df84e3a37e602162f8925bdb4bb9a" @@ -19398,17 +17828,6 @@ web3-core-requestmanager@1.10.4: web3-providers-ipc "1.10.4" web3-providers-ws "1.10.4" -web3-core-requestmanager@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.8.2.tgz#dda95e83ca4808949612a41e54ecea557f78ef26" - integrity sha512-p1d090RYs5Mu7DK1yyc3GCBVZB/03rBtFhYFoS2EruGzOWs/5Q0grgtpwS/DScdRAm8wB8mYEBhY/RKJWF6B2g== - dependencies: - util "^0.12.5" - web3-core-helpers "1.8.2" - web3-providers-http "1.8.2" - web3-providers-ipc "1.8.2" - web3-providers-ws "1.8.2" - web3-core-subscriptions@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.0.tgz#b534592ee1611788fc0cb0b95963b9b9b6eacb7c" @@ -19417,14 +17836,6 @@ web3-core-subscriptions@1.10.0: eventemitter3 "4.0.4" web3-core-helpers "1.10.0" -web3-core-subscriptions@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.1.tgz#1e19ef59f9844c31a85f1b9c17088a786029c118" - integrity sha512-6B7cA7lUwCAh7X55gTMfFkC9L8en3bddqFi+VNO9SO9af62t2L5xTb8pxZEFirIF4s4qKxKekLgZrRhpmlO3eA== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.10.1" - web3-core-subscriptions@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.10.4.tgz#2f4dcb404237e92802a563265d11a33934dc38e6" @@ -19433,14 +17844,6 @@ web3-core-subscriptions@1.10.4: eventemitter3 "4.0.4" web3-core-helpers "1.10.4" -web3-core-subscriptions@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.8.2.tgz#0c8bd49439d83c6f0a03c70f00b24a915a70a5ed" - integrity sha512-vXQogHDmAIQcKpXvGiMddBUeP9lnKgYF64+yQJhPNE5PnWr1sAibXuIPV7mIPihpFr/n/DORRj6Wh1pUv9zaTw== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.8.2" - web3-core@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.0.tgz#9aa07c5deb478cf356c5d3b5b35afafa5fa8e633" @@ -19454,20 +17857,7 @@ web3-core@1.10.0: web3-core-requestmanager "1.10.0" web3-utils "1.10.0" -web3-core@1.10.1, web3-core@^1.8.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.1.tgz#a4cb471356c4a197654b61adc9d0b03357da4258" - integrity sha512-a45WF/e2VeSs17UTmmWhEaMDv/A+N6qchA7zepvdvwUGCZME39YWCmbsjAYjkq0btsXueOIBpS6fLuq5VoLkFg== - dependencies: - "@types/bn.js" "^5.1.1" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.10.1" - web3-core-method "1.10.1" - web3-core-requestmanager "1.10.1" - web3-utils "1.10.1" - -web3-core@1.10.4: +web3-core@1.10.4, web3-core@^1.8.1: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.4.tgz#639de68b8b9871d2dc8892e0dd4e380cb1361a98" integrity sha512-B6elffYm81MYZDTrat7aEhnhdtVE3lDBUZft16Z8awYMZYJDbnykEbJVS+l3mnA7AQTnSDr/1MjWofGDLBJPww== @@ -19480,19 +17870,6 @@ web3-core@1.10.4: web3-core-requestmanager "1.10.4" web3-utils "1.10.4" -web3-core@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.8.2.tgz#333e93d7872b1a36efe758ed8b89a7acbdd962c2" - integrity sha512-DJTVEAYcNqxkqruJE+Rxp3CIv0y5AZMwPHQmOkz/cz+MM75SIzMTc0AUdXzGyTS8xMF8h3YWMQGgGEy8SBf1PQ== - dependencies: - "@types/bn.js" "^5.1.0" - "@types/node" "^12.12.6" - bignumber.js "^9.0.0" - web3-core-helpers "1.8.2" - web3-core-method "1.8.2" - web3-core-requestmanager "1.8.2" - web3-utils "1.8.2" - web3-eth-abi@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.0.tgz#53a7a2c95a571e205e27fd9e664df4919483cce1" @@ -19501,14 +17878,6 @@ web3-eth-abi@1.10.0: "@ethersproject/abi" "^5.6.3" web3-utils "1.10.0" -web3-eth-abi@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.1.tgz#4d64d1d272f35f7aaae7be9679493474e0af86c7" - integrity sha512-hk5NyeGweJYTjes7lBW7gtG7iYoN6HLt6E4FQDrHPdwZjwNmvzaOH9N8zMTCxNFXUlg0bzeTOzWwMA717a+4eg== - dependencies: - "@ethersproject/abi" "^5.6.3" - web3-utils "1.10.1" - web3-eth-abi@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.10.4.tgz#16c19d0bde0aaf8c1a56cb7743a83156d148d798" @@ -19525,14 +17894,6 @@ web3-eth-abi@1.7.0: "@ethersproject/abi" "5.0.7" web3-utils "1.7.0" -web3-eth-abi@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.8.2.tgz#16e1e9be40e2527404f041a4745111211488f31a" - integrity sha512-Om9g3kaRNjqiNPAgKwGT16y+ZwtBzRe4ZJFGjLiSs6v5I7TPNF+rRMWuKnR6jq0azQZDj6rblvKFMA49/k48Og== - dependencies: - "@ethersproject/abi" "^5.6.3" - web3-utils "1.8.2" - web3-eth-accounts@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.0.tgz#2942beca0a4291455f32cf09de10457a19a48117" @@ -19549,22 +17910,6 @@ web3-eth-accounts@1.10.0: web3-core-method "1.10.0" web3-utils "1.10.0" -web3-eth-accounts@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.1.tgz#24960e68c1ff3aaa57b96195c151f21bf9b9a0c0" - integrity sha512-x8zevaF4FYOIZqR3fdzdeKPf1Ek/O3HFptYH42IucYI5bK+o6ORebDuOOaIZqrF/c8ijcjGoo+cUDN9/5jU6Cw== - dependencies: - "@ethereumjs/common" "2.5.0" - "@ethereumjs/tx" "3.3.2" - "@ethereumjs/util" "^8.1.0" - eth-lib "0.2.8" - scrypt-js "^3.0.1" - uuid "^9.0.0" - web3-core "1.10.1" - web3-core-helpers "1.10.1" - web3-core-method "1.10.1" - web3-utils "1.10.1" - web3-eth-accounts@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.10.4.tgz#df30e85a7cd70e475f8cf52361befba408829e34" @@ -19581,22 +17926,6 @@ web3-eth-accounts@1.10.4: web3-core-method "1.10.4" web3-utils "1.10.4" -web3-eth-accounts@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.8.2.tgz#b894f5d5158fcae429da42de75d96520d0712971" - integrity sha512-c367Ij63VCz9YdyjiHHWLFtN85l6QghgwMQH2B1eM/p9Y5lTlTX7t/Eg/8+f1yoIStXbk2w/PYM2lk+IkbqdLA== - dependencies: - "@ethereumjs/common" "2.5.0" - "@ethereumjs/tx" "3.3.2" - eth-lib "0.2.8" - ethereumjs-util "^7.1.5" - scrypt-js "^3.0.1" - uuid "^9.0.0" - web3-core "1.8.2" - web3-core-helpers "1.8.2" - web3-core-method "1.8.2" - web3-utils "1.8.2" - web3-eth-contract@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.0.tgz#8e68c7654576773ec3c91903f08e49d0242c503a" @@ -19611,20 +17940,6 @@ web3-eth-contract@1.10.0: web3-eth-abi "1.10.0" web3-utils "1.10.0" -web3-eth-contract@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.1.tgz#8f8a514db8a1c81337e67a60054840edfc8c26bb" - integrity sha512-eRZItYq8LzSPOKqgkTaT1rRruXTNkjbeIe9Cs+VFx3+p/GHyUI1Rj4rfBXp1MBR6p4WK+oy05sB+FNugOYxe8Q== - dependencies: - "@types/bn.js" "^5.1.1" - web3-core "1.10.1" - web3-core-helpers "1.10.1" - web3-core-method "1.10.1" - web3-core-promievent "1.10.1" - web3-core-subscriptions "1.10.1" - web3-eth-abi "1.10.1" - web3-utils "1.10.1" - web3-eth-contract@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.10.4.tgz#22d39f04e11d9ff4e726e8025a56d78e843a2c3d" @@ -19639,20 +17954,6 @@ web3-eth-contract@1.10.4: web3-eth-abi "1.10.4" web3-utils "1.10.4" -web3-eth-contract@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.8.2.tgz#5388b7130923d2b790c09a420391a81312a867fb" - integrity sha512-ID5A25tHTSBNwOPjiXSVzxruz006ULRIDbzWTYIFTp7NJ7vXu/kynKK2ag/ObuTqBpMbobP8nXcA9b5EDkIdQA== - dependencies: - "@types/bn.js" "^5.1.0" - web3-core "1.8.2" - web3-core-helpers "1.8.2" - web3-core-method "1.8.2" - web3-core-promievent "1.8.2" - web3-core-subscriptions "1.8.2" - web3-eth-abi "1.8.2" - web3-utils "1.8.2" - web3-eth-ens@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.0.tgz#96a676524e0b580c87913f557a13ed810cf91cd9" @@ -19667,20 +17968,6 @@ web3-eth-ens@1.10.0: web3-eth-contract "1.10.0" web3-utils "1.10.0" -web3-eth-ens@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.1.tgz#3064b77f2de2c0f77136479f8b388a48e3fb1a85" - integrity sha512-WtcLhYTBeoKj+CbuyG3JQWcQynOXmv/l5CB27C3hJ42WWPa/XfUAsDmPbJp3YkqUbK3lE6iLT2yzwQIHfqmd0g== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - web3-core "1.10.1" - web3-core-helpers "1.10.1" - web3-core-promievent "1.10.1" - web3-eth-abi "1.10.1" - web3-eth-contract "1.10.1" - web3-utils "1.10.1" - web3-eth-ens@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.10.4.tgz#3d991adac52bc8e598f1f1b8528337fa6291004c" @@ -19695,20 +17982,6 @@ web3-eth-ens@1.10.4: web3-eth-contract "1.10.4" web3-utils "1.10.4" -web3-eth-ens@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.8.2.tgz#0a086ad4d919102e28b9fd3036df246add9df22a" - integrity sha512-PWph7C/CnqdWuu1+SH4U4zdrK4t2HNt0I4XzPYFdv9ugE8EuojselioPQXsVGvjql+Nt3jDLvQvggPqlMbvwRw== - dependencies: - content-hash "^2.5.2" - eth-ens-namehash "2.0.8" - web3-core "1.8.2" - web3-core-helpers "1.8.2" - web3-core-promievent "1.8.2" - web3-eth-abi "1.8.2" - web3-eth-contract "1.8.2" - web3-utils "1.8.2" - web3-eth-iban@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.0.tgz#5a46646401965b0f09a4f58e7248c8a8cd22538a" @@ -19717,14 +17990,6 @@ web3-eth-iban@1.10.0: bn.js "^5.2.1" web3-utils "1.10.0" -web3-eth-iban@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.1.tgz#8a40f16218cd774e965b72dc2ca36449f8fdb072" - integrity sha512-3n1ibzYIza9ac/iB/wEnzvnmut/u6g/x6WitxxdEMVUZshGqqnBv6HDVx25iO9TxWmala+GgmRKHnEMKCh74Yg== - dependencies: - bn.js "^5.2.1" - web3-utils "1.10.1" - web3-eth-iban@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.10.4.tgz#bc61b4a1930d19b1df8762c606d669902558e54d" @@ -19733,14 +17998,6 @@ web3-eth-iban@1.10.4: bn.js "^5.2.1" web3-utils "1.10.4" -web3-eth-iban@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.8.2.tgz#5cb3022234b13986f086353b53f0379a881feeaf" - integrity sha512-h3vNblDWkWMuYx93Q27TAJz6lhzpP93EiC3+45D6xoz983p6si773vntoQ+H+5aZhwglBtoiBzdh7PSSOnP/xQ== - dependencies: - bn.js "^5.2.1" - web3-utils "1.8.2" - web3-eth-personal@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.0.tgz#94d525f7a29050a0c2a12032df150ac5ea633071" @@ -19753,18 +18010,6 @@ web3-eth-personal@1.10.0: web3-net "1.10.0" web3-utils "1.10.0" -web3-eth-personal@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.1.tgz#ebeb86d4f812ef0a562129144b0ab10f0d07a0e7" - integrity sha512-Th4AEMbxUhH+GEqYpluWYBb+PszZ9GsdmsOhN8fo4aQHSKMfvyP+scqgOMqxK3rvobpSy/EZ6zdbAkinhoi55g== - dependencies: - "@types/node" "^12.12.6" - web3-core "1.10.1" - web3-core-helpers "1.10.1" - web3-core-method "1.10.1" - web3-net "1.10.1" - web3-utils "1.10.1" - web3-eth-personal@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.10.4.tgz#e2ee920f47e84848288e03442659cdbb2c4deea2" @@ -19777,18 +18022,6 @@ web3-eth-personal@1.10.4: web3-net "1.10.4" web3-utils "1.10.4" -web3-eth-personal@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.8.2.tgz#3526c1ebaa4e7bf3a0a8ec77e34f067cc9a750b2" - integrity sha512-Vg4HfwCr7doiUF/RC+Jz0wT4+cYaXcOWMAW2AHIjHX6Z7Xwa8nrURIeQgeEE62qcEHAzajyAdB1u6bJyTfuCXw== - dependencies: - "@types/node" "^12.12.6" - web3-core "1.8.2" - web3-core-helpers "1.8.2" - web3-core-method "1.8.2" - web3-net "1.8.2" - web3-utils "1.8.2" - web3-eth@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.0.tgz#38b905e2759697c9624ab080cfcf4e6c60b3a6cf" @@ -19807,24 +18040,6 @@ web3-eth@1.10.0: web3-net "1.10.0" web3-utils "1.10.0" -web3-eth@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.1.tgz#6a8b46e95e0df58afc06bb052aadca89448e5af6" - integrity sha512-EV/d/TFVZcB54wpx2ndFnApla+aztsBOpZkbDreHcETLN1v6XmXyKozo0gYoQMZElKZ6QRRPEFvDjPeXdA7DBw== - dependencies: - web3-core "1.10.1" - web3-core-helpers "1.10.1" - web3-core-method "1.10.1" - web3-core-subscriptions "1.10.1" - web3-eth-abi "1.10.1" - web3-eth-accounts "1.10.1" - web3-eth-contract "1.10.1" - web3-eth-ens "1.10.1" - web3-eth-iban "1.10.1" - web3-eth-personal "1.10.1" - web3-net "1.10.1" - web3-utils "1.10.1" - web3-eth@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.10.4.tgz#3a908c635cb5d935bd30473e452f3bd7f2ee66a5" @@ -19843,24 +18058,6 @@ web3-eth@1.10.4: web3-net "1.10.4" web3-utils "1.10.4" -web3-eth@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.8.2.tgz#8562287ae1803c30eb54dc7d832092e5739ce06a" - integrity sha512-JoTiWWc4F4TInpbvDUGb0WgDYJsFhuIjJlinc5ByjWD88Gvh+GKLsRjjFdbqe5YtwIGT4NymwoC5LQd1K6u/QQ== - dependencies: - web3-core "1.8.2" - web3-core-helpers "1.8.2" - web3-core-method "1.8.2" - web3-core-subscriptions "1.8.2" - web3-eth-abi "1.8.2" - web3-eth-accounts "1.8.2" - web3-eth-contract "1.8.2" - web3-eth-ens "1.8.2" - web3-eth-iban "1.8.2" - web3-eth-personal "1.8.2" - web3-net "1.8.2" - web3-utils "1.8.2" - web3-net@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.0.tgz#be53e7f5dafd55e7c9013d49c505448b92c9c97b" @@ -19870,15 +18067,6 @@ web3-net@1.10.0: web3-core-method "1.10.0" web3-utils "1.10.0" -web3-net@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.1.tgz#66c6a24c4b9b0bbd9603cc58decda346a8222365" - integrity sha512-06VgKyabOvj0mE7LkT1lY2A17sP32jpMAh2TniZ8ZgC3Dq36+C5LtrY17LgLSaModpvCPbpzPgbTlqB0xhssew== - dependencies: - web3-core "1.10.1" - web3-core-method "1.10.1" - web3-utils "1.10.1" - web3-net@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.10.4.tgz#20e12c60e4477d4298979d8d5d66b9abf8e66a09" @@ -19888,15 +18076,6 @@ web3-net@1.10.4: web3-core-method "1.10.4" web3-utils "1.10.4" -web3-net@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.8.2.tgz#97e1e0015fabc4cda31017813e98d0b5468dd04f" - integrity sha512-1itkDMGmbgb83Dg9nporFes9/fxsU7smJ3oRXlFkg4ZHn8YJyP1MSQFPJWWwSc+GrcCFt4O5IrUTvEkHqE3xag== - dependencies: - web3-core "1.8.2" - web3-core-method "1.8.2" - web3-utils "1.8.2" - web3-provider-engine@16.0.3: version "16.0.3" resolved "https://registry.yarnpkg.com/web3-provider-engine/-/web3-provider-engine-16.0.3.tgz#8ff93edf3a8da2f70d7f85c5116028c06a0d9f07" @@ -19935,16 +18114,6 @@ web3-providers-http@1.10.0: es6-promise "^4.2.8" web3-core-helpers "1.10.0" -web3-providers-http@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.10.1.tgz#cc35007edf1483b971652414c1d4d1820fbcd5c7" - integrity sha512-haHlG4Ig8VQdx+HdnJgJPpJwLWkAE1aXcacOfaGd2hnXPqVYRocwYqgZD/Q9pUq3u4rIZezhUaFXNRByzAfMsw== - dependencies: - abortcontroller-polyfill "^1.7.3" - cross-fetch "^3.1.4" - es6-promise "^4.2.8" - web3-core-helpers "1.10.1" - web3-providers-http@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.10.4.tgz#ca7aa58aeaf8123500c24ffe0595896319f830e8" @@ -19955,16 +18124,6 @@ web3-providers-http@1.10.4: es6-promise "^4.2.8" web3-core-helpers "1.10.4" -web3-providers-http@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.8.2.tgz#fbda3a3bbc8db004af36e91bec35f80273b37885" - integrity sha512-2xY94IIEQd16+b+vIBF4IC1p7GVaz9q4EUFscvMUjtEq4ru4Atdzjs9GP+jmcoo49p70II0UV3bqQcz0TQfVyQ== - dependencies: - abortcontroller-polyfill "^1.7.3" - cross-fetch "^3.1.4" - es6-promise "^4.2.8" - web3-core-helpers "1.8.2" - web3-providers-ipc@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.0.tgz#9747c7a6aee96a51488e32fa7c636c3460b39889" @@ -19973,14 +18132,6 @@ web3-providers-ipc@1.10.0: oboe "2.1.5" web3-core-helpers "1.10.0" -web3-providers-ipc@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.1.tgz#8d1126745da4e273034bb79aae483612768cc612" - integrity sha512-eYrLoC2OEOlxHdsWjKpw3gwKQuQG6rcd3lc41S6cC6UpkR2pszkXUTpXVKTKFFT3eWgVAYIVz/lCeilbYLgw5A== - dependencies: - oboe "2.1.5" - web3-core-helpers "1.10.1" - web3-providers-ipc@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.10.4.tgz#2e03437909e4e7771d646ff05518efae44b783c3" @@ -19989,14 +18140,6 @@ web3-providers-ipc@1.10.4: oboe "2.1.5" web3-core-helpers "1.10.4" -web3-providers-ipc@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.8.2.tgz#e52a7250f40c83b99a2482ec5b4cf2728377ae5c" - integrity sha512-p6fqKVGFg+WiXGHWnB1hu43PbvPkDHTz4RgoEzbXugv5rtv5zfYLqm8Ba6lrJOS5ks9kGKR21a0y3NzE3u7V4w== - dependencies: - oboe "2.1.5" - web3-core-helpers "1.8.2" - web3-providers-ws@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.0.tgz#cb0b87b94c4df965cdf486af3a8cd26daf3975e5" @@ -20006,15 +18149,6 @@ web3-providers-ws@1.10.0: web3-core-helpers "1.10.0" websocket "^1.0.32" -web3-providers-ws@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.1.tgz#b079f0a030ddc33b64a0692e7054fcfefe9fae43" - integrity sha512-ZCHGVH4YTVA5MCaOgmV0UJya7jTh4Vd0CFWiGqruha9/xF0fBZRYMm0awYcI9eDvVP0hRU/C9CeH5tj7UQBnTw== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.10.1" - websocket "^1.0.32" - web3-providers-ws@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.10.4.tgz#55d0c3ba36c6a79d105f02e20a707eb3978e7f82" @@ -20024,15 +18158,6 @@ web3-providers-ws@1.10.4: web3-core-helpers "1.10.4" websocket "^1.0.32" -web3-providers-ws@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.8.2.tgz#56a2b701387011aca9154ca4bc06ea4b5f27e4ef" - integrity sha512-3s/4K+wHgbiN+Zrp9YjMq2eqAF6QGABw7wFftPdx+m5hWImV27/MoIx57c6HffNRqZXmCHnfWWFCNHHsi7wXnA== - dependencies: - eventemitter3 "4.0.4" - web3-core-helpers "1.8.2" - websocket "^1.0.32" - web3-shh@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.0.tgz#c2979b87e0f67a7fef2ce9ee853bd7bfbe9b79a8" @@ -20043,16 +18168,6 @@ web3-shh@1.10.0: web3-core-subscriptions "1.10.0" web3-net "1.10.0" -web3-shh@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.1.tgz#ded94bf40bef752f52f0380f62386dd5ea00f0af" - integrity sha512-PoRfyM5NtHiQufxWDEgLhxpeDkkZos/ijjiT1IQafmD0iurMBxLU+k9OjRX2oblVyP3nPl1sSBQTYFe3b33JGA== - dependencies: - web3-core "1.10.1" - web3-core-method "1.10.1" - web3-core-subscriptions "1.10.1" - web3-net "1.10.1" - web3-shh@1.10.4: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.10.4.tgz#9852d6f3d05678e31e49235a60fea10ca7a9e21d" @@ -20063,16 +18178,6 @@ web3-shh@1.10.4: web3-core-subscriptions "1.10.4" web3-net "1.10.4" -web3-shh@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.8.2.tgz#217a417f0d6e243dd4d441848ffc2bd164cea8a0" - integrity sha512-uZ+3MAoNcaJsXXNCDnizKJ5viBNeHOFYsCbFhV755Uu52FswzTOw6DtE7yK9nYXMtIhiSgi7nwl1RYzP8pystw== - dependencies: - web3-core "1.8.2" - web3-core-method "1.8.2" - web3-core-subscriptions "1.8.2" - web3-net "1.8.2" - web3-utils@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.0.tgz#ca4c1b431a765c14ac7f773e92e0fd9377ccf578" @@ -20086,21 +18191,7 @@ web3-utils@1.10.0: randombytes "^2.1.0" utf8 "3.0.0" -web3-utils@1.10.1, web3-utils@^1.0.0-beta.31, web3-utils@^1.2.5, web3-utils@^1.3.4, web3-utils@^1.3.6, web3-utils@^1.8.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.1.tgz#97532130d85358628bc0ff14d94b7e9449786983" - integrity sha512-r6iUUw/uMnNcWXjhRv33Nyrhxq3VGOPBXeSzxhOXIci4SvC/LPTpROY0uTrMX7ztKyODYrHp8WhTkEf+ZnHssw== - dependencies: - "@ethereumjs/util" "^8.1.0" - bn.js "^5.2.1" - ethereum-bloom-filters "^1.0.6" - ethereum-cryptography "^2.1.2" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - utf8 "3.0.0" - -web3-utils@1.10.4: +web3-utils@1.10.4, web3-utils@^1.0.0-beta.31, web3-utils@^1.2.5, web3-utils@^1.3.4, web3-utils@^1.3.6, web3-utils@^1.8.1: version "1.10.4" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== @@ -20153,33 +18244,7 @@ web3@1.10.0: web3-shh "1.10.0" web3-utils "1.10.0" -web3@1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.8.2.tgz#95a4e5398fd0f01325264bf8e5e8cdc69a7afe86" - integrity sha512-92h0GdEHW9wqDICQQKyG4foZBYi0OQkyg4CRml2F7XBl/NG+fu9o6J19kzfFXzSBoA4DnJXbyRgj/RHZv5LRiw== - dependencies: - web3-bzz "1.8.2" - web3-core "1.8.2" - web3-eth "1.8.2" - web3-eth-personal "1.8.2" - web3-net "1.8.2" - web3-shh "1.8.2" - web3-utils "1.8.2" - -web3@^1.0.0-beta.34, web3@^1.2.5, web3@^1.8.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.1.tgz#6435783cb2c0a8347f62b7b1a6ade431f19dce2a" - integrity sha512-Ry+teufg6GYwIlLijyVTzZmnP+pu55vBU6P7rwK/rZidsMhc3m1lA5UXxiUVzBYZ8dvzV6+dVvOh68RrwrsI1w== - dependencies: - web3-bzz "1.10.1" - web3-core "1.10.1" - web3-eth "1.10.1" - web3-eth-personal "1.10.1" - web3-net "1.10.1" - web3-shh "1.10.1" - web3-utils "1.10.1" - -web3@^1.10.4: +web3@^1.0.0-beta.34, web3@^1.10.4, web3@^1.2.5, web3@^1.8.1: version "1.10.4" resolved "https://registry.yarnpkg.com/web3/-/web3-1.10.4.tgz#5d5e59b976eaf758b060fe1a296da5fe87bdc79c" integrity sha512-kgJvQZjkmjOEKimx/tJQsqWfRDPTTcBfYPa9XletxuHLpHcXdx67w8EFn5AW3eVxCutE9dTVHgGa9VYe8vgsEA== @@ -20333,7 +18398,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== -which-typed-array@^1.1.11, which-typed-array@^1.1.13: +which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.2: version "1.1.13" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== @@ -20344,18 +18409,6 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13: gopd "^1.0.1" has-tostringtag "^1.0.0" -which-typed-array@^1.1.2, which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" - which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -20401,7 +18454,7 @@ window-size@^0.2.0: resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" integrity sha512-UD7d8HFA2+PZsbKyaOCEy8gMh1oDtHgJh1LfgjQ4zVXmYjAT/kvz3PueITKuqDiIXQe7yzpPnxX3lNc+AhQMyw== -word-wrap@^1.2.3, word-wrap@~1.2.3: +word-wrap@~1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== @@ -20664,12 +18717,7 @@ yaml@1.10.2, yaml@^1.10.0, yaml@^1.10.2: resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== -yaml@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" - integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== - -yaml@^2.4.5: +yaml@^2.3.1, yaml@^2.4.5: version "2.5.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== @@ -20728,7 +18776,7 @@ yargs@16.2.0, yargs@^16.1.0, yargs@^16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@17.7.2, yargs@^17.6.2: +yargs@17.7.2, yargs@^17.0.0, yargs@^17.2.1, yargs@^17.6.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -20758,19 +18806,6 @@ yargs@^15.0.2, yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^17.0.0, yargs@^17.2.1: - version "17.7.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967" - integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw== - dependencies: - cliui "^8.0.1" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.1.1" - yargs@^4.7.1: version "4.8.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" From cf5a500692d858112af8648c55b2fba6085bd3ea Mon Sep 17 00:00:00 2001 From: Kaspar Kallas Date: Thu, 8 May 2025 12:26:00 +0300 Subject: [PATCH 58/59] fix test --- .../scheduler/test/VestingSchedulerV3.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol index 8b712841bd..ce5282242e 100644 --- a/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol +++ b/packages/automation-contracts/scheduler/test/VestingSchedulerV3.t.sol @@ -2399,7 +2399,7 @@ contract VestingSchedulerV3Tests is FoundrySuperfluidTester { } function test_executeCliffAndFlow_claimableScheduleWithCliffAmount_cannotClaimOnBehalf(address _claimer) public { - vm.assume(isTrustedForwarder(_claimer) == false); + vm.assume(vestingScheduler.isTrustedForwarder(_claimer) == false); vm.assume(_claimer != address(0) && _claimer != alice && _claimer != bob); _setACL_AUTHORIZE_FULL_CONTROL(alice, FLOW_RATE); _createClaimableVestingScheduleWithDefaultData(alice, bob); From 66c98e7c77df1f3987b9b738f3eb1d93a5bcf3f5 Mon Sep 17 00:00:00 2001 From: "Miao, ZhiCheng" Date: Thu, 8 May 2025 14:39:19 +0300 Subject: [PATCH 59/59] use codecov-action 5.4.2 --- .github/workflows/call.upload-coverage-reports.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/call.upload-coverage-reports.yml b/.github/workflows/call.upload-coverage-reports.yml index c2279b0f7d..4daa1372fb 100644 --- a/.github/workflows/call.upload-coverage-reports.yml +++ b/.github/workflows/call.upload-coverage-reports.yml @@ -21,7 +21,7 @@ jobs: name: ethereum-contracts-coverage path: packages/ethereum-contracts/coverage - name: Upload ethereum-contracts-coverage to codecov - uses: codecov/codecov-action@v4.3.0 + uses: codecov/codecov-action@v5.4.2 with: token: ${{ secrets.codecov_token }} files: packages/ethereum-contracts/coverage/lcov.info @@ -35,7 +35,7 @@ jobs: name: sdk-core-coverage path: packages/sdk-core/coverage - name: Upload sdk-core-coverage to codecov - uses: codecov/codecov-action@v4.2.0 + uses: codecov/codecov-action@v5.4.2 with: token: ${{ secrets.codecov_token }} files: packages/sdk-core/coverage/lcov.info