CertiK reviewed The Sandbox (Sandbox L1) on 5 Jul 2022: 5 findings, 0 critical; 4 marked fixed, 1 acknowledged.
Report hosted on skynet.certik.com.
Trustblock does not review code.
Statuses are the security firm’s own, from its report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 0 |
| High | 0 | 1 | 0 |
| Medium | 0 | 0 | 0 |
| Low | 0 | 0 | 4 |
The NatSpec of the `mintQuad()` function states that mints a quad of size 3, 6, 12, or 24 only. However, the function allows mints of size 1. ```solidity=75 function mintQuad(address to, uint256 size, uint256 x, uint256 y, bytes calldata data) external { require(to != address(0), "to is zero address"); require( isMinter(msg.sender), "Only a minter can mint" ); require(x % size == 0 && y % size == 0, "Invalid coordinates"); require(x <= GRID_SIZE - size && y <= GRID_SIZE - size, "Out of bounds"); uint256 quadId; uint256 id = x + y * GRID_SIZE; if (size == 1) { quadId = id; ```
When the contract tries to mint a quad via `_mintQuad()`, there is a check to ensure that no quads containing the quad to mint and no quads (or LANDS) within the quad to mint have already been minted by calling the function `exists()`. This leads to a possible denial-of-service attack where the attacker mints 1x1 LANDS at specific locations to prevent the minting of larger quads. For example, out of the possible 166,464 LAND placements, only 289 LANDS need to be minted to prevent 24x24 quads from occurring.
The internal function `_ownerOfQuad()` is used to find the owner or parent owner of a quad and can only be called by itself or one of the regroup functions. As none of these will call `_ownerOfQuad()` with a value of 1 for the input variable `size`, the `if` code branch `size == 1` will never be reached.
Minting of tokens on L1 is done by the function `mintQuad()`, which checks whether the tokens already exist or not. In detail, the `mintQuad()` function checks the owner of each token ID to decide whether or not the token has already been minted. For example, for 1x1 LAND tokens, it checks to see if the owner is non-zero. ```solidity=141 for (uint256 i = 0; i < size*size; i++) { uint256 id = _idInPath(i, size, x, y); require(_owners[id] == 0, "Already minted"); emit Transfer(address(0), to, id); } ``` However, for burnt tokens, their value in the `_owners` mapping is non-zero due to the burning flag. This means that burnt tokens cannot be minted again. **Proof of Concept** ```typescript it('Burnt land cannot be minted again', async function () { const { landContract, getNamedAccounts, ethers, mintQuad, } = await setupLand(); const {deployer, landAdmin} = await getNamedAccounts(); const contract = landContract.connect(ethers.provider.getSigner(deployer)); const x = 0; const y = 0; await mintQuad(deployer, 3, x, y); const tokenId = x + y * GRID_SIZE; await contract.burn(tokenId); await expect(mintQuad(deployer,1,x,y)).to.be.revertedWith('Already minted as 3x3'); }); ``` The test passed: ```typescript ✓ Burnt land cannot be minted again (64ms) ``` From the result, the burnt token is unable to be minted again.
The report names a repository or files. Open the report to see its scope.
Repository and files the report names (10)
1,044 projects reviewed · 1,375 reports · latest May 2025
See CertiK’s auditsSomething wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.
Tell us