Starting with Solidity version 0.8.8, using the override keyword when the function solely overrides an interface function, and the function doesn't exist in multiple base contracts, is unnecessary.
Click to show 64 findings
File: v2-core/src/SablierV2LockupDynamic.sol
function getSegments(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupDynamic.Segment[] memory segments)
{
segments = _segments[streamId];
}
function getStream(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupDynamic.StreamLD memory stream)
{
Lockup.Stream memory lockupStream = _streams[streamId];
if (_statusOf(streamId) == Lockup.Status.SETTLED) {
lockupStream.isCancelable = false;
}
stream = LockupDynamic.StreamLD({
amounts: lockupStream.amounts,
asset: lockupStream.asset,
endTime: lockupStream.endTime,
isCancelable: lockupStream.isCancelable,
isDepleted: lockupStream.isDepleted,
isStream: lockupStream.isStream,
isTransferable: lockupStream.isTransferable,
recipient: _ownerOf(streamId),
segments: _segments[streamId],
sender: lockupStream.sender,
startTime: lockupStream.startTime,
wasCanceled: lockupStream.wasCanceled
});
}
function getTimestamps(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupDynamic.Timestamps memory timestamps)
{
timestamps = LockupDynamic.Timestamps({ start: _streams[streamId].startTime, end: _streams[streamId].endTime });
}
function createWithDurations(LockupDynamic.CreateWithDurations calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
LockupDynamic.Segment[] memory segments = Helpers.calculateSegmentTimestamps(params.segments);
streamId = _create(
LockupDynamic.CreateWithTimestamps({
sender: params.sender,
recipient: params.recipient,
totalAmount: params.totalAmount,
asset: params.asset,
cancelable: params.cancelable,
transferable: params.transferable,
startTime: uint40(block.timestamp),
segments: segments,
broker: params.broker
})
);
}
function createWithTimestamps(LockupDynamic.CreateWithTimestamps calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
streamId = _create(params);
}
function _calculateStreamedAmount(uint256 streamId) internal view override returns (uint128) {
uint40 blockTimestamp = uint40(block.timestamp);
if (_streams[streamId].startTime >= blockTimestamp) {
return 0;
}
uint40 endTime = _streams[streamId].endTime;
if (endTime <= blockTimestamp) {
return _streams[streamId].amounts.deposited;
}
if (_segments[streamId].length > 1) {
return _calculateStreamedAmountForMultipleSegments(streamId);
} else {
return _calculateStreamedAmountForOneSegment(streamId);
}
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/SablierV2LockupDynamic.sol#L191:211
File: v2-core/src/SablierV2LockupLinear.sol
function getStream(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupLinear.StreamLL memory stream)
{
Lockup.Stream memory lockupStream = _streams[streamId];
if (_statusOf(streamId) == Lockup.Status.SETTLED) {
lockupStream.isCancelable = false;
}
stream = LockupLinear.StreamLL({
amounts: lockupStream.amounts,
asset: lockupStream.asset,
cliffTime: _cliffs[streamId],
endTime: lockupStream.endTime,
isCancelable: lockupStream.isCancelable,
isTransferable: lockupStream.isTransferable,
isDepleted: lockupStream.isDepleted,
isStream: lockupStream.isStream,
recipient: _ownerOf(streamId),
sender: lockupStream.sender,
startTime: lockupStream.startTime,
wasCanceled: lockupStream.wasCanceled
});
}
function getTimestamps(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupLinear.Timestamps memory timestamps)
{
timestamps = LockupLinear.Timestamps({
start: _streams[streamId].startTime,
cliff: _cliffs[streamId],
end: _streams[streamId].endTime
});
}
function createWithDurations(LockupLinear.CreateWithDurations calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
LockupLinear.Timestamps memory timestamps;
timestamps.start = uint40(block.timestamp);
unchecked {
if (params.durations.cliff > 0) {
timestamps.cliff = timestamps.start + params.durations.cliff;
}
timestamps.end = timestamps.start + params.durations.total;
}
streamId = _create(
LockupLinear.CreateWithTimestamps({
sender: params.sender,
recipient: params.recipient,
totalAmount: params.totalAmount,
asset: params.asset,
cancelable: params.cancelable,
transferable: params.transferable,
timestamps: timestamps,
broker: params.broker
})
);
}
function createWithTimestamps(LockupLinear.CreateWithTimestamps calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
streamId = _create(params);
}
function _calculateStreamedAmount(uint256 streamId) internal view override returns (uint128) {
uint256 cliffTime = uint256(_cliffs[streamId]);
uint256 blockTimestamp = block.timestamp;
if (cliffTime > blockTimestamp) {
return 0;
}
uint256 endTime = uint256(_streams[streamId].endTime);
if (blockTimestamp >= endTime) {
return _streams[streamId].amounts.deposited;
}
unchecked {
uint256 startTime = uint256(_streams[streamId].startTime);
UD60x18 elapsedTime = ud(blockTimestamp - startTime);
UD60x18 totalDuration = ud(endTime - startTime);
UD60x18 elapsedTimePercentage = elapsedTime.div(totalDuration);
UD60x18 depositedAmount = ud(_streams[streamId].amounts.deposited);
UD60x18 streamedAmount = elapsedTimePercentage.mul(depositedAmount);
if (streamedAmount.gt(depositedAmount)) {
return _streams[streamId].amounts.withdrawn;
}
return uint128(streamedAmount.intoUint256());
}
}
function getCliffTime(uint256 streamId) external view override notNull(streamId) returns (uint40 cliffTime) {
cliffTime = _cliffs[streamId];
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/SablierV2LockupLinear.sol#L71:73
File: v2-core/src/SablierV2LockupTranched.sol
function getStream(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupTranched.StreamLT memory stream)
{
Lockup.Stream memory lockupStream = _streams[streamId];
if (_statusOf(streamId) == Lockup.Status.SETTLED) {
lockupStream.isCancelable = false;
}
stream = LockupTranched.StreamLT({
amounts: lockupStream.amounts,
asset: lockupStream.asset,
endTime: lockupStream.endTime,
isCancelable: lockupStream.isCancelable,
isDepleted: lockupStream.isDepleted,
isStream: lockupStream.isStream,
isTransferable: lockupStream.isTransferable,
recipient: _ownerOf(streamId),
sender: lockupStream.sender,
startTime: lockupStream.startTime,
tranches: _tranches[streamId],
wasCanceled: lockupStream.wasCanceled
});
}
function getTimestamps(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupTranched.Timestamps memory timestamps)
{
timestamps = LockupTranched.Timestamps({ start: _streams[streamId].startTime, end: _streams[streamId].endTime });
}
function createWithDurations(LockupTranched.CreateWithDurations calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
LockupTranched.Tranche[] memory tranches = Helpers.calculateTrancheTimestamps(params.tranches);
streamId = _create(
LockupTranched.CreateWithTimestamps({
sender: params.sender,
recipient: params.recipient,
totalAmount: params.totalAmount,
asset: params.asset,
cancelable: params.cancelable,
transferable: params.transferable,
startTime: uint40(block.timestamp),
tranches: tranches,
broker: params.broker
})
);
}
function createWithTimestamps(LockupTranched.CreateWithTimestamps calldata params)
external
override
noDelegateCall
returns (uint256 streamId)
{
streamId = _create(params);
}
function _calculateStreamedAmount(uint256 streamId) internal view override returns (uint128) {
uint40 blockTimestamp = uint40(block.timestamp);
LockupTranched.Tranche[] memory tranches = _tranches[streamId];
if (tranches[0].timestamp > blockTimestamp) {
return 0;
}
if (_streams[streamId].endTime <= blockTimestamp) {
return _streams[streamId].amounts.deposited;
}
uint128 streamedAmount = tranches[0].amount;
for (uint256 i = 1; i < tranches.length; ++i) {
if (tranches[i].timestamp > blockTimestamp) {
break;
}
unchecked {
streamedAmount += tranches[i].amount;
}
}
return streamedAmount;
}
function getTranches(uint256 streamId)
external
view
override
notNull(streamId)
returns (LockupTranched.Tranche[] memory tranches)
{
tranches = _tranches[streamId];
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/SablierV2LockupTranched.sol#L118:126
File: v2-core/src/SablierV2NFTDescriptor.sol
function tokenURI(IERC721Metadata sablier, uint256 streamId) external view override returns (string memory uri) {
TokenURIVars memory vars;
vars.sablier = ISablierV2Lockup(address(sablier));
vars.sablierModel = mapSymbol(sablier);
vars.sablierStringified = address(sablier).toHexString();
vars.asset = address(vars.sablier.getAsset(streamId));
vars.assetSymbol = safeAssetSymbol(vars.asset);
vars.depositedAmount = vars.sablier.getDepositedAmount(streamId);
vars.status = stringifyStatus(vars.sablier.statusOf(streamId));
vars.streamedPercentage = calculateStreamedPercentage({
streamedAmount: vars.sablier.streamedAmountOf(streamId),
depositedAmount: vars.depositedAmount
});
vars.svg = NFTSVG.generateSVG(
NFTSVG.SVGParams({
accentColor: generateAccentColor(address(sablier), streamId),
amount: abbreviateAmount({ amount: vars.depositedAmount, decimals: safeAssetDecimals(vars.asset) }),
assetAddress: vars.asset.toHexString(),
assetSymbol: vars.assetSymbol,
duration: calculateDurationInDays({
startTime: vars.sablier.getStartTime(streamId),
endTime: vars.sablier.getEndTime(streamId)
}),
sablierAddress: vars.sablierStringified,
progress: stringifyPercentage(vars.streamedPercentage),
progressNumerical: vars.streamedPercentage,
status: vars.status,
sablierModel: vars.sablierModel
})
);
(vars.success, vars.returnData) =
address(vars.sablier).staticcall(abi.encodeCall(ISablierV2Lockup.isTransferable, (streamId)));
vars.isTransferable = vars.success ? abi.decode(vars.returnData, (bool)) : true;
vars.json = string.concat(
'{"attributes":',
generateAttributes({
assetSymbol: vars.assetSymbol,
sender: vars.sablier.getSender(streamId).toHexString(),
status: vars.status
}),
',"description":"',
generateDescription({
sablierModel: vars.sablierModel,
assetSymbol: vars.assetSymbol,
sablierStringified: vars.sablierStringified,
assetAddress: vars.asset.toHexString(),
streamId: streamId.toString(),
isTransferable: vars.isTransferable
}),
'","external_url":"https://sablier.com","name":"',
generateName({ sablierModel: vars.sablierModel, streamId: streamId.toString() }),
'","image":"data:image/svg+xml;base64,',
Base64.encode(bytes(vars.svg)),
'"}'
);
uri = string.concat("data:application/json;base64,", Base64.encode(bytes(vars.json)));
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/SablierV2NFTDescriptor.sol#L47:117
File: v2-core/src/abstracts/SablierV2Lockup.sol
function tokenURI(uint256 streamId) public view override(IERC721Metadata, ERC721) returns (string memory uri) {
_requireOwned({ tokenId: streamId });
uri = nftDescriptor.tokenURI({ sablier: this, streamId: streamId });
}
function getAsset(uint256 streamId) external view override notNull(streamId) returns (IERC20 asset) {
asset = _streams[streamId].asset;
}
function getDepositedAmount(uint256 streamId)
external
view
override
notNull(streamId)
returns (uint128 depositedAmount)
{
depositedAmount = _streams[streamId].amounts.deposited;
}
function getEndTime(uint256 streamId) external view override notNull(streamId) returns (uint40 endTime) {
endTime = _streams[streamId].endTime;
}
function getRecipient(uint256 streamId) external view override returns (address recipient) {
recipient = _requireOwned({ tokenId: streamId });
}
function getRefundedAmount(uint256 streamId)
external
view
override
notNull(streamId)
returns (uint128 refundedAmount)
{
refundedAmount = _streams[streamId].amounts.refunded;
}
function getSender(uint256 streamId) external view override notNull(streamId) returns (address sender) {
sender = _streams[streamId].sender;
}
function getStartTime(uint256 streamId) external view override notNull(streamId) returns (uint40 startTime) {
startTime = _streams[streamId].startTime;
}
function getWithdrawnAmount(uint256 streamId)
external
view
override
notNull(streamId)
returns (uint128 withdrawnAmount)
{
withdrawnAmount = _streams[streamId].amounts.withdrawn;
}
function isCancelable(uint256 streamId) external view override notNull(streamId) returns (bool result) {
if (_statusOf(streamId) != Lockup.Status.SETTLED) {
result = _streams[streamId].isCancelable;
}
}
function isCold(uint256 streamId) external view override notNull(streamId) returns (bool result) {
Lockup.Status status = _statusOf(streamId);
result = status == Lockup.Status.SETTLED || status == Lockup.Status.CANCELED || status == Lockup.Status.DEPLETED;
}
function isDepleted(uint256 streamId) external view override notNull(streamId) returns (bool result) {
result = _streams[streamId].isDepleted;
}
function isStream(uint256 streamId) external view override returns (bool result) {
result = _streams[streamId].isStream;
}
function isTransferable(uint256 streamId) external view override notNull(streamId) returns (bool result) {
result = _streams[streamId].isTransferable;
}
function isWarm(uint256 streamId) external view override notNull(streamId) returns (bool result) {
Lockup.Status status = _statusOf(streamId);
result = status == Lockup.Status.PENDING || status == Lockup.Status.STREAMING;
}
function refundableAmountOf(uint256 streamId)
external
view
override
notNull(streamId)
returns (uint128 refundableAmount)
{
if (_streams[streamId].isCancelable && !_streams[streamId].isDepleted) {
refundableAmount = _streams[streamId].amounts.deposited - _calculateStreamedAmount(streamId);
}
}
function statusOf(uint256 streamId) external view override notNull(streamId) returns (Lockup.Status status) {
status = _statusOf(streamId);
}
function streamedAmountOf(uint256 streamId)
public
view
override
notNull(streamId)
returns (uint128 streamedAmount)
{
streamedAmount = _streamedAmountOf(streamId);
}
function wasCanceled(uint256 streamId) external view override notNull(streamId) returns (bool result) {
result = _streams[streamId].wasCanceled;
}
function withdrawableAmountOf(uint256 streamId)
external
view
override
notNull(streamId)
returns (uint128 withdrawableAmount)
{
withdrawableAmount = _withdrawableAmountOf(streamId);
}
function burn(uint256 streamId) external override noDelegateCall notNull(streamId) {
if (!_streams[streamId].isDepleted) {
revert Errors.SablierV2Lockup_StreamNotDepleted(streamId);
}
if (!_isCallerStreamRecipientOrApproved(streamId)) {
revert Errors.SablierV2Lockup_Unauthorized(streamId, msg.sender);
}
_burn({ tokenId: streamId });
}
function cancel(uint256 streamId) public override noDelegateCall notNull(streamId) {
if (_streams[streamId].isDepleted) {
revert Errors.SablierV2Lockup_StreamDepleted(streamId);
} else if (_streams[streamId].wasCanceled) {
revert Errors.SablierV2Lockup_StreamCanceled(streamId);
}
if (!_isCallerStreamSender(streamId)) {
revert Errors.SablierV2Lockup_Unauthorized(streamId, msg.sender);
}
_cancel(streamId);
}
function cancelMultiple(uint256[] calldata streamIds) external override noDelegateCall {
uint256 count = streamIds.length;
for (uint256 i = 0; i < count; ++i) {
cancel(streamIds[i]);
}
}
function renounce(uint256 streamId) external override noDelegateCall notNull(streamId) updateMetadata(streamId) {
Lockup.Status status = _statusOf(streamId);
if (status == Lockup.Status.DEPLETED) {
revert Errors.SablierV2Lockup_StreamDepleted(streamId);
} else if (status == Lockup.Status.CANCELED) {
revert Errors.SablierV2Lockup_StreamCanceled(streamId);
} else if (status == Lockup.Status.SETTLED) {
revert Errors.SablierV2Lockup_StreamSettled(streamId);
}
if (!_isCallerStreamSender(streamId)) {
revert Errors.SablierV2Lockup_Unauthorized(streamId, msg.sender);
}
_renounce(streamId);
emit ISablierV2Lockup.RenounceLockupStream(streamId);
address recipient = _ownerOf(streamId);
if (recipient.code.length > 0) {
try ISablierV2Recipient(recipient).onLockupStreamRenounced(streamId) { } catch { }
}
}
function setNFTDescriptor(ISablierV2NFTDescriptor newNFTDescriptor) external override onlyAdmin {
ISablierV2NFTDescriptor oldNftDescriptor = nftDescriptor;
nftDescriptor = newNFTDescriptor;
emit ISablierV2Lockup.SetNFTDescriptor({
admin: msg.sender,
oldNFTDescriptor: oldNftDescriptor,
newNFTDescriptor: newNFTDescriptor
});
emit BatchMetadataUpdate({ _fromTokenId: 1, _toTokenId: nextStreamId - 1 });
}
function withdraw(
uint256 streamId,
address to,
uint128 amount
)
public
override
noDelegateCall
notNull(streamId)
updateMetadata(streamId)
{
if (_streams[streamId].isDepleted) {
revert Errors.SablierV2Lockup_StreamDepleted(streamId);
}
if (to == address(0)) {
revert Errors.SablierV2Lockup_WithdrawToZeroAddress(streamId);
}
if (amount == 0) {
revert Errors.SablierV2Lockup_WithdrawAmountZero(streamId);
}
address recipient = _ownerOf(streamId);
if (to != recipient && !_isCallerStreamRecipientOrApproved(streamId)) {
revert Errors.SablierV2Lockup_WithdrawalAddressNotRecipient(streamId, msg.sender, to);
}
uint128 withdrawableAmount = _withdrawableAmountOf(streamId);
if (amount > withdrawableAmount) {
revert Errors.SablierV2Lockup_Overdraw(streamId, amount, withdrawableAmount);
}
address sender = _streams[streamId].sender;
_withdraw(streamId, to, amount);
if (msg.sender != recipient && recipient.code.length > 0) {
try ISablierV2Recipient(recipient).onLockupStreamWithdrawn({
streamId: streamId,
caller: msg.sender,
to: to,
amount: amount
}) { } catch { }
}
if (msg.sender != sender && sender.code.length > 0 && sender != recipient) {
try ISablierV2Sender(sender).onLockupStreamWithdrawn({
streamId: streamId,
caller: msg.sender,
to: to,
amount: amount
}) { } catch { }
}
}
function withdrawMax(uint256 streamId, address to) external override {
withdraw({ streamId: streamId, to: to, amount: _withdrawableAmountOf(streamId) });
}
function withdrawMaxAndTransfer(
uint256 streamId,
address newRecipient
)
external
override
noDelegateCall
notNull(streamId)
{
address currentRecipient = _ownerOf(streamId);
if (msg.sender != currentRecipient) {
revert Errors.SablierV2Lockup_Unauthorized(streamId, msg.sender);
}
uint128 withdrawableAmount = _withdrawableAmountOf(streamId);
if (withdrawableAmount > 0) {
withdraw({ streamId: streamId, to: currentRecipient, amount: withdrawableAmount });
}
_transfer({ from: currentRecipient, to: newRecipient, tokenId: streamId });
}
function withdrawMultiple(
uint256[] calldata streamIds,
uint128[] calldata amounts
)
external
override
noDelegateCall
{
uint256 streamIdsCount = streamIds.length;
uint256 amountsCount = amounts.length;
if (streamIdsCount != amountsCount) {
revert Errors.SablierV2Lockup_WithdrawArrayCountsNotEqual(streamIdsCount, amountsCount);
}
for (uint256 i = 0; i < streamIdsCount; ++i) {
withdraw({ streamId: streamIds[i], to: _ownerOf(streamIds[i]), amount: amounts[i] });
}
}
function _update(
address to,
uint256 streamId,
address auth
)
internal
override
updateMetadata(streamId)
returns (address)
{
address from = _ownerOf(streamId);
if (from != address(0) && to != address(0) && !_streams[streamId].isTransferable) {
revert Errors.SablierV2Lockup_NotTransferable(streamId);
}
return super._update(to, streamId, auth);
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/abstracts/SablierV2Lockup.sol#L522:539
File: v2-core/src/abstracts/Adminable.sol
function transferAdmin(address newAdmin) public virtual override onlyAdmin {
admin = newAdmin;
emit IAdminable.TransferAdmin({ oldAdmin: msg.sender, newAdmin: newAdmin });
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/abstracts/Adminable.sol#L34:40
File: v2-periphery/src/SablierV2BatchLockup.sol
function createWithDurationsLD(
ISablierV2LockupDynamic lockupDynamic,
IERC20 asset,
BatchLockup.CreateWithDurationsLD[] calldata batch
)
external
override
returns (uint256[] memory streamIds)
{
uint256 batchSize = batch.length;
if (batchSize == 0) {
revert Errors.SablierV2BatchLockup_BatchSizeZero();
}
uint256 i;
uint256 transferAmount;
for (i = 0; i < batchSize; ++i) {
unchecked {
transferAmount += batch[i].totalAmount;
}
}
_handleTransfer(address(lockupDynamic), asset, transferAmount);
streamIds = new uint256[](batchSize);
for (i = 0; i < batchSize; ++i) {
streamIds[i] = lockupDynamic.createWithDurations(
LockupDynamic.CreateWithDurations({
sender: batch[i].sender,
recipient: batch[i].recipient,
totalAmount: batch[i].totalAmount,
asset: asset,
cancelable: batch[i].cancelable,
transferable: batch[i].transferable,
segments: batch[i].segments,
broker: batch[i].broker
})
);
}
}
function createWithTimestampsLD(
ISablierV2LockupDynamic lockupDynamic,
IERC20 asset,
BatchLockup.CreateWithTimestampsLD[] calldata batch
)
external
override
returns (uint256[] memory streamIds)
{
uint256 batchSize = batch.length;
if (batchSize == 0) {
revert Errors.SablierV2BatchLockup_BatchSizeZero();
}
uint256 i;
uint256 transferAmount;
for (i = 0; i < batchSize; ++i) {
unchecked {
transferAmount += batch[i].totalAmount;
}
}
_handleTransfer(address(lockupDynamic), asset, transferAmount);
streamIds = new uint256[](batchSize);
for (i = 0; i < batchSize; ++i) {
streamIds[i] = lockupDynamic.createWithTimestamps(
LockupDynamic.CreateWithTimestamps({
sender: batch[i].sender,
recipient: batch[i].recipient,
totalAmount: batch[i].totalAmount,
asset: asset,
cancelable: batch[i].cancelable,
transferable: batch[i].transferable,
startTime: batch[i].startTime,
segments: batch[i].segments,
broker: batch[i].broker
})
);
}
}
function createWithDurationsLL(
ISablierV2LockupLinear lockupLinear,
IERC20 asset,
BatchLockup.CreateWithDurationsLL[] calldata batch
)
external
override
returns (uint256[] memory streamIds)
{
uint256 batchSize = batch.length;
if (batchSize == 0) {
revert Errors.SablierV2BatchLockup_BatchSizeZero();
}
uint256 i;
uint256 transferAmount;
for (i = 0; i < batchSize; ++i) {
unchecked {
transferAmount += batch[i].totalAmount;
}
}
_handleTransfer(address(lockupLinear), asset, transferAmount);
streamIds = new uint256[](batchSize);
for (i = 0; i < batchSize; ++i) {
streamIds[i] = lockupLinear.createWithDurations(
LockupLinear.CreateWithDurations({
sender: batch[i].sender,
recipient: batch[i].recipient,
totalAmount: batch[i].totalAmount,
asset: asset,
cancelable: batch[i].cancelable,
transferable: batch[i].transferable,
durations: batch[i].durations,
broker: batch[i].broker
})
);
}
}
function createWithTimestampsLL(
ISablierV2LockupLinear lockupLinear,
IERC20 asset,
BatchLockup.CreateWithTimestampsLL[] calldata batch
)
external
override
returns (uint256[] memory streamIds)
{
uint256 batchSize = batch.length;
if (batchSize == 0) {
revert Errors.SablierV2BatchLockup_BatchSizeZero();
}
uint256 i;
uint256 transferAmount;
for (i = 0; i < batchSize; ++i) {
unchecked {
transferAmount += batch[i].totalAmount;
}
}
_handleTransfer(address(lockupLinear), asset, transferAmount);
streamIds = new uint256[](batchSize);
for (i = 0; i < batchSize; ++i) {
streamIds[i] = lockupLinear.createWithTimestamps(
LockupLinear.CreateWithTimestamps({
sender: batch[i].sender,
recipient: batch[i].recipient,
totalAmount: batch[i].totalAmount,
asset: asset,
cancelable: batch[i].cancelable,
transferable: batch[i].transferable,
timestamps: batch[i].timestamps,
broker: batch[i].broker
})
);
}
}
function createWithDurationsLT(
ISablierV2LockupTranched lockupTranched,
IERC20 asset,
BatchLockup.CreateWithDurationsLT[] calldata batch
)
external
override
returns (uint256[] memory streamIds)
{
uint256 batchSize = batch.length;
if (batchSize == 0) {
revert Errors.SablierV2BatchLockup_BatchSizeZero();
}
uint256 i;
uint256 transferAmount;
for (i = 0; i < batchSize; ++i) {
unchecked {
transferAmount += batch[i].totalAmount;
}
}
_handleTransfer(address(lockupTranched), asset, transferAmount);
streamIds = new uint256[](batchSize);
for (i = 0; i < batchSize; ++i) {
streamIds[i] = lockupTranched.createWithDurations(
LockupTranched.CreateWithDurations({
sender: batch[i].sender,
recipient: batch[i].recipient,
totalAmount: batch[i].totalAmount,
asset: asset,
cancelable: batch[i].cancelable,
transferable: batch[i].transferable,
tranches: batch[i].tranches,
broker: batch[i].broker
})
);
}
}
function createWithTimestampsLT(
ISablierV2LockupTranched lockupTranched,
IERC20 asset,
BatchLockup.CreateWithTimestampsLT[] calldata batch
)
external
override
returns (uint256[] memory streamIds)
{
uint256 batchSize = batch.length;
if (batchSize == 0) {
revert Errors.SablierV2BatchLockup_BatchSizeZero();
}
uint256 i;
uint256 transferAmount;
for (i = 0; i < batchSize; ++i) {
unchecked {
transferAmount += batch[i].totalAmount;
}
}
_handleTransfer(address(lockupTranched), asset, transferAmount);
streamIds = new uint256[](batchSize);
for (i = 0; i < batchSize; ++i) {
streamIds[i] = lockupTranched.createWithTimestamps(
LockupTranched.CreateWithTimestamps({
sender: batch[i].sender,
recipient: batch[i].recipient,
totalAmount: batch[i].totalAmount,
asset: asset,
cancelable: batch[i].cancelable,
transferable: batch[i].transferable,
startTime: batch[i].startTime,
tranches: batch[i].tranches,
broker: batch[i].broker
})
);
}
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-periphery/src/SablierV2BatchLockup.sol#L274:320
File: v2-periphery/src/SablierV2MerkleLL.sol
function claim(
uint256 index,
address recipient,
uint128 amount,
bytes32[] calldata merkleProof
)
external
override
returns (uint256 streamId)
{
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount))));
_checkClaim(index, leaf, merkleProof);
_claimedBitMap.set(index);
streamId = LOCKUP_LINEAR.createWithDurations(
LockupLinear.CreateWithDurations({
sender: admin,
recipient: recipient,
totalAmount: amount,
asset: ASSET,
cancelable: CANCELABLE,
transferable: TRANSFERABLE,
durations: streamDurations,
broker: Broker({ account: address(0), fee: ud(0) })
})
);
emit Claim(index, recipient, amount, streamId);
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-periphery/src/SablierV2MerkleLL.sol#L59:95
File: v2-periphery/src/SablierV2MerkleLT.sol
function claim(
uint256 index,
address recipient,
uint128 amount,
bytes32[] calldata merkleProof
)
external
override
returns (uint256 streamId)
{
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount))));
_checkClaim(index, leaf, merkleProof);
LockupTranched.TrancheWithDuration[] memory tranches = _calculateTranches(amount);
_claimedBitMap.set(index);
streamId = LOCKUP_TRANCHED.createWithDurations(
LockupTranched.CreateWithDurations({
sender: admin,
recipient: recipient,
totalAmount: amount,
asset: ASSET,
cancelable: CANCELABLE,
transferable: TRANSFERABLE,
tranches: tranches,
broker: Broker({ account: address(0), fee: ZERO })
})
);
emit Claim(index, recipient, amount, streamId);
}
function getTranchesWithPercentages() external view override returns (MerkleLT.TrancheWithPercentage[] memory) {
return _tranchesWithPercentages;
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-periphery/src/SablierV2MerkleLT.sol#L65:67
File: v2-periphery/src/abstracts/SablierV2MerkleLockup.sol
function getFirstClaimTime() external view override returns (uint40) {
return _firstClaimTime;
}
function hasClaimed(uint256 index) public view override returns (bool) {
return _claimedBitMap.get(index);
}
function hasExpired() public view override returns (bool) {
return EXPIRATION > 0 && EXPIRATION <= block.timestamp;
}
function name() external view override returns (string memory) {
return string(abi.encodePacked(NAME));
}
function clawback(address to, uint128 amount) external override onlyAdmin {
if (_hasGracePeriodPassed() && !hasExpired()) {
revert Errors.SablierV2MerkleLockup_ClawbackNotAllowed({
blockTimestamp: block.timestamp,
expiration: EXPIRATION,
firstClaimTime: _firstClaimTime
});
}
ASSET.safeTransfer(to, amount);
emit Clawback(admin, to, amount);
}
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-periphery/src/abstracts/SablierV2MerkleLockup.sol#L107:122
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/SablierV2NFTDescriptor.sol#L0:0
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-periphery/src/abstracts/SablierV2MerkleLockup.sol#L0:0
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-periphery/src/abstracts/SablierV2MerkleLockup.sol#L0:0
The Solidity style guide says to use mixedCase for local and state variable names. Note that while OpenZeppelin may not follow this advice, it still is the recommended way of naming variables.
The normal name for loop variables is i, and when there is a nested loop, to use j. Not following this convention may lead to some reviewer confusion.
https://github.com/Cyfrin/2024-05-Sablier/tree/main/v2-core/src/libraries/Helpers.sol#L0:0