Cherry token contract: the code
Here we'll cover the code of the Cherry token smart contract. That's what's interesting with smart contracts: they are deployed on a blockchain. Most of them cannot be updated (this one cannot). In blockchain speech, we say they are immutable.
And if their author published the source code of the smart contract (that was validated by the blockchain scanner site, so the author cannot cheat by submitting a tampered source code), then you can read it line by line.
Why that? Well, it all comes down to trust.
If you read the lines of the smart contract, which are quite easy to understand if you have some basic understanding of programming, then you can figure out how it works. And if you know how it works, then you can trust it.
This is the beauty of the blockchain, and the reason it's a game changer: a contract that will execute no matter what. The author cannot change it any more. This is set in stone.
Unstoppable code, as they say.
The code: initial standard contracts
You can see the full code on FTMscan: the Fantom block explorer.
The first parts are functions that are very common and were mostly written by the Open Zeppelin team, a bunch of blockchain developers that publish well written code for any other person to build their projects upon.
It starts with the Ownable contract. It is just here to attach an owner to the main smart contract.
Then there is the classic ERC-20 token contract. It's the common Open Zeppelin implementation of the ERC-20 standard for token contracts: you can own, transfer, approve, transfert on behalf of someone who approved you, ... etc. Nothing very fancy here, appart that it has these two options:
- tokens are burnable: it means that the owner of a cherry token can destroy it.
- there is a mint() function: it means that someone can mint() new cherries out of thin air. Hey, that's a bit suspicious don't you think?
The code: the real stuff
Now that Owner and ERC20 have been declared, we have all the tools to create our Cherry token smart contract.
Here is the important code:
// Cherry.sol
// Minimal interface for Minter contract
interface IMinter {
// returns the token address that the minter wants to mint
function token() external view returns (address);
}
contract CherryToken is ERC20, Ownable {
// ==== Constants ====
uint constant PERIOD = 6 hours;
uint constant minSupply = 10000 ether;
// ==== Storage ====
bool public isSealed;
uint32 public epoch;
address public minter;
// ==== Constructor ====
constructor() ERC20("Cherry", "CHRY") {
// 10 000 for initial liquidity pool
// + 10 000 for airdrops and Cherrific team
_mint(msg.sender, 20000 ether);
}
// ==== Governance ====
// seal contract for ever
function seal() external onlyOwner {
isSealed = true;
}
// set minter as long as contract isn't sealed
function setMinter(address minter_) external onlyOwner {
// function disabled once contract is sealed
require( ! isSealed , "Contract is sealed");
// Safeguard: make sure we set the correct minter
// If this fails, it means minter is not set correctly
require( IMinter(minter_).token() == address(this), "Minter not correctly set");
minter = minter_;
}
// ==== Views ====
// Current epoch
function getEpoch() public view returns (uint) {
return block.timestamp / PERIOD;
}
// ==== Mutators ====
// from OpenZeppelin
function burn(uint256 amount) external returns (bool) {
_burn(msg.sender, amount);
return true;
}
// from OpenZeppelin
function burnFrom(address account, uint256 amount) external returns (bool) {
_spendAllowance(account, msg.sender, amount);
_burn(account, amount);
return true;
}
// only minter can mint
function mint(address recipient_, uint256 amount_) external returns (bool) {
require(msg.sender == minter, "Only minter can mint");
uint newEpoch = getEpoch();
require(newEpoch > epoch, "Mint only once per epoch");
// all below code can only run once per epoch
epoch = uint32(newEpoch);
// make sure we don't mint too much
uint supply = totalSupply();
if (supply < minSupply) {
// during low supply, we don't want to mint up to more than 120% minSupply
require( supply + amount_ <= (minSupply * 120) / 100, "Minting too much while low supply");
} else {
// make sure minter does not go crazy
// don't mint more than 5% of total supply per epoch
// +5 if to allow for rounding errors on the minter side
require( amount_ <= (supply / 20) + 5, "Don't mint > 5% supply per epoch");
}
_mint(recipient_, amount_);
return true;
}
}
I'll finish this story as soon as I have a few minutes.
I'll explain how this Cherry token contract works, and why it is not a contract that you should fully trust ... yet.
If you want to learn how to write smart contracts (it's not that complicated), then it will be a good example to start with.
Stay tuned.