β RACE #7 - Bored Ape

pragma solidity ^0.7.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/access/Ownable.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.4/contracts/math/SafeMath.sol";
contract InSecureumApe is ERC721, Ownable {
using SafeMath for uint256;
string public IA_PROVENANCE = "";
uint256 public startingIndexBlock;
uint256 public startingIndex;
uint256 public constant apePrice = 800000000000000000; //0.08 ETH
uint public constant maxApePurchase = 20;
uint256 public MAX_APES;
bool public saleIsActive = false;
uint256 public REVEAL_TIMESTAMP;
constructor(string memory name, string memory symbol, uint256 maxNftSupply, uint256 saleStart) ERC721(name, symbol) {
MAX_APES = maxNftSupply;
REVEAL_TIMESTAMP = saleStart + (86400 * 9);
}
function withdraw() public onlyOwner {
uint balance = address(this).balance;
msg.sender.transfer(balance);
}
function reserveApes() public onlyOwner {
uint supply = totalSupply();
uint i;
for (i = 0; i < 30; i++) {
_safeMint(msg.sender, supply + i);
}
}
function setRevealTimestamp(uint256 revealTimeStamp) public onlyOwner {
REVEAL_TIMESTAMP = revealTimeStamp;
}
function setProvenanceHash(string memory provenanceHash) public onlyOwner {
IA_PROVENANCE = provenanceHash;
}
function setBaseURI(string memory baseURI) public onlyOwner {
_setBaseURI(baseURI);
}
function flipSaleState() public onlyOwner {
saleIsActive = !saleIsActive;
}
function mintApe(uint numberOfTokens) public payable {
require(saleIsActive, "Sale must be active to mint Ape");
require(numberOfTokens < maxApePurchase, "Can only mint 20 tokens at a time");
require(totalSupply().add(numberOfTokens) <= MAX_APES, "Purchase would exceed max supply of Apes");
require(apePrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");
for(uint i = 0; i < numberOfTokens; i++) {
uint mintIndex = totalSupply();
if (totalSupply() < MAX_APES) {
_safeMint(msg.sender, mintIndex);
}
}
// If we haven't set the starting index and this is either 1) the last saleable token or 2) the first token to be sold after
// the end of pre-sale, set the starting index block
if (startingIndexBlock == 0 && (totalSupply() == MAX_APES || block.timestamp >= REVEAL_TIMESTAMP)) {
startingIndexBlock = block.number;
}
}
function setStartingIndex() public {
require(startingIndex == 0, "Starting index is already set");
require(startingIndexBlock != 0, "Starting index block must be set");
startingIndex = uint(blockhash(startingIndexBlock)) % MAX_APES;
if (block.number.sub(startingIndexBlock) > 255) {
startingIndex = uint(blockhash(block.number - 1)) % MAX_APES;
}
if (startingIndex == 0) {
startingIndex = startingIndex.add(1);
}
}
function emergencySetStartingIndexBlock() public onlyOwner {
require(startingIndex == 0, "Starting index is already set");
startingIndexBlock = block.number;
}
}
Question 1 β
The mint price of an InSecureumApe is:
My comment:

Question 2 β
The security concern(s) with InSecureumApe access control is/are
Question 3 β
The security concern(s) with InSecureumApe constructor is/are
Question 4 β
The total number of InSecureumApes that can ever be minted is
Question 5 β
The public minting of InSecureumApes
Question 6 β
The security concerns with InSecureumApe is/are
Question 7 - This one tricky
The starting index determination
Comment:
You can read about how this is used for post-mint reveal randomization in this article.
The 9-day delay of the REVEAL_TIMESTAMP
variable can be overriden at any point in time, it can also be triggered earlier if the totalSupply matches MAX_APES
exactly, or be triggered at any time by the owner via emergencySetStartingIndexBlock()
.
It accounts for the block hash access limitation by falling back to using the hash of the previous block instead.
Question 8 β
Potential gas optimization(s) in InSecureumApe is/are
Comment:
Whenever storage variables are read from multiple times, they should be cached in memory to safe gas. This is missing for MAX_APES
in mintApe()
and startingIndexBlock
in setStartingIndex()
.
All state variables are zero-initialized by default, therefore there's no need to manually set saleIsActive
to false, for example.
The state variable MAX_APES
is only set once during construction and should be immutable to save gas
Last updated
Was this helpful?