Decentraland Audit Report

Our audit report on components of the Marketplace and Districts features.

Nomic Foundation
Nomic Foundation
4 min readJan 29, 2019

--

Summary

The Decentraland team asked us to audit their MarketplaceBurner contract at commit d5ff5e089e3204b0138b440dd77cae573a3108f9 and their PassThrough contract at commit 9d83d9edd6c7b22cbf63c428ae0ab39a7d618557.

No vulnerabilities were found. Additional recommendations were made around the implementation.

Full Report

Related contracts and assumptions

The following assumptions were made during the audit:

Estate registry contract

Implementation’s source code: EstateRegistry.sol

Land registry contract

Implementation’s source code: LANDRegistry.sol

MANA contract

Source code: MANAToken.sol

Marketplace contract

Implementation’s source code: Marketplace.sol

Contracts ownership

  • PassThrough contracts will be owned by Decentraland, and their operators will be external parties.
  • MarketplaceBurner will own the Marketplace and will be owned by Decentraland.

Audit results

Critical severity

No critical severity issues were found.

High severity

No high severity issues were found.

Medium severity

[DCL-M01] Possible accidents on Estate registry upgrades

If the Estates registry contract is updated with a new function for transferring LAND, such function must be blacklisted in advance in every instance of PassThrough. A failure to do so can result in an Estate being disassembled.

Our recommendation is to invert the PassThrough contract logic, implementing it as a whitelist instead of a blacklist. A whitelist approach has a much smaller impact if a function isn’t listed, just temporarily degrading the user experience.

One thing to note is that the whitelist should only be applied when the target is the estateRegistry. Otherwise, the users won’t be able to change the target to remedy any incident on their own, as most/all functions they need to call won’t be whitelisted.

Update: Decentraland informed that this alternative was already considered, and they will take care of updating all PassThrough instances as part of the LAND upgrade process.

Low severity

[DCL-L01] PassThrough payable target functions can’t be called with ETH

There’s no way to call a target’s function with ETH, as PassThrough’s fallback function is not payable, and its assembly call invocation always set the value as 0. In order to fix this, the fallback function has to be payable, and msg.value should be passed to call.

This may have an impact for users that are changing the contract’s target to fix some mistake. It’s hard to predict if this scenario is feasible or not, but the fixes needed to avoid it are really straight forward and we recommend implementing them.

Update: This issue has been fixed in PR #3.

[DCL-L02] Missing checks in PassThrough constructor

The PassThrough constructor doesn’t validate the values of the _estateRegistry and _operator addresses. While this could lead to accidentally deploying it with wrong values, its impact would be very low, as the contract can be redeployed.

Update: This issue has been fixed in PR #3.

[DCL-L03] Missing checks in MarketplaceBurner constructor

The MarketplaceBurner constructor doesn’t validate the values of the MANA and Marketplace addresses. While this could lead to accidentally deploying it with wrong values, its impact would be very low, as the contract can be redeployed.

Our recommendation is to provide a deployment script that uses the suitable addresses when running against mainnet. This makes the deployment auditable and testable.

[DCL-L04] Disabled functionality

The following Marketplace functions are not exposed by the MarketplaceBurner:

The impact of this depends on how often these functions are called and should be assessed by Decentraland. Our opinion is that only cancelOrder and setPublicationFee are going to be needed.

Comments

Multiple unit tests don’t check that the expected results are met

There are multiple tests that call a PassThrough contract without checking its result (e.g. this test). This can hide possible bugs, as every non-blacklisted call will be redirected to the target, but not necessarily to the right function.

Missing functions in the end to end tests

The following functions aren’t tested in the end to end tests:

  • safeTransferFrom(address,address,uint256,bytes)
  • safeTransferManyFrom(address,address,uint256[],bytes)

No test of failures in the target contract

The test suite doesn’t include cases where the call to the target contract reverts. This is especially important to be sure that error messages are correctly forwarded.

Update: This issue has been fixed in PR #3 and PR #4.

Code coverage couldn’t be computed

As part of the audit process, we tried to analyze the code coverage of the contract, with no luck in computing it.

The standard tool for doing so, solidity-coverage, failed to work with this contract due to its parser being outdated. After manually changing the source code to work around this situation, other errors arose that couldn’t be solved.

Despite not being able to quantify the coverage and with exception of the comments stated above, the contract is very well tested.

Missing check in transferMarketplaceOwnership

This function doesn’t check if the new target is address(0), which is considered a good practice but Ownable#transferOwnership does it, so address(0) can’t be accidentally used.

Missing tests

No automated tests were provided along the MarketplaceBurner contract. We recognize that there’s little value to be added by unit tests in this case, but integration tests would be valuable.

We recommend automating the deployment of a test environment for every, or at least most, Decentraland smart contracts, as there are multiple interconnected parts. Not having such script leads to repeating work or avoiding tests altogether.

Conclusion

No security issues were found. Some changes were proposed to reduce the risk of potential future human mistakes.

--

--