-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathBCFactory.sol
175 lines (150 loc) · 6.03 KB
/
BCFactory.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
// Author : Francesco Sullo < [email protected]>
// (c) Superpower Labs Inc.
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
import {MerkleProofUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/cryptography/MerkleProofUpgradeable.sol";
import "./tokens/BCGenesisToken.sol";
import "./tokens/BCOracleToken.sol";
import "./interfaces/IAttributes.sol";
//import "hardhat/console.sol";
contract BCFactory is OwnableUpgradeable, UUPSUpgradeable {
using AddressUpgradeable for address;
using SafeMathUpgradeable for uint256;
event OracleMinted(uint256 id, uint256 partId1, uint256 partId2, uint256 partId3, uint256 partId4);
event GuaranteedAllowListMintingFinished();
event RootSet(bytes32 root);
error NotAndERC721(address);
error InvalidSignature();
error SignatureAlreadyUsed();
error NotGenesisOwner();
error OracleMintingFinished();
error RootNotSet();
error RootAlreadySet();
error InvalidProof();
error AllowListFinished();
error NotAllSameRarity();
error NotAFullSet();
error TooManyValues();
error InvalidRarity();
BCGenesisToken public genesisToken;
BCOracleToken public oracleToken;
bytes32 public merkleRoot;
// Version 2
bool public phaseOneFinished;
mapping(uint256 => uint256) internal _rarityIndex;
function initialize(address genesis_, address oracle_) public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
if (!IERC165Upgradeable(genesis_).supportsInterface(type(IERC721Upgradeable).interfaceId)) revert NotAndERC721(genesis_);
if (!IERC165Upgradeable(oracle_).supportsInterface(type(IERC721Upgradeable).interfaceId)) revert NotAndERC721(oracle_);
genesisToken = BCGenesisToken(genesis_);
oracleToken = BCOracleToken(oracle_);
}
function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner {}
function setRoot(bytes32 root_) external onlyOwner {
// allows to update the root, if no genesis has been minted yet
if (genesisToken.totalSupply() > 0) revert RootAlreadySet();
merkleRoot = root_;
emit RootSet(root_);
}
function finishAllowListMinting() external onlyOwner {
phaseOneFinished = true;
emit GuaranteedAllowListMintingFinished();
}
function _encodeLeaf(address recipient, uint256 tokenId) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(recipient, tokenId));
}
function mintGenesis(uint256 tokenId, bytes32[] calldata proof) external {
if (merkleRoot == 0) revert RootNotSet();
if (phaseOneFinished) revert AllowListFinished();
if (!MerkleProofUpgradeable.verify(proof, merkleRoot, _encodeLeaf(_msgSender(), tokenId))) revert InvalidProof();
genesisToken.mint(_msgSender(), tokenId);
}
function _validateBodyParts(
uint256 partId1,
uint256 partId2,
uint256 partId3,
uint256 partId4
) internal view returns (IAttributes.Rarity) {
if (
genesisToken.ownerOf(partId1) != _msgSender() ||
genesisToken.ownerOf(partId2) != _msgSender() ||
genesisToken.ownerOf(partId3) != _msgSender() ||
genesisToken.ownerOf(partId4) != _msgSender()
) revert NotGenesisOwner();
return _validateQuadruples(partId1, partId2, partId3, partId4);
}
function mintOracle(
uint256 partId1,
uint256 partId2,
uint256 partId3,
uint256 partId4
) external {
if (oracleToken.totalSupply() >= 1000) revert OracleMintingFinished();
IAttributes.Rarity rarity = _validateBodyParts(partId1, partId2, partId3, partId4);
uint256 oracleId = oracleToken.mint(_msgSender(), rarity);
genesisToken.burnBatch([partId1, partId2, partId3, partId4]);
emit OracleMinted(oracleId, partId1, partId2, partId3, partId4);
}
function saveRarityIndex(uint256[] memory rarityIndex_) public onlyOwner {
for (uint256 i = 0; i < rarityIndex_.length; i++) {
_rarityIndex[i] = rarityIndex_[i];
}
}
function part(uint256 genesisId) public view returns (uint256) {
uint256 _rangeSize = 40;
uint256 base = (genesisId - 1) / _rangeSize;
uint256 diff = (base * _rangeSize);
genesisId -= diff;
uint256 factorInverse = 1;
for (uint256 i = 1; i <= _rangeSize; i++) {
if ((13 * i) % _rangeSize == 1) {
factorInverse = i;
break;
}
}
uint256 baseId = diff + ((((genesisId - 1 + _rangeSize - 17) % _rangeSize) * factorInverse) % _rangeSize) + 1;
return (((baseId - 1) % _rangeSize) / 10)**2;
}
function _validateQuadruples(
uint256 id1,
uint256 id2,
uint256 id3,
uint256 id4
) internal view returns (IAttributes.Rarity) {
uint256 rarity_ = _rarityByIndex(id1);
if (rarity_ != _rarityByIndex(id2) || rarity_ != _rarityByIndex(id3) || rarity_ != _rarityByIndex(id4)) {
revert NotAllSameRarity();
}
if (part(id1) + part(id2) + part(id3) + part(id4) != 14) {
revert NotAFullSet();
}
return IAttributes.Rarity(rarity_ - 1);
}
function encode(uint256[] memory arr) public pure returns (uint256) {
uint256 res;
if (arr.length > 77) revert TooManyValues();
for (uint256 i = 0; i < arr.length; i++) {
if (arr[i] > 4) revert InvalidRarity();
res += arr[i] * (10**i);
}
return res;
}
function _decode(uint256 encoded, uint256 index) public pure returns (uint256) {
uint256 val = encoded / (10**index);
return val % 10;
}
function _rarityByIndex(uint256 index_) internal view returns (uint256) {
uint256 elem = _rarityIndex[index_ / (40 * 77)];
uint256 onElem = index_ % (40 * 77);
uint256 remainder = onElem / 40;
return _decode(elem, remainder);
}
}