Bored Ape Yacht Club is one of the most popular NFT collection. Many celebrities bought them, they were advertised in Time Square in New York, and recently they became even more expensive than Cryptopunks.
If you want a quick summary, checkout our video:
Introduction
The Bored Ape Yacht Club is a collection of 10,000 Non-Fungible Tokens (NFTs) styled as Apes created on the Ethereum blockchain. It takes inspiration from NFT projects like CryptoPunks, where each NFT looks totally unique and individual. Each Ape has a different rarity depending on what the Ape is wearing, doing, and its background.
The project was founded by Yuga Labs’ four pseudonymous founders: Gargamel, Gordon Goner, Emperor Tomato Ketchup, and No Sass. Since their initial mint, BAYC has created new lines of NFTs, NFT upgrades, and an exclusive club for BAYC holders. These developments have kept up an interest in the project, leading to incredible sale prices like $3.4 million (USD) for the NFT below at Sotheby’s.
Each Ape avatar is an ERC-721 NFT on the Ethereum blockchain. Each token is non-fungible, meaning that it is unique and not equally interchangeable with any other cryptocurrency or token. An Ape is made up of seven possible traits:
- Background
- Clothes
- Earring
- Eyes
- Fur
- Hat
- Mouth
Each attribute has a variety of different looks and styles. For example, you can see below that an Ape can have solid gold, trippy, and noise fur, among others. Each look is limited to a certain number, so some are statistically rarer.
Source Code
The smart contract of BAYC is not available on Github, but you can see it on Etherscan. When you read smart contracts on Etherscan, all the imported contracts and libraries are put in the same file.
[Source code Link][1]
For example, for BAYC, we find the code of ERC721. The following is the core contract:
contract BoredApeYachtClub is ERC721, Ownable {
using SafeMath for uint256;
string public BAYC_PROVENANCE = "";
uint256 public startingIndexBlock;
uint256 public startingIndex;
uint256 public constant apePrice = 80000000000000000; //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);
}
/**
* Set some Bored Apes aside
*/
function reserveApes() public onlyOwner {
uint supply = totalSupply();
uint i;
for (i = 0; i < 30; i++) {
_safeMint(msg.sender, supply + i);
}
}
/**
* DM Gargamel in Discord that you're standing right behind him.
*/
function setRevealTimestamp(uint256 revealTimeStamp) public onlyOwner {
REVEAL_TIMESTAMP = revealTimeStamp;
}
/*
* Set provenance once it's calculated
*/
function setProvenanceHash(string memory provenanceHash) public onlyOwner {
BAYC_PROVENANCE = provenanceHash;
}
function setBaseURI(string memory baseURI) public onlyOwner {
_setBaseURI(baseURI);
}
/*
* Pause sale if active, make active if paused
*/
function flipSaleState() public onlyOwner {
saleIsActive = !saleIsActive;
}
/**
* Mints Bored Apes
*/
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;
}
}
/**
* Set the starting index for the collection
*/
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;
// Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
if (block.number.sub(startingIndexBlock) > 255) {
startingIndex = uint(blockhash(block.number - 1)) % MAX_APES;
}
// Prevent default sequence
if (startingIndex == 0) {
startingIndex = startingIndex.add(1);
}
}
/**
* Set the starting index block for the collection, essentially unblocking
* setting starting index
*/
function emergencySetStartingIndexBlock() public onlyOwner {
require(startingIndex == 0, "Starting index is already set");
startingIndexBlock = block.number;
}
}
The first three values we see are BAYC_PROVENANCE, MAX_APES, and REVEAL_TIMESTAMP. They have been assigned names in all caps with underscores, which according to the Solidity style guide, suggests they are constants. These are a good set of constants, because they suggest a fixed image set that will be revealed at a given time.
When buyers bought an ape during the primary sale, they didn’t know how it looked like. This is to avoid bidding wars for gas and introduce fairness.
Let’s now review how what each function does:
The function allows the owner of the contract to withdraw the ether of the primary sale:
function withdraw() public onlyOwner {
uint balance = address(this).balance;
msg.sender.transfer(balance);
}
This function allows the owner of the contract to mint 30 apes for themselves, free of charge. This was very controversial at the time of launch.
function reserveApes() public onlyOwner {
uint supply = totalSupply();
uint i;
for (i = 0; i < 30; i++) {
_safeMint(msg.sender, supply + i);
}
}
This function is to update the hash of the image containing all the apes. This image is generated and stored outside of the blockchain
function setProvenanceHash(string memory provenanceHash) public onlyOwner {
BAYC_PROVENANCE = provenanceHash;
}
This function is to update the URL of the metadata server, which contains the image of all the apes, as well json data which describes the attribute of each ape, like the hat, the fur or the eyes
function setBaseURI(string memory baseURI) public onlyOwner {
_setBaseURI(baseURI);
}
This is to toggle on and off the primary sale. Apes can only be minted if primary sale is active.
function flipSaleState() public onlyOwner {
saleIsActive = !saleIsActive;
}
This is the function called by users to mint each ape. It’s a payable function, because you have to send ether to buy an ape.
The code checks that:
– the primary sale is active
– the buyer doesn’t try to buy too many ape
– and enough ether was sent
After that, it mints the apes.
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;
}
}
The starting index block is going to begin uninitialized, set to 0. In mintApe’s final if statement, the mint function will check if it’s sold out, or if the current time is after REVEAL_TIMESTAMP. If they are sold out or after the reveal, the contract is going to set the starting index block to the current block number (each block in the blockchain gets an ID number). So this function is used to generate a random ape.
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;
// Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
if (block.number.sub(startingIndexBlock) > 255) {
startingIndex = uint(blockhash(block.number - 1)) % MAX_APES;
}
// Prevent default sequence
if (startingIndex == 0) {
startingIndex = startingIndex.add(1);
}
}
And finally, this function is used to reset the startingIndexBlock. It can only be called by the owner of the contract.
function emergencySetStartingIndexBlock() public onlyOwner {
require(startingIndex == 0, "Starting index is already set");
startingIndexBlock = block.number;
}
Conclusion
Bored Ape Yacht Club not only dominated the NFT community’s “JPEG Summer”, but was the catalyst that started the summer 2021 PFP project mayhem. As Punks continued to moon throughout the first half of the year, and Punk derivative projects started to peter out, BAYC filled a demand for originality and substance within the collectible and art sections of the NFT market.
Leave a Reply