Skip to content

Commit 112f9e3

Browse files
authored
Macro audit fixes (#116)
* Fix [M-1]: Encryption key can be potentially intercepted to retrieve revealed URI * Fix [L-2]: User must approve themselves to burn their own token * Fix [M-2]: Callback marked as OPTIONAL does revert when not implemented in extension * Complete fix [L-2]: User must approve themselves to burn their own token * Fix [M-3]: ERC1155Core's mint() doesn’t follow the CEI pattern can lead to potential reentrancy * Fix [M-4]: Extension may fail to get uninstalled * Fix [L-1]: name and symbol properties are not correctly initiated * Fix [M-5]: Update to ClaimCondition can cause the caller paying more than expected * Fix [L-3]: Not including EIP712’s eip712Domain() function as a fallback function, lead to the function unreachable * Fix [L-5]: Allowing core contracts to receive native tokens without any efficient way to take it out * Fix [L-6]: Not following ERC4906 in BatchMetadata and DelayedRevealBatchMetadata contracts * Fix [L-7]: tokenURI should revert for invalid tokenIds * Fix [L-8]: _validateClaimCondition() returns an outdated condition * Fix tests from fix for [L-7] * Fix [L-9]: Native tokens can be locked in MintableERC20 * Fix [M-7]: supportsInterface() issues * Fix [Q-1]: batchMint for ERC1155Core contract and safeMint for ERC721Core contract are currently not supported * Fix [Q-2] Support for multiple requiredInterfaceIds * Fix [Q-3]: Core contract implementations are not protected against initialization * Fix [Q-4]: MintableERC721 should have getAllMetadataBatches function * Fix [Q-5]: Inconsistent payable tags * Fix [Q-6]: Nitpicks
1 parent 3d914b7 commit 112f9e3

39 files changed

+808
-319
lines changed

src/ModularCore.sol

+11-18
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ abstract contract ModularCore is IModularCore, OwnableRoles {
7777
FALLBACK FUNCTION
7878
//////////////////////////////////////////////////////////////*/
7979

80-
receive() external payable {}
81-
8280
/// @notice Routes a call to the appropriate extension contract.
8381
fallback() external payable {
8482
// Get extension function data.
@@ -147,7 +145,7 @@ abstract contract ModularCore is IModularCore, OwnableRoles {
147145
}
148146

149147
/// @notice Returns whether a given interface is implemented by the contract.
150-
function supportsInterface(bytes4 interfaceId) external view virtual returns (bool) {
148+
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
151149
if (interfaceId == 0xffffffff) return false;
152150
if (supportedInterfaceRefCounter[interfaceId] > 0) return true;
153151
return false;
@@ -174,9 +172,11 @@ abstract contract ModularCore is IModularCore, OwnableRoles {
174172
ExtensionConfig memory config = IModularExtension(_extension).getExtensionConfig();
175173

176174
// Check: ModularCore supports interface required by extension.
177-
if (config.requiredInterfaceId != bytes4(0)) {
178-
if (!this.supportsInterface(config.requiredInterfaceId)) {
179-
revert ExtensionInterfaceNotCompatible(config.requiredInterfaceId);
175+
if (config.requiredInterfaces.length != 0) {
176+
for (uint256 i = 0; i < config.requiredInterfaces.length; i++) {
177+
if (!supportsInterface(config.requiredInterfaces[i])) {
178+
revert ExtensionInterfaceNotCompatible(config.requiredInterfaces[i]);
179+
}
180180
}
181181
}
182182

@@ -271,11 +271,7 @@ abstract contract ModularCore is IModularCore, OwnableRoles {
271271
}
272272

273273
if (config.registerInstallationCallback) {
274-
(bool success, bytes memory returndata) =
275-
_extension.delegatecall(abi.encodeCall(IInstallationCallback.onUninstall, (_data)));
276-
if (!success) {
277-
_revert(returndata, CallbackExecutionReverted.selector);
278-
}
274+
_extension.delegatecall(abi.encodeCall(IInstallationCallback.onUninstall, (_data)));
279275
}
280276

281277
emit ExtensionUninstalled(msg.sender, _extension, _extension);
@@ -307,14 +303,11 @@ abstract contract ModularCore is IModularCore, OwnableRoles {
307303

308304
if (callbackFunction.implementation != address(0)) {
309305
(success, returndata) = callbackFunction.implementation.delegatecall(_abiEncodedCalldata);
310-
} else {
311-
if (callbackMode == CallbackMode.REQUIRED) {
312-
revert CallbackFunctionRequired();
306+
if (!success) {
307+
_revert(returndata, CallbackExecutionReverted.selector);
313308
}
314-
}
315-
316-
if (!success) {
317-
_revert(returndata, CallbackExecutionReverted.selector);
309+
} else if (callbackMode == CallbackMode.REQUIRED) {
310+
revert CallbackFunctionRequired();
318311
}
319312
}
320313

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// SPDX-License-Identifier: Apache 2.0
2+
pragma solidity ^0.8.0;
3+
4+
contract BeforeBatchMintCallbackERC1155 {
5+
/*//////////////////////////////////////////////////////////////
6+
ERRORS
7+
//////////////////////////////////////////////////////////////*/
8+
9+
error BeforeBatchMintCallbackERC1155NotImplemented();
10+
11+
/*//////////////////////////////////////////////////////////////
12+
EXTERNAL FUNCTIONS
13+
//////////////////////////////////////////////////////////////*/
14+
15+
/**
16+
* @notice The beforeBatchMintERC1155 hook that is called by a core token before minting tokens.
17+
* @param to The address to mint the token to.
18+
* @param ids The tokenIds to mint.
19+
* @param amounts The amounts of tokens to mint.
20+
* @param data ABI encoded data to pass to the beforeBatchMint hook.
21+
* @return result Abi encoded bytes result of the hook.
22+
*/
23+
function beforeBatchMintERC1155(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
24+
external
25+
payable
26+
virtual
27+
returns (bytes memory result)
28+
{
29+
revert BeforeBatchMintCallbackERC1155NotImplemented();
30+
}
31+
}

src/core/token/ERC1155Core.sol

+64-26
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {ERC1155} from "@solady/tokens/ERC1155.sol";
77
import {ModularCore} from "../../ModularCore.sol";
88

99
import {BeforeMintCallbackERC1155} from "../../callback/BeforeMintCallbackERC1155.sol";
10+
import {BeforeBatchMintCallbackERC1155} from "../../callback/BeforeBatchMintCallbackERC1155.sol";
1011
import {BeforeTransferCallbackERC1155} from "../../callback/BeforeTransferCallbackERC1155.sol";
1112
import {BeforeBatchTransferCallbackERC1155} from "../../callback/BeforeBatchTransferCallbackERC1155.sol";
1213
import {BeforeBurnCallbackERC1155} from "../../callback/BeforeBurnCallbackERC1155.sol";
@@ -19,13 +20,13 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
1920
//////////////////////////////////////////////////////////////*/
2021

2122
/// @notice The name of the NFT collection.
22-
string private _name;
23+
string private name_;
2324

2425
/// @notice The symbol of the NFT collection.
25-
string private _symbol;
26+
string private symbol_;
2627

2728
/// @notice The contract metadata URI of the contract.
28-
string private _contractURI;
29+
string private contractURI_;
2930

3031
/// @notice The total supply of a tokenId of the NFT collection.
3132
mapping(uint256 => uint256) private _totalSupply;
@@ -42,23 +43,23 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
4243
//////////////////////////////////////////////////////////////*/
4344

4445
constructor(
45-
string memory name,
46-
string memory symbol,
47-
string memory contractURI,
48-
address owner,
49-
address[] memory extensions,
50-
bytes[] memory extensionInstallData
46+
string memory _name,
47+
string memory _symbol,
48+
string memory _contractURI,
49+
address _owner,
50+
address[] memory _extensions,
51+
bytes[] memory _extensionInstallData
5152
) payable {
5253
// Set contract metadata
53-
_name = _name;
54-
_symbol = _symbol;
55-
_setupContractURI(contractURI);
56-
_initializeOwner(owner);
54+
name_ = _name;
55+
symbol_ = _symbol;
56+
_setupContractURI(_contractURI);
57+
_initializeOwner(_owner);
5758

5859
// Install and initialize extensions
59-
require(extensions.length == extensions.length);
60-
for (uint256 i = 0; i < extensions.length; i++) {
61-
_installExtension(extensions[i], extensionInstallData[i]);
60+
require(_extensions.length == _extensionInstallData.length);
61+
for (uint256 i = 0; i < _extensions.length; i++) {
62+
_installExtension(_extensions[i], _extensionInstallData[i]);
6263
}
6364
}
6465

@@ -68,20 +69,20 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
6869

6970
/// @notice Returns the name of the NFT Collection.
7071
function name() public view returns (string memory) {
71-
return _name;
72+
return name_;
7273
}
7374

7475
/// @notice Returns the symbol of the NFT Collection.
7576
function symbol() public view returns (string memory) {
76-
return _symbol;
77+
return symbol_;
7778
}
7879

7980
/**
8081
* @notice Returns the contract URI of the contract.
8182
* @return uri The contract URI of the contract.
8283
*/
8384
function contractURI() external view returns (string memory) {
84-
return _contractURI;
85+
return contractURI_;
8586
}
8687

8788
/**
@@ -110,7 +111,8 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
110111
return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165
111112
|| interfaceId == 0xd9b67a26 // ERC165 Interface ID for ERC1155
112113
|| interfaceId == 0x0e89341c // ERC165 Interface ID for ERC1155MetadataURI
113-
|| interfaceId == 0x2a55205a // ERC165 Interface ID for ERC-2981
114+
|| interfaceId == 0xe8a3d485 // ERC-7572
115+
|| interfaceId == 0x7f5828d0 // ERC-173
114116
|| super.supportsInterface(interfaceId); // right-most ModularCore
115117
}
116118

@@ -120,7 +122,7 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
120122
override
121123
returns (SupportedCallbackFunction[] memory supportedCallbackFunctions)
122124
{
123-
supportedCallbackFunctions = new SupportedCallbackFunction[](6);
125+
supportedCallbackFunctions = new SupportedCallbackFunction[](7);
124126
supportedCallbackFunctions[0] = SupportedCallbackFunction({
125127
selector: BeforeMintCallbackERC1155.beforeMintERC1155.selector,
126128
mode: CallbackMode.REQUIRED
@@ -143,6 +145,10 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
143145
});
144146
supportedCallbackFunctions[5] =
145147
SupportedCallbackFunction({selector: OnTokenURICallback.onTokenURI.selector, mode: CallbackMode.REQUIRED});
148+
supportedCallbackFunctions[6] = SupportedCallbackFunction({
149+
selector: BeforeBatchMintCallbackERC1155.beforeBatchMintERC1155.selector,
150+
mode: CallbackMode.REQUIRED
151+
});
146152
}
147153

148154
/*//////////////////////////////////////////////////////////////
@@ -168,9 +174,30 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
168174
*/
169175
function mint(address to, uint256 tokenId, uint256 value, bytes memory data) external payable {
170176
_beforeMint(to, tokenId, value, data);
171-
_mint(to, tokenId, value, "");
172177

173178
_totalSupply[tokenId] += value;
179+
_mint(to, tokenId, value, "");
180+
}
181+
182+
/**
183+
* @notice Batch mints tokens. Calls the beforeBatchMint hook.
184+
* @dev Reverts if beforeBatchMint hook is absent or unsuccessful.
185+
* @param to The address to mint the token to.
186+
* @param ids The tokenIds to mint.
187+
* @param amounts The amounts of tokens to mint.
188+
* @param data ABI encoded data to pass to the beforeBatchMint hook.
189+
*/
190+
function batchMint(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
191+
external
192+
payable
193+
{
194+
_beforeBatchMint(to, ids, amounts, data);
195+
196+
for (uint256 i = 0; i < ids.length; i++) {
197+
_totalSupply[ids[i]] += amounts[i];
198+
}
199+
200+
_batchMint(to, ids, amounts, "");
174201
}
175202

176203
/**
@@ -181,11 +208,11 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
181208
* @param value The amount of tokens to burn.
182209
* @param data ABI encoded data to pass to the beforeBurn hook.
183210
*/
184-
function burn(address from, uint256 tokenId, uint256 value, bytes memory data) external {
211+
function burn(address from, uint256 tokenId, uint256 value, bytes memory data) external payable {
185212
_beforeBurn(from, tokenId, value, data);
186-
_burn(msg.sender, from, tokenId, value);
187213

188214
_totalSupply[tokenId] -= value;
215+
_burn(msg.sender, from, tokenId, value);
189216
}
190217

191218
/**
@@ -239,8 +266,8 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
239266
//////////////////////////////////////////////////////////////*/
240267

241268
/// @dev Sets contract URI
242-
function _setupContractURI(string memory uri) internal {
243-
_contractURI = uri;
269+
function _setupContractURI(string memory _contractURI) internal {
270+
contractURI_ = _contractURI;
244271
emit ContractURIUpdated();
245272
}
246273

@@ -256,6 +283,17 @@ contract ERC1155Core is ERC1155, ModularCore, Multicallable {
256283
);
257284
}
258285

286+
/// @dev Calls the beforeBatchMint hook.
287+
function _beforeBatchMint(address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data)
288+
internal
289+
virtual
290+
{
291+
_executeCallbackFunction(
292+
BeforeBatchMintCallbackERC1155.beforeBatchMintERC1155.selector,
293+
abi.encodeCall(BeforeBatchMintCallbackERC1155.beforeBatchMintERC1155, (to, ids, amounts, data))
294+
);
295+
}
296+
259297
/// @dev Calls the beforeTransfer hook, if installed.
260298
function _beforeTransfer(address from, address to, uint256 tokenId, uint256 value) internal virtual {
261299
_executeCallbackFunction(

0 commit comments

Comments
 (0)