-
Notifications
You must be signed in to change notification settings - Fork 3
BrainPass Nft Contract #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 5 commits
4e773ae
c4aec5a
c2f67f9
bc3a1cc
51cd490
1c6e3fe
d88b6e8
4482b93
2959b63
a3e0bd7
9e52d56
ff9f7db
347c98c
a74cb88
ae58340
566e6a9
ef3731a
5ef8ca1
9a9bc99
77d4183
0301aa2
7444fee
3bbca93
f7c7a5d
888dc99
6696764
b21b030
639d9a0
8379083
92444b8
d9b5bda
9ed21bc
f0dc368
86eb11d
cef37c7
b8fffdf
9a230bf
3e6c1f6
a4065fc
49a3661
2fe00bd
721b931
3f9a1eb
f717fe8
717d7a0
6260675
fc932ed
8669373
fa5b0fb
b7f9781
bb03b8e
f63d3fa
7095eb3
903fdfc
b2283b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // SPDX-License-Identifier: UNLICENSED | ||
| pragma solidity ^0.8.13; | ||
|
|
||
| import {Script} from "../lib/forge-std/src/Script.sol"; | ||
| import {console} from "../lib/forge-std/src/console.sol"; | ||
|
|
||
| import {BrainPassCollectibles} from "../src/BrainPass/BrainPass.sol"; | ||
|
|
||
|
|
||
| contract BrainPassDeployer is Script { | ||
| address constant owner = address(0xE161eB85f00eC6471E0de06bA1Cfc136C053fFfe); | ||
|
|
||
| function run() external { | ||
| vm.startBroadcast(); | ||
|
|
||
| console.log("Deploying Brainpass deployer...."); | ||
| BrainPassCollectibles validator = new BrainPassCollectibles(0x5E959c60f86D17fb7D764AB69B654227d464E820); | ||
| console.log("Brainpass Deployed", address(validator)); | ||
|
|
||
| vm.stopBroadcast(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // SPDX-License-Identifier: UNLICENSED | ||
| pragma solidity ^0.8.13; | ||
|
|
||
| import {Script} from "../lib/forge-std/src/Script.sol"; | ||
| import {console} from "../lib/forge-std/src/console.sol"; | ||
| import {BrainPassCollectibles} from "../src/BrainPass/BrainPass.sol"; | ||
|
|
||
| contract BrainPassMinter is Script { | ||
| address constant owner = address(0xaCa39B187352D9805DECEd6E73A3d72ABf86E7A0); | ||
|
|
||
| function run() external { | ||
| vm.startBroadcast(); | ||
|
|
||
|
|
||
| vm.stopBroadcast(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,284 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.13; | ||
|
|
||
| import "openzeppelin-contracts/contracts/token/ERC721/ERC721.sol"; | ||
| import {Counters} from "openzeppelin-contracts/contracts/utils/Counters.sol"; | ||
| import {Owned} from "solmate/auth/Owned.sol"; | ||
| import {SafeMath} from "openzeppelin-contracts/contracts/utils/math/SafeMath.sol"; | ||
|
|
||
| interface IERC20 { | ||
| function transferFrom( | ||
| address from, | ||
| address to, | ||
| uint256 amount | ||
| ) external returns (bool); | ||
|
|
||
| function transfer(address to, uint256 amount) external returns (bool); | ||
|
|
||
| function balanceOf(address account) external view returns (uint256); | ||
| } | ||
|
|
||
| /// @title BRAIN Pass NFT | ||
| /// @author Oleanji | ||
| /// @notice A pass for IQ Wiki Editors | ||
|
|
||
| contract BrainPassCollectibles is ERC721, Owned { | ||
| /// ----------------------------------------------------------------------- | ||
| /// Errors | ||
| /// ----------------------------------------------------------------------- | ||
|
|
||
| error MintingPaymentFailed(); | ||
| error IncreseTimePaymentFailed(); | ||
| error UserBalanceNotEnough(); | ||
|
|
||
| /// ----------------------------------------------------------------------- | ||
| /// Inheritances | ||
| /// ----------------------------------------------------------------------- | ||
| using SafeMath for uint256; | ||
|
OleanjiKingCode marked this conversation as resolved.
Outdated
|
||
| using Counters for Counters.Counter; | ||
|
|
||
| /// ----------------------------------------------------------------------- | ||
| /// Structs | ||
| /// ----------------------------------------------------------------------- | ||
| struct UserPassItem { | ||
| uint256 tokenId; | ||
| uint256 passId; | ||
| uint256 startTimestamp; | ||
| uint256 endTimestamp; | ||
| } | ||
|
|
||
| struct PassType { | ||
| uint256 passId; | ||
| string name; | ||
| uint256 pricePerDay; | ||
| string tokenURI; | ||
| uint256 maxTokens; | ||
| uint256 discount; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whats maxTokens & discount? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think maxTokens should be the max no of NFTS that can be minted (in the case of a limited supply). and for the discount, we felt we might want to give a bit of discount to those subscribing for a long time at once(say a yr subscriptions).
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mention here that the discount param is "to those subscribing for a long time at once". Code doesnt do that at all. it just apply a generic discount to price. which its the same to not having that param and reduce the price per day.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this has never been addressed. Please review my comments before asking for a new code review
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. third time: please remove discount. it doesnt make any sense current implementation. if you do a 10% discount its the same that if you just reduce price by 10%. |
||
| uint256 lastTokenIdMinted; | ||
| } | ||
|
|
||
| /// ----------------------------------------------------------------------- | ||
| /// Mappings | ||
| /// ----------------------------------------------------------------------- | ||
| mapping(uint256 => PassType) public passTypes; | ||
| mapping(address => mapping(uint256 => UserPassItem)) | ||
| public addressToNFTPass; | ||
| mapping(address => mapping(uint256 => bool)) public addressToPassId; | ||
|
|
||
| /// ----------------------------------------------------------------------- | ||
| /// Constant | ||
| /// ----------------------------------------------------------------------- | ||
| address public IqToken; | ||
|
|
||
| /// ----------------------------------------------------------------------- | ||
| /// Variables | ||
| /// ----------------------------------------------------------------------- | ||
| string public baseTokenURI; | ||
|
kesar marked this conversation as resolved.
|
||
| Counters.Counter private tokenIds; | ||
|
|
||
| /// ----------------------------------------------------------------------- | ||
| /// Constructor | ||
| /// ----------------------------------------------------------------------- | ||
| constructor( | ||
| address IqAddr | ||
| ) ERC721("BRAINY EDITOR PASS", "BEP") Owned(msg.sender) { | ||
|
kesar marked this conversation as resolved.
Outdated
|
||
| IqToken =IqAddr; | ||
| } | ||
|
|
||
| function baseURI() internal view virtual returns (string memory) { | ||
| return baseTokenURI; | ||
| } | ||
|
|
||
| function setBaseURI(string memory tokenURI) internal { | ||
| baseTokenURI = tokenURI; | ||
| } | ||
|
|
||
| /// @notice Add a new Pass Type | ||
| /// @param passId and others are the details needed for a passType | ||
| function addPassType( | ||
| uint256 passId, | ||
| uint256 pricePerDay, | ||
| string memory tokenURI, | ||
| string memory name, | ||
| uint256 maxTokens, | ||
| uint256 discount | ||
| ) public onlyOwner { | ||
| require(bytes(tokenURI).length > 0, "Invalid token URI"); | ||
| require(maxTokens > 0, "Invalid max tokens"); | ||
|
|
||
| passTypes[passId] = PassType( | ||
| passId, | ||
| name, | ||
| pricePerDay, | ||
| tokenURI, | ||
| maxTokens, | ||
| discount, | ||
| 0 | ||
| ); | ||
|
|
||
| emit NewPassAdded(passId, name, maxTokens, pricePerDay); | ||
| } | ||
|
|
||
| /// @notice Mint and NFT of a particular passtype | ||
| /// @param passId The id of the passtype to mint | ||
| function mintNFT( | ||
| uint256 passId, | ||
| uint256 startTimestamp, | ||
| uint256 endTimestamp | ||
| ) public { | ||
| require( | ||
| addressToPassId[msg.sender][passId] != true, | ||
| "Max NFTs per address reached" | ||
| ); | ||
|
|
||
| PassType storage passType = passTypes[passId]; | ||
|
|
||
| require(passType.maxTokens != 0, "Pass type not found"); | ||
|
|
||
| require( | ||
| passType.lastTokenIdMinted.add(1) <= passType.maxTokens, | ||
| "Max supply reached" | ||
| ); | ||
|
|
||
| uint256 price = calculatePrice(passId, startTimestamp, endTimestamp); | ||
|
|
||
| if (IERC20(IqToken).balanceOf(msg.sender) < price) | ||
| revert UserBalanceNotEnough(); | ||
|
kesar marked this conversation as resolved.
Outdated
|
||
|
|
||
| bool success = IERC20(IqToken).transferFrom(msg.sender, owner, 1e18); | ||
|
kesar marked this conversation as resolved.
Outdated
|
||
| if (!success) revert MintingPaymentFailed(); | ||
|
|
||
| uint256 tokenId = passType.lastTokenIdMinted + 1; | ||
| setBaseURI(passType.tokenURI); | ||
|
kesar marked this conversation as resolved.
Outdated
|
||
| _safeMint(msg.sender, tokenId); | ||
|
kesar marked this conversation as resolved.
Outdated
|
||
|
|
||
| addressToPassId[msg.sender][passId] = true; | ||
|
|
||
| UserPassItem memory purchase = UserPassItem( | ||
| tokenId, | ||
| passId, | ||
| startTimestamp, | ||
| endTimestamp | ||
| ); | ||
| addressToNFTPass[msg.sender][tokenId] = purchase; | ||
| passType.lastTokenIdMinted = tokenId; | ||
|
|
||
| emit BrainPassBought( | ||
| msg.sender, | ||
| tokenId, | ||
| passId, | ||
| startTimestamp, | ||
| endTimestamp | ||
| ); | ||
| } | ||
|
|
||
| /// @notice Calculate the price of an Nft | ||
| /// @param startTimestamp and endTimestamp are used to calc the price to be paid | ||
| function calculatePrice( | ||
| uint256 passId, | ||
| uint256 startTimestamp, | ||
| uint256 endTimestamp | ||
| ) public view returns (uint256) { | ||
| PassType memory passType = passTypes[passId]; | ||
| //duration in days | ||
| uint256 duration = endTimestamp.sub(startTimestamp); | ||
| uint256 totalPrice = duration.mul(passType.pricePerDay); | ||
|
kesar marked this conversation as resolved.
Outdated
|
||
| if (passType.discount > 0) { | ||
| uint256 discountAmount = totalPrice.mul(passType.discount).div(100); | ||
| totalPrice = totalPrice.sub(discountAmount); | ||
| } | ||
|
|
||
| return totalPrice; | ||
| } | ||
|
|
||
| /// @notice Increase the time to hold a PassNft | ||
| /// @param tokenId The Id of the NFT whose time is to be increased | ||
| function increasePassTime( | ||
| uint256 tokenId, | ||
| uint256 passIdNum, | ||
| uint newStartTime, | ||
| uint256 newEndTime | ||
| ) public { | ||
| require( | ||
| msg.sender == ownerOf(tokenId), | ||
| "You cannot increase the time for an NFT you don't own" | ||
| ); | ||
|
|
||
| UserPassItem storage pass = addressToNFTPass[ownerOf(tokenId)][tokenId]; | ||
| uint256 price = calculatePrice(passIdNum, newStartTime, newEndTime); | ||
|
kesar marked this conversation as resolved.
Outdated
|
||
| bool success = IERC20(IqToken).transferFrom(msg.sender, owner, price); | ||
| if (!success) revert IncreseTimePaymentFailed(); | ||
|
|
||
| pass.startTimestamp = newStartTime; | ||
| pass.endTimestamp = newEndTime; | ||
|
|
||
| emit TimeIncreased( | ||
| msg.sender, | ||
| tokenId, | ||
| pass.startTimestamp, | ||
| pass.endTimestamp | ||
| ); | ||
| } | ||
|
|
||
| /// @notice Gets all the NFT owned by an address | ||
| /// @param user The address of the user | ||
| function getUserNFTs( | ||
| address user, | ||
| uint passIdNum | ||
| ) public view returns (UserPassItem[] memory) { | ||
| uint256 userTokenCount = balanceOf(user); | ||
| PassType memory passType = passTypes[passIdNum]; | ||
| UserPassItem[] memory userTokens = new UserPassItem[](userTokenCount); | ||
| uint256 counter = 0; | ||
| for (uint256 i = 0; i < passType.maxTokens; i++) { | ||
| if (ownerOf(i) == user) { | ||
| userTokens[counter] = addressToNFTPass[msg.sender][i]; | ||
| counter++; | ||
| } | ||
| } | ||
| return userTokens; | ||
| } | ||
|
|
||
| /// @notice Gets all the details of a passtype | ||
| /// @param passId The id of the passtype | ||
| function getAllPassType( | ||
| uint256 passId | ||
| ) public view returns (PassType memory) { | ||
| PassType memory passType = passTypes[passId]; | ||
| return (passType); | ||
| } | ||
|
|
||
| /// @notice Withdraws any amount in the contract | ||
| function withdraw() public payable onlyOwner { | ||
| uint256 balance = address(this).balance; | ||
| require(balance > 0, "No ether left to withdraw"); | ||
| (bool success, ) = (msg.sender).call{value: balance}(""); | ||
| require(success, "Transfer failed."); | ||
| } | ||
|
|
||
| /// ----------------------------------------------------------------------- | ||
| /// Events | ||
| /// ----------------------------------------------------------------------- | ||
|
|
||
| event BrainPassBought( | ||
| address indexed _owner, | ||
| uint256 _passId, | ||
| uint256 _tokenId, | ||
| uint256 _startTimestamp, | ||
| uint256 _endTimestamp | ||
| ); | ||
|
|
||
| event TimeIncreased( | ||
| address indexed _owner, | ||
| uint256 _tokenId, | ||
| uint256 _startTimestamp, | ||
| uint256 _newEndTimestamp | ||
| ); | ||
|
|
||
| event NewPassAdded( | ||
| uint256 indexed _passId, | ||
| string _name, | ||
| uint256 _maxtokens, | ||
| uint256 _pricePerDay | ||
| ); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some improvements I've been thinking after seeing the codebase:
Besides that, there is a missing contract needed that connects the owning of these NFTs with IQ wiki possibility to push wikis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the Wiki contract part of the ep contracts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the validator / whitelist contracts