Omniscia Boson Protocol Audit
BundleBase Manual Review Findings
BundleBase Manual Review Findings
BBS-01M: Inexistent Validation of Valid Bundle Creation
Type | Severity | Location |
---|---|---|
Logical Fault | ![]() | BundleBase.sol:L45, L48 |
Description:
The createBundleInternal
function does not properly validate that either offerIds.length
or twinIds.length
is non-zero, permitting a bundle to be created with zero offer IDs and zero twin IDs.
Impact:
Incorrectly created bundles can lead to misbehaviours across the greater Boson Protocol ecosystem.
Example:
contracts/protocol/bases/BundleBase.sol
36function createBundleInternal(Bundle memory _bundle) internal {37 // get message sender38 address sender = msgSender();39
40 // get seller id, make sure it exists and store it to incoming struct41 (bool exists, uint256 sellerId) = getSellerIdByOperator(sender);42 require(exists, NOT_OPERATOR);43
44 // limit maximum number of offers to avoid running into block gas limit in a loop45 require(_bundle.offerIds.length <= protocolLimits().maxOffersPerBundle, TOO_MANY_OFFERS);46
47 // limit maximum number of twins to avoid running into block gas limit in a loop48 require(_bundle.twinIds.length <= protocolLimits().maxTwinsPerBundle, TOO_MANY_TWINS);49
50 // Get the next bundle and increment the counter51 uint256 bundleId = protocolCounters().nextBundleId++;52 // Sum of offers quantity available53 uint256 offersTotalQuantityAvailable;54
55 for (uint256 i = 0; i < _bundle.offerIds.length; i++) {56 uint256 offerId = _bundle.offerIds[i];57
58 // Calculate bundle offers total quantity available.59 offersTotalQuantityAvailable = calculateOffersTotalQuantity(offersTotalQuantityAvailable, offerId);60
61 (bool bundleByOfferExists, ) = fetchBundleIdByOffer(offerId);62 require(!bundleByOfferExists, BUNDLE_OFFER_MUST_BE_UNIQUE);63
64 (bool exchangeIdsForOfferExists, ) = getExchangeIdsByOffer(offerId);65 // make sure exchange does not already exist for this offer id.66 require(!exchangeIdsForOfferExists, EXCHANGE_FOR_OFFER_EXISTS);67
68 // Add to bundleIdByOffer mapping69 protocolLookups().bundleIdByOffer[offerId] = bundleId;70 }71
72 for (uint256 i = 0; i < _bundle.twinIds.length; i++) {73 uint256 twinId = _bundle.twinIds[i];74
75 // A twin can't belong to multiple bundles76 (bool bundleForTwinExist, ) = fetchBundleIdByTwin(twinId);77 require(!bundleForTwinExist, BUNDLE_TWIN_MUST_BE_UNIQUE);78
79 if (_bundle.offerIds.length > 0) {80 bundleSupplyChecks(offersTotalQuantityAvailable, twinId);81 }82
83 // Push to bundleIdsByTwin mapping84 protocolLookups().bundleIdByTwin[_bundle.twinIds[i]] = bundleId;85 }86
87 // Get storage location for bundle88 (, Bundle storage bundle) = fetchBundle(bundleId);89
90 // Set bundle props individually since memory structs can't be copied to storage91 bundle.id = _bundle.id = bundleId;92 bundle.sellerId = _bundle.sellerId = sellerId;93 bundle.offerIds = _bundle.offerIds;94 bundle.twinIds = _bundle.twinIds;95
96 // Notify watchers of state change97 emit BundleCreated(bundleId, sellerId, _bundle, sender);98}
Recommendation:
We advise this trait of the system to be re-evaluated as it currently permits incorrect bundle creation.
Alleviation (44009967e4f68092941d841e9e0f5dd2bb31bf0b):
The bundle creation process now mandates that at least one twin and one offer ID has been specified when a bundle is created thus alleviating this exhibit.