Audit 14
Audit 14
Audit 14
thirdweb A-14
Security Audit
August 21st, 2023
Version 1.0.0
https://0xmacro.com/library/audits/thirdweb-14.html 1/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Presented by 0xMacro
https://0xmacro.com/library/audits/thirdweb-14.html 2/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Table of Contents
Introduction
Overall Assessment
Specification
Source Code
Issue Descriptions and Recommendations
Security Levels Reference
Disclaimer
https://0xmacro.com/library/audits/thirdweb-14.html 3/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Introduction
This document includes the results of the security audit for thirdweb's smart contract
code as found in the section titled ‘Source Code’. The security audit was performed by
the Macro security team from August 8, 2023 to August 21, 2023.
The purpose of this audit is to review the source code of certain thirdweb Solidity
contracts, and provide feedback on the design, architecture, and quality of the source
code with an emphasis on validating the correctness and security of the software in its
entirety.
Disclaimer: While Macro’s review is comprehensive and has surfaced some changes
that should be made to the source code, this audit should not solely be relied upon for
security, as no single audit is guaranteed to catch all possible bugs.
https://0xmacro.com/library/audits/thirdweb-14.html 4/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Overall Assessment
The following is an aggregation of issues found by the Macro Audit team:
Specification
Our understanding of the specification was based on the following sources:
https://0xmacro.com/library/audits/thirdweb-14.html 5/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Source Code
The following source code was reviewed during the audit:
Repository: contracts
Commit Hash: cfc3b05e719941d7787263a897542bddb05a6017
Contract SHA256
contracts/dynamic- 5a29b078c40eb0c585775e7e5cc4026529
contracts/extension/RulesEngine.sol 9023165cc969054b0c09e40781026a
contracts/dynamic-
contracts/extension/SharedMetadataBatch. 6ae8b789f73e18584343bd6a2665f49896
sol b13a534613e2115919cb349502f56a
contracts/smart- 5e986de2977d9ecf9360931be97eaad976
wallet/dynamic/DynamicAccount.sol 509acb7026ee1825b0b4e795842100
contracts/smart- 416d40d2f3aa0c8f0895705477ab23973b
wallet/dynamic/DynamicAccountFactory.sol 41c1002751628427ac6bcf5f0fa5a9
contracts/smart- c3b4c601c4106391d59a9b3dff6ae1c89b
wallet/managed/ManagedAccount.sol 83c8992a51adaac89f73d8b7bb0e63
contracts/smart-
wallet/managed/ManagedAccountFactory.s 5b33e3ef1a0491147e24c1047e68f852cf
ol 7061e1671894a2e0e2a9242a80914d
contracts/smart-wallet/non- 8bce0fb10cac41141478a228a96dad12af
upgradeable/Account.sol e39db384d1938f2529cc455d27081f
contracts/smart-wallet/non- ca1b595fe2f19497d4c1a01b76144ec3b2
upgradeable/AccountFactory.sol d5c635192171c00332ad258ec5fdec
https://0xmacro.com/library/audits/thirdweb-14.html 6/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Contract SHA256
contracts/smart- ae8078e8955b24c02c588a5b132813b9c9
wallet/utils/AccountCore.sol a08b32ae0db39b1cf7348e2c29ff2c
contracts/smart- 2311f64bd2875e63ff395cacb120834115
wallet/utils/AccountExtension.sol 97c3731d42a70e676f2fec925c065a
contracts/smart- 05841a1d7f05df8113f5e3c2e692121f69
wallet/utils/BaseAccount.sol c190bdc3c6072c954a9925d44ab6a2
contracts/smart- d7181834e702d81b76569e722e342208fe
wallet/utils/BaseAccountFactory.sol a45db7f21a38c28839fb427c2d0a35
contracts/unaudited/LoyaltyPoints.sol b33c12660ea5b934875a66f992c228da0d
7cd019fabe7feeaf81efac2fffcc72
contracts/unaudited/evolving- 90c1072ed646669a51d7bcadaf92d6891d
nfts/EvolvingNFT.sol 030739a2ea67123d143901443938b8
contracts/unaudited/evolving- 4fb77518d6c3134349bd6a420e35a1c30f
nfts/EvolvingNFTLogic.sol b155892f1ae78b2c108ff9c134d503
contracts/unaudited/evolving- cbccc956811152c42e3804b848f387fb6b
nfts/extension/RulesEngineExtension.sol d9ac68d2576f10af61ec25cb380854
Note: This document contains an audit solely of the Solidity contracts listed above.
Specifically, the audit pertains only to the contracts themselves, and does not pertain
to any other programs or scripts, including deployment scripts.
https://0xmacro.com/library/audits/thirdweb-14.html 7/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
https://0xmacro.com/library/audits/thirdweb-14.html 9/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
https://0xmacro.com/library/audits/thirdweb-14.html 10/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Severity Description
The issue identified does not pose any obvious risk, but
(Q-x) fixing could improve overall code quality, on-chain
Code Quality composability, developer ergonomics, or even certain
aspects of protocol design.
https://0xmacro.com/library/audits/thirdweb-14.html 11/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Issue Details
_setupContractURI(_contractURI);
_setupOwner(_defaultAdmin);
_setupOperatorFilterer();
_setupRole(DEFAULT_ADMIN_ROLE, _defaultAdmin);
_setupRole(keccak256("MINTER_ROLE"), _defaultAdmin);
_setupRole(_transferRole, _defaultAdmin);
_setupRole(_transferRole, address(0));
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
_setupPrimarySaleRecipient(_saleRecipient);
}
Reference: EvolvingNFT.sol#L44-L74
This allows them to set themselves as the EXTENSION_ROLE , which they then can make
calls to BaseRouter 's addExtension() . Doing so can allow an extension to be added
that calls selfdestruct , which would cause the implementation contact to be
destroyed.
This issue is also present in BurnToClaimDropERC721.sol
Remediations to Consider
Add a call to in EvolvingNFT.sol and
_disableInitializers()
As found initially and described in their report, signers set by the account admin can
sign valid signatures for target contracts that they are not authorized to interact with.
Signers should be restricted to only interact with contracts that have been explicitly
set as a approvedTarget by a admin of the wallet. However in isValidSignature() , it
is only checked if the signer of the signature is active, and not if the sender is an
approvedTarget of the signer.
if (isAdmin(signer) || isActiveSigner(signer)) {
magicValue = MAGICVALUE;
}
}
https://0xmacro.com/library/audits/thirdweb-14.html 14/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
The smart wallet contracts currently set an immutable value for the
entrypointContract . However as expressed in the ERC-4337, it is possible that the
entryPoint contract could change “to add new functionality, improve gas efficiency, or
fix a critical security bug”. Since there is no way currently to set a new entryPoint
contract, it would require a new wallet to be deployed and assets to be migrated in
order to use the updated entryPoint.
Remediations to Consider
Add a function that allows the admin to update the entryPoint contract, allowing users
to adapt to a changing entryPoint, and continue to utilize the full functionality of ERC-
4337.
become invalidated and revert. Having wallets revert this way can effect bundlers
reputation and potentially cause bundlers to not include operations from these
wallets.
Remediations to Consider
As mentioned in the ERC, pack the timestamps validUntil and validAfter into the
returned validationData of validateUserOp() , this can be done using Helpers.sol ’s
packValidationData() function. This allows a bundler to simulate an operation and
check if it would expire before it would likely execute, allowing them to reject these
nearly expired operations.
In RulesEngine.sol scores are calculated for users that have balances that meet the
sets rules criteria, and are either calculated as a flat score for Threshold rules, or the
score is multiplied by the users balance for Multiplicative rules. However, ERC20
tokens balance is returned as a large number with a set Decimals , unlike ERC721 or
ERC1155 tokens. This can lead to scores being multiplied by large values that may be
unintended.
Remediations to Consider
Consider using the ERC20 tokens decimals() and converting the balance in terms of
full tokens before multiplying by the score to receive a score more in line with the
scores returned from ERC721 and ERC1155 .
https://0xmacro.com/library/audits/thirdweb-14.html 16/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
M-4 Shared metadata will get out of order when deleting metadata
Shared metadata is set sequentially for 0, 10, 50, and 150 target scores.
The user score is 150, and the correct token URI is returned.
The metadata for a target score of 10 is removed.
Now the order of shared metadata is 0, 150, and 50.
The user scores the same at 150, yet the token URI that gets returned is 50.
The above happens due to the efficient implementation of remove() in the
EnumerableSet . However, it also disrupts the shared metadata order. As a result, the
function tokenURI(...) {
// ...
// ...
}
Reference: EvolvingNFTLogic.sol#L75-L99
Remediations to Consider
Do not assume shared metadata order, and set targetId by identifying the
shared metadata with the largest target score. Then use that as the final result to
be used for displaying the token URI.
Add a way to alter the existing shared metadata so that the ordering of shared
metadata is kept intact.
Use another mechanism for storing shared mechanism such as a custom
implementation of EnumerableSet which does not alter the ordering.
One interesting aspect of a ERC-4337 wallet, or smart contract wallets in general, is the
ability to react to receiving native tokens, like ETH. For ManagedAccount.sol and
DynamicAccount.sol adding the ability to update the receive() function may be
https://0xmacro.com/library/audits/thirdweb-14.html 18/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
https://0xmacro.com/library/audits/thirdweb-14.html 19/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
possibility that users may want custom functionality for verifying valid signatures, it
could be added to AccountExtension.sol to allow users to update it as desired.
Remediations to Consider
Move isValidSignature() to AccountExtension.sol to allow users to have the ability
to customize how the contract validates signatures.
/// @notice Callback function for an Account to register itself on the factory.
function onRegister() external {
address account = msg.sender;
require(_isAccountOfFactory(account), "AccountFactory: not an account.");
Reference: BaseAccountFactory.sol#L74-L80
However, there are no checks to ensure the caller is an account created by this factory.
A contract could potentially call onRegister() and become registered with this
factory, if it passes the checks of _isAccountOfFactory , which does not guarantee the
https://0xmacro.com/library/audits/thirdweb-14.html 20/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
account was created by the factory, as it only checks if the factory’s implementation
address is in the bytecode of the calling contract at the expected location.
/// @dev Returns whether the caller is an account deployed by this factory.
function _isAccountOfFactory(address _account) internal view virtual returns (b
address impl = _getImplementation(_account);
return _account.code.length > 0 && impl == accountImplementation;
}
Reference: BaseAccountFactory.sol#L134-143
Setting an invalid address for an account can lead to inaccurate book keeping, and if
any contract or protocol were to query the factory to check if an address is a account
created by the factory, it may not be accurate.
Since these contracts are generated using create2 , if the initial seed to generate the
account is provided, the generated address that the factory would have deployed can
be predicted using Clones.predictDeterministicAddress() and checked with the
calling contract to verify the account was created by the factory.
Remediations to Consider
When registering an account with a factory, accept the initial admin and data
parameter used to generate the account address and verify that the caller is the same
as the predicted address generated by those values. This will ensure only accounts
created by the factory can be registered.
https://0xmacro.com/library/audits/thirdweb-14.html 21/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
L-5 Payable transfer and approvals can lead to native tokens stuck in
contract
EvolvingNFTLogic.sol 's
, transferFrom() and safeTransferFrom()
approve()
functions are set to payable which allows native tokens to be sent into the contract
when making these function calls. However, native tokens sent in via these function
calls are not used, and there is no way to withdraw these tokens without a
permissioned user adding an extension to do so. In cases where extension permissions
have been revoked, there would be no way to withdraw these sent in funds. There is a
chance that funds could be sent to the contract accidentally, especially when
interacting with third party protocols like etherscan to transfer tokens or set approvals
as it would prompt the user to enter a value of native tokens to send, which may get
confused for other function parameters. It is understood that using the payable
keyword reduces gas costs as there is no check to ensure that msg.value == 0 , but
the added gas cost is negligible compared to the potential downsides.
Remediations to Consider
Remove the payable keywords from EvolvingNFTLogic.sol 's approve() ,
transferFrom() and both safeTransferFrom() functions, in order to prevent native
RESPONSE BY THIRDWEB
These functions are payable since they override from the ERC721AUpgradeable
contract, where these functions are payable ref.
We will fix this issue later on, where we do a sweep of our external dependencies.
https://0xmacro.com/library/audits/thirdweb-14.html 22/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Account.sol and AccountCore.sol share a lot of the same functions, and the code is
identical, and both inherit BaseAccount.sol . Duplicate code can cause errors as
changes to the code has to be done in multiple places.
Remediations to Consider
Refactor Account.sol and AccountCore.sol to prevent duplicate code, and reducing the
chance of errors when updating these contracts.
// We use the underlying storage instead of high level view functions to save g
// We use the underlying storage instead of high level view functions to save g
Reference: Account.sol#L81-L82
Remediations to Consider
Remove the duplicate comment.
https://0xmacro.com/library/audits/thirdweb-14.html 23/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Constant MAX_BPS is declared in EvolvingNFTLogic.sol, yet it’s not used in the contract.
Remediations to Consider
Remove MAX_BPS .
_canOverrieRulesEngine() → _canOverrideRulesEngine()
createRuleMulitiplicative*() →* createRuleMultiplicative()
https://0xmacro.com/library/audits/thirdweb-14.html 24/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Functions in these contracts use NatSpec documentation, but they tend to not include
the @param and @return tags, which give more information about the intent of the
function, and is used by some protocols like etherscan to make improve the user
experience when making these calls. Additionally RulesEngine.sol does not have any
NatSpec documentation.
Remediations to Consider
Add missing NatSpec documentation.
RESPONSE BY THIRDWEB
Reference: EvolvingNFT.sol#L41-L42
Remediations to Consider
Update the comment to accurately describe the EXTENSION_ROLE .
https://0xmacro.com/library/audits/thirdweb-14.html 25/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
emit PlatformFeeTypeUpdated(_feeType);
}
emit PlatformFeeTypeUpdated(_feeType);
}
Reference: PlatformFee.sol#L88-L103
Remediations to Consider
Consider having setPlatformFeeType() call _setPlatformFeeType() after the checks
to prevent duplicate code and follow to the set style pattern.
https://0xmacro.com/library/audits/thirdweb-14.html 26/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Reference: LoyaltyPoints.sol#L119
Additionally, in most contracts with an initializer, there is a comment that misspells the
word “initializes”.
Reference: EvolvingNFT.sol#L48
Remediations to Consider
Fix these spelling mistakes.
https://0xmacro.com/library/audits/thirdweb-14.html 27/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
import "./extension/PrimarySale.sol";
import "./extension/PrimarySale.sol";
Reference: LoyaltyPoints.sol#L27-L28
Remediations to Consider
Remove the duplicate import.
https://0xmacro.com/library/audits/thirdweb-14.html 28/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
is a PlatformFeeType enum which takes up 1 byte of space, and since the second slot
above it is full, it will take up a the 3rd storage slot on its own. If platformFeeType is
defined before flatPlatformFee , it will share the first storage slot with
platformFeeRecipient and platformFeeBps , saving a new storage write when set, and
since these values are typically read together it would benefit from warm SLOAD ’s
whenever they are read together.
/// @dev The address that receives all platform fees from all sales.
address private platformFeeRecipient;
/// @dev The flat amount collected by the contract as fees on primary sales.
uint256 private flatPlatformFee;
/// @dev Fee type variants: percentage fee and flat fee
PlatformFeeType private platformFeeType;
Reference: PrimarySale.sol#L16-L26
Remediations to Consider
Swap the positions of platformFeeType and flatPlatformFee to save users gas on
storage reads and writes.
https://0xmacro.com/library/audits/thirdweb-14.html 29/30
11/28/23, 4:27 AM thirdweb A-14 | Macro Audits | The 0xMacro Library
Disclaimer
Macro makes no warranties, either express, implied, statutory, or otherwise, with respect to
the services or deliverables provided in this report, and Macro specifically disclaims all
implied warranties of merchantability, fitness for a particular purpose, noninfringement and
those arising from a course of dealing, usage or trade with respect thereto, and all such
warranties are hereby excluded to the fullest extent permitted by law.
Macro will not be liable for any lost profits, business, contracts, revenue, goodwill,
production, anticipated savings, loss of data, or costs of procurement of substitute goods or
services or for any claim or demand by any other party. In no event will Macro be liable for
consequential, incidental, special, indirect, or exemplary damages arising out of this
agreement or any work statement, however caused and (to the fullest extent permitted by
law) under any theory of liability (including negligence), even if Macro has been advised of
the possibility of such damages.
The scope of this report and review is limited to a review of only the code presented by the
thirdweb team and only the source code Macro notes as being within the scope of Macro’s
review within this report. This report does not include an audit of the deployment scripts
used to deploy the Solidity contracts in the repository corresponding to this audit.
Specifically, for the avoidance of doubt, this report does not constitute investment advice, is
not intended to be relied upon as investment advice, is not an endorsement of this project
or team, and it is not a guarantee as to the absolute security of the project. In this report
you may through hypertext or other computer links, gain access to websites operated by
persons other than Macro. Such hyperlinks are provided for your reference and convenience
only, and are the exclusive responsibility of such websites’ owners. You agree that Macro is
not responsible for the content or operation of such websites, and that Macro shall have no
liability to your or any other person or entity for the use of third party websites. Macro
assumes no responsibility for the use of third party software and shall have no liability
whatsoever to any person or entity for the accuracy or completeness of any outcome
generated by such software.
https://0xmacro.com/library/audits/thirdweb-14.html 30/30