Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions hardhat-tutorial/contracts/Greeter.sol

This file was deleted.

91 changes: 91 additions & 0 deletions hardhat-tutorial/contracts/tests/CryptoDevToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./ICryptoDevs.sol";

contract CryptoDevToken is ERC20, Ownable {
// Price of one Crypto Dev token
uint256 public constant tokenPrice = 0.001 ether;
// Each NFT would give the user 10 tokens
// It needs to be represented as 10 * (10 ** 18) as ERC20 tokens are represented by the smallest denomination possible for the token
// By default, ERC20 tokens have the smallest denomination of 10^(-18). This means, having a balance of (1)
// is actually equal to (10 ^ -18) tokens.
// Owning 1 full token is equivalent to owning (10^18) tokens when you account for the decimal places.
// More information on this can be found in the Freshman Track Cryptocurrency tutorial.
uint256 public constant tokensPerNFT = 10 * 10**18;
// the max total supply is 10000 for Crypto Dev Tokens
uint256 public constant maxTotalSupply = 10000 * 10**18;
// CryptoDevsNFT contract instance
ICryptoDevs CryptoDevsNFT;
// Mapping to keep track of which tokenIds have been claimed
mapping(uint256 => bool) public tokenIdsClaimed;

constructor(address _cryptoDevsContract) ERC20("Crypto Dev Token", "CD") {
CryptoDevsNFT = ICryptoDevs(_cryptoDevsContract);
}

/**
* @dev Mints `amount` number of CryptoDevTokens
* Requirements:
* - `msg.value` should be equal or greater than the tokenPrice * amount
*/
function mint(uint256 amount) public payable {
// the value of ether that should be equal or greater than tokenPrice * amount;
uint256 _requiredAmount = tokenPrice * amount;
require(msg.value >= _requiredAmount, "Ether sent is incorrect");
// total tokens + amount <= 10000, otherwise revert the transaction
uint256 amountWithDecimals = amount * 10**18;
require(
(totalSupply() + amountWithDecimals) <= maxTotalSupply,
"Exceeds the max total supply available."
);
// call the internal function from Openzeppelin's ERC20 contract
_mint(msg.sender, amountWithDecimals);
}

/**
* @dev Mints tokens based on the number of NFT's held by the sender
* Requirements:
* balance of Crypto Dev NFT's owned by the sender should be greater than 0
* Tokens should have not been claimed for all the NFTs owned by the sender
*/
function claim() public {
address sender = msg.sender;
// Get the number of CryptoDev NFT's held by a given sender address
uint256 balance = CryptoDevsNFT.balanceOf(sender);
// If the balance is zero, revert the transaction
require(balance > 0, "You dont own any Crypto Dev NFT's");
// amount keeps track of number of unclaimed tokenIds
uint256 amount = 0;
// loop over the balance and get the token ID owned by `sender` at a given `index` of its token list.
for (uint256 i = 0; i < balance; i++) {
uint256 tokenId = CryptoDevsNFT.tokenOfOwnerByIndex(sender, i);
// if the tokenId has not been claimed, increase the amount
if (!tokenIdsClaimed[tokenId]) {
amount += 1;
tokenIdsClaimed[tokenId] = true;
}
}
// If all the token Ids have been claimed, revert the transaction;
require(amount > 0, "You have already claimed all the tokens");
// call the internal function from Openzeppelin's ERC20 contract
// Mint (amount * 10) tokens for each NFT
_mint(msg.sender, amount * tokensPerNFT);
}

// Enables withdrawal of ether sent to contract for ICO
function withdraw() public onlyOwner {
address _owner = owner();
uint256 amount = address(this).balance;
(bool sent, ) = _owner.call{value: amount}("");
require(sent, "Failed to send Ether");
}

// Function to receive Ether. msg.data must be empty
receive() external payable {}

// Fallback function is called when msg.data is not empty
fallback() external payable {}
}
119 changes: 119 additions & 0 deletions hardhat-tutorial/contracts/tests/CryptoDevs.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "./IWhitelist.sol";

contract CryptoDevs is ERC721Enumerable, Ownable {
/**
* @dev _baseTokenURI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`.
*/
string _baseTokenURI;

// _price is the price of one Crypto Dev NFT
uint256 public _price = 0.01 ether;

// _paused is used to pause the contract in case of an emergency
bool public _paused;

// max number of CryptoDevs
uint256 public maxTokenIds = 10; // changed to 10 just for test sake, it should be on the constructor

// total number of tokenIds minted
uint256 public tokenIds;

// Whitelist contract instance
IWhitelist whitelist;

// boolean to keep track of when presale started
bool public presaleStarted;

// timestamp for even presale would end
uint256 public presaleEnded;

modifier onlyWhenNotPaused {
require(!_paused, "Contract currently paused");
_;
}

/**
* @dev ERC721 constructor takes in a `name` and a `symbol` to the token collection.
* name in our case is `Crypto Devs` and symbol is `CD`.
* Constructor for Crypto Devs takes in the baseURI to set _baseTokenURI for the collection.
* It also initializes an instance of whitelist interface.
*/
constructor (string memory baseURI, address whitelistContract) ERC721("Crypto Devs", "CD") {
_baseTokenURI = baseURI;
whitelist = IWhitelist(whitelistContract);
}

/**
* @dev startPresale starts a presale for the whitelisted addresses
*/
function startPresale() public onlyOwner {
presaleStarted = true;
// Set presaleEnded time as current timestamp + 5 minutes
// Solidity has cool syntax for timestamps (seconds, minutes, hours, days, years)
presaleEnded = block.timestamp + 5 minutes;
}

/**
* @dev presaleMint allows an user to mint one NFT per transaction during the presale.
*/
function presaleMint() public payable onlyWhenNotPaused {
require(presaleStarted && block.timestamp < presaleEnded, "Presale is not running");
require(whitelist.whitelistedAddresses(msg.sender), "You are not whitelisted");
require(tokenIds < maxTokenIds, "Exceeded maximum Cypto Devs supply");
require(msg.value >= _price, "Ether sent is not correct");
tokenIds += 1;
//_safeMint is a safer version of the _mint function as it ensures that
// if the address being minted to is a contract, then it knows how to deal with ERC721 tokens
// If the address being minted to is not a contract, it works the same way as _mint
_safeMint(msg.sender, tokenIds);
}

/**
* @dev mint allows an user to mint 1 NFT per transaction after the presale has ended.
*/
function mint() public payable onlyWhenNotPaused {
require(presaleStarted && block.timestamp >= presaleEnded, "Presale has not ended yet");
require(tokenIds < maxTokenIds, "Exceed maximum Cypto Devs supply");
require(msg.value >= _price, "Ether sent is not correct");
tokenIds += 1;
_safeMint(msg.sender, tokenIds);
}

/**
* @dev _baseURI overides the Openzeppelin's ERC721 implementation which by default
* returned an empty string for the baseURI
*/
function _baseURI() internal view virtual override returns (string memory) {
return _baseTokenURI;
}

/**
* @dev setPaused makes the contract paused or unpaused
*/
function setPaused(bool val) public onlyOwner {
_paused = val;
}

/**
* @dev withdraw sends all the ether in the contract
* to the owner of the contract
*/
function withdraw() public onlyOwner {
address _owner = owner();
uint256 amount = address(this).balance;
(bool sent, ) = _owner.call{value: amount}("");
require(sent, "Failed to send Ether");
}

// Function to receive Ether. msg.data must be empty
receive() external payable {}

// Fallback function is called when msg.data is not empty
fallback() external payable {}
}
18 changes: 18 additions & 0 deletions hardhat-tutorial/contracts/tests/ICryptoDevs.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface ICryptoDevs {
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index)
external
view
returns (uint256 tokenId);

/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
}
6 changes: 6 additions & 0 deletions hardhat-tutorial/contracts/tests/IWhitelist.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

interface IWhitelist {
function whitelistedAddresses(address) external view returns (bool);
}
36 changes: 36 additions & 0 deletions hardhat-tutorial/contracts/tests/Whitelist.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;


contract Whitelist {

// Max number of whitelisted addresses allowed
uint8 public maxWhitelistedAddresses;

// Create a mapping of whitelistedAddresses
// if an address is whitelisted, we would set it to true, it is false my default for all other addresses.
mapping(address => bool) public whitelistedAddresses;

// numAddressesWhitelisted would be used to keep track of how many addresses have been whitelisted
uint8 public numAddressesWhitelisted;

constructor(uint8 _maxWhitelistedAddresses) {
maxWhitelistedAddresses = _maxWhitelistedAddresses;
}

/**
addAddressToWhitelist - This function adds the address of the sender to the
whitelist
*/
function addAddressToWhitelist() public {
// check if the user has already been whitelisted
require(!whitelistedAddresses[msg.sender], "Sender has already been whitelisted");
// check if the numAddressesWhitelisted < maxWhitelistedAddresses, if not then throw an error.
require(numAddressesWhitelisted < maxWhitelistedAddresses, "More addresses cant be added, limit reached");
// Add the address which called the function to the whitelistedAddress array
whitelistedAddresses[msg.sender] = true;
// Increase the number of whitelisted addresses
numAddressesWhitelisted += 1;
}

}
4 changes: 2 additions & 2 deletions hardhat-tutorial/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
require("@nomiclabs/hardhat-waffle");
require("@nomicfoundation/hardhat-toolbox");
require("dotenv").config({ path: ".env" });

const ALCHEMY_API_KEY_URL = process.env.ALCHEMY_API_KEY_URL;

const RINKEBY_PRIVATE_KEY = process.env.RINKEBY_PRIVATE_KEY;

module.exports = {
solidity: "0.8.4",
solidity: "0.8.10",
networks: {
rinkeby: {
url: ALCHEMY_API_KEY_URL,
Expand Down
21 changes: 15 additions & 6 deletions hardhat-tutorial/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,21 @@
"author": "",
"license": "ISC",
"devDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.4",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"chai": "^4.3.4",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.5.3",
"hardhat": "^2.8.2"
"@ethersproject/abi": "^5.7.0",
"@ethersproject/providers": "^5.7.1",
"@nomicfoundation/hardhat-chai-matchers": "^1.0.3",
"@nomicfoundation/hardhat-network-helpers": "^1.0.6",
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
"@nomiclabs/hardhat-ethers": "^2.1.1",
"@nomiclabs/hardhat-etherscan": "^3.1.0",
"@typechain/ethers-v5": "^10.1.0",
"@typechain/hardhat": "^6.1.3",
"chai": "^4.3.6",
"ethers": "^5.7.1",
"hardhat": "^2.11.2",
"hardhat-gas-reporter": "^1.0.9",
"solidity-coverage": "^0.8.2",
"typechain": "^8.1.0"
},
"dependencies": {
"@openzeppelin/contracts": "^4.4.1",
Expand Down
32 changes: 0 additions & 32 deletions hardhat-tutorial/scripts/sample-script.js

This file was deleted.

Loading