Platform: Code4rena
Start Date: 05/09/2023
Pot Size: $50,000 USDC
Total HM: 2
Participants: 16
Period: 6 days
Judge: GalloDaSballo
Total Solo HM: 2
Id: 284
League: ETH
Rank: 3/16
Findings: 2
Award: $1,118.51
🌟 Selected for report: 1
🚀 Solo Findings: 0
404.9585 USDC - $404.96
Id | Title |
---|---|
[L-01] | previewOrder should not revert |
[L-02] | Withdraw should revert with a not supported delegationType |
[L-03] | Lack of data on flashloan could make some ERC1155 unusable |
[L-04] | Using delegatecall inside a loop may cause issues with payable functions |
[L-05] | CreateOfferer uses a custom context implementation instead of an existing SIP |
previewOrder
should not revertThe official documentation says that previewOrder
should not revert, as it may cause issues to the caller:
An optimal Seaport app should return an order with penalties when its previewOrder function is called with unacceptable minimumReceived and maximumSpent arrays, so that the caller can learn what the Seaport app expects. But it should revert when its generateOrder is called with unacceptable minimumReceived and maximumSpent arrays, so the function fails fast, gets skipped, and avoids wasting gas by leaving the validation to Seaport.
function previewOrder(address caller, address, SpentItem[] calldata minimumReceived, SpentItem[] calldata maximumSpent, bytes calldata context) external view onlySeaport(caller) returns (SpentItem[] memory offer, ReceivedItem[] memory consideration) { if (context.length != 160) revert Errors.InvalidContextLength(); (offer, consideration) = Helpers.processSpentItems(minimumReceived, maximumSpent); }
https://github.com/code-423n4/2023-09-delegate/blob/main/src/CreateOfferer.sol#L176-L184
delegationType
There seems to be no way to create a DelegateToken that is not ERC721/ERC20/ERC1155, but due to the fact that DelegationType
supports also ALL
and CONTRACT
:
enum DelegationType { NONE, ALL, CONTRACT, ERC721, ERC20, ERC1155 }
And there are multiple ways to create DelegateToken (e.g. DelegateToken.create
and CreateOfferer.transferFrom
, but more contracts could be created in the future), it would be safer to revert the transaction on withdraw to avoid burning unsupported tokens:
function withdraw(uint256 delegateTokenId) external nonReentrant { ... } else if (delegationType == IDelegateRegistry.DelegationType.ERC1155) { uint256 erc1155UnderlyingAmount = StorageHelpers.readUnderlyingAmount(delegateTokenInfo, delegateTokenId); StorageHelpers.writeUnderlyingAmount(delegateTokenInfo, delegateTokenId, 0); // Deletes amount uint256 erc11551UnderlyingTokenId = RegistryHelpers.loadTokenId(delegateRegistry, registryHash); RegistryHelpers.decrementERC1155( delegateRegistry, registryHash, delegateTokenHolder, underlyingContract, erc11551UnderlyingTokenId, erc1155UnderlyingAmount, underlyingRights ); StorageHelpers.burnPrincipal(principalToken, principalBurnAuthorization, delegateTokenId); IERC1155(underlyingContract).safeTransferFrom(address(this), msg.sender, erc11551UnderlyingTokenId, erc1155UnderlyingAmount, ""); } /* @audit Should revert here to avoid burning a not supported delegate token else { revert Errors.InvalidTokenType(delegationType); } */ }
https://github.com/code-423n4/2023-09-delegate/blob/main/src/DelegateToken.sol#L353-L386
data
on flashloan could make some ERC1155 unusableWhen doing an ERC1155 flashloan, the data parameter is always empty:
IERC1155(info.tokenContract).safeTransferFrom(address(this), info.receiver, info.tokenId, info.amount, "");
https://github.com/code-423n4/2023-09-delegate/blob/main/src/DelegateToken.sol#L406
Some collections might need this parameter to be usable. This is an example of such case:
Consider adding a flashloanData
parameter to Structs.FlashInfo
and pass it while transfering.
delegatecall
inside a loop may cause issues with payable
functionsIf one of the delegatecall
consumes part of the msg.value
, other calls might fail, if they expect the full msg.value
. Consider using a different design, or fully document this decision to avoid potential issues.
function multicall(bytes[] calldata data) external payable override returns (bytes[] memory results) { results = new bytes[](data.length); bool success; unchecked { for (uint256 i = 0; i < data.length; ++i) { //slither-disable-next-line calls-loop,delegatecall-loop (success, results[i]) = address(this).delegatecall(data[i]); if (!success) revert MulticallFailed(); } } }
CreateOfferer
uses a custom context implementation instead of an existing SIPIt is suggested to use an existing SIP implementation instead of creating a new standard from scratch, which might be prone to errors:
Structs.Context memory decodedContext = abi.decode(context, (Structs.Context));
https://github.com/code-423n4/2023-09-delegate/blob/main/src/CreateOfferer.sol#L59
Please note that the contract needs to be SIP compliant before it's possible to implement SIP-7, as it requires SIP-5 and SIP-6.
The issue describing non-compliance is described here: #280
#0 - 0xfoobar
2023-09-18T18:10:21Z
Useful QA report
#1 - GalloDaSballo
2023-09-24T17:40:12Z
[L-01] previewOrder should not revert L
[L-02] Withdraw should revert with a not supported delegationType R
[L-03] Lack of data on flashloan could make some ERC1155 unusable L
[L-04] Using delegatecall inside a loop may cause issues with payable functions R
[L-05] CreateOfferer uses a custom context implementation instead of an existing SIP L
3+ L from dups
6L+ 2R
#2 - c4-judge
2023-10-02T09:15:35Z
GalloDaSballo marked the issue as selected for report
#3 - c4-judge
2023-10-02T09:15:39Z
GalloDaSballo marked the issue as grade-a
#4 - GalloDaSballo
2023-10-02T09:16:03Z
By far the best submission, great work!
#5 - 0xfoobar
2023-10-03T23:33:34Z
"[L-01] previewOrder should not revert" -> the quoted documentation actually says it can revert, we're not doing order penalties here just straightforward fulfillment
"[L-02] Withdraw should revert with non-supported delegationType" -> it does
"[L-03] Lack of data on flashloan could make some ERC1155 unusable" -> acknowledged, we won't be transferring directly to staking contracts with merkle roots so this is fine
"[L-04] Using delegatecall inside a loop may cause issues with payable functions" -> but here it does not, we can see in the quoted code that it's not looping over msg.value
"[L-05] CreateOfferer uses a custom context implementation instead of an existing SIP" -> acknowledged
#6 - c4-sponsor
2023-10-03T23:33:41Z
0xfoobar (sponsor) acknowledged
Id | Title |
---|---|
01 | High-level architecture |
02 | Analysis of the codebase |
03 | Architecture feedback |
04 | Centralization risks |
05 | Systemic risks |
create
function).20 hours
#0 - c4-judge
2023-09-24T16:09:43Z
GalloDaSballo marked the issue as grade-a
#1 - GalloDaSballo
2023-09-24T16:10:29Z
Chart + Recommendations on the Systemic Risk