qvtx-developer-kit 1.0.0 → 1.2.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/.env.example +108 -0
- package/README.md +0 -0
- package/abis/QVTXBridge.json +273 -0
- package/abis/QVTXDirectPurchase.json +621 -0
- package/abis/QVTXGovernance.json +267 -0
- package/abis/QVTXNFT.json +370 -0
- package/abis/QVTXRewards.json +155 -0
- package/abis/QVTXToken.json +311 -0
- package/abis/QVTXVesting.json +216 -0
- package/abis/index.js +16 -0
- package/bin/qvtx-developer-cli.js +99 -0
- package/config/index.js +108 -0
- package/config/networks.js +247 -0
- package/examples/basic-usage.js +39 -0
- package/examples/bridge-example.js +123 -0
- package/examples/direct-purchase.js +300 -0
- package/examples/governance-example.js +140 -0
- package/examples/nft-example.js +141 -0
- package/examples/staking-example.js +96 -0
- package/index.js +145 -0
- package/languages/blockchain_ai_sdk.js +0 -0
- package/languages/node_sdk.js +0 -0
- package/languages/solana_sdk.js +383 -0
- package/package.json +30 -3
- package/purchase/index.js +567 -0
- package/rewards/index.js +71 -0
- package/smart-contracts/QVTXBridge.sol +305 -0
- package/smart-contracts/QVTXDirectPurchase.sol +543 -0
- package/smart-contracts/QVTXGovernance.sol +325 -0
- package/smart-contracts/QVTXNFT.sol +338 -0
- package/smart-contracts/QVTXRewards.sol +102 -0
- package/smart-contracts/QVTXToken.sol +227 -0
- package/smart-contracts/QVTXVesting.sol +411 -0
- package/smart-contracts/interfaces/IERC20.sol +14 -0
- package/smart-contracts/interfaces/IERC20Metadata.sol +8 -0
- package/smart-contracts/interfaces/IERC721.sol +18 -0
- package/smart-contracts/interfaces/IERC721Metadata.sol +8 -0
- package/smart-contracts/interfaces/IERC721Receiver.sol +11 -0
- package/storage/index.js +112 -0
- package/templates/contract/ERC20Token.sol +116 -0
- package/templates/dapp/index.html +93 -0
- package/test/index.js +182 -0
- package/tools/build-tool.js +63 -0
- package/tools/create-template.js +116 -0
- package/tools/deploy-tool.js +55 -0
- package/tools/generate-docs.js +149 -0
- package/tools/init-project.js +138 -0
- package/tools/run-tests.js +64 -0
- package/types/index.d.ts +386 -0
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
5
|
+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
6
|
+
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
7
|
+
import "@openzeppelin/contracts/security/Pausable.sol";
|
|
8
|
+
import "@openzeppelin/contracts/access/Ownable.sol";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @title QVTXDirectPurchase
|
|
12
|
+
* @dev Direct purchase contract for QVTX token ecosystem
|
|
13
|
+
* Allows users to buy QVTX directly with native currency (ETH/BNB/MATIC) or stablecoins
|
|
14
|
+
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - Multi-currency support (native + ERC20 stablecoins)
|
|
17
|
+
* - Dynamic pricing with admin controls
|
|
18
|
+
* - Purchase limits (min/max per transaction, per wallet)
|
|
19
|
+
* - Whitelist support for private sales
|
|
20
|
+
* - Vesting options for large purchases
|
|
21
|
+
* - Referral system with rewards
|
|
22
|
+
* - Emergency pause functionality
|
|
23
|
+
*/
|
|
24
|
+
contract QVTXDirectPurchase is ReentrancyGuard, Pausable, Ownable {
|
|
25
|
+
using SafeERC20 for IERC20;
|
|
26
|
+
|
|
27
|
+
// QVTX Token
|
|
28
|
+
IERC20 public immutable qvtxToken;
|
|
29
|
+
|
|
30
|
+
// Pricing (in wei per QVTX, 18 decimals)
|
|
31
|
+
uint256 public priceInNative; // Price in ETH/BNB/MATIC
|
|
32
|
+
mapping(address => uint256) public priceInToken; // Price in specific ERC20 (USDT, USDC, etc.)
|
|
33
|
+
|
|
34
|
+
// Supported payment tokens
|
|
35
|
+
address[] public supportedTokens;
|
|
36
|
+
mapping(address => bool) public isTokenSupported;
|
|
37
|
+
|
|
38
|
+
// Purchase limits
|
|
39
|
+
uint256 public minPurchaseAmount; // Minimum QVTX per purchase
|
|
40
|
+
uint256 public maxPurchaseAmount; // Maximum QVTX per purchase
|
|
41
|
+
uint256 public maxWalletPurchase; // Maximum total QVTX per wallet
|
|
42
|
+
uint256 public totalSaleCap; // Total QVTX available for sale
|
|
43
|
+
uint256 public totalSold; // Total QVTX sold
|
|
44
|
+
|
|
45
|
+
// Wallet tracking
|
|
46
|
+
mapping(address => uint256) public walletPurchased;
|
|
47
|
+
mapping(address => PurchaseRecord[]) public purchaseHistory;
|
|
48
|
+
|
|
49
|
+
// Whitelist (for private sales)
|
|
50
|
+
bool public whitelistEnabled;
|
|
51
|
+
mapping(address => bool) public isWhitelisted;
|
|
52
|
+
|
|
53
|
+
// Referral system
|
|
54
|
+
uint256 public referralRewardPercent; // Reward for referrer (basis points, 100 = 1%)
|
|
55
|
+
uint256 public referralDiscountPercent; // Discount for buyer using referral (basis points)
|
|
56
|
+
mapping(address => address) public referredBy;
|
|
57
|
+
mapping(address => uint256) public referralEarnings;
|
|
58
|
+
mapping(address => uint256) public referralCount;
|
|
59
|
+
|
|
60
|
+
// Vesting for large purchases
|
|
61
|
+
uint256 public vestingThreshold; // Amount above which vesting applies
|
|
62
|
+
uint256 public vestingDuration; // Vesting period in seconds
|
|
63
|
+
mapping(address => VestingSchedule[]) public vestingSchedules;
|
|
64
|
+
|
|
65
|
+
// Treasury
|
|
66
|
+
address public treasury;
|
|
67
|
+
|
|
68
|
+
// Structs
|
|
69
|
+
struct PurchaseRecord {
|
|
70
|
+
uint256 amount;
|
|
71
|
+
uint256 pricePaid;
|
|
72
|
+
address paymentToken; // address(0) for native currency
|
|
73
|
+
uint256 timestamp;
|
|
74
|
+
address referrer;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
struct VestingSchedule {
|
|
78
|
+
uint256 totalAmount;
|
|
79
|
+
uint256 released;
|
|
80
|
+
uint256 startTime;
|
|
81
|
+
uint256 duration;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Events
|
|
85
|
+
event TokensPurchased(
|
|
86
|
+
address indexed buyer,
|
|
87
|
+
uint256 qvtxAmount,
|
|
88
|
+
uint256 pricePaid,
|
|
89
|
+
address paymentToken,
|
|
90
|
+
address indexed referrer
|
|
91
|
+
);
|
|
92
|
+
event PriceUpdated(address token, uint256 newPrice);
|
|
93
|
+
event PaymentTokenAdded(address token, uint256 price);
|
|
94
|
+
event PaymentTokenRemoved(address token);
|
|
95
|
+
event ReferralReward(address indexed referrer, address indexed buyer, uint256 reward);
|
|
96
|
+
event VestingCreated(address indexed beneficiary, uint256 amount, uint256 duration);
|
|
97
|
+
event VestingReleased(address indexed beneficiary, uint256 amount);
|
|
98
|
+
event WhitelistUpdated(address indexed account, bool status);
|
|
99
|
+
event TreasuryUpdated(address newTreasury);
|
|
100
|
+
event FundsWithdrawn(address token, uint256 amount);
|
|
101
|
+
|
|
102
|
+
constructor(
|
|
103
|
+
address _qvtxToken,
|
|
104
|
+
address _treasury,
|
|
105
|
+
uint256 _priceInNative,
|
|
106
|
+
uint256 _totalSaleCap
|
|
107
|
+
) {
|
|
108
|
+
require(_qvtxToken != address(0), "Invalid QVTX token");
|
|
109
|
+
require(_treasury != address(0), "Invalid treasury");
|
|
110
|
+
|
|
111
|
+
qvtxToken = IERC20(_qvtxToken);
|
|
112
|
+
treasury = _treasury;
|
|
113
|
+
priceInNative = _priceInNative;
|
|
114
|
+
totalSaleCap = _totalSaleCap;
|
|
115
|
+
|
|
116
|
+
// Default limits
|
|
117
|
+
minPurchaseAmount = 100 * 10**18; // 100 QVTX minimum
|
|
118
|
+
maxPurchaseAmount = 10_000_000 * 10**18; // 10M QVTX maximum per tx
|
|
119
|
+
maxWalletPurchase = 100_000_000 * 10**18; // 100M QVTX max per wallet
|
|
120
|
+
|
|
121
|
+
// Default referral settings
|
|
122
|
+
referralRewardPercent = 500; // 5% to referrer
|
|
123
|
+
referralDiscountPercent = 200; // 2% discount for buyer
|
|
124
|
+
|
|
125
|
+
// Default vesting
|
|
126
|
+
vestingThreshold = 1_000_000 * 10**18; // 1M QVTX threshold
|
|
127
|
+
vestingDuration = 180 days; // 6 months vesting
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============ PURCHASE FUNCTIONS ============
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* @dev Purchase QVTX with native currency (ETH/BNB/MATIC)
|
|
134
|
+
* @param qvtxAmount Amount of QVTX to purchase
|
|
135
|
+
* @param referrer Optional referrer address
|
|
136
|
+
*/
|
|
137
|
+
function purchaseWithNative(
|
|
138
|
+
uint256 qvtxAmount,
|
|
139
|
+
address referrer
|
|
140
|
+
) external payable nonReentrant whenNotPaused {
|
|
141
|
+
_validatePurchase(msg.sender, qvtxAmount);
|
|
142
|
+
|
|
143
|
+
uint256 cost = calculateNativeCost(qvtxAmount, referrer);
|
|
144
|
+
require(msg.value >= cost, "Insufficient payment");
|
|
145
|
+
|
|
146
|
+
_processPurchase(msg.sender, qvtxAmount, cost, address(0), referrer);
|
|
147
|
+
|
|
148
|
+
// Refund excess
|
|
149
|
+
if (msg.value > cost) {
|
|
150
|
+
(bool refunded, ) = msg.sender.call{value: msg.value - cost}("");
|
|
151
|
+
require(refunded, "Refund failed");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* @dev Purchase QVTX with ERC20 token (USDT, USDC, etc.)
|
|
157
|
+
* @param paymentToken Address of the payment token
|
|
158
|
+
* @param qvtxAmount Amount of QVTX to purchase
|
|
159
|
+
* @param referrer Optional referrer address
|
|
160
|
+
*/
|
|
161
|
+
function purchaseWithToken(
|
|
162
|
+
address paymentToken,
|
|
163
|
+
uint256 qvtxAmount,
|
|
164
|
+
address referrer
|
|
165
|
+
) external nonReentrant whenNotPaused {
|
|
166
|
+
require(isTokenSupported[paymentToken], "Token not supported");
|
|
167
|
+
_validatePurchase(msg.sender, qvtxAmount);
|
|
168
|
+
|
|
169
|
+
uint256 cost = calculateTokenCost(paymentToken, qvtxAmount, referrer);
|
|
170
|
+
|
|
171
|
+
// Transfer payment
|
|
172
|
+
IERC20(paymentToken).safeTransferFrom(msg.sender, address(this), cost);
|
|
173
|
+
|
|
174
|
+
_processPurchase(msg.sender, qvtxAmount, cost, paymentToken, referrer);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @dev Internal purchase processing
|
|
179
|
+
*/
|
|
180
|
+
function _processPurchase(
|
|
181
|
+
address buyer,
|
|
182
|
+
uint256 qvtxAmount,
|
|
183
|
+
uint256 cost,
|
|
184
|
+
address paymentToken,
|
|
185
|
+
address referrer
|
|
186
|
+
) internal {
|
|
187
|
+
// Update state
|
|
188
|
+
totalSold += qvtxAmount;
|
|
189
|
+
walletPurchased[buyer] += qvtxAmount;
|
|
190
|
+
|
|
191
|
+
// Record purchase
|
|
192
|
+
purchaseHistory[buyer].push(PurchaseRecord({
|
|
193
|
+
amount: qvtxAmount,
|
|
194
|
+
pricePaid: cost,
|
|
195
|
+
paymentToken: paymentToken,
|
|
196
|
+
timestamp: block.timestamp,
|
|
197
|
+
referrer: referrer
|
|
198
|
+
}));
|
|
199
|
+
|
|
200
|
+
// Process referral
|
|
201
|
+
if (referrer != address(0) && referrer != buyer) {
|
|
202
|
+
_processReferral(buyer, referrer, qvtxAmount);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Check if vesting applies
|
|
206
|
+
if (qvtxAmount >= vestingThreshold && vestingDuration > 0) {
|
|
207
|
+
_createVesting(buyer, qvtxAmount);
|
|
208
|
+
} else {
|
|
209
|
+
// Direct transfer
|
|
210
|
+
qvtxToken.safeTransfer(buyer, qvtxAmount);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
emit TokensPurchased(buyer, qvtxAmount, cost, paymentToken, referrer);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @dev Process referral rewards
|
|
218
|
+
*/
|
|
219
|
+
function _processReferral(
|
|
220
|
+
address buyer,
|
|
221
|
+
address referrer,
|
|
222
|
+
uint256 qvtxAmount
|
|
223
|
+
) internal {
|
|
224
|
+
if (referredBy[buyer] == address(0)) {
|
|
225
|
+
referredBy[buyer] = referrer;
|
|
226
|
+
referralCount[referrer]++;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
uint256 reward = (qvtxAmount * referralRewardPercent) / 10000;
|
|
230
|
+
if (reward > 0 && qvtxToken.balanceOf(address(this)) >= reward) {
|
|
231
|
+
referralEarnings[referrer] += reward;
|
|
232
|
+
qvtxToken.safeTransfer(referrer, reward);
|
|
233
|
+
emit ReferralReward(referrer, buyer, reward);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* @dev Create vesting schedule for large purchases
|
|
239
|
+
*/
|
|
240
|
+
function _createVesting(address beneficiary, uint256 amount) internal {
|
|
241
|
+
vestingSchedules[beneficiary].push(VestingSchedule({
|
|
242
|
+
totalAmount: amount,
|
|
243
|
+
released: 0,
|
|
244
|
+
startTime: block.timestamp,
|
|
245
|
+
duration: vestingDuration
|
|
246
|
+
}));
|
|
247
|
+
|
|
248
|
+
emit VestingCreated(beneficiary, amount, vestingDuration);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ============ VESTING FUNCTIONS ============
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* @dev Release vested tokens
|
|
255
|
+
* @param scheduleIndex Index of the vesting schedule
|
|
256
|
+
*/
|
|
257
|
+
function releaseVested(uint256 scheduleIndex) external nonReentrant {
|
|
258
|
+
VestingSchedule storage schedule = vestingSchedules[msg.sender][scheduleIndex];
|
|
259
|
+
require(schedule.totalAmount > 0, "No vesting schedule");
|
|
260
|
+
|
|
261
|
+
uint256 releasable = _vestedAmount(schedule) - schedule.released;
|
|
262
|
+
require(releasable > 0, "Nothing to release");
|
|
263
|
+
|
|
264
|
+
schedule.released += releasable;
|
|
265
|
+
qvtxToken.safeTransfer(msg.sender, releasable);
|
|
266
|
+
|
|
267
|
+
emit VestingReleased(msg.sender, releasable);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* @dev Calculate vested amount
|
|
272
|
+
*/
|
|
273
|
+
function _vestedAmount(VestingSchedule memory schedule) internal view returns (uint256) {
|
|
274
|
+
if (block.timestamp < schedule.startTime) {
|
|
275
|
+
return 0;
|
|
276
|
+
} else if (block.timestamp >= schedule.startTime + schedule.duration) {
|
|
277
|
+
return schedule.totalAmount;
|
|
278
|
+
} else {
|
|
279
|
+
return (schedule.totalAmount * (block.timestamp - schedule.startTime)) / schedule.duration;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @dev Get releasable vested amount for a user
|
|
285
|
+
*/
|
|
286
|
+
function getReleasableAmount(address user, uint256 scheduleIndex) external view returns (uint256) {
|
|
287
|
+
VestingSchedule memory schedule = vestingSchedules[user][scheduleIndex];
|
|
288
|
+
return _vestedAmount(schedule) - schedule.released;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @dev Get all vesting schedules for a user
|
|
293
|
+
*/
|
|
294
|
+
function getVestingSchedules(address user) external view returns (VestingSchedule[] memory) {
|
|
295
|
+
return vestingSchedules[user];
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ============ PRICING FUNCTIONS ============
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* @dev Calculate cost in native currency
|
|
302
|
+
*/
|
|
303
|
+
function calculateNativeCost(uint256 qvtxAmount, address referrer) public view returns (uint256) {
|
|
304
|
+
uint256 cost = (qvtxAmount * priceInNative) / 10**18;
|
|
305
|
+
|
|
306
|
+
// Apply referral discount
|
|
307
|
+
if (referrer != address(0) && referrer != msg.sender && referralDiscountPercent > 0) {
|
|
308
|
+
cost = cost - (cost * referralDiscountPercent) / 10000;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return cost;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* @dev Calculate cost in ERC20 token
|
|
316
|
+
*/
|
|
317
|
+
function calculateTokenCost(
|
|
318
|
+
address paymentToken,
|
|
319
|
+
uint256 qvtxAmount,
|
|
320
|
+
address referrer
|
|
321
|
+
) public view returns (uint256) {
|
|
322
|
+
require(isTokenSupported[paymentToken], "Token not supported");
|
|
323
|
+
uint256 cost = (qvtxAmount * priceInToken[paymentToken]) / 10**18;
|
|
324
|
+
|
|
325
|
+
// Apply referral discount
|
|
326
|
+
if (referrer != address(0) && referrer != msg.sender && referralDiscountPercent > 0) {
|
|
327
|
+
cost = cost - (cost * referralDiscountPercent) / 10000;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return cost;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* @dev Get QVTX amount for native currency
|
|
335
|
+
*/
|
|
336
|
+
function getQVTXForNative(uint256 nativeAmount) external view returns (uint256) {
|
|
337
|
+
return (nativeAmount * 10**18) / priceInNative;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* @dev Get QVTX amount for ERC20 token
|
|
342
|
+
*/
|
|
343
|
+
function getQVTXForToken(address paymentToken, uint256 tokenAmount) external view returns (uint256) {
|
|
344
|
+
require(isTokenSupported[paymentToken], "Token not supported");
|
|
345
|
+
return (tokenAmount * 10**18) / priceInToken[paymentToken];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ============ VIEW FUNCTIONS ============
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* @dev Get purchase history for a wallet
|
|
352
|
+
*/
|
|
353
|
+
function getPurchaseHistory(address wallet) external view returns (PurchaseRecord[] memory) {
|
|
354
|
+
return purchaseHistory[wallet];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* @dev Get remaining allocation for a wallet
|
|
359
|
+
*/
|
|
360
|
+
function getRemainingAllocation(address wallet) external view returns (uint256) {
|
|
361
|
+
if (walletPurchased[wallet] >= maxWalletPurchase) return 0;
|
|
362
|
+
return maxWalletPurchase - walletPurchased[wallet];
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* @dev Get remaining tokens available for sale
|
|
367
|
+
*/
|
|
368
|
+
function getRemainingSupply() external view returns (uint256) {
|
|
369
|
+
if (totalSold >= totalSaleCap) return 0;
|
|
370
|
+
return totalSaleCap - totalSold;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @dev Get all supported payment tokens
|
|
375
|
+
*/
|
|
376
|
+
function getSupportedTokens() external view returns (address[] memory) {
|
|
377
|
+
return supportedTokens;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* @dev Check if purchase is valid
|
|
382
|
+
*/
|
|
383
|
+
function canPurchase(address buyer, uint256 amount) external view returns (bool, string memory) {
|
|
384
|
+
if (paused()) return (false, "Sale paused");
|
|
385
|
+
if (whitelistEnabled && !isWhitelisted[buyer]) return (false, "Not whitelisted");
|
|
386
|
+
if (amount < minPurchaseAmount) return (false, "Below minimum");
|
|
387
|
+
if (amount > maxPurchaseAmount) return (false, "Above maximum");
|
|
388
|
+
if (walletPurchased[buyer] + amount > maxWalletPurchase) return (false, "Wallet limit exceeded");
|
|
389
|
+
if (totalSold + amount > totalSaleCap) return (false, "Sale cap exceeded");
|
|
390
|
+
if (qvtxToken.balanceOf(address(this)) < amount) return (false, "Insufficient supply");
|
|
391
|
+
return (true, "");
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ============ VALIDATION ============
|
|
395
|
+
|
|
396
|
+
function _validatePurchase(address buyer, uint256 amount) internal view {
|
|
397
|
+
require(!whitelistEnabled || isWhitelisted[buyer], "Not whitelisted");
|
|
398
|
+
require(amount >= minPurchaseAmount, "Below minimum purchase");
|
|
399
|
+
require(amount <= maxPurchaseAmount, "Above maximum purchase");
|
|
400
|
+
require(walletPurchased[buyer] + amount <= maxWalletPurchase, "Wallet limit exceeded");
|
|
401
|
+
require(totalSold + amount <= totalSaleCap, "Sale cap exceeded");
|
|
402
|
+
require(qvtxToken.balanceOf(address(this)) >= amount, "Insufficient supply");
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ============ ADMIN FUNCTIONS ============
|
|
406
|
+
|
|
407
|
+
function setNativePrice(uint256 newPrice) external onlyOwner {
|
|
408
|
+
priceInNative = newPrice;
|
|
409
|
+
emit PriceUpdated(address(0), newPrice);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function addPaymentToken(address token, uint256 price) external onlyOwner {
|
|
413
|
+
require(token != address(0), "Invalid token");
|
|
414
|
+
require(!isTokenSupported[token], "Already supported");
|
|
415
|
+
|
|
416
|
+
supportedTokens.push(token);
|
|
417
|
+
isTokenSupported[token] = true;
|
|
418
|
+
priceInToken[token] = price;
|
|
419
|
+
|
|
420
|
+
emit PaymentTokenAdded(token, price);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function updateTokenPrice(address token, uint256 newPrice) external onlyOwner {
|
|
424
|
+
require(isTokenSupported[token], "Token not supported");
|
|
425
|
+
priceInToken[token] = newPrice;
|
|
426
|
+
emit PriceUpdated(token, newPrice);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function removePaymentToken(address token) external onlyOwner {
|
|
430
|
+
require(isTokenSupported[token], "Token not supported");
|
|
431
|
+
|
|
432
|
+
isTokenSupported[token] = false;
|
|
433
|
+
priceInToken[token] = 0;
|
|
434
|
+
|
|
435
|
+
// Remove from array
|
|
436
|
+
for (uint i = 0; i < supportedTokens.length; i++) {
|
|
437
|
+
if (supportedTokens[i] == token) {
|
|
438
|
+
supportedTokens[i] = supportedTokens[supportedTokens.length - 1];
|
|
439
|
+
supportedTokens.pop();
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
emit PaymentTokenRemoved(token);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function setPurchaseLimits(
|
|
448
|
+
uint256 _min,
|
|
449
|
+
uint256 _max,
|
|
450
|
+
uint256 _maxWallet,
|
|
451
|
+
uint256 _totalCap
|
|
452
|
+
) external onlyOwner {
|
|
453
|
+
minPurchaseAmount = _min;
|
|
454
|
+
maxPurchaseAmount = _max;
|
|
455
|
+
maxWalletPurchase = _maxWallet;
|
|
456
|
+
totalSaleCap = _totalCap;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function setReferralSettings(uint256 reward, uint256 discount) external onlyOwner {
|
|
460
|
+
require(reward <= 2000, "Reward too high"); // Max 20%
|
|
461
|
+
require(discount <= 1000, "Discount too high"); // Max 10%
|
|
462
|
+
referralRewardPercent = reward;
|
|
463
|
+
referralDiscountPercent = discount;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function setVestingSettings(uint256 threshold, uint256 duration) external onlyOwner {
|
|
467
|
+
vestingThreshold = threshold;
|
|
468
|
+
vestingDuration = duration;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function setWhitelistEnabled(bool enabled) external onlyOwner {
|
|
472
|
+
whitelistEnabled = enabled;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function updateWhitelist(address[] calldata accounts, bool status) external onlyOwner {
|
|
476
|
+
for (uint i = 0; i < accounts.length; i++) {
|
|
477
|
+
isWhitelisted[accounts[i]] = status;
|
|
478
|
+
emit WhitelistUpdated(accounts[i], status);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function setTreasury(address newTreasury) external onlyOwner {
|
|
483
|
+
require(newTreasury != address(0), "Invalid treasury");
|
|
484
|
+
treasury = newTreasury;
|
|
485
|
+
emit TreasuryUpdated(newTreasury);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function pause() external onlyOwner {
|
|
489
|
+
_pause();
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function unpause() external onlyOwner {
|
|
493
|
+
_unpause();
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// ============ WITHDRAWAL FUNCTIONS ============
|
|
497
|
+
|
|
498
|
+
function withdrawNative() external onlyOwner {
|
|
499
|
+
uint256 balance = address(this).balance;
|
|
500
|
+
require(balance > 0, "No balance");
|
|
501
|
+
|
|
502
|
+
(bool sent, ) = treasury.call{value: balance}("");
|
|
503
|
+
require(sent, "Transfer failed");
|
|
504
|
+
|
|
505
|
+
emit FundsWithdrawn(address(0), balance);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function withdrawToken(address token) external onlyOwner {
|
|
509
|
+
uint256 balance = IERC20(token).balanceOf(address(this));
|
|
510
|
+
require(balance > 0, "No balance");
|
|
511
|
+
|
|
512
|
+
// Don't withdraw QVTX sale supply
|
|
513
|
+
if (token == address(qvtxToken)) {
|
|
514
|
+
uint256 reserved = totalSaleCap - totalSold;
|
|
515
|
+
require(balance > reserved, "Cannot withdraw sale supply");
|
|
516
|
+
balance = balance - reserved;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
IERC20(token).safeTransfer(treasury, balance);
|
|
520
|
+
|
|
521
|
+
emit FundsWithdrawn(token, balance);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function withdrawUnsoldQVTX() external onlyOwner {
|
|
525
|
+
uint256 balance = qvtxToken.balanceOf(address(this));
|
|
526
|
+
qvtxToken.safeTransfer(treasury, balance);
|
|
527
|
+
totalSaleCap = totalSold; // Close sale
|
|
528
|
+
|
|
529
|
+
emit FundsWithdrawn(address(qvtxToken), balance);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Emergency function
|
|
533
|
+
function emergencyWithdraw(address token) external onlyOwner {
|
|
534
|
+
if (token == address(0)) {
|
|
535
|
+
(bool sent, ) = treasury.call{value: address(this).balance}("");
|
|
536
|
+
require(sent, "Transfer failed");
|
|
537
|
+
} else {
|
|
538
|
+
IERC20(token).safeTransfer(treasury, IERC20(token).balanceOf(address(this)));
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
receive() external payable {}
|
|
543
|
+
}
|