Kolor Audit Report
Copyright © 2022 by Verilog Solutions. All rights reserved.August 19, 2022
by Verilog Solutions

This report presents our engineering engagement with the Kolor dev team on their metaverse Web3 application – Kolor’s dApp.
Project Name | Kolor |
---|---|
Repository Link | https://github.com/yieniggu/kolor-contracts |
Commit Hash | 496ac663b37df81b3ea590d72f381d845768ed24 |
Language | Solidity |
Chain | Celo |
About Verilog Solutions
About Verilog Solutions
Founded by a group of cryptography researchers and smart contract engineers in North America, Verilog Solutions elevates the security standards for Web3 ecosystems by being a full-stack Web3 security firm covering smart contract security, consensus security, and operational security for Web3 projects.
Verilog Solutions team works closely with major ecosystems and Web3 projects and applies a quality above quantity approach with a continuous security model. Verilog Solutions onboards the best and most innovative projects and provides the best-in-class advisory services on security needs, including on-chain and off-chain components.
Table of Contents
Table of Contents
Service Scope
Service Scope
Service Stages
Service Stages
- Architecture Consultancy Service
- Protocol Security & Design Discussion Meeting
- As a part of the audit service, the Verilog Solutions team worked closely with the Kolor development team to discuss potential vulnerability and smart contract development best practices in a timely fashion. Verilog Solutions team was very appreciative of establishing an efficient and effective communication channel with the Kolor team, as new findings were exchanged promptly and fixes were deployed quickly, during the preliminary report stage.
- Protocol Security & Design Discussion Meeting
- Smart Contract Auditing Service
- The Verilog Solutions team analyzed the entire project using a detailed-oriented approach to capture the fundamental logic and suggested improvements to the existing code. Details can be found under Findings & Improvement Suggestions.
Methodology
Methodology
- Code Assessment
- We evaluate the overall quality of the code and comments as well as the architecture of the repository.
- We help the project dev team improve the overall quality of the repository by providing suggestions on refactorization to follow the best practice of Web3 software engineering.
- Code Logic Analysis
- We dive into the data structures and algorithms in the repository and provide suggestions to improve the data structures and algorithms for the lower time and space complexities.
- We analyze the hierarchy among multiple modules and the relations among the source code files in the repository and provide suggestions to improve the code architecture with better readability, reusability, and extensibility.
- Access Control Analysis
- We perform a comprehensive assessment of the special roles of the project, including their authorities and privileges.
- We provide suggestions regarding the best practice of privilege role management according to the standard operating procedures (SOP).
- Off-Chain Components Analysis
- We analyze the off-chain modules that are interacting with the on-chain functionalities and provide suggestions according to the SOP.
- We conduct a comprehensive investigation for potential risks and hacks that may happen on the off-chain components and provide suggestions for patches.
Audit Scope
Audit Scope
Project Summary
Project Summary
Kolor’s protocol is a metaverse project that includes tokens, NFTs, and a marketplace system. Regular users receive land tokens that represent investments in land NFTs minted and customized by the Kolor team with species and geospatial points. On the other hand, companies are able to add value to those NFTs by offsetting emissions on them. All the interactions happen on Kolor’s platform.

Kolor’s dApp System Architecture
Kolor’s dApp consists of three smart contracts with the following main logic blocks:
KolorLandNFT.sol
contains the main logic of minting NFTs and transferring them to the marketplace.
This contract has the following main functions:
safeMint()
: It mints NFT and updates relevant information about it.
updateLandState()
: It updates the land NFT state.
addBuyer()
: Kolor marketplace can add a new buyer to the land NFT.
safeTransferToMarketplace()
: It transfers the NFT to the marketplace and marks it as published for buyers to offset emissions.
setSpecies()
: If the species haven’t been set, it sets all species of a certain land NFT.
addSpecies()
: It adds species to the input land NFT.
updateSpecies()
: It updates the species info of the input land NFT.
setPoints()
: If the points haven’t been set, sets geospatial points of the input land NFT.
addPoint()
: It adds the geospatial point to the input land NFT.
updatePoint()
: It updates geospatial point info of the input land NFT.
offsetEmissions()
: Kolor marketplace can call this function to update the sold TCO2 of land NFT.
KolorLandToken.sol
contains the logic for minting tokens and adding investments.
It contains the main logic of minting land tokens and using them to add investments to land NFTs, 1 land token is associated with 1 square meter of the NFT. This contract has the following main functions:
setLandTokenInfo()
: It sets land token info.
addNewTokens()
: It mints land tokens.
newInvestment()
: It updates the number of holders, sets approval for dev or another operator, and updates balances and investments.
safeTransferFrom()
: It transfers tokens from one user to another.
KolorMarketplace.sol
contains the logic for offsetting emissions on the minted NFTs.
Once the NFT is transferred to the marketplace, this contract contains the logic for offsetting emissions on them. This contract has the following main functions:
removeLand()
: It updates the state of input land to paused.
offset emissions()
: If land NFT is published and has enough TCO2 to offset, it adds buyers to the land and creates information about the offset.
addOffsetsEmissionsOfBuyer()
: It updates registry emissions per buyer.
addOffsetsEmmisionsOfLand()
: It updates registry emissions per land.
Use Case Scenario
Use Case Scenario
The Kolor’s dApp is a metaverse platform that allows users to invest in NFTs that represent different lands in the real world. Each land is defined by the species and geospatial points that shape them. Once the land is created, it will start emitting carbon dioxide which can be offset as long as the land has been transferred to the marketplace. Value and excitement are added once big companies offset emissions on the land.
Findings & Improvement Suggestions
Findings & Improvement Suggestions
Severity | Total | Acknowledged | Resolved |
High | 1 | 0 | 1 |
Medium | 9 | 2 | 7 |
Low | 2 | 1 | 1 |
Informational | 11 | 8 | 3 |
High
High
- Addresses issue
Severity High Source KolorLandNFT.sol#L120, L127; Status Resolved in commit 7c091960656518c20169e3493ec476f44938e93b; Description
Mappings in
safeMint()
function don’t get updated with the correct address. This causes several critical functions to not work properly. There are two important address type variables passed into functionKolorLandNFT.safeMint()
, which areto
andlandOwner
. All land NFTs are minted to theto
address, which is an address controlled by the Kolor team. The function only records and updates the NFT owner (i.e.,landOwner
) in the internal mappingsmintedNFTSInfo
,ownedLand
,_totalLandOwned
. However, the addressto
is used when updating some of those mappings wherelandOwner
should be used. Setting this incorrectly causesownedLands
and_totalLandsOwned
to update the wrong address. This affects functions liketotalLandOwnedOf()
,landOfOwnerByIndex()
,updateLandOwner()
, etc.
Exploit Scenario
- Kolor team mints NFT and set Alice as the
landOwner
;
- Since mapping are not correctly updated,
landOwner
mappings get assigned to Kolor team;
- Several functions cannot work correctly because of the wrong information in the mapping.
- Kolor team mints NFT and set Alice as the
Recommendations
Update mappings using correct addresses. Consider the following lines:
// set the tokenId to current landowner collection index ownedLands[to][_landsOwned] = currentTokenId;
// increase total lands owned by address _totalLandOwned[to]++;
Change them to this:
// set the tokenId to current landowner collection index ownedLands[landOwner][_landsOwned] = currentTokenId;
// increase total lands owned by address _totalLandOwned[landOwner]++;
Results
Resolved in commit 7c091960656518c20169e3493ec476f44938e93b.
landOwner
now has been used to update the mappings.
Medium
Medium
- Inconsistency in business logic and actual implementation
Severity Medium Source KolorMarketplace.sol#L54; KolorLandNFT.sol#L187; Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
Functions
removeLand()
andupdateLandState()
remove NFTs from the marketplace by changing the published state. However, there’s no logic to returning the emissions offset to buyers or tokens to investors and returning NFT to the sender. These functions update the state of the land token. So, it does not allow any more buyers to offset emissions on the land NFTs. Nevertheless, if a buyer had already offset emissions, they would lose all the TCO2 since there’s no logic to return the already offset emissions. Additionally, there’s no restriction for investors to add investments but they should get their tokens back when the state of the NFT is changed. Buyers and investors cannot recover their emissions and tokens respectively when the state is switched, which prevents them from using those emissions and tokens on other NFTs.
Exploit Scenario
Buyer offsets emissions on land NFT that is already in the marketplace and the user already has investment on it, and then the NFT is removed from it by updating its state. Now, the buyer is not able to recover those emissions and the user is not able to recover tokens unless the state is once again changed to published.
Recommendations
Add logic to return the emissions to buyers and tokens to investors when the land NFT is removed from the marketplace and returned to the initial sender.
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
Land NFT is now transferred back to the
owner
when calling theremoveLand()
function.
- Inconsistency in the function name and actual implementation
Severity Medium Source KolorLandNFT.sol#L282; Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
The require statement in function
landOfOwnerByIndex()
uses thebalanceOf()
thelandOwner
instead oftotalLandOwnedOf()
. The false require statement inlandOfOwnerByIndex
can cause unexpected reverts. FunctionbalanceOf()
is inherited fromERC721.sol
and therefore returns the count of NFT minted by a certain address. Nevertheless, when creating an NFT one can assign a differentlandOwner
, sobalanceOf()
would not be really counting the number of lands owned by a certain address which is what functionlandOfOwnerByIndex()
tries to return for an input index. Not checking with the correct function in therequire
statement of functionlandOfOwnerByIndex()
can cause a revert.
Exploit Scenario
Kolor team mints an NFT and updates the
landOwner
to Alice, then Alice is the owner of one NFT but callinglandOfOwnerByIndex()
will cause a revert since the NFT wasn’t minted to her.
Recommendations
Change
balanceOf()
tototalLandOwnedOf()
in therequire
statement in functionlandOfOwnerByIndex()
. Also update_totalLandOwned
mapping when callingupdateLandOwner()
.
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
Mapping
_totalLandOwned
now gets updated when callingupdateLandOwner()
. Additionally,require
statement for functionlandOfOwnerByIndex()
has been fixed.
- Excessive access control
Severity Medium Source Global Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
The protocol has access control risks. Since the project team address has the ability to control almost all the functionality, a key leakage would have devastating effects. Most of the functions use the
onlyAuthorized()
modifier which means only the account address authorized can get access to the functionality. Since this is set to true for the sender in theconstructor()
, the authorization relies solely on one account in the beginning. A private key leakage from the authorized address will result in the loss of both control and access to the protocol.
Exploit Scenario
If the team holds the key and the key got leaked, an attacker can transfer ownership of the contracts, authorize their address and unauthorize the team’s address. This will lead to the team not having access and control over the protocol anymore.
Recommendations
Implement an extra admin layer to the protocol following something similar to a hot and cold wallet admin system.
- Access control
Most functions for the three contracts come with the
onlyAuthorized()
modifier that only allows authorized addresses to execute functions. This is set to true on the deployment of the contract for the sender of the transaction, however, other addresses can get authorized by calling theauthorize()
function on any of the three contracts. Theauthorize()
function can only be called by the owner of the respective contract.Since the access control for the protocol relies very heavily on only one address, we recommend the use of an extra admin layer, so that in case the private key of the admin address gets leaked, the funds will not get drained. In this case, a cold wallet will be the admin storing the protected keys and will allow the hot wallet to perform all the functionality.
- OpSec on the usage of hot and cold wallet
The two-layer admin system would consist of a cold wallet, of which keys will be used really infrequently, preferably only in the case of an attack, and a hot wallet that will be the one that performs all the logic of the protocol and which keys will be used very often. The cold wallet will be able to set and revoke the hot wallet in charge of executing the functionality of the project.
The use of this system allows for extra security throughout the entire protocol since the hot wallet is making many transactions, and it becomes really exposed to possible attacks. Therefore, having this extra layer prevents from losing control over the protocol if the keys of the hot wallet get compromised or hacked.
- Access control
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
owner
will authorize an account to perform all the functionality of the protocol. If the key gets leaked,owner
will unauthorize that account and authorize a different account.
- Incorrect payable modifier
Severity Medium Source KolorMarketplace.sol#L65, L231; Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
Function
offsetEmissions()
ispayable
, therefore the contract is able to receive CELO through that function and there’s no way to withdraw it. Thepayable
functions in solidity are able to receive the native currency of the blockchain when being called. However, for this contract, this is not necessary since all the functionality lies in updating variables and mappings. Also, since there’s no withdraw function in the contract, there’s no way to recover sent tokens through that function. So, mistakenly sending tokens through this function will get them locked in the contract forever.
Exploit Scenario
If mistakenly send CELO through that function, those tokens will be locked forever.
Recommendations
Remove the
payable
keyword and thegetBalance()
function.
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
The
payable
keyword has been removed.
- LandToken info can be initialized twice
Severity Medium Source KolorLandToken.sol#L90; Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
KolorLandToken.setLandTokenInfo()
initializes info for land tokens. However, this function does not check whether the land token has been set before and thus the land token info can be initialized again. FunctionKolorLandToken.setLandTokenInfo()
sets the land token’s initial amount, sold amount, and creation date. By setting the initial amount, the amount will be added to the land token’s current amount and available amount. And the sold amount will be reset to 0. However, these are critical info that will be updated by other functions likeKolorLandToken.NewInvestment()
. By being able to set the land token info again, that critical Investment info for this land token can be reset or overwritten.
Exploit Scenario
- Authorized account Alice set initial land info for land token
123
by calling functionKolorLandToken.setLandTokenInfo()
;
- Alice adds a new investment for investor Bob on land token
123
by calling the functionKolorLandToken.NewInvestment()
;
- Alice called the function
KolorLandToken.setLandTokenInfo()
to reset the investment amount and sold amount on the land token.
- Authorized account Alice set initial land info for land token
Recommendations
Check if the land token info initialized in
KolorLandToken.setLandTokenInfo()
.function setLandTokenInfo(uint256 tokenId, uint256 initialAmount) external onlyAuthorized { require(exists(tokenId), "KolorLandToken: land must exists!"); require(landTokensInfo[tokenId].initialAmount == 0, "KolorLandToken: land token has initialized!"); landTokensInfo[tokenId].initialAmount = initialAmount; landTokensInfo[tokenId].sold = 0; landTokensInfo[tokenId].creationDate = block.timestamp; addNewTokens(tokenId, initialAmount); }
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
Require statement has been added to prevent this.
- Authorization issue
Severity Medium Source KolorLandToken.sol#L103; Status Acknowledged. Description
Function
KolorLandToken.addNewTokens()
calls functionKolorLandToken.mint()
to mint new tokens. The function call may fail becauseKolorLandToken.mint()
function requires only an authorized caller. When functionKolorLandToken.addNewTokens()
calls the functionKolorLandToken.mint()
, the caller of functionKolorLandToken.mint()
is the contract itself,address(this)
. FunctionKolorLandToken.mint()
requires the caller to be an authorized user only. The function call may revert if the contract itself is not added to the authorized address list.
Exploit Scenario
Contract
KolorLandToken.sol
hasn’t been authorized and authorized user callsaddNewTokens()
, transaction will revert because the internal call is done fromKolorLandToken.sol
which isn’t authorized.
Recommendations
In the contract design, we recommend marking the function
KolorLandToken.mint()
be private or internal if it will only be called inside functionKolorLandToken.addNewTokens()
.
Results
Acknowledged.
Kolor team will call the
authorize()
function on the contract to authorize the contract address.
- Land NFT token id should start from a non-zero value
Severity Medium Source KolorLandNFT.sol#L287; Status Acknowledged; Description
The default value 0 is the return result when accessing mapping
KolorLandNFT.ownedLands
. Its default return is 0 when the mapping is not set. To avoid confusion between token id 0 and empty value, the land NFT token id should increment from a non-zero value. The mappingKolorLandNFT.ownedLands
returns the land NFT token id given owner and the owner’s land NFT index. As the mapping’s default return is zero when a value or mapping does not exist, we might confuse it with the return of the actual NFT token id.
Exploit Scenario
- Alice has an NFT, token id is 0, index 7;
- Bob does not own any land NFT;
- The mapping
KolorLandNFT.ownedLands
return 0 when querying with Bob’s address;
- The system will assume Bob owned the token id 0 NFT.
Recommendations
Start Land NFT’s token id from 0.
Results
Acknowledged.
Kolor team decided to leave this part as it was.
- Missing updates for land owner-related mapping
Severity Medium Source KolorLandNFT.sol#L197; Status Resolved in commit 7c091960656518c20169e3493ec476f44938e93b; Description
KolorLandNFT.ownedLands
is not updated when updating land owners.Function
KolorLandNFT.updateLandOwner()
is used to update a land’s owner. MappingKolorLandNFT.ownedLands
which records all lands owned by a person is not updated when the function updates the land owner. The mappingKolorLandNFT.ownedLands
is also used in functionKolorLandNFT.landOfOwnerByIndex()
. Given the land owner and the index of the land owner’s land, it will return the corresponding land token Index. If the mappingKolorLandNFT.ownedLands
is not updated correctly, then false results might be returned when we query the function. Land may have been transferred to others but the function still returns the land’s previous owner.
Exploit Scenario
- Alice has one land NFT with token id 678 and the index is 100 in mapping
ownedLands
;
- The land owner has been updated to Bob;
- When searching the land index in Alice’s owned lands, it still returns land NFT 678.
- Alice has one land NFT with token id 678 and the index is 100 in mapping
Recommendations
Update
ownedLands
mapping when callingupdateLandOwner()
function.When updating the land owner with the given token id and new land owner:
- Update the
mintedNFTInfo
and Iterate through the current land owner’s lands to get the correct land idIdOfLandNeedsUpdated
;
- Update the old owner’s
ownedLands
:- Get the last token index of the current owner by using
_totalLandOwned[oldOwner]
and then get the token id with the index:uint256 lastTokenIndex = _totalLandOwned[currentOwner] - 1; // need to check uint256 tokenId = ownedLands[oldOwner][lastTokenIndex];
- Move the last land NFT token to the position where the land needs to be removed and update the
_totalLandOwned[currentOwner]
:ownedLands[currentOwner][IdOfLandNeedsRemove] = tokenId; ownedLands[currentOwner][lastTokenIndex] = 0; // assume 0 represent empty / not exist. see the finding Medium 7 _totalLandOwned[currentOwner]--;
- Get the last token index of the current owner by using
- Update the new owner’s
ownedLands
:- Append the land NFT to the new owner and update the
_totalLandOwned[oldOwner]
:uint256 newTokenIndexOfNewOwner = _totalLandOwned[newOwner]; ownedLands[newOwner][newTokenIndexOfNewOwner] = IdOfLandNeedsUpdated; _totalLandOwned[newOwner]++;
- Append the land NFT to the new owner and update the
- Update the
Results
Resolved in commit 7c091960656518c20169e3493ec476f44938e93b.
The
updateLandOwner()
function was removed. Kolor team acknowledges that this results in not being able to change the land owner after minting the NFT anymore.
- Should check before adding new buyers
Severity Medium Source KolorLandNFT.sol#L214; Status Resolved in commit 7c091960656518c20169e3493ec476f44938e93b; Description
Function
addBuyer()
doesn’t check whether the buyer has already been added or not. The missing check for this might causetotalBuyers[tokenId]
to be bigger than it should be as it is incremented even if a buyer already exists:function addBuyer(uint256 tokenId, address newBuyer) public override onlyMarketplace notBurned(tokenId){ buyers[tokenId][newBuyer] = true; totalBuyers[tokenId]++; }
Function
addBuyer()
adds input address to mappingtotalBuyers
that counts the number of addresses that have offset emissions on certain land. However, this function doesn’t check whether the input address has already been added or not. This can result in counting the same address multiple times.
Exploit Scenario
- Kolor team adds a buyer to offset emission on a land NFT;
- Kolor team adds the same buyer to offset emissions on that same NFT;
- Mapping
totalBuyers
buyer is 2 instead of 1.
Recommendations
Change it to this:
function addBuyer(uint256 tokenId, address newBuyer) public override onlyMarketplace notBurned(tokenId){ if(!buyers[tokenId][newBuyer]) { buyers[tokenId][newBuyer] = true; totalBuyers[tokenId]++; } }
Low
Low
- Lack of updates
Severity Low Source KolorLandToken.sol#L199; Status Acknowledged; Description
Function
safetransferFrom()
doesn’t update mapping variablestotalInvestmentsByAddress
,totalInvestmentsByLand
, andtotalInvestments
when called directly by users.
Exploit Scenario
- Alice gets investment for a land NFT;
- Alice calls the
safeTransferFrom()
to send part of the tokens that represent the investment to Bob;
- Since mappings weren’t updated, functions that depend on those mappings will not consider the new investment. For example, calling
totalInvestmentsOfLand()
will not consider Bob’s investment.
Recommendations
Update mapping variables in
safetransferFrom()
function.
Results
Acknowledged.
Kolor team expects users not to call the
safeTransferFrom()
. If they call it, they will consider only the initial investment. No view function for mappingKolorLandNFT.landIndex
.
- Mapping visibility
Severity Low Source KolorLandToken.sol#L199; Status Resolved in commit 7c091960656518c20169e3493ec476f44938e93b; Description
Mapping
landIndex
gets updated only atKolorLandNFT.safeMint()
and it doesn’t have public getter functions. This makes it difficult to get values directly from it.
Exploit Scenario
N/A.
Recommendations
Change the mapping’s visibility from
private
topublic
or create a public getter function for the mapping.
Informational
Informational
- Incorrect function visibility
Severity Informational Source KolorMarketplace.sol#L239; Status Acknowledged. Description
Function
onERC721Received()
visibility can be restricted to pure since it is only returning the selector of the function. This allows for gas optimization.
Exploit Scenario
N/A.
Recommendations
Change visibility of the function to pure.
Results
Acknowledged.
- Unused functions
Severity Informational Source KolorMarketplace.sol#24; KolorLandNFT.sol#L167; Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
Internal function
isLandOwner()
inKolorLandNFT.sol
is never used. Private mappinglockStartTime
is defined but never used.
Exploit Scenario
N/A.
Recommendations
Remove that function or change visibility.
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
- Functions with the same names
Severity Informational Source KolorMarketplace.sol#L158, L162, L169, L188, L208, L220; Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
There are functions with the same but with different parameters. It’s good practice to have explicit and explanatory names for each function. Additionally, having the same name for two functions that have the same number of parameters can be confusing to other developers.
Exploit Scenario
N/A.
Recommendations
Change the names of functions so that names are different.
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
- Missing comments
Severity Informational Source KolorLandToken.sol#L103, L125; KolorLandNFT.sol#L447; Status Acknowledged. Description
There are no comments in some functions. The use of natspec is recommended for writing comments and it is important for crucial functions so that other developers can easily understand the use of these functions and how to interact with them.
Exploit Scenario
N/A.
Recommendations
Add comments to the functions specified in the source.
Results
Acknowledged.
- Unused parameters
Severity Informational Source KolorMarketplace.sol#L239; Status Acknowledged. Description
Input parameters
address operator
,address from
,uint256 tokenId
andbytes calldata data
are not used on functiononERC721Received()
.
Exploit Scenario
N/A.
Recommendations
Comment unused parameters.
Results
Acknowledged.
- Naming issue
Severity Informational Source KolorMarketplace.sol#L85, L97; KolorLandToken.sol#L94, L107, L119, L131; Status Acknowledged. Description
Typo in comment for require statements and in developer comment.
require(erc721.ownerOf(tokenId) != address(0), "Token doesn't exists!");
/** @dev Adds a new offset structure to a account to represent
Exploit Scenario
N/A.
Recommendations
Change the word “exists” to “exist” and “a account” to “an account”.
Results
Acknowledged.
- Floating solidity pragma version
Severity Informational Source Global Status Acknowledged. Description
Current smart contracts use
^0.8.0
. And compilers within versions≥ 0.8.0
and<0.9.0
can be used to compile those contracts. Therefore, the contract may be deployed with a newer or latest compiler version which generally has higher risks of undiscovered bugs. It is a good practice to fix the solidity pragma version if the contract is not designed as a package or library that will be used by other projects or developers
Exploit Scenario
N/A.
Recommendations
Use the fixed solidity pragma version.
Results
Acknowledged.
- Update condition in require statement
Severity Informational Source KolorLandToken.sol#L133; Status Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf; Description
Update the
require
condition to receive proper revert messages. In the following require statement, the transaction will not revert with the desired message “KolorLandToken: exceeds max amount” whenavailableTokensOf(tokenId)
is smaller than theamount
. It will only revert with the subtraction underflow error message.require(availableTokensOf(tokenId) - amount >= 0, "KolorLandToken: exceeds max amount" );
Exploit Scenario
N/A.
Recommendations
Change the require statement to:
require(availableTokensOf(tokenId) >= amount, "KolorLandToken: exceeds max amount" );
Results
Resolved in commit 4ce7e209dcdb3842b545aa14585359a727479abf.
- Follow check-effects-interactions
Severity Informational Source KolorLandToken.sol#L125; Status Acknowledged. Description
Check check-effects-interactions pattern for functions like function with external function calls, such as
KolorLandToken.newInvestment()
to reduce the attacking surface. Check states first, then apply changes to related states and call the external function in the final step. In this way, attacking surfaces for the external function is reduced. For normal external functions without before-call hooks, there is no surface for them to reenter the function because there is no attacking interface after the external function call.
Exploit Scenario
N/A.
Recommendations
Follow check-effects-interactions practices.
Results
Acknowledged.
- Incorrect comment/missing event
Severity Informational Source KolorLandToken.sol#L383, L392; Status Acknowledged. Description
Function comment says that an event is emitted when calling
setApprovalForAll()
, but there are no events emitted in the contract and specifically in this function.* @dev Approve `operator` to operate on all of `owner` tokens * * Emits a {ApprovalForAll} event. */ function setApprovalForAll( address owner, address operator, bool approved ) internal virtual { require(owner != operator, "ERC1155: setting approval status for self"); operatorApprovals[owner][operator] = approved; //emit ApprovalForAll(owner, operator, approved); }
Exploit Scenario
N/A.
Recommendations
Event emission is recommended as it can help monitor the function related to critical operations.
Results
Acknowledged.
- Redundant code
Severity Informational Source KolorMarketplace.sol#L70, L71; Status Acknowledged; Description
Contract
KolorLandNFT.sol
, interfaceIKolorLandNFT
, andIERC721
are used to make calls at the same time. This is unnecessary since the contract has already all the functionality and all the calls could be made by directly importing it. Even better would be if the interface had access to all the functionality and this was the one getting used to making the calls.IKolorLandNFT ilandInterface = IKolorLandNFT(KolorLandNFTAddress); KolorLandNFT kolorLand = KolorLandNFT(KolorLandNFTAddress); IERC721 erc721 = IERC721(KolorLandNFTAddress);
Exploit Scenario
N/A.
Recommendations
Make the interface
IKolorLandNFT
inheritsIERC721
and add other needed functions to interfaceIKolorLandNFT
. In this way, onlyIKolorLandNFT
is needed. No need to use all three of them.
Results
Acknowledged.
Access Control Analysis
Access Control Analysis
The owner
of the contracts is the deployer who can set and unset authorized addresses. Most functions for the three contracts come with the onlyAuthorized()
modifier that only allows authorized addresses to execute functions. The main access control for each of the contracts is the following:
KolorLandNFT.sol
KolorLandNFT.sol
- Allows to mint NFTs and customize them with the species and geospatial points.
- Allows to transfer the NFTs to the marketplace.
KolorLandToken.sol
KolorLandToken.sol
- Allows to mint tokens that are associated one to one with the size of the land NFT.
- Allows to add investments by setting users with balances for the respective tokens.
KolorMarketplace.sol
KolorMarketplace.sol
- Allows to offset emissions on land NFTs once they’re transferred to the marketplace.
- Allows to set the state of the NFT to removed.
Appendix I: Severity Categories
Appendix I: Severity Categories
Severity | Description |
---|---|
High | Issues that are highly exploitable security vulnerabilities. It may cause direct loss of funds / permanent freezing of funds. All high severity issues should be resolved. |
Medium | Issues that are only exploitable under some conditions or with some privileged access to the system. Users’ yields/rewards/information is at risk. All medium severity issues should be resolved unless there is a clear reason not to. |
Low | Issues that are low risk. Not fixing those issues will not result in the failure of the system. A fix on low severity issues is recommended but subject to the clients’ decisions. |
Informational | Issues that pose no risk to the system and are related to the security best practices. Not fixing those issues will not result in the failure of the system. A fix on informational issues or adoption of those security best practices-related suggestions is recommended but subject to clients’ decision. |
Appendix II: Status Categories
Appendix II: Status Categories
Status | Description |
---|---|
Unresolved | The issue is not acknowledged and not resolved. |
Partially Resolved | The issue has been partially resolved. |
Acknowledged | The Finding / Suggestion is acknowledged but not fixed / not implemented. |
Resolved | The issue has been sufficiently resolved. |
Disclaimer
Disclaimer
Verilog Solutions receives compensation from one or more clients for performing the smart contract and auditing analysis contained in these reports. The report created is solely for Clients and published with their consent. As such, the scope of our audit is limited to a review of code, and only the code we note as being within the scope of our audit detailed in this report. It is important to note that the Solidity code itself presents unique and unquantifiable risks since the Solidity language itself remains under current development and is subject to unknown risks and flaws. Our sole goal is to help reduce the attack vectors and the high level of variance associated with utilizing new and consistently changing technologies. Thus, Verilog Solutions in no way claims any guarantee of security or functionality of the technology we agree to analyze.
In addition, Verilog Solutions reports do not provide any indication of the technologies proprietors, business, business model, or legal compliance. As such, reports do not provide investment advice and should not be used to make decisions about investment or involvement with any particular project. Verilog Solutions has the right to distribute the Report through other means, including via Verilog Solutions publications and other distributions. Verilog Solutions makes the reports available to parties other than the Clients (i.e., “third parties”) – on its website in hopes that it can help the blockchain ecosystem develop technical best practices in this rapidly evolving area of innovation.