├── .gitattributes ├── .gitignore ├── LICENSE └── contracts ├── dan.sol └── erc1111.sol /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | *.vy linguist-language=Python 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .env 3 | .history 4 | .hypothesis/ 5 | build/ 6 | reports/ 7 | scripts/ 8 | .DS_Store 9 | metadata/ 10 | brownie-config.yaml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 ColorfuLabs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contracts/dan.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./erc1111.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; 7 | import "@openzeppelin/contracts/interfaces/IERC2981.sol"; 8 | import "@openzeppelin/contracts/utils/Strings.sol"; 9 | import "@openzeppelin/contracts/utils/Address.sol"; 10 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 11 | 12 | contract DAN is ERC1111, IERC2981, Ownable{ 13 | using Strings for uint256; 14 | 15 | bytes32 public merkleRoot; 16 | mapping(address => bool) public withdrawn; 17 | 18 | bool public isRedirect; 19 | 20 | address private _royaltyRecipient; 21 | 22 | // metadata URI 23 | string private _baseTokenURI; 24 | 25 | mapping(address => bool) public isFairLaunch; 26 | 27 | uint256 startTimestamp = 1707534671; // 2024-02-10 11:11:11 28 | 29 | uint256 mintedFairLaunch; 30 | 31 | mapping(bytes32 => bool) public evidenceUsed; 32 | address public signer; 33 | 34 | 35 | 36 | constructor( 37 | address _signer 38 | ) ERC1111("PFPAsia", "PFPAsia", 18) { 39 | signer = _signer; 40 | 41 | _mintFT(msg.sender, 278 * 10000 * 10**18); // team reamins 5.555% 42 | } 43 | 44 | function setMerkleRoot(bytes32 _merkleRoot) public onlyOwner { 45 | merkleRoot = _merkleRoot; 46 | } 47 | 48 | function claim(uint256 _amount, bytes32[] calldata _proof) external { 49 | address _account = msg.sender; 50 | require(!withdrawn[_account], "withdrawned token."); 51 | 52 | // Verify the merkle proof. 53 | bytes32 leaf = keccak256(abi.encodePacked(_account, _amount)); 54 | require(MerkleProof.verify(_proof, merkleRoot, leaf), "Invalid proof"); 55 | 56 | withdrawn[_account] = true; 57 | 58 | for (uint256 i = 0; i < _amount;i++){ 59 | ERC1111._mint(_account); 60 | } 61 | } 62 | 63 | function openRedirect() public onlyOwner { 64 | isRedirect = true; 65 | } 66 | 67 | function closeRedirect() public onlyOwner { 68 | isRedirect = false; 69 | } 70 | function openFtTransfer() public onlyOwner { 71 | enableFtTransfer = true; 72 | } 73 | 74 | function closeFtTransfer() public onlyOwner { 75 | enableFtTransfer = false; 76 | } 77 | 78 | function ftRedirectNFT(uint256 amount) public { 79 | require(isRedirect, "redirect not open"); 80 | _ft_to_nft(amount); 81 | } 82 | function nftRedirectFT(uint256 tokenId) public { 83 | require(isRedirect, "redirect not open"); 84 | _nft_to_ft(tokenId); 85 | } 86 | 87 | function _baseURI() internal view virtual returns (string memory) { 88 | return _baseTokenURI; 89 | } 90 | 91 | function setBaseURI(string calldata baseURI) external onlyOwner { 92 | _baseTokenURI = baseURI; 93 | } 94 | 95 | function fairLaunch( 96 | bytes memory evidence 97 | ) external{ 98 | require(block.timestamp >= startTimestamp, "not start"); 99 | require(!Address.isContract(msg.sender), "contract"); 100 | require(!isFairLaunch[msg.sender],"claimed"); 101 | require(mintedFairLaunch + 10000 * 10**18 <= 5000 * 10000 * 10**18, "exceed"); 102 | 103 | 104 | require( 105 | !evidenceUsed[keccak256(evidence)] && 106 | ECDSA.recover(ECDSA.toEthSignedMessageHash(keccak256( 107 | abi.encodePacked( 108 | msg.sender, 109 | block.chainid 110 | ) 111 | )), evidence) == signer, 112 | "invalid evidence" 113 | ); 114 | evidenceUsed[keccak256(evidence)] = true; 115 | 116 | _mintFT(msg.sender, 10000 * 10**18); 117 | mintedFairLaunch += 10000 * 10**18; 118 | 119 | isFairLaunch[msg.sender] = true; 120 | 121 | } 122 | 123 | function setSigner(address _signer) public onlyOwner { 124 | signer = _signer; 125 | } 126 | function setStartTimestamp(uint256 _startTimestamp) public onlyOwner { 127 | startTimestamp = _startTimestamp; 128 | } 129 | 130 | function tokenURI(uint256 tokenId) 131 | public 132 | view 133 | virtual 134 | override 135 | returns (string memory) 136 | { 137 | require( 138 | _exists(tokenId), 139 | "ERC721Metadata: URI query for nonexistent token" 140 | ); 141 | 142 | string memory baseURI = _baseURI(); 143 | return 144 | bytes(baseURI).length > 0 145 | ? string(abi.encodePacked(baseURI, tokenId.toString())) 146 | : ""; 147 | } 148 | 149 | 150 | /** 151 | * --------- IERC2981 --------- 152 | */ 153 | 154 | function royaltyInfo( 155 | uint256 tokenId, 156 | uint256 salePrice 157 | ) external view returns (address receiver, uint256 royaltyAmount) { 158 | return (_royaltyRecipient, salePrice * 5 / 100); 159 | } 160 | 161 | function setRoyaltyRecipient(address r) public onlyOwner { 162 | _royaltyRecipient = r; 163 | } 164 | 165 | function supportsInterface(bytes4 interfaceId) 166 | public 167 | view 168 | virtual 169 | override(IERC165) 170 | returns (bool) 171 | { 172 | return 173 | interfaceId == type(IERC2981).interfaceId; 174 | } 175 | 176 | } -------------------------------------------------------------------------------- /contracts/erc1111.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | abstract contract ERC1111 { 5 | 6 | // Events 7 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 8 | 9 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 10 | 11 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 12 | 13 | event ERC20Transfer( 14 | address indexed from, 15 | address indexed to, 16 | uint256 amount 17 | ); 18 | 19 | // meatadata 20 | 21 | // Token name 22 | string public name; 23 | 24 | // Token symbol 25 | string public symbol; 26 | 27 | // Token decimals 28 | uint8 public decimals; 29 | 30 | // erc20 31 | 32 | mapping(address => uint256) private _erc20BalanceOf; 33 | 34 | mapping(address => mapping(address => uint256)) private _allowances; 35 | 36 | uint256 private _totalSupply; 37 | 38 | // erc721 39 | 40 | // Mapping owner address to token count 41 | mapping(address => uint256) private _erc721BalanceOf; 42 | 43 | // Mapping from token ID to owner address 44 | mapping(uint256 => address) private _owners; 45 | 46 | // Mapping from token ID to approved address 47 | mapping(uint256 => address) private _tokenApprovals; 48 | 49 | // Mapping from owner to operator approvals 50 | mapping(address => mapping(address => bool)) private _operatorApprovals; 51 | 52 | // Array of owned ids in native representation 53 | mapping(address => uint256[]) internal _owned; 54 | 55 | mapping(uint256 => uint256) internal _ownedIndex; 56 | 57 | uint256 public minted; 58 | 59 | bool enableFtTransfer; 60 | 61 | constructor( 62 | string memory name_, 63 | string memory symbol_, 64 | uint8 decimals_ 65 | ) { 66 | name = name_; 67 | symbol = symbol_; 68 | decimals = decimals_; 69 | } 70 | 71 | 72 | function balanceOf(address owner) public view virtual returns (uint256) { 73 | return _erc20BalanceOf[owner]; 74 | } 75 | 76 | function _ownerOf(uint256 tokenId) internal view virtual returns (address) { 77 | return _owners[tokenId]; 78 | } 79 | 80 | function ownerOf(uint256 tokenId) public view virtual returns (address) { 81 | address owner = _ownerOf(tokenId); 82 | require(owner != address(0), "ERC721: invalid token ID"); 83 | return owner; 84 | } 85 | 86 | function _exists(uint256 tokenId) internal view virtual returns (bool) { 87 | return _ownerOf(tokenId) != address(0); 88 | } 89 | 90 | function getApproved(uint256 tokenId) public view virtual returns (address) { 91 | require(_exists(tokenId), "ERC721: invalid token ID"); 92 | 93 | return _tokenApprovals[tokenId]; 94 | } 95 | 96 | function setApprovalForAll(address operator, bool approved) public virtual { 97 | _setApprovalForAll(msg.sender, operator, approved); 98 | } 99 | 100 | function _setApprovalForAll( 101 | address owner, 102 | address operator, 103 | bool approved 104 | ) internal virtual { 105 | require(owner != operator, "ERC721: approve to caller"); 106 | _operatorApprovals[owner][operator] = approved; 107 | emit ApprovalForAll(owner, operator, approved); 108 | } 109 | 110 | 111 | function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { 112 | return _operatorApprovals[owner][operator]; 113 | } 114 | 115 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { 116 | address owner = ownerOf(tokenId); 117 | return (spender == owner || isApprovedForAll(owner, spender) || getApproved(tokenId) == spender); 118 | } 119 | 120 | function approve(address spender, uint256 amountOrId) public virtual { 121 | if (amountOrId <= minted && amountOrId > 0) { 122 | address owner = ownerOf(amountOrId); 123 | require(spender != owner, "ERC721: approval to current owner"); 124 | require( 125 | msg.sender == owner || isApprovedForAll(owner, msg.sender), 126 | "ERC721: approve caller is not token owner or approved for all" 127 | ); 128 | 129 | _tokenApprovals[amountOrId] = spender; 130 | 131 | emit Approval(owner, spender, amountOrId); 132 | } else { 133 | _allowances[msg.sender][spender] = amountOrId; 134 | 135 | emit Approval(msg.sender, spender, amountOrId); 136 | } 137 | 138 | } 139 | 140 | // erc20// 141 | function allowance(address owner, address spender) public view returns (uint256) { 142 | return _allowances[owner][spender]; 143 | } 144 | 145 | function totalSupply() public view virtual returns (uint256) { 146 | return _totalSupply; 147 | } 148 | 149 | function transferFrom( 150 | address from, 151 | address to, 152 | uint256 amountOrId 153 | ) public virtual { 154 | if (amountOrId <= minted) { 155 | require(_isApprovedOrOwner(msg.sender, amountOrId), "ERC721: caller is not token owner or approved"); 156 | require(ownerOf(amountOrId) == from, "ERC721: transfer from incorrect owner"); 157 | require(to != address(0), "ERC721: transfer to the zero address"); 158 | 159 | delete _tokenApprovals[amountOrId]; 160 | 161 | unchecked { 162 | _erc721BalanceOf[from] -= 1; 163 | _erc721BalanceOf[to] += 1; 164 | } 165 | 166 | _owners[amountOrId] = to; 167 | 168 | // update from 169 | uint256 updatedId = _owned[from][_owned[from].length - 1]; 170 | _owned[from][_ownedIndex[amountOrId]] = updatedId; 171 | _owned[from].pop(); 172 | _ownedIndex[updatedId] = _ownedIndex[amountOrId]; 173 | _owned[to].push(amountOrId); 174 | _ownedIndex[amountOrId] = _owned[to].length - 1; 175 | 176 | emit Transfer(from, to, amountOrId); 177 | } else { 178 | uint256 allowed = _allowances[from][msg.sender]; 179 | 180 | if (allowed != type(uint256).max) 181 | _allowances[from][msg.sender] = allowed - amountOrId; 182 | 183 | _transfer(from, to, amountOrId); 184 | } 185 | } 186 | 187 | function transfer( 188 | address to, 189 | uint256 amount 190 | ) public virtual returns (bool) { 191 | return _transfer(msg.sender, to, amount); 192 | } 193 | 194 | function _transfer( 195 | address from, 196 | address to, 197 | uint256 amount 198 | ) internal returns (bool) { 199 | require(enableFtTransfer, "can not transfer"); 200 | uint256 fromBalance = _erc20BalanceOf[from]; 201 | require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); 202 | 203 | unchecked { 204 | _erc20BalanceOf[from] -= amount; 205 | _erc20BalanceOf[to] += amount; 206 | } 207 | 208 | emit ERC20Transfer(from, to, amount); 209 | return true; 210 | } 211 | 212 | function _mint(address to) internal virtual { 213 | require(to != address(0), "ERC721: mint to the zero address"); 214 | unchecked { 215 | minted++; 216 | } 217 | uint256 tokenId = minted; 218 | 219 | unchecked { 220 | _erc721BalanceOf[to] += 1; 221 | } 222 | _owners[tokenId] = to; 223 | _owned[to].push(tokenId); 224 | _ownedIndex[tokenId] = _owned[to].length - 1; 225 | 226 | emit Transfer(address(0), to, tokenId); 227 | } 228 | 229 | function _burn(address owner) internal virtual { 230 | uint256 tokenId=_owned[owner][_owned[owner].length - 1]; 231 | _owned[owner].pop(); 232 | 233 | // Clear approvals 234 | delete _tokenApprovals[tokenId]; 235 | 236 | unchecked { 237 | _erc721BalanceOf[owner] -= 1; 238 | } 239 | delete _owners[tokenId]; 240 | delete _ownedIndex[tokenId]; 241 | 242 | emit Transfer(owner, address(0), tokenId); 243 | 244 | } 245 | 246 | function _mintFT(address account, uint256 amount) internal virtual { 247 | require(account != address(0), "ERC20: mint to the zero address"); 248 | 249 | _totalSupply += amount; 250 | unchecked { 251 | // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. 252 | _erc20BalanceOf[account] += amount; 253 | } 254 | emit ERC20Transfer(address(0), account, amount); 255 | 256 | } 257 | 258 | function _getUnit() internal view returns (uint256) { 259 | return 10000 * 10 ** decimals; 260 | } 261 | 262 | function _nft_to_ft(uint256 tokenId) internal { 263 | uint256 unit = _getUnit(); 264 | transferFrom(msg.sender, address(this), tokenId); 265 | 266 | _erc20BalanceOf[msg.sender] += unit; 267 | _totalSupply += unit; 268 | 269 | emit ERC20Transfer(address(0), msg.sender, unit); 270 | } 271 | 272 | function _ft_to_nft(uint256 amount) internal { 273 | uint256 unit = _getUnit(); 274 | uint256 nftAmount = amount / unit; 275 | uint256 ftAmount = nftAmount * unit; 276 | 277 | _transfer(msg.sender, address(0), ftAmount); 278 | _totalSupply -= ftAmount; 279 | 280 | uint256 nftMintAmount = _owned[address(this)].length < nftAmount ? nftAmount-_owned[address(this)].length : 0; 281 | uint256 nftTransferAmount= nftAmount - nftMintAmount; 282 | 283 | for (uint256 i=0; i