DN404 engaged Guardian to review the security of its implementation of the ERC7631 co-joined ERC20 and ERC721 standard. From the 23rd of April to the 3rd of May a team of 5 auditors reviewed the source code in scope. All findings and resolutions have been recorded in the following report.
- Published
- Review window
- April 23 to May 3, 2024
- Language
- Solidity
- Chains
- Ethereum
- Sector
- NFTs, Tokens
- 0 Critical
- 2 High
- 4 Medium
- 20 Low
- 0 Informational
Scope
Test suiteGuardianAudits/DN404PoCs
Overview
DN404 engaged Guardian to review the security of its implementation of the ERC7631 co-joined ERC20 and ERC721 standard. From the 23rd of April to the 3rd of May a team of 5 auditors reviewed the source code in scope. All findings and resolutions have been recorded in the following report.
Findings 26
-
H-01 High _mintNext Allows NFTs In The Burn Pool To Be Stolen Logical Error Resolved
Description
Proof of concept: PoC
The
_mintNextfunction ignores the burn pool tokenIds, and mints directly starting from the existingtotalSupply / unit() + 1id.However this id and the ids that follow can be within the burn pool. As a result burn pool NFTs will be minted without adjusting the burn pool head.
Therefore when attempting to mint regularly, these newly minted NFT ids will be duplicated in the
toOwnedmapping and overwritten in theooentry, effectively stealing this NFT from the user it was originally minted to with the_mintNextfunction.Recommendation
Do not allow the burn pool feature to be used in tandem with the
_mintNextfunction, otherwise refactor the_mintNextfunction to account for the burn pool.Resolution
DN404 Team: The issue was resolved in PR#136.
-
H-02 High _mintNext Unexpectedly Wraps NFT Ids Logical Error Resolved
Description
Proof of concept: PoC
In the
_mintNextfunction it is possible for the minted ERC721 IDs to wrap around unexpectedly, causing several potential issues for systems inheriting the DN404 contract. Consider the following scenario:- User A has a balance of 3.6 erc20s
- Existing
totalSupplyis 42.55 - We
_mintNext74.43 tokens
The
_mintNextfunction will wrap the final minted ERC721 id with the_wrapNFTIdfunction, this is because we are attempting to mint 75 nfts to the receiver's address, however themaxIdhas only increased by 74. This is because:For User A: 3.6 + 74.43 = 78.03 => +75 ERC721s for User A For
totalSupply&maxId: 42.55 + 74.43 = 116.98 => + 74 ERC721s allowed bymaxId.As a result, the
_mintNextfunction can unexpectedly mint ERC721 IDs that are a part of the burn pool, and burn pool NFTs will be minted without adjusting the burn pool head. Therefore when attempting to mint regularly, these newly minted NFT ids will be duplicated in thetoOwnedmapping and overwritten in theooentry, effectively stealing this NFT from the user it was originally minted to with the_mintNextfunction.Recommendation
In the
_mintNextfunction revert if thetoAddresswould receive more ERC721s than themaxIdincrease would allow, e.g._zeroFloorSub(t.toEnd, toIndex) > (totalSupply_ / _unit()) - preTotalSupply.This behavior is also present in the
_mintand_transferfunctions, however there are no assumptions broken by this edge case for these functions. Therefore no code changes are necessary in these functions. Consider documenting this edge case behavior for these functions for users.Resolution
DN404 Team: The issue was resolved in PR#136. 14
-
M-01 Medium NFT Marketplace Bidding Bait-And-Switch Protocol Gaming Acknowledged
Description
Proof of concept: PoC
Whenever a mirror ERC721 token is transferred, minted, or burned through ERC20 transfers, the order in which the mirror tokens are taken from the holder is LIFO (last in first out). This behavior can be abused in certain circumstance by a malicious actor to steal user funds when interacting with NFT marketplaces.
The exact situation happens when a user that has a bid on a mirror NFT also sells a unit or more of base tokens from the same wallet. In that case, a malicious holder of the mirror NFT can make a profit and leave the user without the rare NFT.
Attack scenario using the live Asterix collection as an example:
- Bob has one of the rarest NFTs, 1960 and waits for people to bid on it
- At this point, Alice bids on it for 1.2938 WETH
- The floor for the collection is 0.674 ETH and buying an NFT by buying the base tokens from the liquidity pool is 0.5671 ETH.
- Alice has another NFT, lowest rarity, and sells by selling a unit of base tokens
- Since the collection is on Ethereum, the base swap transaction can be seen in the mempool
- Bob sees the base unit sell and front-runs it with accepting Alice’s bid
- Alice gets the rare 1960 ID and pays 1.2938 WETH, but immediately loses it as it was the last in and the base unit sell burns it, marking her a loss of 1.2938 - 0.5671 ETH
- Bob quickly initiates several cycled mints/burns to reclaim the rare NFT
Even if Bob fails to reclaim it, Alice still suffered a loss of the rare NFT. The above case can also happen unintentionally, when a user bid is accepted exactly in the same block as him selling base tokens equivalent to a NFT, having the same financial loss.
Recommendation
Consider modifying the synchronization logic to that of a FIFO (first in first out) instead of LIFO. Meaning that the first NFT to be minted to the wallet is also the first to leave it. Otherwise consider clearly documenting this risk for integrators.
Resolution
DN404 Team: Acknowledged.
-
M-02 Medium Double NFT Minting Via AfterNFTTransfer Hook Logical Error Resolved
Description
Proof of concept: PoC
Contracts that extend DN404 can implement the
_afterNFTTransferhook to be executed after any NFT token transfers, including minting and burning. An attacker can abuse any implementations that pass access to holders from within these hooks as there are situations when the_afterNFTTransferfunction is called before the internal storage is committed, breaking CEI.Consider the following attack scenario in a system implementing the
_afterNFTTransfehook:- An approver initiates a transfer to a user where direct transfers will be made
- In the
_afterNFTTransferfunction the malicious attacker has an existing helper contract that directly transfers several ERC721 tokens to himself - Since this was done before the
balanceOfequivalent was updated, after the initial execution is finalized, the malicious strategy will have a smaller ERC721 balance then the number of ERC721 it owns - As the attacker has less NFTs then they should by the
ownedLengthvariable and because thisownedLengthis used in determining how many NFT a user will have for their base token amount, the attacker can transfer any amount, even 0, to himself and the contract will mint him extra ERC721 tokens, the exact number that was sent by the helper contract
At this point, an attacker owns double the ERC721 tokens that he received, for half the amount of ERC20 base required to own that many.
Recommendation
Completely move the
_afterNFTTransferinto its own separate loop in all cases except the call from_transferFromNFTwhich is already singular. In the particular case of direct transfer, also move it after setting theownedLength.Resolution
DN404 Team: The issue was resolved in PR#136.
-
M-03 Medium Address Initialization Allows Pools To Accumulate NFTs Protocol Gaming Resolved
Description
Upon the first interaction with an address the address data flags are initialized, checking to see if the address holds any bytecode. If the account does not hold any bytecode at the time of account initialization then it receives only the
_ADDRESS_DATA_INITIALIZED_FLAGflag.However if the address is later deployed to (e.g. a new pool is created at this address), the contract will not be excluded from ERC721 minting, as it will not automatically receive the
_ADDRESS_DATA_SKIP_NFT_FLAGflag.As a result malicious actors may transfer tokens to an address where a pool for the token is about to be deployed to in order to avoid getting the address marked with the
_ADDRESS_DATA_SKIP_NFT_FLAG. NFTs will then errantly be minted to and burned from the pool address upon liquidity modification and swaps.This creates a pool of NFTs which are not owned by end users, but instead locked up in a swap pool where a significant amount of them may not be able to move due to locked liquidity. In the case of a swap pool it would be possible for a flashloan to rescue potentially rare ERC721 tokens, however in the case of a locking contract or some other arbitrary integration these ERC721 tokens could be unintentionally locked for a significant amount of time.
Recommendation
Consider checking whether an account houses bytecode when reading the
getSkipNFTfunction, regardless of if the account has been initialized or not. Otherwise be sure to document this risk to users of DN404, advising them to implement their own_getSkipNFTfunctions or adding their own functionality for trusted addresses to mark addresses with the_ADDRESS_DATA_SKIP_NFT_FLAGas needed.Resolution
DN404 Team: The issue was resolved in PR#136.
-
M-04 Medium Missing tokenId Existence Check Unexpected Behavior Resolved
Description
When a
tokenURIfunction is called in the fallback of the DN404 contract, it is never checked whether the giventokenIdexists. According to the recommendation in EIP712, thetokenURIfunction should throw an error if thetokenIdis not a valid NFT. This recommendation is followed in the OpenZeppelin ERC721 implementation contract as well as the ERC721A implementation.Recommendation
Check if the given
tokenIdexists; if it is invalid, revert with a custom error.Resolution
DN404 Team: The issue was resolved in PR#136.
-
L-01 Low Missing DN404 supportsInterface Check Upon Linking Logical Error Resolved
Description
The documentation for the
CannotLinkerror states that this error is thrown when "linking to the DN404 base contract and the DN404supportsInterfacecheck fails or the call reverts". However while linking the Mirror contract with thelinkMirrorContract(address)function selector there is no validation that themsg.senderis indeed a valid DN404 implementation.Recommendation
Implement the appropriate validation when executing the
linkMirrorContractlogic such thatimplementsDN404()function selector is invoked on the purported DN404 contract to ensure that it is a valid implementation.Resolution
DN404 Team: The issue was resolved by removing the
CannotLinkerror in PR#136. -
L-02 Low Lacking safeMint Functionality Missing Feature Partially resolved
Description
Currently, during the transfer of tokens, there is a
safeTransfermechanism where thetoaddress is checked if it implements theonERC721Receivedfunction. However, thissafemechanism does not exist during minting. Therefore, it is possible to mint a token to a smart contract that does not support ERC721, resulting in the token becoming permanently stuck.Recommendation
Implement
_safeMintfunction with the samesafemechanism as in the safeTransferFrom functions. If thetoaddress is a smart contract, check if it implements theonERC721Receivedfunction.Resolution
DN404 Team: The issue was documented in PR#136.
-
L-03 Low Initial Supply Owner Always Skips NFT Minting Documentation Resolved
Description
When the
DN404contract is deployed, an initial supply amount and holder address can be passed. If they are provided, the internal logic within the contract will automatically set the corresponding address to skip NFT minting, regardless if it is an EOA or smart contract.This behavior is not stated and cannot be intuitively considered since the skip NFT logic, as suggested by the EIP and implemented, is implemented to by default skip only smart contracts, where as here any provided address is set as so.
As a result external integrators expecting that by sending the initial
ERC20base tokens to an EOA to have theERC721minted as well to it are mislead.Recommendation
Either clearly document this behavior or change the implementation of
DN404._initializeDN404such that it accepts askipNFTparameter and mints the mirror contract tokens accordingly.Resolution
DN404 Team: The issue was documented in PR#136.
-
L-04 Low SkipNFTSet Emitted When No Changes Are Done Logical Error Acknowledged
Description
In EIP-7631, if the ERC20 base contract implements the
IERC7631BaseNFTSkippableinterface there are certain considerations that must be upheld. As specified in the interface, theSkipNFTSetevent must be “Emitted when the skip NFT status of owner is changed by any mechanism” with the addition that the “initial skip NFT status for owner can be dynamically chosen to be true or false, but any changes to it MUST emit this event”.In the DN404 implementation, the
SkipNFTSetevent is incorrectly emitted both when no change is done, by callingsetSkipNFTwith an already set status, and when setting the status for the initial supply owner, when deploying the DN404 token.Emitting the
SkipNFTSetevent in situations where no change is done is not compliant with the EIP.Recommendation
In the
DN404._setSkipNFTfunction, move the event emission assembly block within theifbranch. By doing so, the_setSkipNFTcall from theDN404._initializeDN404will also not emit the event.If the
_setSkipNFTalteration is not implemented, inDN404._initializeDN404instead of calling_setSkipNFT, directly set the_ADDRESS_DATA_SKIP_NFT_FLAGflag to true, to avoid event emission.Resolution
DN404 Team: Acknowledged.
-
L-05 Low NextTokenID Not Updated On _mintNext Documentation Acknowledged
Description
There are 2 functions that provide minting functionality for the ERC20 and ERC721 balances:
_mint: mints IDs from the burn pool if using the feature; If none available, usesnextTokenId_mintNext: will always mint the next ID outside of the existing supply
An issue appears when a contract uses both mint functions to mint tokens and NFTs as the
_mintNextfunction does not update thenextTokenIdwhich indicates the next free token ID.Consider the following scenario:
- For an implementing contract, transfers are paused until all the base tokens are sent, meaning the collection is minted
- Users mint using the
_mintNextfunction, which does not update thenextTokenId - After a large number of mints, the contract changes minting, for whatever reason, to using the
_mintfunction _mintstarts validating ownership from thenextTokenIdtoken ID, and since it was never set, if a large enough amount of NFTs were minted, this operation will consume a large amount of gas or even revert with OOG in extreme cases before finding the next free ID.
Recommendation
Consider if this behavior should be allowed, if so clearly document this risk so integrating protocols can avoid this scenario.
Resolution
DN404 Team: Acknowledged.
-
L-06 Low ERC721 Minting May Revert Due To Out Of Gas Documentation Resolved
Description
The DN404 implementation co-joins ERC20 and ERC721 standards representing dual nature token pair. Both tokens are deployed separately and then linked. The main idea of solution is to adjust both tokens balances, in such a way that the
_unitrepresents an amount of ERC20 token balance that is equal to one NFT.Thus, the balances are being updated within every burn, mint or transfer operation. Whenever significant amount of tokens are processed, that result in multiple mints or burns, a significant amount of gas is consumed. Eventually, such transactions are prone to revert due to Out Of Gas error, preventing successful execution of aforementioned operations.
Although this is mentioned as a possibility in the EIP7631 itself, it should be mentioned that the current implementation fails at around 2,600 NFTs to be minted. If the unit is low enough, complex integrators, swaps, lenders, will also fail.
Recommendation
Consider documenting these limitations, especially when it comes to the choice of a
unitvalue.Resolution
DN404 Team: The issue was documented in PR#136.
-
L-07 Low Permit2 Infinite Allowance Can Overwrite User Allowance Documentation Resolved
Description
DN404integrators have the possibility to define whether Permit2 has infinite allowances by default for all owners, by means of the_givePermit2DefaultInfiniteAllowanceinternal function.Additionally, when a user decides to overwrite the default allowance for
Permit2the_ADDRESS_DATA_OVERRIDE_PERMIT2_FLAGis set to remember the users choice of a custom allowance set.However, the flag is only set when the
_givePermit2DefaultInfiniteAllowancefunction returnstrue. Integrators may have implementations that change the value returned in the aforementioned function over time.In such a case, the user may firstly set custom allowance for Permit2. Such action will not set on the
_ADDRESS_DATA_OVERRIDE_PERMIT2_FLAGflag, as the_givePermit2DefaultInfiniteAllowancefunction by default returnsfalse.Subsequently, changing the behavior of the
_givePermit2DefaultInfiniteAllowancefunction will overwrite user's custom allowance into maximum allowance. This behavior can be considered unexpected and may be leveraged by the attacker in further attacks.Recommendation
Clearly document this risk that arises from a non-static
_givePermit2DefaultInfiniteAllowancevalue.Resolution
DN404 Team: The issue was documented in PR#136.
-
L-08 Low NFT Minted With A Higher ID Than Available NFTs Logical Error Resolved
Description
When minting using the
_mintNextfunction, IDs are chosen outside of the current NFT supply as distinctly new NFTs are created. However it is possible to mint NFTs with an ID that is above the computedtotalNFTSupplyretrieved by thetotalSupply() / _unit()calculation.This corner case appears because the index where the
_mintNextfunction starts looking for available IDs is chosen as if all the possible NFTs would of been minted up to this pointstartId = preTotalSupply / _unit() + 1and the wrapping function is not triggered due to the ID being free of an owner.This creates odd situations, for example if the unit is 100 ERC20 tokens and 90 ERC20 tokens are minted to 2 addresses each as well as 20 ERC20 tokens to a 3rd address. Now, when initiating a mint of 10 base tokens to any of the first addresses, the ID minted will be 3, however the
totalSupply() /_unit() = 210 / 100 = 2indicates that only the IDs 1 and 2 ought to exist.Recommendation
When implementing a fix for H-01, be sure to not allow the ID of minted NFTs with the
_mintNextfunction to surpass thetotalSupply / _unit().Resolution
DN404 Team: The issue was resolved in PR#136.
-
L-09 Low Undocumented LIFO NFT Transfer Logic Documentation Resolved
Description
Whenever a mirror ERC721 token is transferred/minted/burned as a result of transferring enough base ERC20 tokens, the order in which the mirror tokens are taken from the holder is LIFO (last in first out). This behavior has a high impact on users and is virtually unmentioned.
Additionally, as the current draft of EIP-7631 does not cover the token synchronization logic it needs to be explicitly stated.
Consider a situation where, out of on the Nth base token transfer, a user mints a rare ERC721. Since they are only interested in the rare token, they send their entire base balance, minus enough for a single unit, to another address and by doing so actually lose the rare NFT.
Recommendation
Although this is a design decision, since it has severe economical implications to market participants it must be explicitly stated. Another solution is to add a mechanism to specify which NFTs you wish to have locked-in when transferring base tokens.
Users can currently workaround this by transferring NFT directly to a different (clean) wallet. Any subsequent base
ERC20transfer to it will leave the rare NFT within the wallet of the user provided that a minimum unit value is always kept.Resolution
DN404 Team: The issue was documented in PR#136.
-
L-10 Low ERC20 And ERC721 Simultaneous Allowances Logical Error Partially resolved
Description
The DN404 implementation co-joins ERC20 and ERC721 representing dual nature token pair. Both tokens are deployed separately and then linked. The processing of both tokens is being done simultaneously whenever a transfer, mint or burn is triggered for one of them.
However, the same does not occur for ERC20 and ERC721 token allowances. Whenever one of the token allowance is granted or revoked, the other remain unchanged. This behavior appear to be secure whenever allowance increases or grant is done.
When considering the opposite situation, whenever allowance is decreased or revoked, it can be prone to human errors. In the event of emergency, when one of the contract allowance is removed, the second will remain active, and might be leveraged by an attacker in a vulnerable scenario.
Recommendation
Consider implementing additional functionality, that sets both the ERC20 allowance to 0 and and the ERC721 operator allowance is revoked.
Resolution
DN404 Team: The issue was documented in PR#136.
-
L-11 Low Base Token Holders Control Mirror NFT totalSupply Documentation Resolved
Description
The
setSkipNFTfunction allows the token holder to decide whenever the NFT should be minted upon increasing their base ERC20 tokens balance. While this functionality may have impact on Gas consumption and gives some flexibility, it has significant drawback.A user can leverage this to burn owned NFTs and decrease the
totalSupplyby transferring base ERC20 tokens to secondary account which has this flag set to true. As a result the NFT tokens will be burned, but not minted for destination account.This implementation may have impact on third party integration, e.g. if marketplace will base the NFT token value on total supply.
Recommendation
Clearly highlight the indicated behavior so that any integrating 3rd party does not unknowingly integrate expecting the
totalSupplyinvariant to hold.Resolution
DN404 Team: The issue was documented in PR#136.
-
L-12 Low Operator In setApprovalForAll Can Be Zero Address Validation Partially resolved
Description
A user can allow an operator to manage the tokens of their mirror ERC721 tokens by calling the standard
setApprovalForAllfunction.This function is usually gated to not allow approval to be given to the zero address. In the current
DN404Mirrorimplementation, zero address operator is allowed.Recommendation
Validate that the operator passed to the
_setApprovalForAllfunction is not the zero address.Resolution
DN404 Team: The issue was documented in PR#136.
-
L-13 Low Inaccurate _findFirstUnset Documentation Documentation Resolved
Description
In the documentation for the
_findFirstUnsetfunction it is mentioned that “If no set bit is found, returns type(uint256).max”.However the behavior of the
_findFirstUnsetfunction is such that if no unset bit is found, thetype(uint256).maxis returned.Recommendation
Correct the comment to reflect the behavior: “If no unset bit is found, returns type(uint256).max”.
Resolution
DN404 Team: The issue was resolved in PR#136.
-
L-14 Low Typographical Error Typo Resolved
Description
In the documentation for the
DN404NFTStoragestruct, the comment about the deployer “...link can only be done be the deployer via the ERC20 base contract”.Should read “...link can only be done by the deployer via the ERC20 base contract”.
Recommendation
Replace the second instance of "be" with "by".
Resolution
DN404 Team: The issue was resolved in PR#136.
-
L-15 Low Approve Can Be Called Before Initialization Unexpected Behavior Acknowledged
Description
In the DN404 contract the public approve function can be called before the DN404 contract has been initialized and connected to a corresponding
mirrorERC721contract.While this poses no immediate risk it may be unexpected for users of the DN404 contract that approvals can be made and approval events can be emitted before the DN404 contract is initialized.
Recommendation
Consider adding validation that the
mirrorERC721contract is nonzero in the_approvefunction and revert with theDNNotInitializederror if it is.Resolution
DN404 Team: Acknowledged.
-
L-16 Low DN404 Tokens Cannot Take Over The World DoS Acknowledged
Description
Address aliases are used to track ownership in bytes32 size entries in the
oomapping. In the_registerAndResolveAliasfunction, the resultingaddressAliasis a monotonically increasing number. Whentype(uint32).max = 4,294,967,295address aliases have been assigned, attempting to assign another alias will revert.There are roughly 8,000,000,000 humans on earth, notice that
8,000,000,000 > 4,294,967,295. As a result DN404 tokens cannot reach 100% saturation of the human race, limiting their TAM.Recommendation
Consider documenting this limitation for users of DN404 expecting to convert humanity into a hive-mind of DN404 token holders.
Resolution
DN404 Team: Acknowledged.
-
L-17 Low _initiateTransferFromNFT Errant Documentation Documentation Resolved
Description
In the DN404 contract the documentation for the
_initiateTransferFromNFTfunction indicates that the “Call must originate from the mirror contract”. However there is no in-code validation that specifically requires this to hold.Additionally, there is no dash preceding the “msgSender must be the owner of the token, or be approved to manage the token” requirement, therefore this requirement does not match the format of the others.
Recommendation
Either remove the documented requirement that the “Call must originate from the mirror contract” or implement this validation at the Smart Contract level.
Additionally add a dash preceding the “msgSender must be the owner of the token, or be approved to manage the token” requirement so that it matches the formatting of other requirements in the docstring.
Resolution
DN404 Team: The issue was resolved in PR#136.
-
L-18 Low Deployer Address Can Be Immutable Optimization Acknowledged
Description
The deployer address in the Mirror contract can be immutable as it is never changed and is only used during the linking of the DN404 contract with the Mirror contract.
Recommendation
Make the deployer address immutable.
Resolution
DN404 Team: Acknowledged.
-
L-19 Low _totalSupplyOverflows Errant Documentation Documentation Resolved
Description
In the
DN404contract the documentation for the_totalSupplyOverflowsfunction indicates that it: “Returns whether amount is a valid totalSupply”.However, the function returns whether the amount is not a valid total supply, specifically it returns true if it does not satisfy the required conditions.
Recommendation
Correct the comment to reflect the behavior: Returns whether
amountis an invalidtotalSupply.Resolution
DN404 Team: The issue was resolved in PR#136.
-
L-20 Low Unit Value Alteration DoS Unexpected Behavior Acknowledged
Description
In the
DN404contract users may override theunitfunction in order to specify the denomination of ERC20 tokens that constitutes the ownership of 1 ERC721 token. There is no requirement nor documentation which specifies that users ought to keep the unit value constant, however significant risks arise when the unit value is changed.Most notably, if the unit value is increased there is a risk that all ERC721 IDs from 1 up to the newly computed
maxIdare occupied when minting a new NFT. This scenario will result in an infinite loop while the_mintor_transferfunction attempts to find an unoccupied ERC721 ID within the valid range, and as a result minting and transfers which would result in a mint are DoS’d.Recommendation
Clearly document that the unit value should remain constant, similar to how is done for the
_useExistsLookupfunction.Resolution
DN404 Team: Acknowledged.
No findings match.
Invariants 46
The review's fuzzing suite asserted 46 invariants. 42 held and 4 did not.
Every invariant tested
| ID | Invariant | Result |
|---|---|---|
DN-01 | Sum of Owned NFTs == Mirror Total Supply | Held |
DN-02 | Sum of Owned ERC20 == Token Total Supply | Held |
DN-03 | Mirror And Base Token Are Unchanged Post-Initialization | Held |
DN-04 | Burned Pool Length == Tail - Head | Held |
DN-05 | No User Owns type(uint32).max NFT | Held |
DN-06 | Allowance Matches Approved Amount | Held |
DN-07 | Owner Auxiliary Data Is Not Modified Upon Approval | Held |
DN-08 | Spender Auxiliary Data Is Not Modified Upon Approval | Held |
DN-09 | Direct Transfers Do Not Overlap With Burned Pool Upon Transfer | Broken |
DN-10 | ERC20 Balance Changes By Amount For Sender And Receiver Upon Transfer | Held |
DN-11 | ERC20 Balance Remains The Same Upon Self-Transfer | Held |
DN-12 | ERC20 Total Supply Remains The Same Upon Transfer | Held |
DN-13 | Auxiliary Data Is Not Modified Upon Transfer | Held |
DN-14 | Direct Transfers Do Not Overlap With Burned Pool Upon TransferFrom | Broken |
DN-15 | ERC20 Balance Changes By Amount For Sender And Receiver Upon TransferFrom | Held |
DN-16 | ERC20 Balance Is the Same Upon Self-Transfer Upon TransferFrom | Held |
DN-17 | Auxiliary Data Is Not Modified Upon TransferFrom | Held |
DN-18 | User Balance Increased By Mint Amount | Held |
DN-19 | Total ERC20 Supply Increased By Mint Amount | Held |
DN-20 | Total NFT Supply Post-Mint Is At Least Total NFT Supply Pre-Mint | Held |
DN-21 | Auxiliary Data Is Not Modified Upon Mint | Held |
DN-22 | User’s Owned NFT’s Decreased Upon Burn | Held |
DN-23 | ERC20 Total Supply Decreased By Burn Amount | Held |
DN-24 | Total NFT Supply Post-Burn Is At Most Total NFT Supply Pre-Burn | Held |
DN-25 | Auxiliary Data Is Not Modified Upon Burn | Held |
DN-26 | Approved NFT Spender == Requested Approval | Held |
DN-27 | Owner Of NFT ID Is Not Modified Upon Approval Of NFT | Held |
DN-28 | Owner Auxiliary Data Is Not Modified Upon Approval | Held |
DN-29 | Spender Auxiliary Data Is Not Modified Upon Approval | Held |
DN-30 | NFT Balance Of Sender and Receiver Accurately Updated Upon TransferNFT | Held |
DN-31 | Sender/Receiver ERC20 Balance Decremented/Incremented By Unit | Held |
DN-32 | Receiver Address Is The Owner At The Sent NFT ID | Held |
DN-33 | Total NFT Supply Is Unchanged Upon NFT Transfer | Held |
DN-34 | Approval Is Reset Upon NFT Transfer | Held |
DN-35 | Sender Auxiliary Data Is Not Modified Upon NFT Transfer | Held |
DN-36 | Receiver Auxiliary Data Is Not Modified Upon NFT Transfer | Held |
DN-37 | Skip NFT Status Is Updated To Requested Status | Held |
DN-38 | Auxiliary Data Is Not Modified Upon Set Skip NFT | Held |
DN-39 | Set Approval For All Updated To Requested Status | Held |
DN-40 | Owner Auxiliary Data Is Not Modified Upon Set Approval For All | Held |
DN-41 | Spender Auxiliary Data Is Not Modified Upon Set Approval For All | Held |
DN-42 | Mint Next Does Not Overlap With Burned Pool | Broken |
DN-43 | Mint Next Increases User’s Owned NFT’s If NFT Minted | Held |
DN-44 | User Balance Increased By Mint Next Amount | Held |
DN-45 | Total ERC20 Supply Increased By Mint Next Amount | Held |
DN-46 | _ownerAt(id) Is Always The Same As NFT Holder | Broken |
Put your code through the same review.
This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.