ALTR

CertiKFiles named in the report

CertiK reviewed ALTR on 2 Mar 2023: 18 findings, 0 critical; 12 marked fixed, 6 acknowledged.

No open critical findings
Open the reportOpen ALTR’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings18

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

SeverityOpenAcknowledgedFixed
Critical000
High022
Medium012
Low038
HighFunction Calls User-Provided Addresses With No AcFixed

Calling a user provided address is dangerous, especially in a public function with no access control restriction. An attacker could deploy a malicious contract and use the vulnerable function to trigger a call to the malicious contract, potentially stealing user funds or causing other serious damages.

HighLack Of Access ControlFixed

The function `grantRole` can be called by anyone as it has no access restriction. This enables anyone to call this and grant existing roles to any addresses. For the AltrFractions contract, there are `DEFAULT_ADMIN_ROLE` and `BURN_MANAGER_ROLE`.

MediumIncorrect `maxAmount` Returned in `getFractionsAmFixed

The function `getFractionsAmountByPrice()` in the contract `AltrFractionsSale` loops through the `tiers` mapping, uses the input `price` to retrieve the amount of fractions that an NFT can be divided into. Since `getFractionsAmountByPrice` is a `view` function, the result to be returned is purely based on the on-chain data. That is to say, if the input arrays are not well checked in `constructor()` and `setTiers()`, `getFractionsAmountByPrice()` would return an incorrect result.

MediumPrivileged Address Not Assigned in `AltrFeeManageFixed

In contract AltrFeeManager, there is a function `setSaleInfo()` that can set the redemption fee token address and the first price. An external caller is required to be a privileged role, `TRADE_CHECKER_ROLE`, because of the `onlyRole()` modifier. However, the `TRADE_CHECKER_ROLE` is never assigned in the contract, which means no addresses can successfully call the function.

LowDiscussion on the `FractionsAmount` variableFixed

In the `setupSale()` function of the contract `AltrFractionsSale`, the local variable is a new-ed `TimedTokenSplitter`. The input parameter of the new `TimedTokenSplitter` has `fractionsAmount` as `0`. In the same contract `fractionsAmount` is also used as a required input fields for a new FractionsSale struct, and the `FractionsSale` is saved to the state variable `fractionsSales[currentSaleId]`. We feel like the `fractionsAmount` concept in the TimedTokenSplitter contract is different from the concept in the AltrFractionsSale contract. Our understanding is that `fractionsAmount` represents for the cap of a fraction sale of a NFT in AltrFractionsSale, while in TimedTokenSplitter, `fractionsAmount` is more like `fractionsSold`. We would like to confirm if our understanding is correct.

LowDivide Before MultiplyFixed

Performing integer division before multiplication truncates the low bits, losing the precision of calculation. ```solidity=65 uint256 tokenPrice = redemptionToken.balanceOf(address(this)) / fractionsAmount; ``` ```solidity=69 fractionsPrice = tokenPrice * amounts[i]; ```

LowLocked EtherFixed

In the contract `AltrFeeManager`, the function `receive() external payable` can receive ether but the internal function `_transferEth` that can withdraw the fund is never called. In the contract `AltrTradeChecker`, the function `buyERC721(...) external payable` can receive ether but does not have a function to withdraw the fund. In addition, the `msg.value` of `buyERC721()` is neither recorded nor emitted via an event.

LowMissing minimum time constraint on `openingTime`Fixed

In the contract `AltrFractionsSale`, there is a function `setupSale()`, that can set up a new fraction sale. The requirement and initialization of two variables, `openingTime` and `closingTime`, caught our attention. The current require statements check that `openingTime > block.timestamp` and `closingTime > openingTime`. Our understanding is that there should be one or two constant variables like `MIN_OPENING_GAP` and `MIN_CLOSING_GAP` to make sure that a sale is not only open for a very short period of time, like one active block.

LowMissing Sale Status Check in `withdrawFractionsKeFixed

In contract `AltrFractionsSale`, the comment of the function `withdrawFractionsKept()` says that "The initiator can withdraw the fractions kept after the sale is closed and successful". However, the function implementation has only two checks: 1. the function caller is required to be the initiator 2. the fractions sold amount is required to be greater or equal to `saleMinFractions`, which is equivalent to checking that the sale is successful Neither of the two checks covers the case that the sale is required to be closed.

LowUnused `DEFAULT_ADMIN_ROLE`Fixed

In the contract `AltrAllowList`, the `DEFAULT_ADMIN_ROLE` is granted to the contract deployer in the constructor function. However, the privileged role is never used.

Show 2 more
LowUnused Return ValueFixed

The return value of an external call is not stored in a local or state variable. ```solidity=458 tiers.set(priceLimits[i], fractionsAmounts[i]); ``` --- ```solidity=131 _collectionAddressSet.add(address(collection)); ``` --- ```solidity=81 sellOrder.erc20Token.transferFrom(msg.sender, address(this), sellOrder.erc20TokenAmount); sellOrder.erc20Token.approve(address(zeroExContract), sellOrder.erc20TokenAmount); ```

LowWrong EIP20 ApplicationFixed

According to [EIP-20](https://eips.ethereum.org/EIPS/eip-20), functions `transfer()`, `transferFrom()`, and `approve()` should always have a `bool` return value, for the ERC20 caller to handle, as the callers must not assume that `false` is never returned.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (13)

  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/LucidaoGovernanceNftReserve.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrFeeManager.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrNftCollection.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrFractions.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/TimedTokenSplitter.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrAllowList.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrFractionsSale.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrNftCollectionFactory.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrLicenseManager.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrTradeChecker.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrFractionsBuyout.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/TokenSplitter.sol
  • altr-smart-contracts-7caa89ffc641528cf00a3ad542b37605cc1a99c8/contracts/AltrNftCollateralRetriever.sol

Part of ALTR’s security record

ALTR
Audits1 audit by 1 firmLast audit Mar 2023, by CertiK.
Critical findingsNo open critical findingsIn 1 report.
Open ALTR’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

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