gn-contract 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.openzeppelin/bsc-testnet.json +1847 -0
- package/.prettierrc +15 -0
- package/README.md +14 -0
- package/constants/getter.ts +4 -0
- package/constants/rootAddress.ts +22 -0
- package/contracts/CROWD.sol +32 -0
- package/contracts/CUSDT.sol +32 -0
- package/contracts/NFT.sol +499 -0
- package/contracts/NFTFounder.sol +254 -0
- package/contracts/NFTGenesis.sol +337 -0
- package/contracts/Network.sol +503 -0
- package/contracts/Swap.sol +86 -0
- package/contracts/USDT.sol +32 -0
- package/contracts/lib/AccessControl.sol +35 -0
- package/contracts/lib/NFTGetter.sol +60 -0
- package/contracts/lib/NFTHelpers.sol +111 -0
- package/contracts/lib/ValhallaBlackList.sol +18 -0
- package/contracts/lib/ValhallaPool.sol +44 -0
- package/hardhat.config.ts +44 -0
- package/package.json +50 -0
- package/scripts/DeployNetwork.ts +123 -0
- package/scripts/DeploySwap.ts +57 -0
- package/scripts/DeployUSDT.ts +16 -0
- package/test/Swap.test.ts +182 -0
- package/test/lib/initializer.ts +193 -0
- package/test/nft_genesis.test.ts +399 -0
- package/test/nft_purchase.test.ts +210 -0
- package/test/rank_distribution.test.ts +142 -0
- package/test/registration.test.ts +267 -0
- package/test/registration_reward.test.ts +114 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
|
|
5
|
+
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
6
|
+
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
|
|
7
|
+
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
|
|
8
|
+
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
9
|
+
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
10
|
+
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
|
|
11
|
+
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
|
|
12
|
+
|
|
13
|
+
import "./USDT.sol";
|
|
14
|
+
import "./lib/NFTGetter.sol";
|
|
15
|
+
import "./NFT.sol";
|
|
16
|
+
import "./Network.sol";
|
|
17
|
+
|
|
18
|
+
contract NFTFounder is
|
|
19
|
+
Initializable,
|
|
20
|
+
ERC721Upgradeable,
|
|
21
|
+
OwnableUpgradeable,
|
|
22
|
+
ERC721EnumerableUpgradeable,
|
|
23
|
+
ERC721URIStorageUpgradeable,
|
|
24
|
+
UUPSUpgradeable
|
|
25
|
+
{
|
|
26
|
+
using CountersUpgradeable for CountersUpgradeable.Counter;
|
|
27
|
+
|
|
28
|
+
CountersUpgradeable.Counter private _tokenIdCounter;
|
|
29
|
+
CountersUpgradeable.Counter private _cardIdCounter;
|
|
30
|
+
USDT public usdtERC20;
|
|
31
|
+
Network public valhalla;
|
|
32
|
+
|
|
33
|
+
CardFounder public cardFounder;
|
|
34
|
+
mapping(address => CardsRewardFounder) public nftFounder;
|
|
35
|
+
uint public nftFounderPoolMarker;
|
|
36
|
+
address public receiverAddress;
|
|
37
|
+
address public receiverAddress2;
|
|
38
|
+
uint256 public nftFounderPool;
|
|
39
|
+
|
|
40
|
+
// events
|
|
41
|
+
event BuyNFTs(address indexed _from, uint[] _tokenIds);
|
|
42
|
+
event ClaimReward(address indexed _from, uint value);
|
|
43
|
+
event DistributeRewards(address indexed from, uint value);
|
|
44
|
+
|
|
45
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
46
|
+
constructor() {
|
|
47
|
+
_disableInitializers();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function initialize(
|
|
51
|
+
USDT _usdtERC20,
|
|
52
|
+
Network _valhalla,
|
|
53
|
+
address _receiver
|
|
54
|
+
) public initializer {
|
|
55
|
+
__ERC721_init("NFTFounder", "NFTF");
|
|
56
|
+
__ERC721Enumerable_init();
|
|
57
|
+
__Ownable_init();
|
|
58
|
+
__UUPSUpgradeable_init();
|
|
59
|
+
valhalla = _valhalla;
|
|
60
|
+
receiverAddress = _receiver;
|
|
61
|
+
usdtERC20 = _usdtERC20;
|
|
62
|
+
|
|
63
|
+
cardFounder.totalMinted = 0;
|
|
64
|
+
_cardIdCounter.increment();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
modifier onlyValhalla() {
|
|
68
|
+
require(msg.sender == address(valhalla), "Only Admin can perform action");
|
|
69
|
+
_;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function setFounderProps(uint _price, uint _maxMinted) public {
|
|
73
|
+
cardFounder.price = _price * 10 ** usdtERC20.decimals();
|
|
74
|
+
cardFounder.maxMinted = _maxMinted;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function myNftRewards(address _address) public view returns (uint) {
|
|
78
|
+
CardsRewardFounder memory nftCards = nftFounder[_address];
|
|
79
|
+
uint poolMarker = nftFounderPoolMarker;
|
|
80
|
+
|
|
81
|
+
if (!(poolMarker > nftCards.currentRewards)) return 0;
|
|
82
|
+
return (poolMarker - nftCards.currentRewards) * nftCards.ownedNfts;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function buyNFTs(uint amount) public {
|
|
86
|
+
bool isRegistered;
|
|
87
|
+
address referrer;
|
|
88
|
+
(isRegistered, , , referrer, , , , ) = valhalla.accountMap(msg.sender);
|
|
89
|
+
|
|
90
|
+
uint8[3] memory distributedValue = [5, 3, 2];
|
|
91
|
+
|
|
92
|
+
require(isRegistered, "Not registered yet");
|
|
93
|
+
|
|
94
|
+
uint[] memory tokenIds = new uint[](amount);
|
|
95
|
+
// usdtERC20.transferFrom(msg.sender, receiverAddress, cardFounder.price * amount);
|
|
96
|
+
|
|
97
|
+
require(
|
|
98
|
+
cardFounder.maxMinted >= cardFounder.totalMinted + amount,
|
|
99
|
+
"Max minted reached"
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
for (uint i = 0; i < 3; i++) {
|
|
103
|
+
if (referrer == address(0)) {
|
|
104
|
+
usdtERC20.transferFrom(
|
|
105
|
+
msg.sender,
|
|
106
|
+
receiverAddress,
|
|
107
|
+
(distributedValue[i] * cardFounder.price * amount) / 100
|
|
108
|
+
);
|
|
109
|
+
} else {
|
|
110
|
+
usdtERC20.transferFrom(
|
|
111
|
+
msg.sender,
|
|
112
|
+
referrer,
|
|
113
|
+
(distributedValue[i] * cardFounder.price * amount) / 100
|
|
114
|
+
);
|
|
115
|
+
(, , , referrer, , , , ) = valhalla.accountMap(referrer);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
usdtERC20.transferFrom(
|
|
120
|
+
msg.sender,
|
|
121
|
+
receiverAddress,
|
|
122
|
+
((36 * cardFounder.price) / 100) * amount
|
|
123
|
+
);
|
|
124
|
+
usdtERC20.transferFrom(
|
|
125
|
+
msg.sender,
|
|
126
|
+
receiverAddress2,
|
|
127
|
+
((54 * cardFounder.price) / 100) * amount
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
for (uint i = 0; i < amount; i++) {
|
|
131
|
+
uint256 tokenId = _tokenIdCounter.current();
|
|
132
|
+
tokenIds[i] = tokenId;
|
|
133
|
+
_tokenIdCounter.increment();
|
|
134
|
+
_safeMint(msg.sender, tokenId);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
uint rewards = myNftRewards(msg.sender);
|
|
138
|
+
if (rewards > 0) claimRewards();
|
|
139
|
+
|
|
140
|
+
CardsRewardFounder storage nftCards = nftFounder[msg.sender];
|
|
141
|
+
nftCards.ownedNfts += amount;
|
|
142
|
+
cardFounder.totalMinted += amount;
|
|
143
|
+
nftCards.currentRewards = nftFounderPoolMarker;
|
|
144
|
+
|
|
145
|
+
emit BuyNFTs(msg.sender, tokenIds);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function claimRewards() public {
|
|
149
|
+
uint rewards = myNftRewards(msg.sender);
|
|
150
|
+
require(rewards > 0, "No rewards yet");
|
|
151
|
+
|
|
152
|
+
CardsRewardFounder storage nftCards = nftFounder[msg.sender];
|
|
153
|
+
nftCards.currentRewards = nftFounderPoolMarker;
|
|
154
|
+
nftFounderPool -= rewards;
|
|
155
|
+
|
|
156
|
+
usdtERC20.transfer(msg.sender, rewards);
|
|
157
|
+
emit ClaimReward(msg.sender, rewards);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function setReceiverAddress2(address _receiver) public onlyOwner {
|
|
161
|
+
receiverAddress2 = _receiver;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function distributeFounderRewards(uint value) external onlyValhalla {
|
|
165
|
+
// Always add value to pool so it's tracked
|
|
166
|
+
nftFounderPool += value;
|
|
167
|
+
|
|
168
|
+
// Only distribute per-NFT rewards if NFTs have been minted
|
|
169
|
+
// This prevents division by zero and accumulates rewards for future distribution
|
|
170
|
+
if (cardFounder.totalMinted == 0) return;
|
|
171
|
+
|
|
172
|
+
nftFounderPoolMarker += value / cardFounder.totalMinted;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function safeMint(address to) public onlyOwner {
|
|
176
|
+
uint256 tokenId = _tokenIdCounter.current();
|
|
177
|
+
_tokenIdCounter.increment();
|
|
178
|
+
_safeMint(to, tokenId);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function _authorizeUpgrade(
|
|
182
|
+
address newImplementation
|
|
183
|
+
) internal override onlyOwner {}
|
|
184
|
+
|
|
185
|
+
// The following functions are overrides required by Solidity.
|
|
186
|
+
|
|
187
|
+
function _beforeTokenTransfer(
|
|
188
|
+
address from,
|
|
189
|
+
address to,
|
|
190
|
+
uint256 tokenId,
|
|
191
|
+
uint256 batchSize
|
|
192
|
+
) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
|
|
193
|
+
super._beforeTokenTransfer(from, to, tokenId, batchSize);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function _burn(
|
|
197
|
+
uint256 tokenId
|
|
198
|
+
) internal override(ERC721Upgradeable, ERC721URIStorageUpgradeable) {
|
|
199
|
+
super._burn(tokenId);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function tokenURI(
|
|
203
|
+
uint256 tokenId
|
|
204
|
+
)
|
|
205
|
+
public
|
|
206
|
+
view
|
|
207
|
+
override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
|
|
208
|
+
returns (string memory)
|
|
209
|
+
{
|
|
210
|
+
return super.tokenURI(tokenId);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function supportsInterface(
|
|
214
|
+
bytes4 interfaceId
|
|
215
|
+
)
|
|
216
|
+
public
|
|
217
|
+
view
|
|
218
|
+
override(
|
|
219
|
+
ERC721EnumerableUpgradeable,
|
|
220
|
+
ERC721URIStorageUpgradeable,
|
|
221
|
+
ERC721Upgradeable
|
|
222
|
+
)
|
|
223
|
+
returns (bool)
|
|
224
|
+
{
|
|
225
|
+
return super.supportsInterface(interfaceId);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function _baseURI() internal pure override returns (string memory) {
|
|
229
|
+
return "https://crowdstrike.club/api/image/founder/";
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function transferFrom(
|
|
233
|
+
address from,
|
|
234
|
+
address to,
|
|
235
|
+
uint256 tokenId
|
|
236
|
+
) public override(ERC721Upgradeable, IERC721Upgradeable) {
|
|
237
|
+
super.transferFrom(from, to, tokenId);
|
|
238
|
+
|
|
239
|
+
CardsRewardFounder storage cardSender = nftFounder[from];
|
|
240
|
+
|
|
241
|
+
uint rewards = myNftRewards(from);
|
|
242
|
+
if (rewards > 0) {
|
|
243
|
+
cardSender.currentRewards = nftFounderPoolMarker;
|
|
244
|
+
nftFounderPool -= rewards;
|
|
245
|
+
usdtERC20.transfer(msg.sender, rewards);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
require(cardSender.ownedNfts > 0, "no card left");
|
|
249
|
+
cardSender.ownedNfts -= 1;
|
|
250
|
+
CardsRewardFounder storage cardReceiver = nftFounder[to];
|
|
251
|
+
cardReceiver.ownedNfts += 1;
|
|
252
|
+
cardReceiver.currentRewards = nftFounderPoolMarker;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.17;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
|
|
5
|
+
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
|
6
|
+
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
|
|
7
|
+
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
|
|
8
|
+
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
|
9
|
+
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
|
10
|
+
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
|
|
11
|
+
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
|
|
12
|
+
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
|
|
13
|
+
import "./lib/NFTGetter.sol";
|
|
14
|
+
import "./NFT.sol";
|
|
15
|
+
import "./CROWD.sol";
|
|
16
|
+
import "./CUSDT.sol";
|
|
17
|
+
import "./Network.sol";
|
|
18
|
+
|
|
19
|
+
contract NFTGenesis is
|
|
20
|
+
Initializable,
|
|
21
|
+
ERC721Upgradeable,
|
|
22
|
+
OwnableUpgradeable,
|
|
23
|
+
ERC721EnumerableUpgradeable,
|
|
24
|
+
ERC721URIStorageUpgradeable,
|
|
25
|
+
UUPSUpgradeable,
|
|
26
|
+
ReentrancyGuardUpgradeable
|
|
27
|
+
{
|
|
28
|
+
using CountersUpgradeable for CountersUpgradeable.Counter;
|
|
29
|
+
using StringsUpgradeable for uint256;
|
|
30
|
+
|
|
31
|
+
CountersUpgradeable.Counter private _tokenIdCounter;
|
|
32
|
+
CountersUpgradeable.Counter private _cardIdCounter;
|
|
33
|
+
CROWD public gnetERC20;
|
|
34
|
+
CUSDT public usdtERC20;
|
|
35
|
+
Network public valhalla;
|
|
36
|
+
NFT valhallaNFT;
|
|
37
|
+
|
|
38
|
+
// define token id
|
|
39
|
+
mapping(uint => uint) public tokenIdToCardId;
|
|
40
|
+
mapping(uint => CardGenesis) public cardMap;
|
|
41
|
+
// address -> nftType -> flagRewards
|
|
42
|
+
mapping(address => mapping(uint => CardsRewardGenesis)) public nftGenesis;
|
|
43
|
+
// nftType -> poolMarker
|
|
44
|
+
mapping(uint => uint) public nftGenesisPoolMarker;
|
|
45
|
+
mapping(address => uint) public genesisReferralRewardMap;
|
|
46
|
+
address public receiverAddress;
|
|
47
|
+
uint256 public nftGenesisPool;
|
|
48
|
+
|
|
49
|
+
// events
|
|
50
|
+
event BuyMultipleNFT(address indexed _from, uint[] _tokenIds);
|
|
51
|
+
event ClaimReward(address indexed _from, uint value);
|
|
52
|
+
event DistributeRewards(address indexed from, uint value);
|
|
53
|
+
event GenesisReferralReward(
|
|
54
|
+
address indexed referrer,
|
|
55
|
+
address indexed buyer,
|
|
56
|
+
uint value
|
|
57
|
+
);
|
|
58
|
+
event GenesisClaimReferralReward(address indexed referrer, uint value);
|
|
59
|
+
event NFTTypeAdded(
|
|
60
|
+
uint indexed cardId,
|
|
61
|
+
uint price,
|
|
62
|
+
uint percentage,
|
|
63
|
+
uint maxMinted
|
|
64
|
+
);
|
|
65
|
+
event GenesisPoolDistributed(
|
|
66
|
+
uint indexed typePool,
|
|
67
|
+
uint value,
|
|
68
|
+
uint newMarker
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
/// @custom:oz-upgrades-unsafe-allow constructor
|
|
72
|
+
constructor() {
|
|
73
|
+
_disableInitializers();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function initialize(
|
|
77
|
+
CROWD _gnetERC20,
|
|
78
|
+
CUSDT _usdtERC20,
|
|
79
|
+
NFT _valhallaNFT,
|
|
80
|
+
Network _valhalla,
|
|
81
|
+
address _receiver
|
|
82
|
+
) public initializer {
|
|
83
|
+
require(address(_gnetERC20) != address(0), "Invalid GNET address");
|
|
84
|
+
require(address(_usdtERC20) != address(0), "Invalid USDT address");
|
|
85
|
+
require(address(_valhallaNFT) != address(0), "Invalid NFT address");
|
|
86
|
+
require(address(_valhalla) != address(0), "Invalid Network address");
|
|
87
|
+
require(_receiver != address(0), "Invalid receiver address");
|
|
88
|
+
|
|
89
|
+
__ERC721_init("NFTGenesis", "NFTG");
|
|
90
|
+
__ERC721Enumerable_init();
|
|
91
|
+
__ERC721URIStorage_init();
|
|
92
|
+
__Ownable_init();
|
|
93
|
+
__UUPSUpgradeable_init();
|
|
94
|
+
__ReentrancyGuard_init();
|
|
95
|
+
gnetERC20 = _gnetERC20;
|
|
96
|
+
valhallaNFT = _valhallaNFT;
|
|
97
|
+
receiverAddress = _receiver;
|
|
98
|
+
usdtERC20 = _usdtERC20;
|
|
99
|
+
valhalla = _valhalla;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
modifier onlyValhallaNFT() {
|
|
103
|
+
require(
|
|
104
|
+
msg.sender == address(valhallaNFT),
|
|
105
|
+
"Only Admin can perform action"
|
|
106
|
+
);
|
|
107
|
+
_;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function amountNftTypes() public view returns (uint) {
|
|
111
|
+
return _cardIdCounter.current();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function percentageByNftType(uint nftType) public view returns (uint) {
|
|
115
|
+
return cardMap[nftType].genesisPercentage;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function addNft(
|
|
119
|
+
uint _price,
|
|
120
|
+
uint _percentage,
|
|
121
|
+
uint _maxMinted
|
|
122
|
+
) public onlyOwner {
|
|
123
|
+
require(_price > 0, "Price must be greater than 0");
|
|
124
|
+
require(_percentage > 0 && _percentage <= 100, "Invalid percentage");
|
|
125
|
+
require(_maxMinted > 0, "Max minted must be greater than 0");
|
|
126
|
+
|
|
127
|
+
uint _cardId = _cardIdCounter.current();
|
|
128
|
+
CardGenesis storage _card = cardMap[_cardId];
|
|
129
|
+
_card.price = _price * 10 ** usdtERC20.decimals();
|
|
130
|
+
_card.genesisPercentage = _percentage;
|
|
131
|
+
_card.totalMinted = 0;
|
|
132
|
+
_card.maxMinted = _maxMinted;
|
|
133
|
+
_cardIdCounter.increment();
|
|
134
|
+
|
|
135
|
+
emit NFTTypeAdded(_cardId, _card.price, _percentage, _maxMinted);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function myNftRewards(
|
|
139
|
+
uint cardId,
|
|
140
|
+
address _address
|
|
141
|
+
) public view returns (uint) {
|
|
142
|
+
CardsRewardGenesis memory nftCards = nftGenesis[_address][cardId];
|
|
143
|
+
uint poolMarker = nftGenesisPoolMarker[cardId];
|
|
144
|
+
|
|
145
|
+
if (!(poolMarker > nftCards.currentRewards)) return 0;
|
|
146
|
+
return (poolMarker - nftCards.currentRewards) * nftCards.ownedNfts;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function buyMultipleNFT(uint cardId, uint amount) public nonReentrant {
|
|
150
|
+
// 1. CHECKS - All validations first
|
|
151
|
+
CardGenesis storage _card = cardMap[cardId];
|
|
152
|
+
require(_card.maxMinted > _card.totalMinted + amount, "Max minted reached");
|
|
153
|
+
|
|
154
|
+
bool isRegistered;
|
|
155
|
+
address referrer;
|
|
156
|
+
(isRegistered, , , referrer, , , , ) = valhalla.accountMap(msg.sender);
|
|
157
|
+
require(isRegistered, "Registration required");
|
|
158
|
+
|
|
159
|
+
uint totalPrice = _card.price * amount;
|
|
160
|
+
uint commission = (totalPrice * 10) / 100;
|
|
161
|
+
uint toReceiver = totalPrice - commission;
|
|
162
|
+
|
|
163
|
+
// Calculate rewards before state change
|
|
164
|
+
uint rewards = myNftRewards(cardId, msg.sender);
|
|
165
|
+
|
|
166
|
+
// 2. EFFECTS - Update state BEFORE external calls
|
|
167
|
+
CardsRewardGenesis storage nftCards = nftGenesis[msg.sender][cardId];
|
|
168
|
+
nftCards.ownedNfts += amount;
|
|
169
|
+
_card.totalMinted += amount;
|
|
170
|
+
nftCards.currentRewards = nftGenesisPoolMarker[cardId];
|
|
171
|
+
|
|
172
|
+
// Prepare token IDs for minting
|
|
173
|
+
uint[] memory tokenIds = new uint[](amount);
|
|
174
|
+
for (uint i = 0; i < amount; i++) {
|
|
175
|
+
uint256 tokenId = _tokenIdCounter.current();
|
|
176
|
+
tokenIds[i] = tokenId;
|
|
177
|
+
tokenIdToCardId[tokenId] = cardId;
|
|
178
|
+
_tokenIdCounter.increment();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// 3. INTERACTIONS - External calls last
|
|
182
|
+
usdtERC20.transferFrom(msg.sender, address(this), totalPrice);
|
|
183
|
+
usdtERC20.transfer(receiverAddress, toReceiver);
|
|
184
|
+
|
|
185
|
+
if (referrer != address(0)) {
|
|
186
|
+
genesisReferralRewardMap[referrer] += commission;
|
|
187
|
+
emit GenesisReferralReward(referrer, msg.sender, commission);
|
|
188
|
+
} else {
|
|
189
|
+
usdtERC20.transfer(receiverAddress, commission);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Mint NFTs
|
|
193
|
+
for (uint i = 0; i < amount; i++) {
|
|
194
|
+
_safeMint(msg.sender, tokenIds[i]);
|
|
195
|
+
_setTokenURI(tokenIds[i], cardId.toString());
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Claim rewards if available
|
|
199
|
+
if (rewards > 0) _distributeReward(msg.sender, rewards);
|
|
200
|
+
|
|
201
|
+
emit BuyMultipleNFT(msg.sender, tokenIds);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function claimRewards(uint cardId) public nonReentrant {
|
|
205
|
+
uint rewards = myNftRewards(cardId, msg.sender);
|
|
206
|
+
require(rewards > 0, "No rewards yet");
|
|
207
|
+
|
|
208
|
+
CardsRewardGenesis storage nftCards = nftGenesis[msg.sender][cardId];
|
|
209
|
+
nftCards.currentRewards = nftGenesisPoolMarker[cardId];
|
|
210
|
+
|
|
211
|
+
_distributeReward(msg.sender, rewards);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function _distributeReward(address to, uint amount) private {
|
|
215
|
+
require(nftGenesisPool >= amount, "Insufficient pool balance");
|
|
216
|
+
nftGenesisPool -= amount;
|
|
217
|
+
gnetERC20.transfer(to, amount);
|
|
218
|
+
emit ClaimReward(to, amount);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function claimReferralReward() public nonReentrant {
|
|
222
|
+
uint reward = genesisReferralRewardMap[msg.sender];
|
|
223
|
+
require(reward > 0, "No referral reward to claim");
|
|
224
|
+
|
|
225
|
+
genesisReferralRewardMap[msg.sender] = 0;
|
|
226
|
+
usdtERC20.transfer(msg.sender, reward);
|
|
227
|
+
|
|
228
|
+
emit GenesisClaimReferralReward(msg.sender, reward);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function distributeGenesisRewards(
|
|
232
|
+
uint typePool,
|
|
233
|
+
uint value
|
|
234
|
+
) external onlyValhallaNFT {
|
|
235
|
+
uint totalMinted = cardMap[typePool].totalMinted;
|
|
236
|
+
require(totalMinted > 0, "No NFTs minted for this type");
|
|
237
|
+
|
|
238
|
+
uint perNftReward = value / totalMinted;
|
|
239
|
+
nftGenesisPoolMarker[typePool] += perNftReward;
|
|
240
|
+
nftGenesisPool += value;
|
|
241
|
+
|
|
242
|
+
emit GenesisPoolDistributed(
|
|
243
|
+
typePool,
|
|
244
|
+
value,
|
|
245
|
+
nftGenesisPoolMarker[typePool]
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function _baseURI() internal pure override returns (string memory) {
|
|
250
|
+
return "https://globalnetwork.finance/api/image/genesis/";
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function safeMint(address to) public onlyOwner {
|
|
254
|
+
uint256 tokenId = _tokenIdCounter.current();
|
|
255
|
+
_tokenIdCounter.increment();
|
|
256
|
+
_safeMint(to, tokenId);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function _authorizeUpgrade(
|
|
260
|
+
address newImplementation
|
|
261
|
+
) internal override onlyOwner {}
|
|
262
|
+
|
|
263
|
+
// The following functions are overrides required by Solidity.
|
|
264
|
+
|
|
265
|
+
function _beforeTokenTransfer(
|
|
266
|
+
address from,
|
|
267
|
+
address to,
|
|
268
|
+
uint256 tokenId,
|
|
269
|
+
uint256 batchSize
|
|
270
|
+
) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) {
|
|
271
|
+
super._beforeTokenTransfer(from, to, tokenId, batchSize);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function _burn(
|
|
275
|
+
uint256 tokenId
|
|
276
|
+
) internal override(ERC721Upgradeable, ERC721URIStorageUpgradeable) {
|
|
277
|
+
super._burn(tokenId);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function tokenURI(
|
|
281
|
+
uint256 tokenId
|
|
282
|
+
)
|
|
283
|
+
public
|
|
284
|
+
view
|
|
285
|
+
override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
|
|
286
|
+
returns (string memory)
|
|
287
|
+
{
|
|
288
|
+
return super.tokenURI(tokenId);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function transferFrom(
|
|
292
|
+
address from,
|
|
293
|
+
address to,
|
|
294
|
+
uint256 tokenId
|
|
295
|
+
) public override(ERC721Upgradeable, IERC721Upgradeable) nonReentrant {
|
|
296
|
+
// Get card info and calculate rewards before any state changes
|
|
297
|
+
uint cardId = tokenIdToCardId[tokenId];
|
|
298
|
+
CardsRewardGenesis storage cardSender = nftGenesis[from][cardId];
|
|
299
|
+
uint rewards = myNftRewards(cardId, from);
|
|
300
|
+
|
|
301
|
+
// Update state for sender
|
|
302
|
+
if (rewards > 0) {
|
|
303
|
+
cardSender.currentRewards = nftGenesisPoolMarker[cardId];
|
|
304
|
+
nftGenesisPool -= rewards;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
require(cardSender.ownedNfts > 0, "no card left");
|
|
308
|
+
cardSender.ownedNfts -= 1;
|
|
309
|
+
|
|
310
|
+
// Update state for receiver
|
|
311
|
+
CardsRewardGenesis storage cardReceiver = nftGenesis[to][cardId];
|
|
312
|
+
cardReceiver.ownedNfts += 1;
|
|
313
|
+
cardReceiver.currentRewards = nftGenesisPoolMarker[cardId];
|
|
314
|
+
|
|
315
|
+
// External calls last
|
|
316
|
+
if (rewards > 0) {
|
|
317
|
+
gnetERC20.transfer(from, rewards); // Send to 'from', not msg.sender
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
super.transferFrom(from, to, tokenId);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function supportsInterface(
|
|
324
|
+
bytes4 interfaceId
|
|
325
|
+
)
|
|
326
|
+
public
|
|
327
|
+
view
|
|
328
|
+
override(
|
|
329
|
+
ERC721EnumerableUpgradeable,
|
|
330
|
+
ERC721URIStorageUpgradeable,
|
|
331
|
+
ERC721Upgradeable
|
|
332
|
+
)
|
|
333
|
+
returns (bool)
|
|
334
|
+
{
|
|
335
|
+
return super.supportsInterface(interfaceId);
|
|
336
|
+
}
|
|
337
|
+
}
|