AsyncArt v3

February 26, 2021

1. Preface

The team of AsyncArt contracted byterocket to conduct a smart contract audit of AsyncArt V3. AsyncArt is developing and providing a novel platform, allowing artists and users to experience art uniquely through modifiable NFTs. Art pieces are separated into master layers and controller layers, which users may control to alter the appearance of the NFT.

The functionality is mainly contained in the AsyncArtwork_v3.sol file, with ERC721 (NFT) and ERC165 (interface support) related imports.

The team of byterocket reviewed and audited the above smart contracts in the course of this audit. We started on the 22nd of February and finished on the 26th of February.

The audit included the following services:

  • Manual Multi-Pass Code Review
  • Automated Code Review
  • In-Depth Protocol Analysis
  • Deploy & Test on our Testnet
  • Formal Report

byterocket gained access to the code via their public GitHub repository. We initially based the audit on the master branch’s state on February 10th, 2021 (commit hash 1bf330c4004de0ffdfced5e95a17e6765566b960).

2. Manual Code Review

We conducted a manual multi-pass code review of the smart contracts mentioned in section (1). Three different people went through the smart contract independently and compared their results in multiple concluding discussions.

These contracts are written according to the latest standards used within the Ethereum community and the Solidity community’s best practices. The naming of variables is very logical and understandable, which results in the contract being useful to understand. The code is very well documented and up to the latest standards.

On the code level, we found one low severity and one no severity bug or flaw. A further check with multiple automated reviewing tools (MythX, Slither, Manticore, and different fuzzing tools) did not find any additional bugs besides some common false positives.

2.1. Bugs & Vulnerabilities

A) AsyncArtwork_v3.sol - Line 392  [LOW SEVERITY]
if (controlToken.numRemainingUpdates > 0) {
controlToken.numRemainingUpdates = controlToken.numRemainingUpdates - 1;

The numRemainingUpdates variable is only being set after the whole execution happened, which is mostly fine. However, we want to point out that this potentially opens up the possibility of a re-entrancy abuse where the controller of the controlToken could execute more updates than they are allowed. Potentially, as soon as only one remaining update is left, an attacker could call the function twice at the same time within the same block; this could reduce numRemainingUpdates to -1, allowing them an infinite amount of future updates.

Potential mitigation would be to move this code section further up.

B) AsyncArtwork_v3.sol - Line 346  [NO SEVERITY]
function grantControlPermission(uint256 tokenId, address permissioned)

This function does not check whether the token exists. This does not have any security implications that we are aware of, but it doesn’t seem ideal to set permissions for non-existent tokens.

3. Protocol/Logic Review

Part of our audits are also analyses of the protocol and its logic. A team of three auditors went through the implementation and documentation of the implemented protocol.

As we already audited that second version of the AsyncArt smart contracts, we are familiar with the contracts’ general concept and inner workings. The main difference between the second and third versions is the different handling of auctions, which are now handled outside of the contract itself, enabled through ERC165 compliant secondary markets.

We were not able to discover any problems in the protocol implemented in the smart contract. As the overall changes mostly simplified the contract’s logic instead of adding to it, we are confident that these changes introduced no problems.

4. Testnet Deployment

As per our testing strategy, we deploy audited smart contracts (if requested by the client) onto a testnet to verify the implementation’s functionality. We usually deploy simple smart contracts to a local Ganache instance, which is sufficient for most cases. In this case, given the contracts’ complexity, we wanted to ensure no Ganache-related coverups of the contracts’ misbehavior. We created two testnets: a geth-based one and a parity/openethereum-based one. All of our tests have been executed on both testnets without any difference in the results. We were able to use the contracts as intended and could not maliciously game the protocol in practice.

We used fuzzing tools that generate random and semi-random inputs and interact with the contracts, trying to produce any unforeseen states within the contracts, especially the treasury contract. Throughout our tests, we were not able to create or observe any issues or problems. The contract behaved as expected and reverted correctly, given wrong inputs. No unforeseen states occurred during our fuzz-tests.

5. Summary

During our code review (which was done manually and automated), we found one low severity and one no severity bug or flaw. Our automated systems and review tools also did not find any additional ones.

The protocol review and analysis did neither uncover any game-theoretical nature problems nor any other functions prone to abuse.

During our multiple deployments to various local testnets, we haven’t been able to find any additional problems or unforeseen issues.

In general, we are delighted with the overall quality of the code and its documentation.

Stored on IPFS

We store our public audit reports on IPFS; a peer-to-peer network called the "Inter Planetary File System". This allows us to store our reports in a distributed network instead of just a single server, so even if our website is down, every report is still available.

Learn more about IPFS
Signed On-Chain

The IPFS Hash, a unique identifier of the report, is signed on-chain by both the client and us to prove that both sides have approved this audit report. This signing mechanism allows users to verify that neither side has faked or tampered with the audit.

Check the Signatures