Transaction Hash:
Block:
15183248 at Jul-21-2022 02:25:40 AM +UTC
Transaction Fee:
0.00108489555460135 ETH
$2.73
Gas Used:
82,175 Gas / 13.202258042 Gwei
Emitted Events:
177 |
AlphaToken.Transfer( from=[Sender] 0x3c37849203d653ee0752c964418a043f1e2747f0, to=0x0000000000000000000000000000000000000000, amount=600000000000000000000 )
|
178 |
AlphaDogs.Transfer( from=0x0000000000000000000000000000000000000000, to=[Sender] 0x3c37849203d653ee0752c964418a043f1e2747f0, id=77410034460066304 )
|
179 |
AlphaDogs.Breeded( child=77410034460066304, mom=5354638874968576, dad=5352487666778624 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x09A8fB01...510A466d3 | |||||
0x32f8EE2B...B104dc141 | |||||
0x3C378492...f1e2747f0 |
0.002588885254853357 Eth
Nonce: 603
|
0.001503989700252007 Eth
Nonce: 604
| 0.00108489555460135 | ||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 861.051011158001472923 Eth | 861.051134420501472923 Eth | 0.0001232625 |
Execution Trace
breed[AlphaDogs (ln:143)]
genesisLeft[AlphaDogs (ln:149)]
genesisSupply[AlphaDogs (ln:347)]
NotActive[AlphaDogs (ln:149)]
InsufficientTokensAvailable[AlphaDogs (ln:151)]
isPuppy[AlphaDogs (ln:152)]
isPuppy[AlphaDogs (ln:152)]
FusionWithPuppyForbidden[AlphaDogs (ln:153)]
FusionWithSameParentsForbidden[AlphaDogs (ln:154)]
_generatePuppyTokenIdWithNoCollision[AlphaDogs (ln:158)]
uniformCrossOver[AlphaDogs (ln:173)]
incrementByte[AlphaDogs (ln:176)]
random[AlphaDogs (ln:161)]
burn[AlphaDogs (ln:163)]
_mint[AlphaDogs (ln:165)]
Breeded[AlphaDogs (ln:166)]
File 1 of 2: AlphaDogs
File 2 of 2: AlphaToken
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; import {Ownable} from "@openzeppelin/access/Ownable.sol"; import {Strings} from "@openzeppelin/utils/Strings.sol"; import {MerkleProof} from "@openzeppelin/utils/cryptography/MerkleProof.sol"; import {ReentrancyGuard} from "@solmate/utils/ReentrancyGuard.sol"; import {IAlphaToken} from "$/interfaces/IAlphaToken.sol"; import {IAlphaDogs} from "$/interfaces/IAlphaDogs.sol"; import {IAlphaDogsAttributes} from "$/interfaces/IAlphaDogsAttributes.sol"; import {Genetics} from "$/libraries/Genetics.sol"; import {Gene} from "$/libraries/Gene.sol"; import {ERC721} from "$/ERC721.sol"; /// @title AlphaDogs /// @author Aleph Retamal <github.com/alephao>, Gustavo Tiago <github.com/gutiago> contract AlphaDogs is IAlphaDogs, ERC721, Ownable, ReentrancyGuard { using Gene for uint256; using Strings for uint160; // ======================================== // Immutable // ======================================== address private constant BLACKHOLE = address(0); /// @notice amount of $ALPHA a staked genesis dog earn per day uint256 public constant GENESIS_TOKEN_PER_DAY = 10 ether; /// @notice amount of $ALPHA a staked puppy dog earn per day uint256 public constant PUPPY_TOKEN_PER_DAY = 2.5 ether; /// @notice price in $ALPHA to breed uint256 public constant BREEDING_PRICE = 600 ether; /// @notice price in $ALPHA to update name or lore of a dog uint256 public constant UPDATE_PRICE = 100 ether; /// @notice max amount of genesis tokens uint32 public immutable maxGenesis; /// @notice max amount of puppy tokens uint32 public immutable maxPuppies; /// @notice address of the $ALPHA ERC20 IAlphaToken public immutable alphaToken; /// @notice merkle tree root for allow-list bytes32 public immutable merkleRoot; /// @notice number of reserved genesis tokens for wallets in the allow-list uint32 public immutable maxReserved; // ======================================== // Mutable // ======================================== /// @notice if the mint function is open bool public isSaleActive = false; /// @notice if supply should be reserved for allow-list bool public isSupplyReserved = true; /// @notice amount of genesis minted so far not via allow-list uint32 public genesisNonReservedSupply = 0; /// @notice amount of genesis minted so far via allow-list uint32 public genesisReservedSupply = 0; /// @notice amount of puppied minted so far uint32 public puppySupply = 0; /// @notice map from dog id to custom Name and Lore mapping(uint256 => CustomMetadata) internal metadata; /// @notice map from dog id to its staked state mapping(uint256 => Stake) public getStake; /// @notice check if an address already minted mapping(address => bool) public didMint; /// @notice address of the AlphaDogsAttributes contract IAlphaDogsAttributes public attributes; // ======================================== // Constructor // ======================================== constructor( uint32 _maxGenesis, uint32 _maxPuppies, uint32 _maxReserved, IAlphaToken _alphaToken, IAlphaDogsAttributes _attributes, bytes32 _merkleRoot ) ERC721("AlphaDogs", "AD") { maxGenesis = _maxGenesis; maxPuppies = _maxPuppies; maxReserved = _maxReserved; alphaToken = _alphaToken; attributes = _attributes; merkleRoot = _merkleRoot; } // ======================================== // Modifiers // ======================================== modifier dogzOwner(uint256 id) { if (ownerOf[id] != msg.sender) revert InvalidTokenOwner(); _; } modifier whenSaleIsActive() { if (!isSaleActive) revert NotActive(); _; } // " and \\ are not valid modifier isValidString(string calldata value) { bytes memory str = bytes(value); for (uint256 i; i < str.length; i++) { bytes1 char = str[i]; if ((char == 0x22) || (char == 0x5c)) revert InvalidChar(); } _; } // ======================================== // Owner only // ======================================== function setIsSaleActive(bool _isSaleActive) external onlyOwner { if (isSaleActive == _isSaleActive) revert NotChanged(); isSaleActive = _isSaleActive; } function setIsSupplyReserved(bool _isSupplyReserved) external onlyOwner { if (isSupplyReserved == _isSupplyReserved) revert NotChanged(); isSupplyReserved = _isSupplyReserved; } // ======================================== // Change NFT Data // ======================================== function setName(uint256 id, string calldata newName) external override dogzOwner(id) isValidString(newName) { bytes memory n = bytes(newName); if (n.length > 25) revert InvalidNameLength(); if (keccak256(n) == keccak256(bytes(metadata[id].name))) revert InvalidSameValue(); metadata[id].name = newName; alphaToken.burn(msg.sender, UPDATE_PRICE); emit NameChanged(id, newName); } function setLore(uint256 id, string calldata newLore) external override dogzOwner(id) isValidString(newLore) { bytes memory n = bytes(newLore); if (keccak256(n) == keccak256(bytes(metadata[id].lore))) revert InvalidSameValue(); metadata[id].lore = newLore; alphaToken.burn(msg.sender, UPDATE_PRICE); emit LoreChanged(id, newLore); } // ======================================== // Breeding // ======================================== function breed(uint256 mom, uint256 dad) external override dogzOwner(mom) dogzOwner(dad) { if (genesisLeft() != 0) revert NotActive(); uint256 mintIndex = puppySupply; if (mintIndex == maxPuppies) revert InsufficientTokensAvailable(); if (Gene.isPuppy(mom) || Gene.isPuppy(dad)) revert FusionWithPuppyForbidden(); if (mom == dad) revert FusionWithSameParentsForbidden(); unchecked { puppySupply++; } uint256 puppyId = _generatePuppyTokenIdWithNoCollision( mom, dad, random(mintIndex) ); alphaToken.burn(msg.sender, BREEDING_PRICE); //slither-disable-next-line reentrancy-no-eth _mint(msg.sender, puppyId); emit Breeded(puppyId, mom, dad); } function _generatePuppyTokenIdWithNoCollision( uint256 mom, uint256 dad, uint256 seed ) internal view returns (uint256 tokenId) { tokenId = Genetics.uniformCrossOver(mom, dad, seed); uint256 i = 3; while (ownerOf[tokenId] != BLACKHOLE) { tokenId = Genetics.incrementByte(tokenId, i); unchecked { i++; } } } // ======================================== // Stake / Unstake // ======================================== function stake(uint256[] calldata tokenIds) external override { if (tokenIds.length == 0) revert InvalidInput(); if (msg.sender == address(0)) revert InvalidSender(); uint256 tokenId; for (uint256 i = 0; i < tokenIds.length; ) { tokenId = tokenIds[i]; // No need to check ownership since transferFrom already checks that // and the caller of this function should be the token Owner getStake[tokenId] = Stake(msg.sender, uint96(block.timestamp)); _transfer(msg.sender, address(this), tokenId); emit Staked(tokenId); unchecked { ++i; } } } function unstake(uint256[] calldata tokenIds) external override { _claim(tokenIds, true); } function claim(uint256[] calldata tokenIds) external override { _claim(tokenIds, false); } function _claim(uint256[] calldata tokenIds, bool shouldUnstake) internal { if (tokenIds.length == 0) revert InvalidInput(); if (msg.sender == address(0)) revert InvalidSender(); // total rewards amount to claim uint256 totalRewards; // loop variables // rewards for current genzee in the loop below uint256 rewards; // current genzeeid in the loop below uint256 tokenId; // staking information for the current genzee in the loop below Stake memory stakeInfo; for (uint256 i = 0; i < tokenIds.length; ) { tokenId = tokenIds[i]; stakeInfo = getStake[tokenId]; if (stakeInfo.owner != msg.sender) revert InvalidTokenOwner(); uint256 tokensPerDay = tokenId.isPuppy() ? PUPPY_TOKEN_PER_DAY : GENESIS_TOKEN_PER_DAY; rewards = stakeInfo.stakedAt > 1 ? ((tokensPerDay * (block.timestamp - stakeInfo.stakedAt)) / 1 days) : 0; totalRewards += rewards; if (shouldUnstake) { getStake[tokenId] = Stake(BLACKHOLE, 1); _transfer(address(this), msg.sender, tokenId); emit Unstaked(tokenId, rewards); } else { //slither-disable-next-line incorrect-equality if (rewards == 0) revert InvalidAmountToClaim(); getStake[tokenId].stakedAt = uint96(block.timestamp); emit ClaimedTokens(tokenId, rewards); } unchecked { ++i; } } //slither-disable-next-line incorrect-equality if (totalRewards == 0) return; alphaToken.mint(msg.sender, totalRewards); } // ======================================== // Mint // ======================================== function _generateTokenIdWithNoCollision(uint256 seed) internal view returns (uint256 tokenId) { tokenId = Genetics.generateGenes(seed); uint256 i = 3; while (ownerOf[tokenId] != BLACKHOLE) { tokenId = Genetics.incrementByte(tokenId, i); unchecked { i++; } } } function premint(bytes32[] calldata proof) external whenSaleIsActive { uint256 reservedSupply = genesisReservedSupply; if (didMint[msg.sender]) revert TokenLimitReached(); if (reservedSupply + 2 > maxReserved) revert InsufficientReservedTokensAvailable(); if (reservedSupply + genesisNonReservedSupply + 2 > maxGenesis) revert InsufficientTokensAvailable(); bytes32 leaf = keccak256( abi.encodePacked(uint160(msg.sender).toHexString(20)) ); bool isProofValid = MerkleProof.verify(proof, merkleRoot, leaf); if (!isProofValid) revert InvalidMerkleProof(); didMint[msg.sender] = true; unchecked { uint256 mintIndex = genesisSupply(); genesisReservedSupply += 2; _safeMint( msg.sender, _generateTokenIdWithNoCollision(random(mintIndex + 1)) ); _safeMint( msg.sender, _generateTokenIdWithNoCollision(random(mintIndex + 2)) ); } } function mint() external whenSaleIsActive { // Can only mint once per address if (didMint[msg.sender]) { revert TokenLimitReached(); } uint256 reservedSupply = genesisReservedSupply; uint256 nonReservedSupply = genesisNonReservedSupply; if (reservedSupply + nonReservedSupply + 2 > maxGenesis) revert InsufficientTokensAvailable(); // When minting, if isSupplyReserved is on, public minters won't be able // to mint the amount reserved for allow-listed wallets if ( isSupplyReserved && nonReservedSupply + 2 > maxGenesis - maxReserved ) { revert InsufficientNonReservedTokensAvailable(); } didMint[msg.sender] = true; unchecked { uint256 mintIndex = genesisSupply(); genesisNonReservedSupply += 2; _safeMint( msg.sender, _generateTokenIdWithNoCollision(random(mintIndex + 1)) ); _safeMint( msg.sender, _generateTokenIdWithNoCollision(random(mintIndex + 2)) ); } } function random(uint256 nonce) internal view returns (uint256) { return uint256( keccak256( abi.encodePacked( tx.origin, // solhint-disable-line avoid-tx-origin tx.gasprice, nonce, block.number, block.timestamp ) ) ); } // ======================================== // View // ======================================== function genesisSupply() public view returns (uint32) { unchecked { return genesisReservedSupply + genesisNonReservedSupply; } } /// @notice amount of tokens left to be minted function genesisLeft() public view returns (uint32) { unchecked { return maxGenesis - genesisSupply(); } } /// @notice amount do puppies left to be created function puppyTokensLeft() external view returns (uint32) { unchecked { return maxPuppies - puppySupply; } } /// @notice total supply of nfts function totalSupply() external view returns (uint32) { unchecked { return genesisSupply() + puppySupply; } } function getMetadata(uint256 id) external view override returns (CustomMetadata memory) { return metadata[id]; } // ======================================== // Overrides // ======================================== function tokenURI(uint256 id) public view override(ERC721) returns (string memory) { if (ownerOf[id] == BLACKHOLE) revert InvalidTokenID(); CustomMetadata memory md = metadata[id]; return attributes.tokenURI(id, bytes(md.name), md.lore); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Trees proofs. * * The proofs can be generated using the JavaScript library * https://github.com/miguelmota/merkletreejs[merkletreejs]. * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled. * * See `test/utils/cryptography/MerkleProof.test.js` for some examples. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify( bytes32[] memory proof, bytes32 root, bytes32 leaf ) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merklee tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { bytes32 proofElement = proof[i]; if (computedHash <= proofElement) { // Hash(current computed hash + current element of the proof) computedHash = _efficientHash(computedHash, proofElement); } else { // Hash(current element of the proof + current computed hash) computedHash = _efficientHash(proofElement, computedHash); } } return computedHash; } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; interface IAlphaToken { /// @dev 0x36a1c33f error NotChanged(); /// @dev 0x3d693ada error NotAllowed(); function mint(address addr, uint256 amount) external; function burn(address from, uint256 amount) external; } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; import {IAlphaDogsEvents} from "./IAlphaDogsEvents.sol"; import {IAlphaDogsErrors} from "./IAlphaDogsErrors.sol"; interface IAlphaDogs is IAlphaDogsEvents, IAlphaDogsErrors { struct CustomMetadata { string name; string lore; } struct Stake { address owner; uint96 stakedAt; } // mapping(uint256 => CustomMetadata) getMetadata; function getMetadata(uint256 id) external view returns (CustomMetadata memory); function setName(uint256 id, string calldata newName) external; function setLore(uint256 id, string calldata newLore) external; function stake(uint256[] calldata tokenIds) external; function unstake(uint256[] calldata tokenIds) external; function claim(uint256[] calldata tokenIds) external; function premint(bytes32[] calldata proof) external; function mint() external; function breed(uint256 mom, uint256 dad) external; } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; interface IAlphaDogsAttributes { function tokenURI( uint256 id, bytes memory name, string memory lore ) external view returns (string memory); } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; import {Chromossomes} from "./Chromossomes.sol"; /// @title AlphaDogs Genetics Library /// @author Aleph Retamal <github.com/alephao>, Gustavo Tiago <github.com/gutiago> /// @notice Library containing functions for creating and manipulating genes. /// /// ### Creating a new gene /// /// • When creating a new gene, we get a pseudo-random seed derive other seeds for each trait /// • We're using A.J. Walker Alias Algorithm to pick traits with pre-defined rarity table /// these are the weird hard-coded arrays in the `seedTo{Trait}` functions /// • Note: we use a pseudo-random seed, meaning that the result can be somewhat manipulated /// by mad-scientists of the chain. /// /// ### Breeding /// /// • For breeding we use uniform cross-over algorithm which is commonly used /// in genetic algorithms. We walk throught each chromossome, picking from either mom or dad. library Genetics { /// @dev Generate genes from a seed /// /// • Start with 0x0 /// • Add background chromossome 0x77 = 0x0 + 0x77 /// • Shift 1 byte to the left 0x7700 = 0x77 << 8 /// • Add fur chromossome 0x7766 = 0x7700 + 0x66 /// • Same for each chromossome function generateGenes(uint256 seed) internal pure returns (uint256 genes) { genes |= Chromossomes.seedToBackground(seed); genes <<= 8; genes |= Chromossomes.seedToFur(seed); genes <<= 8; genes |= Chromossomes.seedToNeck(seed); genes <<= 8; genes |= Chromossomes.seedToEyes(seed); genes <<= 8; genes |= Chromossomes.seedToHat(seed); genes <<= 8; genes |= Chromossomes.seedToMouth(seed); genes <<= 8; genes |= Chromossomes.seedToNosering(seed); } /// @dev Increments the gene i in n (big endian/from right to left) /// /// ### Examples /// /// • incrementByte(0x110000, 0) = 0x110001 /// • incrementByte(0x110000, 1) = 0x110100 /// • incrementByte(0x110000, 2) = 0x120000 /// /// ### A more readable version of the code /// /// unchecked { /// uint256 shift = (i % 7) * 8; /// uint256 mask = 0xFF << shift; /// uint256 trait = gene & mask; /// uint256 traitRaw = trait >> shift; /// uint256 newTrait = (traitRaw + 1) % [4, 28, 11, 70, 36, 10, 21][i]; /// uint256 tokenIdWithoutOldTrait = ~mask & gene; /// uint256 newGene = tokenIdWithoutOldTrait | (newTrait << shift); /// } /// /// ### Step by step explanation /// /// Explaining this for devs that look into other contracts to learn stuff like myself /// /// ### Glossary /// • Every 2 positions in an hexadecimal representation of a number = 1 byte /// E.g.: In 0x112233, 11 is a byte, 22 is another byte, 33 is another byte /// • Zeros on the left can be ignored so 0x00011 = 0x11, using them here to make /// it easier to see the math /// • 1 byte = 8 bits, so 0x1 << 8 will move 1 byte to the left (2 positions) resulting in 0x100 /// /// In this example we have 0x1111221111 and want to increment `22` to `23` /// /// 1. Create a mask to get only the 22 /// /// 0x1111221111 (gene) /// AND /// 0x0000FF0000 (mask) /// = /// 0x0000220000 (result) /// /// 2. Shift the byte "22" to the least significant byte, so we can increment /// /// 0x220000 >> (8 * 2) = 0x22 /// /// 3. Increment /// /// 0x22 + 1 = 0x23. We're also checking against the amount of variants a trait has, that's why /// we're doing (byte + 1) % [X, X, X][i] /// /// 4. Move the byte back to its original position /// /// 0x23 << (8 * 2) = 0x230000 /// /// 5. Invert the original mask to get all original bytes except the position we're manipulating /// /// ~0x0000FF0000 = 0xFFFF00FFFF /// /// 0xFFFF00FFFF /// AND /// 0x1111221111 /// = /// 0x1111001111 /// /// 6. Put the incremented byte back in the original value /// /// 0x1111001111 /// OR /// 0x0000230000 /// = /// 0x1111231111 function incrementByte(uint256 gene, uint256 i) internal pure returns (uint256) { unchecked { // Number of bytes to shift, should be between 0 and 7 uint256 shift = (i % 7) * 8; // Create the mask to do all the stuff mentioned in natspec uint256 mask = 0xFF << shift; return (~mask & gene) | (((((gene & mask) >> shift) + 1) % [4, 28, 11, 70, 36, 10, 21][i % 7]) << shift); } } /// @dev Uniform cross-over two "uint7", returns a "uint8" because a child has an extra byte /// @param mom genes from mom /// @param dad genes from dad /// @param seed the seed is used to pick chromossomes between dad and mom. /// /// @dev If a specific byte in the seed is even, picks mom, otherwise picks dad. /// /// ### Examples /// /// • uniformCrossOver(0x11111111111111, 0x22222222222222, 0x0) = 0x0111111111111111 /// • uniformCrossOver(0x11111111111111, 0x22222222222222, 0x1) = 0x0111111111111122 /// • uniformCrossOver(0x11111111111111, 0x22222222222222, 0x0101) = 0x0111111111112222 /// • uniformCrossOver(0x11111111111111, 0x22222222222222, 0x010101) = 0x0111111111222222 /// • uniformCrossOver(0x11111111111111, 0x22222222222222, 0x01000100010001) = 0x0122112211221122 function uniformCrossOver( uint256 mom, uint256 dad, uint256 seed ) internal pure returns (uint256) { unchecked { uint256 child = 0x0100000000000000; for (uint256 i = 0; i < 7; i++) { // Choose mom or dad to pick the chromossome from // If the byte on seed is even, pick mom uint256 chromossome = ((seed >> (8 * i)) & 0xFF) % 2 == 0 ? mom : dad; // Create a mask to pick only the current byte/chromossome // E.g.: 3rd byte = 0xFF0000 uint256 mask = 0xFF << (8 * i); // Add byte/chromossome to the child child |= (chromossome & mask); } return child; } } } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; /// @title AlphaDogs Gene Library /// @author Aleph Retamal <github.com/alephao> /// @notice Library containing functions for querying info about a gene. library Gene { /// @notice A gene is puppy if its 8th byte is greater than 0 function isPuppy(uint256 gene) internal pure returns (bool) { return (gene & 0xFF00000000000000) > 0; } /// @notice Get a specific chromossome in a gene, first position is 0 function getChromossome(uint256 gene, uint32 position) internal pure returns (uint256) { unchecked { uint32 shift = 8 * position; return (gene & (0xFF << shift)) >> shift; } } function getBackground(uint256 gene) internal pure returns (uint256) { return getChromossome(gene, 6); } function getFur(uint256 gene) internal pure returns (uint256) { return getChromossome(gene, 5); } function getNeck(uint256 gene) internal pure returns (uint256) { return getChromossome(gene, 4); } function getEyes(uint256 gene) internal pure returns (uint256) { return getChromossome(gene, 3); } function getHat(uint256 gene) internal pure returns (uint256) { return getChromossome(gene, 2); } function getMouth(uint256 gene) internal pure returns (uint256) { return getChromossome(gene, 1); } function getNosering(uint256 gene) internal pure returns (uint256) { return getChromossome(gene, 0); } } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; // solhint-disable /// @notice A modified version of Solmate's ERC721 (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) /// @author Solmate, Aleph Retamal <github.com/alephao> /// @dev Note that balanceOf does not revert if passed the zero address, in defiance of the ERC. abstract contract ERC721 { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer( address indexed from, address indexed to, uint256 indexed id ); event Approval( address indexed owner, address indexed spender, uint256 indexed id ); event ApprovalForAll( address indexed owner, address indexed operator, bool approved ); /*/////////////////////////////////////////////////////////////// METADATA STORAGE/LOGIC //////////////////////////////////////////////////////////////*/ string public name; string public symbol; function tokenURI(uint256 id) public view virtual returns (string memory); /*/////////////////////////////////////////////////////////////// ERC721 STORAGE //////////////////////////////////////////////////////////////*/ mapping(address => uint256) public balanceOf; mapping(uint256 => address) public ownerOf; mapping(uint256 => address) public getApproved; mapping(address => mapping(address => bool)) public isApprovedForAll; /*/////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(string memory _name, string memory _symbol) { name = _name; symbol = _symbol; } /*/////////////////////////////////////////////////////////////// ERC721 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 id) public virtual { address owner = ownerOf[id]; require( msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED" ); getApproved[id] = spender; emit Approval(owner, spender, id); } function setApprovalForAll(address operator, bool approved) public virtual { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function _transfer( address from, address to, uint256 id ) internal { require(from == ownerOf[id], "WRONG_FROM"); // Underflow of the sender's balance is impossible because we check for // ownership above and the recipient's balance can't realistically overflow. unchecked { balanceOf[from]--; balanceOf[to]++; } ownerOf[id] = to; delete getApproved[id]; emit Transfer(from, to, id); } function transferFrom( address from, address to, uint256 id ) public virtual { require(to != address(0), "INVALID_RECIPIENT"); require( msg.sender == from || msg.sender == getApproved[id] || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED" ); _transfer(from, to, id); } function safeTransferFrom( address from, address to, uint256 id ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, from, id, "" ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function safeTransferFrom( address from, address to, uint256 id, bytes memory data ) public virtual { transferFrom(from, to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, from, id, data ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } /*/////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public pure virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721 interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata } /*/////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 id) internal virtual { require(to != address(0), "INVALID_RECIPIENT"); require(ownerOf[id] == address(0), "ALREADY_MINTED"); // Counter overflow is incredibly unrealistic. unchecked { balanceOf[to]++; } ownerOf[id] = to; emit Transfer(address(0), to, id); } function _burn(uint256 id) internal virtual { address owner = ownerOf[id]; require(ownerOf[id] != address(0), "NOT_MINTED"); // Ownership check above ensures no underflow. unchecked { balanceOf[owner]--; } delete ownerOf[id]; delete getApproved[id]; emit Transfer(owner, address(0), id); } /*/////////////////////////////////////////////////////////////// INTERNAL SAFE MINT LOGIC //////////////////////////////////////////////////////////////*/ function _safeMint(address to, uint256 id) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, address(0), id, "" ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } function _safeMint( address to, uint256 id, bytes memory data ) internal virtual { _mint(to, id); require( to.code.length == 0 || ERC721TokenReceiver(to).onERC721Received( msg.sender, address(0), id, data ) == ERC721TokenReceiver.onERC721Received.selector, "UNSAFE_RECIPIENT" ); } } /// @notice A generic interface for a contract which properly accepts ERC721 tokens. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC721.sol) interface ERC721TokenReceiver { function onERC721Received( address operator, address from, uint256 id, bytes calldata data ) external returns (bytes4); } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; interface IAlphaDogsEvents { event NameChanged(uint256 indexed id, string name); event LoreChanged(uint256 indexed id, string lore); event Breeded(uint256 indexed child, uint256 mom, uint256 dad); event Staked(uint256 indexed id); event Unstaked(uint256 indexed id, uint256 amount); event ClaimedTokens(uint256 indexed id, uint256 amount); } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; interface IAlphaDogsErrors { /// @dev 0x2783839d error InsufficientTokensAvailable(); /// @dev 0x154e0758 error InsufficientReservedTokensAvailable(); /// @dev 0x8152a42e error InsufficientNonReservedTokensAvailable(); /// @dev 0x53bb24f9 error TokenLimitReached(); /// @dev 0xb05e92fa error InvalidMerkleProof(); /// @dev 0x2c5211c6 error InvalidAmount(); /// @dev 0x50e55ae1 error InvalidAmountToClaim(); /// @dev 0x6aa2a937 error InvalidTokenID(); /// @dev 0x1ae3550b error InvalidNameLength(); /// @dev 0x8a0fcaee error InvalidSameValue(); /// @dev 0x2a7c6b6e error InvalidTokenOwner(); /// @dev 0x8e8ede30 error FusionWithSameParentsForbidden(); /// @dev 0x6d074376 error FusionWithPuppyForbidden(); /// @dev 0x36a1c33f error NotChanged(); /// @dev 0x80cb55e2 error NotActive(); /// @dev 0xb4fa3fb3 error InvalidInput(); /// @dev 0xddb5de5e error InvalidSender(); /// @dev 0x21029e82 error InvalidChar(); } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; // Generated code. Do not modify! /// @title AlphaDogs Chromossome Generator Library /// @author Aleph Retamal <github.com/alephao> /// @notice Library containing functions to pick AlphaDogs chromossomes from an uint256 seed. library Chromossomes { // Each of those seedTo{Trait} function select 4 bytes from the seed // and use those selected bytes to pick a trait using the A.J. Walker // algorithm. The rarity and aliases are calculated beforehand. function seedToBackground(uint256 seed) internal pure returns (uint256) { unchecked { uint256 traitSeed = (seed >> 16) & 0xFFFF; uint256 trait = traitSeed % 21; if ( traitSeed >> 8 < [ 154, 222, 166, 200, 150, 333, 97, 158, 33, 162, 44, 170, 93, 234, 123, 94, 345, 134, 66, 255, 99 ][trait] ) return trait; return [ 1, 20, 1, 2, 1, 3, 3, 3, 5, 5, 13, 9, 16, 11, 16, 16, 13, 16, 19, 16, 19 ][trait]; } } function seedToFur(uint256 seed) internal pure returns (uint256) { unchecked { uint256 traitSeed = (seed >> 32) & 0xFFFF; uint256 trait = traitSeed % 12; if ( traitSeed >> 8 < [44, 345, 299, 450, 460, 88, 166, 177, 369, 470, 188, 277][ trait ] ) return trait; return [3, 11, 1, 2, 3, 4, 9, 9, 4, 8, 9, 9][trait]; } } function seedToNeck(uint256 seed) internal pure returns (uint256) { unchecked { uint256 traitSeed = (seed >> 48) & 0xFFFF; uint256 trait = traitSeed % 34; if ( traitSeed >> 8 < [ 140, 333, 147, 134, 878, 92, 53, 100, 25, 115, 90, 122, 40, 6, 9, 130, 3, 5, 222, 4, 45, 52, 57, 23, 98, 50, 48, 95, 27, 21, 55, 47, 32, 35 ][trait] ) return trait; return [ 33, 0, 1, 2, 3, 0, 1, 4, 1, 7, 1, 9, 1, 2, 4, 11, 4, 4, 15, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 9, 15, 18, 18 ][trait]; } } function seedToEyes(uint256 seed) internal pure returns (uint256) { unchecked { uint256 traitSeed = (seed >> 64) & 0xFFFF; uint256 trait = traitSeed % 43; if ( traitSeed >> 8 < [ 30, 21, 89, 7, 500, 135, 52, 59, 125, 88, 22, 81, 120, 228, 15, 90, 32, 39, 17, 83, 42, 12, 82, 100, 84, 20, 58, 56, 28, 180, 40, 35, 54, 55, 86, 85, 24, 53, 240, 80, 44, 26, 16 ][trait] ) return trait; return [ 4, 4, 42, 4, 2, 4, 4, 4, 5, 8, 4, 9, 11, 12, 4, 13, 4, 4, 5, 15, 8, 12, 19, 22, 23, 13, 13, 13, 13, 24, 22, 29, 29, 29, 29, 34, 35, 38, 35, 38, 38, 38, 39 ][trait]; } } function seedToHat(uint256 seed) internal pure returns (uint256) { unchecked { uint256 traitSeed = (seed >> 80) & 0xFFFF; uint256 trait = traitSeed % 68; if ( traitSeed >> 8 < [ 18, 4, 30, 35, 28, 45, 46, 25, 48, 22, 20, 1260, 38, 43, 24, 59, 38, 29, 56, 30, 7, 18, 25, 23, 58, 42, 22, 9, 6, 15, 35, 22, 12, 66, 27, 27, 44, 46, 37, 11, 28, 38, 15, 42, 40, 60, 37, 28, 53, 50, 15, 12, 5, 40, 30, 8, 18, 49, 48, 29, 30, 10, 44, 3, 35, 35, 46, 35 ][trait] ) return trait; return [ 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 67, 11, 11, 11, 11, 11, 11, 15, 11, 11, 11, 11, 11, 18, 11, 11, 11, 11, 11, 11, 11, 11, 24, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 33, 11, 11, 45, 48, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 18, 33, 33, 45, 49 ][trait]; } } function seedToMouth(uint256 seed) internal pure returns (uint256) { unchecked { uint256 traitSeed = (seed >> 96) & 0xFFFF; uint256 trait = traitSeed % 18; if ( traitSeed >> 8 < [ 156, 96, 1480, 48, 333, 96, 84, 32, 156, 72, 24, 60, 72, 84, 120, 120, 168, 132 ][trait] ) return trait; return [2, 2, 17, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4][trait]; } } function seedToNosering(uint256 seed) internal pure returns (uint256) { unchecked { uint256 traitSeed = (seed >> 112) & 0xFFFF; uint256 trait = traitSeed % 4; if (traitSeed >> 8 < [3201, 12, 84, 36][trait]) return trait; return [3, 0, 0, 0][trait]; } } }
File 2 of 2: AlphaToken
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; import {ERC20} from "@solmate/tokens/ERC20.sol"; import {Ownable} from "@openzeppelin/access/Ownable.sol"; import {IAlphaToken} from "$/interfaces/IAlphaToken.sol"; /// @title $ALPHA /// @author Aleph Retamal <github.com/alephao>, Gustavo Tiago <github.com/gutiago> contract AlphaToken is IAlphaToken, ERC20, Ownable { /// @notice Addresses with special access to use mint/burn functions mapping(address => bool) private allowed; modifier onlyAllowed() { if (!allowed[msg.sender]) revert NotAllowed(); _; } // solhint-disable-next-line no-empty-blocks constructor() ERC20("ALPHA", "ALPHA", 18) {} /// @notice Mints an amount of tokens to an address. /// Caller has to be allowed. function mint(address addr, uint256 amount) external onlyAllowed { _mint(addr, amount); } /// @notice Burn an amount of tokens from an address. /// Caller has to be a allowed. function burn(address from, uint256 amount) external onlyAllowed { _burn(from, amount); } /// @notice Set or unset an address as a controller. /// Owner Only. function setAllowed(address addr, bool isAllowed) external onlyOwner { if (allowed[addr] == isAllowed) revert NotChanged(); allowed[addr] = isAllowed; } } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*/////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*/////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*/////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*/////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*/////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*/////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { bytes32 digest = keccak256( abi.encodePacked( "\\x19\\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*/////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } } // SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.14; interface IAlphaToken { /// @dev 0x36a1c33f error NotChanged(); /// @dev 0x3d693ada error NotAllowed(); function mint(address addr, uint256 amount) external; function burn(address from, uint256 amount) external; } // SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }