The Sandbox security record

Ethereum, Polygon (POS)sandbox.gameXGitHub

As of 3 Nov 2026, The Sandbox has 8 audit reports by 2 security firms on Trustblock; the latest is by OpenZeppelin, dated 9 Aug 2023. Those reports list no open critical findings.

Critical findings
No open critical findings
Across the 8 reports, 1 critical finding was reported. It is marked fixed by the firm that reported it. See the findings
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.

Audits8

Newest first

AuditSecurity firmDateScopeCritical findingsReport
Land and Tunnel AuditSecurity firm: OpenZeppelinDate: Scope: Files named in the reportNo open critical findingsOpen · blog.openzeppelin.com
Sandbox L2Security firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
Sandbox L1Security firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
Sandbox Sand Reward PoolSecurity firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
Sandbox PRSecurity firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
Sandbox V1Security firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
SandBox V4Security firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
Sandbox V3Security firm: CertiKDate: Scope: Files named in the report1 critical finding, fixedOpen · skynet.certik.com

Findings100

Statuses are the security firm’s own, from each report.

SeverityOpenAcknowledgedFixed
Critical001
High124
Medium104
Low36150
HighCentralization Related RisksSandbox L2 · CertiK · 5 Jul 2022Open

In the contract `PolygonLandV1`, the role `_admin` has authority over the following functions: - `setPolygonLandTunnel()`: Modify the `PolygonLandTunnel` address; - `setTrustedForwarder()`: Modify the Trusted Forwarder for the Meta Transactions. In addition, the role `polygonLandTunnel` has authority over the following function: - `mint()`: mints quads to an address. The contract `PolygonLandV1` inherits the contract `PolygonLandBaseToken`, where the role `superOperator` has authority over the following functions: - `batchTransferQuad()`: Transfer any user's quads to an address; - `transferQuad()`: Transfer any user's quads to an address. The contract `PolygonLandBaseToken` inherits the contract `ERC721BaseToken`, where `_admin` has authority over the following functions: - `setSuperOperator()`: Give or remove the `superOperator` role to or from an address; - `changeAdmin()`: Change the address of the role `_admin`. In addition, the `superOperator` role has authority over the following functions: - `approve()`: Decide the allowance of any token; - `approveFor()`: Decide the allowance of any token; - `transferFrom()`: Transfer any user's tokens to an address; - `safeTransferFrom()`: Transfer any user's tokens to an address; - `batchTransferFrom()`: Transfer several of a user's tokens to an address; - `safeBatchTransferFrom()`: Transfer several of a user's tokens to an address; - `setApprovalForAllFor()`: Set the approval for an address to manage all of a user's tokens; - `burnFrom()`: Burn any user's tokens. In the contract `LandTunnel`, the role `_owner` has authority over the following functions: - `setTrustedForwarder()`: Modify the Trusted Forwarder for the Meta Transactions; - `pause()`: Pause the contract to disable transfers from L1 (Ethereum) to L2 (Polygon); - `unpause()`: Unpause the contract, reenabling transfers from L1 (Ethereum) to L2 (Polygon). In the contract `PolygonLandTunnel`, the role `_owner` has authority over the following functions: - `setMaxLimitOnL1()`: Define the maximum amount of gas to spend for `batchTransferQuadToL1()` function; - `setMaxAllowedQuads()`: Define the maximum amount of quads to transfer for `batchTransferQuadToL1()` function; - `setLimit()`: Define the maximum amount of gas to spend on transfers for one quad type; - `setupLimits()`: Define the maximum amount of gas to spend on transfers for each quad type; - `setTrustedForwarder()`: Modify the Trusted Forwarder for the Meta Transactions; - `pause()`: Pause the contract, preventing transfers from L2 (Polygon) to L1 (Ethereum); - `unpause()`: Unpause the contract, reenabling transfers from L2 (Polygon) to L1 (Ethereum). Any compromise to the aforementioned privileged accounts may allow a hacker to take advantage of this authority and manipulate the reward system.

Open the report
MediumMisconception of RandomnessSandBox V4 · CertiK · 19 Aug 2020Open

The function “_computeValue” is meant to calculate a random value for assigning the value per gem ID. The result of its computation is stored in a variable named “randomValue”.

Open the report
LowLack of input validationLand and Tunnel Audit · OpenZeppelin · 9 Aug 2023Open

The report gives no description here.

Open the report
LowBurning Logic Inconsistency on L1 and L2Sandbox L2 · CertiK · 5 Jul 2022Open

Sandbox has implemented Land token protocols on both L1 (Ethereum) and L2 (Polygon). The burning logic of the two protocols is different.\ On L2, the `_burn()` function sets the 160st bit of the token's owner record to be 1 (as an identification for burnt tokens) yet maintains the previous owner's address. ```solidity= //Burning Logic on L2 function _burn( address from, address owner, uint256 id ) internal { require(from == owner, "NOT_OWNER"); uint256 storageId = _storageId(id); _owners[storageId] = (_owners[storageId] & NOT_OPERATOR_FLAG) | BURNED_FLAG; // record as non owner but keep track of last owner _numNFTPerAddress[from]--; emit Transfer(from, address(0), id); } ``` On L1, the `_burn()` function sets the token's owner record as 2**160 and removes the previous owner's address. ```solidity= //Burning Logic on L1 function _burn(address from, address owner, uint256 id) internal { require(from == owner, "not owner"); _owners[id] = 2**160; // cannot mint it again _numNFTPerAddress[from]--; emit Transfer(from, address(0), id); } ``` If tokens on L1 and L2 are designed to be equal, it is recommended to maintain the same logic to avoid unexpected consequences resulting from protocol incompatibility.

Open the report
LowPotential Denial-of-Service AttackSandbox L2 · CertiK · 5 Jul 2022Open

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. **Proof of Concept**\ The following test shows that if 289 1x1 Land is minted, no one can mint 24x24 land anymore. ```typescript it('Mint 289 land to prevent others mint 24x24', async function () { const {landOwners} = await setupTest(); const bytes = '0x3333'; let totalMint = 0; for (let x = 0; x < GRID_SIZE; x = x + 24) { for (let y = 0; y < GRID_SIZE; y = y + 24 ){ await waitFor( landOwners[0].MockLandWithMint.mintQuad( landOwners[0].address, 1, x, y, bytes )); //console.log('Minted a land on (%d,%d)', x , y) totalMint = totalMint + 1; } } console.log('Step1: Mint lands in each 24x24 land. In total %d 1x1 land is minted', totalMint); let totalTry = 0; for (let x = 0; x < GRID_SIZE; x = x + 24) { for (let y = 0; y < GRID_SIZE; y = y + 24) { await expect( landOwners[0].MockLandWithMint.mintQuad( landOwners[0].address, 24, x, y, bytes ) ).to.be.revertedWith('Already minted'); totalTry = totalTry + 1; } } console.log('Step2: Cannot Mint 24x24 land Anymore. In total %d times tried to mint 24x24 land', totalTry) }); ``` The test case passed, meaning 24x24 land can no longer be minted ```typescript Step1: Mint lands in each 24x24 land. In total 289 1x1 land is minted Step2: Cannot Mint 24x24 land Anymore. In total 289 times tried to mint 24x24 land ✓ Mint 289 land to prevent others mint 24x24 (18107ms) ```

Open the report
LowThird Party DependenciesSandbox L2 · CertiK · 5 Jul 2022Open

The contract is serving as the underlying entity to interact with third-party **fx-portal** protocols. The **fx-portal** protocol performs the cross-chain functionality to exchange messages between Ethereum and Polygon. Land Bridge interacts with the following contracts: - [FxBaseChildTunnel](https://github.com/fx-portal/contracts/blob/main/contracts/tunnel/FxBaseChildTunnel.sol) - [FxBaseRootTunnel](https://github.com/fx-portal/contracts/blob/main/contracts/tunnel/FxBaseRootTunnel.sol) The scope of the audit treats 3rd party entities as black boxes and assumes their functional correctness. However, in the real world, 3rd parties can be compromised, which may lead to lost or stolen assets.

Open the report
LowLack of Access Control for `burn` FunctionSandbox PR · CertiK · 26 Jan 2022Open

In the current implementation, when the `LandSwap` contract is not in the pause condition, the `burn()` function allows everyone to burn `oldLand` tokens with given `ids`. The `LandSwap` contract receives `oldLand` tokens during the `swap()` invocation and those tokens are supposed to be burnt immediately. The concern is if those `_oldland` tokens are still valuable to some extent (f.e., causing market fluctuation), a user might call this function at a certain time for his own profit.

Open the report
LowMissing Input ValidationSandbox PR · CertiK · 26 Jan 2022Open

In `LandSwap.sol`, no checks are performed on input parameters to ensure that those are ones that are valid. In the `LandSwap` contract, in the `initialize()` function : - `admin` and `trustedForwarder` addresses should be non-zero addresses; - `oldLand` should not be the same address as `newLand`; - `admin` should not be the same address as `trustForwarder`.

Open the report
LowThird Party DependenciesSandbox PR · CertiK · 26 Jan 2022Open

The contract serves as the underlying entity to interact with the following third-party protocols. - `_oldLand` - `_newLand` - `trustedForwarder` The scope of the audit treats third-party entities as black boxes and assumes their functional correctness. However, in the real world, third parties can be compromised, which may lead to lost or stolen assets. For instance, if the `trustedForwarder` is initialized to a malicious address, it could cause unexpected errors during the land-swapping process.

Open the report
LowUnknown Implementation of Meta Transaction ContraSandbox PR · CertiK · 26 Jan 2022Open

The implementation of `_metaTransactionContracts[msg.sender]` is unknown. In the case that `metaTransaction` contracts are public and propose a `call()` feature, users could call a `metaTransaction` contract, having their call being forwarded to `ERC721BaseToken`. In this scenario, `msg.sender` received by `ERC721BaseToken` would be the address of `metaTransaction`, which would verify the condition `require(_metaTransactionContracts[msg.sender]`. If the check can be bypassed through this way, this would be problematic in the functions using this check: - `ERC721BaseToken`.`approveFor()`; - `ERC721BaseToken`.`_checkTransfer()`; - `ERC721BaseToken`.`_batchTransferFrom()`; - `ERC721BaseToken`.`setApprovalForAllFor()`; - `ERC721BaseToken`.`burnFrom()`. For example, ```solidity=301 function setApprovalForAllFor( address sender, address operator, bool approved ) external { require(sender != address(0), "Invalid sender address"); require( msg.sender == sender || _metaTransactionContracts[msg.sender] || _superOperators[msg.sender], "not authorized to approve for all" ); _setApprovalForAll(sender, operator, approved); } ``` The above function will successfully triggered if the caller is from meta transaction contract (i.e., `_metaTransactionContracts[msg.sender]` is `true`).

Open the report
Show 10 more
LowUnlocked Compiler VersionSandbox PR · CertiK · 26 Jan 2022Open

The `PausableWithAdmin` contract has an unlocked compiler version. An unlocked compiler version in the source code of the contract permits the user to compile it at or above a particular version. This, in turn, leads to differences in the generated bytecode between compilations due to differing compiler version numbers. This can lead to ambiguity when debugging as compiler-specific bugs may occur in the codebase that would be hard to identify over a span of multiple compiler versions rather than a specific one. Additionally, it has been noticed that all contracts are compiled with the compiler version over `0.5.2`, which dates from March 2019. It is recommended to update the compiler versions in the contracts, so they are not exposed to potential security issues related to old compiler versions.

Open the report
LowAssignment OptimizationSandbox V1 · CertiK · 7 Jan 2022Open

Saving information of address owner and bool operatorEnabled in a uint256 is of high efficiency. However, this data structure requires developers to stay aware of the changes when they are trying to make conversion between uint256 and address.

Open the report
LowMissing Input ValidationSandbox V1 · CertiK · 7 Jan 2022Open

The given input `size` is missing the check for the non-zero address.

Open the report
LowProper usage of `pure`Sandbox V1 · CertiK · 7 Jan 2022Open

The pure functions do not read or modify the state variables, which returns the values only using the parameters passed to the function or local variables present in it.

Open the report
LowProper usage of `view`Sandbox V1 · CertiK · 7 Jan 2022Open

The view functions are read-only function, which ensures that state variables cannot be modified after calling them.

Open the report
LowPull-Over-Push Pattern for changeAdmin()Sandbox V1 · CertiK · 7 Jan 2022Open

The change of `admin` by function `changeAdmin()` overrides the previously set `admin` with the new one without guaranteeing the new `admin` can actuate transactions on-chain.

Open the report
LowPull-Over-Push Pattern for constructorSandbox V1 · CertiK · 7 Jan 2022Open

In the constructor, the variable `_admin` is assigned by an explicit `admin` address, and the input is not validated.

Open the report
LowPull-Over-Push Pattern for constructorSandbox V1 · CertiK · 7 Jan 2022Open

In the constructor, the variable `_admin` is assigned by an explicit `admin` address, and the input is not validated.

Open the report
LowSafeMath Not UsedSandbox V1 · CertiK · 7 Jan 2022Open

SafeMath from OpenZeppelin is not used in the following functions which makes them possible for overflow/underflow and will lead to an inaccurate calculation result.

Open the report
LowSafeMath Not UsedSandbox V1 · CertiK · 7 Jan 2022Open

SafeMath from OpenZeppelin is not used in the following functions which makes them possible for overflow/underflow and will lead to an inaccurate calculation result. - `_burn()` - `_transferFrom()` - `_batchTransferFrom()`

Open the report
Show 10 more
LowSafeMath Not UsedSandbox V1 · CertiK · 7 Jan 2022Open

SafeMath from OpenZeppelin is not used in the following functions which makes them possible for overflow/underflow and will lead to an inaccurate calculation result. - `x()` - `y()`

Open the report
LowUsage of `uint` Alias Instead of `uint256`Sandbox V1 · CertiK · 7 Jan 2022Open

According to the coding practice, the `uint` is an alias for `uint256` and both represent the same underlying integer allocation. It is advisable that for clean coding practices the complete form `uint256` should be used instead of the alias `uint`.

Open the report
LowDuplicate Code SegmentsSandBox V4 · CertiK · 19 Aug 2020Open

The aforementioned code segments are exactly the same leading to unnecessary bytecode duplication.

Open the report
LowInconsistent ImplementationSandBox V4 · CertiK · 19 Aug 2020Open

The functions “_batchMint” and “batchTransferFrom” are similar, however they utilize a different variable to conduct the logic and represent the maximum of “uint256” in two different ways (“2**256 - 1” and “0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF”).

Open the report
LowInefficient Struct Member AssignmentSandBox V4 · CertiK · 19 Aug 2020Open

The statements between L26 and L29 can be grouped to a single instantiation and assignment as in its current state it conducts a full word update operation on each line.

Open the report
LowInvalid Bit-Wise ConstantsSandBox V4 · CertiK · 19 Aug 2020Open

Within the smart contracts, two bit-wise operands that are contradictory exist named “IS_NFT” and “NOT_IS_NFT”.

Open the report
LowMisleading Function NameSandBox V4 · CertiK · 19 Aug 2020Open

The function “_checkGemsQuantities” is meant to check the “gemQuantities” provided as arguments to the function, however it also alters them by subtracting 1 on each quantity before returning them.

Open the report
LowOptimization of ID LogicSandBox V4 · CertiK · 19 Aug 2020Open

The function “getValues” internally finds the maximum gem ID possible, instantiates an empty in-memory array with a length equal to that ID plus one and proceeds to utilize the array as a "mapping" of a gem's ID with its computed value.

Open the report
LowRedundant Safe MathSandBox V4 · CertiK · 19 Aug 2020Open

The statements of L67 conduct the same conditional checks whereby “allowance” is ensured to be greater than or equal to amount internally within the sub function of “SafeMathWithRequire”.

Open the report
LowSmart Contract Structure LayoutSandBox V4 · CertiK · 19 Aug 2020Open

The structure of the codebase does not conform to the official Solidity style guide of v0.6.5. An indicative excerpt of the style guide is that functions should be grouped according to their visibility and ordered: * constructor * receive function (if exists) * fallback function (if exists) * external * public * internal * private Additionally, the internal layout of a contract should be as follows: * Type declarations * State variables * Events * Functions However, these paradigms are not followed, increasing the illegibility of the codebase and rendering it harder to orient oneself within the codebase.

Open the report
Show 8 more
LowUnnecessary Named Return VariablesSandBox V4 · CertiK · 19 Aug 2020Open

Within the codebase, multiple types of “return” variables are explicitly named in the function signature, however they are never utilized.

Open the report
LowUtilization of Utility FunctionsSandBox V4 · CertiK · 19 Aug 2020Open

The functions “isApprovedForAll”, “isAuthorizedToTransfer” and “isAuthorizedToApprove” are functions that could be used within the require statements of L150 to L157, L178 to L181, L223, L257 and L274. Additionally, “isAuthorizedToTransfer” internally can call “isAuthorizedToApprove”.

Open the report
LowValue Overriddence LogicSandBox V4 · CertiK · 19 Aug 2020Open

The function “addCatalysts” accepts a “valueOverrides” array that signifies whether the values of a particular catalyst should be overridden. However, the logical check of L21 to L23 would make it impossible to override the value of the f.e. third catalyst without overriding all preceding values.

Open the report
LowRepeated function callsSandbox V3 · CertiK · 19 May 2020Open

The functions “buyLandWithSand”, “buyLandWithETH”, “buyLandWithDAI” don’t check whether the input referral has been processed hence we need to be sure that an unauthorized user cannot call these functions more than the intended number of times.

Open the report
LowUnchecked scenarioSandbox V3 · CertiK · 19 May 2020Open

In the function “buyLandWithETH” it can happen that “msg.sender” is a contract, hence “msg.sender.transfer(msg.sender - ETHRequired)” would trigger the receive or fallback function and can lead to revert. Nowaday “.transfer” only forwards 2300 gas, hence reentrancy is not a threat here.

Open the report
LowUnclear codeSandbox V3 · CertiK · 19 May 2020Open

The function `_checkAdjacency` always checks the adjacency of each newly added quad towards the last quad in the estate. Because of this the respective junction is left out which makes the junctions and quad lists unequal in length and usage of “_checkAdjacency” unintuitive

Open the report
LowUnclear requirementSandbox V3 · CertiK · 19 May 2020Open

The function “approve” cannot be used by “_metaTransactionContracts” even though it is allowed in “approveFor” and we can just set “sender” to be the owner of the estate in “approveFor” to have the same effect for “approve”.

Open the report
LowUnclear requirementSandbox V3 · CertiK · 19 May 2020Open

The implementation of “check_burn_authorized” , “check_add_authorized” and “check_create_authorized” indicates that Meta Transaction Contract, Superoperators, Operators for all, Operators, or even the owners of the estates are deprived of respective burning, adding, creating rights whenever a fixed global minter or breaker are set. Is this behaviour intended?

Open the report

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.

Content

Facts

Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="the-sandbox"
></div>
</body>

Paste the code where the label should appear. It uses no API credit and updates by itself.

Something 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