qvtx-developer-kit 1.1.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.
@@ -0,0 +1,567 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * QVTX Direct Purchase Module
5
+ * SDK for interacting with QVTXDirectPurchase contract
6
+ *
7
+ * Features:
8
+ * - Buy QVTX with native currency (ETH/BNB/MATIC)
9
+ * - Buy QVTX with stablecoins (USDT, USDC, DAI)
10
+ * - Referral system integration
11
+ * - Vesting schedule management
12
+ * - Purchase history tracking
13
+ */
14
+
15
+ const { ethers } = require('ethers');
16
+ const DirectPurchaseABI = require('../abis/QVTXDirectPurchase.json').abi;
17
+
18
+ // Deployed contract addresses per network
19
+ const PURCHASE_CONTRACTS = {
20
+ ethereum: {
21
+ mainnet: null, // Deploy and add address
22
+ goerli: null,
23
+ sepolia: null
24
+ },
25
+ bsc: {
26
+ mainnet: null, // Deploy and add address
27
+ testnet: null
28
+ },
29
+ polygon: {
30
+ mainnet: null, // Deploy and add address
31
+ mumbai: null
32
+ },
33
+ arbitrum: {
34
+ mainnet: null,
35
+ goerli: null
36
+ },
37
+ avalanche: {
38
+ mainnet: null,
39
+ fuji: null
40
+ }
41
+ };
42
+
43
+ // Common stablecoin addresses
44
+ const STABLECOINS = {
45
+ ethereum: {
46
+ USDT: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
47
+ USDC: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
48
+ DAI: '0x6B175474E89094C44Da98b954EescdeCB5BE3830'
49
+ },
50
+ bsc: {
51
+ USDT: '0x55d398326f99059fF775485246999027B3197955',
52
+ USDC: '0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d',
53
+ BUSD: '0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56'
54
+ },
55
+ polygon: {
56
+ USDT: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F',
57
+ USDC: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
58
+ DAI: '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063'
59
+ }
60
+ };
61
+
62
+ class QVTXDirectPurchase {
63
+ /**
64
+ * Create a new QVTXDirectPurchase instance
65
+ * @param {Object} options Configuration options
66
+ * @param {string|ethers.Provider} options.provider - RPC URL or ethers provider
67
+ * @param {string} options.contractAddress - DirectPurchase contract address
68
+ * @param {string} [options.privateKey] - Private key for transactions
69
+ * @param {ethers.Signer} [options.signer] - Ethers signer (alternative to privateKey)
70
+ */
71
+ constructor(options = {}) {
72
+ if (!options.contractAddress && !options.network) {
73
+ throw new Error('Contract address or network required');
74
+ }
75
+
76
+ // Setup provider
77
+ if (typeof options.provider === 'string') {
78
+ this.provider = new ethers.providers.JsonRpcProvider(options.provider);
79
+ } else if (options.provider) {
80
+ this.provider = options.provider;
81
+ } else {
82
+ throw new Error('Provider required');
83
+ }
84
+
85
+ // Setup signer
86
+ if (options.signer) {
87
+ this.signer = options.signer;
88
+ } else if (options.privateKey) {
89
+ this.signer = new ethers.Wallet(options.privateKey, this.provider);
90
+ }
91
+
92
+ // Get contract address
93
+ if (options.contractAddress) {
94
+ this.contractAddress = options.contractAddress;
95
+ } else if (options.network) {
96
+ const [network, env] = options.network.split(':');
97
+ this.contractAddress = PURCHASE_CONTRACTS[network]?.[env || 'mainnet'];
98
+ if (!this.contractAddress) {
99
+ throw new Error(`No contract deployed on ${options.network}`);
100
+ }
101
+ }
102
+
103
+ // Initialize contract
104
+ this.contract = new ethers.Contract(
105
+ this.contractAddress,
106
+ DirectPurchaseABI,
107
+ this.signer || this.provider
108
+ );
109
+ }
110
+
111
+ // ============ READ FUNCTIONS ============
112
+
113
+ /**
114
+ * Get current price in native currency
115
+ * @returns {Promise<string>} Price in wei
116
+ */
117
+ async getPriceInNative() {
118
+ const price = await this.contract.priceInNative();
119
+ return ethers.utils.formatEther(price);
120
+ }
121
+
122
+ /**
123
+ * Get price for a specific payment token
124
+ * @param {string} tokenAddress Payment token address
125
+ * @returns {Promise<string>} Price per QVTX in token units
126
+ */
127
+ async getPriceInToken(tokenAddress) {
128
+ const price = await this.contract.priceInToken(tokenAddress);
129
+ return price.toString();
130
+ }
131
+
132
+ /**
133
+ * Calculate cost for purchasing QVTX with native currency
134
+ * @param {string|number} qvtxAmount Amount of QVTX to buy
135
+ * @param {string} [referrer] Optional referrer address
136
+ * @returns {Promise<string>} Cost in native currency (ETH/BNB/MATIC)
137
+ */
138
+ async calculateNativeCost(qvtxAmount, referrer = ethers.constants.AddressZero) {
139
+ const amount = ethers.utils.parseEther(qvtxAmount.toString());
140
+ const cost = await this.contract.calculateNativeCost(amount, referrer);
141
+ return ethers.utils.formatEther(cost);
142
+ }
143
+
144
+ /**
145
+ * Calculate cost for purchasing QVTX with ERC20 token
146
+ * @param {string} tokenAddress Payment token address
147
+ * @param {string|number} qvtxAmount Amount of QVTX to buy
148
+ * @param {string} [referrer] Optional referrer address
149
+ * @returns {Promise<string>} Cost in token units
150
+ */
151
+ async calculateTokenCost(tokenAddress, qvtxAmount, referrer = ethers.constants.AddressZero) {
152
+ const amount = ethers.utils.parseEther(qvtxAmount.toString());
153
+ const cost = await this.contract.calculateTokenCost(tokenAddress, amount, referrer);
154
+ return cost.toString();
155
+ }
156
+
157
+ /**
158
+ * Get QVTX amount for native currency
159
+ * @param {string|number} nativeAmount Amount of native currency
160
+ * @returns {Promise<string>} QVTX amount
161
+ */
162
+ async getQVTXForNative(nativeAmount) {
163
+ const amount = ethers.utils.parseEther(nativeAmount.toString());
164
+ const qvtx = await this.contract.getQVTXForNative(amount);
165
+ return ethers.utils.formatEther(qvtx);
166
+ }
167
+
168
+ /**
169
+ * Get QVTX amount for ERC20 token
170
+ * @param {string} tokenAddress Payment token address
171
+ * @param {string|number} tokenAmount Amount of tokens
172
+ * @returns {Promise<string>} QVTX amount
173
+ */
174
+ async getQVTXForToken(tokenAddress, tokenAmount) {
175
+ const qvtx = await this.contract.getQVTXForToken(tokenAddress, tokenAmount);
176
+ return ethers.utils.formatEther(qvtx);
177
+ }
178
+
179
+ /**
180
+ * Check if a purchase is valid
181
+ * @param {string} buyer Buyer address
182
+ * @param {string|number} amount QVTX amount
183
+ * @returns {Promise<{valid: boolean, reason: string}>}
184
+ */
185
+ async canPurchase(buyer, amount) {
186
+ const amountWei = ethers.utils.parseEther(amount.toString());
187
+ const [valid, reason] = await this.contract.canPurchase(buyer, amountWei);
188
+ return { valid, reason };
189
+ }
190
+
191
+ /**
192
+ * Get purchase limits
193
+ * @returns {Promise<Object>} Purchase limits
194
+ */
195
+ async getPurchaseLimits() {
196
+ const [min, max, maxWallet, totalCap, totalSold] = await Promise.all([
197
+ this.contract.minPurchaseAmount(),
198
+ this.contract.maxPurchaseAmount(),
199
+ this.contract.maxWalletPurchase(),
200
+ this.contract.totalSaleCap(),
201
+ this.contract.totalSold()
202
+ ]);
203
+
204
+ return {
205
+ minPurchase: ethers.utils.formatEther(min),
206
+ maxPurchase: ethers.utils.formatEther(max),
207
+ maxWalletPurchase: ethers.utils.formatEther(maxWallet),
208
+ totalSaleCap: ethers.utils.formatEther(totalCap),
209
+ totalSold: ethers.utils.formatEther(totalSold),
210
+ remainingSupply: ethers.utils.formatEther(totalCap.sub(totalSold))
211
+ };
212
+ }
213
+
214
+ /**
215
+ * Get wallet purchase info
216
+ * @param {string} wallet Wallet address
217
+ * @returns {Promise<Object>} Wallet purchase info
218
+ */
219
+ async getWalletInfo(wallet) {
220
+ const [purchased, maxWallet, history] = await Promise.all([
221
+ this.contract.walletPurchased(wallet),
222
+ this.contract.maxWalletPurchase(),
223
+ this.contract.getPurchaseHistory(wallet)
224
+ ]);
225
+
226
+ return {
227
+ totalPurchased: ethers.utils.formatEther(purchased),
228
+ remainingAllocation: ethers.utils.formatEther(maxWallet.sub(purchased)),
229
+ purchaseCount: history.length,
230
+ purchases: history.map(p => ({
231
+ amount: ethers.utils.formatEther(p.amount),
232
+ pricePaid: p.pricePaid.toString(),
233
+ paymentToken: p.paymentToken,
234
+ timestamp: new Date(p.timestamp.toNumber() * 1000),
235
+ referrer: p.referrer
236
+ }))
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Get referral info for a wallet
242
+ * @param {string} wallet Wallet address
243
+ * @returns {Promise<Object>} Referral info
244
+ */
245
+ async getReferralInfo(wallet) {
246
+ const [earnings, count, referredBy, rewardPercent, discountPercent] = await Promise.all([
247
+ this.contract.referralEarnings(wallet),
248
+ this.contract.referralCount(wallet),
249
+ this.contract.referredBy(wallet),
250
+ this.contract.referralRewardPercent(),
251
+ this.contract.referralDiscountPercent()
252
+ ]);
253
+
254
+ return {
255
+ totalEarnings: ethers.utils.formatEther(earnings),
256
+ referralCount: count.toNumber(),
257
+ referredBy: referredBy === ethers.constants.AddressZero ? null : referredBy,
258
+ rewardPercent: rewardPercent.toNumber() / 100,
259
+ discountPercent: discountPercent.toNumber() / 100
260
+ };
261
+ }
262
+
263
+ /**
264
+ * Get vesting schedules for a wallet
265
+ * @param {string} wallet Wallet address
266
+ * @returns {Promise<Array>} Vesting schedules
267
+ */
268
+ async getVestingSchedules(wallet) {
269
+ const schedules = await this.contract.getVestingSchedules(wallet);
270
+ return schedules.map((s, index) => ({
271
+ index,
272
+ totalAmount: ethers.utils.formatEther(s.totalAmount),
273
+ released: ethers.utils.formatEther(s.released),
274
+ remaining: ethers.utils.formatEther(s.totalAmount.sub(s.released)),
275
+ startTime: new Date(s.startTime.toNumber() * 1000),
276
+ endTime: new Date((s.startTime.toNumber() + s.duration.toNumber()) * 1000),
277
+ duration: s.duration.toNumber() / 86400, // days
278
+ percentVested: s.totalAmount.gt(0)
279
+ ? (s.released.mul(10000).div(s.totalAmount).toNumber() / 100)
280
+ : 0
281
+ }));
282
+ }
283
+
284
+ /**
285
+ * Get supported payment tokens
286
+ * @returns {Promise<string[]>} Array of token addresses
287
+ */
288
+ async getSupportedTokens() {
289
+ return await this.contract.getSupportedTokens();
290
+ }
291
+
292
+ /**
293
+ * Check if sale is paused
294
+ * @returns {Promise<boolean>}
295
+ */
296
+ async isPaused() {
297
+ return await this.contract.paused();
298
+ }
299
+
300
+ /**
301
+ * Check if whitelist is enabled
302
+ * @returns {Promise<boolean>}
303
+ */
304
+ async isWhitelistEnabled() {
305
+ return await this.contract.whitelistEnabled();
306
+ }
307
+
308
+ /**
309
+ * Check if address is whitelisted
310
+ * @param {string} address Address to check
311
+ * @returns {Promise<boolean>}
312
+ */
313
+ async isWhitelisted(address) {
314
+ return await this.contract.isWhitelisted(address);
315
+ }
316
+
317
+ // ============ PURCHASE FUNCTIONS ============
318
+
319
+ /**
320
+ * Purchase QVTX with native currency
321
+ * @param {string|number} qvtxAmount Amount of QVTX to buy
322
+ * @param {Object} [options] Purchase options
323
+ * @param {string} [options.referrer] Referrer address for discount
324
+ * @param {Object} [options.overrides] Transaction overrides
325
+ * @returns {Promise<ethers.ContractTransaction>}
326
+ */
327
+ async purchaseWithNative(qvtxAmount, options = {}) {
328
+ if (!this.signer) throw new Error('Signer required for transactions');
329
+
330
+ const amount = ethers.utils.parseEther(qvtxAmount.toString());
331
+ const referrer = options.referrer || ethers.constants.AddressZero;
332
+
333
+ // Calculate cost
334
+ const cost = await this.contract.calculateNativeCost(amount, referrer);
335
+
336
+ // Add small buffer for price fluctuation
337
+ const value = cost.mul(101).div(100);
338
+
339
+ const tx = await this.contract.purchaseWithNative(amount, referrer, {
340
+ value,
341
+ ...options.overrides
342
+ });
343
+
344
+ return tx;
345
+ }
346
+
347
+ /**
348
+ * Purchase QVTX with ERC20 token
349
+ * @param {string} tokenAddress Payment token address
350
+ * @param {string|number} qvtxAmount Amount of QVTX to buy
351
+ * @param {Object} [options] Purchase options
352
+ * @param {string} [options.referrer] Referrer address for discount
353
+ * @param {boolean} [options.approve] Auto-approve token spending
354
+ * @param {Object} [options.overrides] Transaction overrides
355
+ * @returns {Promise<ethers.ContractTransaction>}
356
+ */
357
+ async purchaseWithToken(tokenAddress, qvtxAmount, options = {}) {
358
+ if (!this.signer) throw new Error('Signer required for transactions');
359
+
360
+ const amount = ethers.utils.parseEther(qvtxAmount.toString());
361
+ const referrer = options.referrer || ethers.constants.AddressZero;
362
+
363
+ // Calculate cost
364
+ const cost = await this.contract.calculateTokenCost(tokenAddress, amount, referrer);
365
+
366
+ // Auto-approve if requested
367
+ if (options.approve !== false) {
368
+ const token = new ethers.Contract(
369
+ tokenAddress,
370
+ ['function approve(address,uint256) returns (bool)', 'function allowance(address,address) view returns (uint256)'],
371
+ this.signer
372
+ );
373
+
374
+ const allowance = await token.allowance(await this.signer.getAddress(), this.contractAddress);
375
+ if (allowance.lt(cost)) {
376
+ const approveTx = await token.approve(this.contractAddress, ethers.constants.MaxUint256);
377
+ await approveTx.wait();
378
+ }
379
+ }
380
+
381
+ const tx = await this.contract.purchaseWithToken(tokenAddress, amount, referrer, {
382
+ ...options.overrides
383
+ });
384
+
385
+ return tx;
386
+ }
387
+
388
+ /**
389
+ * Release vested tokens
390
+ * @param {number} scheduleIndex Index of vesting schedule
391
+ * @param {Object} [overrides] Transaction overrides
392
+ * @returns {Promise<ethers.ContractTransaction>}
393
+ */
394
+ async releaseVested(scheduleIndex, overrides = {}) {
395
+ if (!this.signer) throw new Error('Signer required for transactions');
396
+ return await this.contract.releaseVested(scheduleIndex, overrides);
397
+ }
398
+
399
+ // ============ ADMIN FUNCTIONS ============
400
+
401
+ /**
402
+ * Set price in native currency (owner only)
403
+ * @param {string|number} price New price in ETH/BNB/MATIC per QVTX
404
+ * @returns {Promise<ethers.ContractTransaction>}
405
+ */
406
+ async setNativePrice(price) {
407
+ if (!this.signer) throw new Error('Signer required');
408
+ const priceWei = ethers.utils.parseEther(price.toString());
409
+ return await this.contract.setNativePrice(priceWei);
410
+ }
411
+
412
+ /**
413
+ * Add payment token (owner only)
414
+ * @param {string} tokenAddress Token address
415
+ * @param {string|number} price Price per QVTX in token units
416
+ * @returns {Promise<ethers.ContractTransaction>}
417
+ */
418
+ async addPaymentToken(tokenAddress, price) {
419
+ if (!this.signer) throw new Error('Signer required');
420
+ return await this.contract.addPaymentToken(tokenAddress, price);
421
+ }
422
+
423
+ /**
424
+ * Update token price (owner only)
425
+ * @param {string} tokenAddress Token address
426
+ * @param {string|number} price New price per QVTX
427
+ * @returns {Promise<ethers.ContractTransaction>}
428
+ */
429
+ async updateTokenPrice(tokenAddress, price) {
430
+ if (!this.signer) throw new Error('Signer required');
431
+ return await this.contract.updateTokenPrice(tokenAddress, price);
432
+ }
433
+
434
+ /**
435
+ * Set purchase limits (owner only)
436
+ * @param {Object} limits Limit configuration
437
+ * @returns {Promise<ethers.ContractTransaction>}
438
+ */
439
+ async setPurchaseLimits(limits) {
440
+ if (!this.signer) throw new Error('Signer required');
441
+ return await this.contract.setPurchaseLimits(
442
+ ethers.utils.parseEther(limits.min?.toString() || '100'),
443
+ ethers.utils.parseEther(limits.max?.toString() || '10000000'),
444
+ ethers.utils.parseEther(limits.maxWallet?.toString() || '100000000'),
445
+ ethers.utils.parseEther(limits.totalCap?.toString() || '1000000000')
446
+ );
447
+ }
448
+
449
+ /**
450
+ * Update whitelist (owner only)
451
+ * @param {string[]} addresses Addresses to update
452
+ * @param {boolean} status Whitelist status
453
+ * @returns {Promise<ethers.ContractTransaction>}
454
+ */
455
+ async updateWhitelist(addresses, status) {
456
+ if (!this.signer) throw new Error('Signer required');
457
+ return await this.contract.updateWhitelist(addresses, status);
458
+ }
459
+
460
+ /**
461
+ * Pause sale (owner only)
462
+ * @returns {Promise<ethers.ContractTransaction>}
463
+ */
464
+ async pause() {
465
+ if (!this.signer) throw new Error('Signer required');
466
+ return await this.contract.pause();
467
+ }
468
+
469
+ /**
470
+ * Unpause sale (owner only)
471
+ * @returns {Promise<ethers.ContractTransaction>}
472
+ */
473
+ async unpause() {
474
+ if (!this.signer) throw new Error('Signer required');
475
+ return await this.contract.unpause();
476
+ }
477
+
478
+ /**
479
+ * Withdraw native currency to treasury (owner only)
480
+ * @returns {Promise<ethers.ContractTransaction>}
481
+ */
482
+ async withdrawNative() {
483
+ if (!this.signer) throw new Error('Signer required');
484
+ return await this.contract.withdrawNative();
485
+ }
486
+
487
+ /**
488
+ * Withdraw tokens to treasury (owner only)
489
+ * @param {string} tokenAddress Token to withdraw
490
+ * @returns {Promise<ethers.ContractTransaction>}
491
+ */
492
+ async withdrawToken(tokenAddress) {
493
+ if (!this.signer) throw new Error('Signer required');
494
+ return await this.contract.withdrawToken(tokenAddress);
495
+ }
496
+
497
+ // ============ UTILITY FUNCTIONS ============
498
+
499
+ /**
500
+ * Generate referral link
501
+ * @param {string} referrerAddress Referrer wallet address
502
+ * @param {string} baseUrl Base URL for the purchase page
503
+ * @returns {string} Referral link
504
+ */
505
+ generateReferralLink(referrerAddress, baseUrl = 'https://quantvestrix.com/buy') {
506
+ return `${baseUrl}?ref=${referrerAddress}`;
507
+ }
508
+
509
+ /**
510
+ * Parse referral code from URL
511
+ * @param {string} url URL with referral parameter
512
+ * @returns {string|null} Referrer address or null
513
+ */
514
+ parseReferralCode(url) {
515
+ try {
516
+ const parsed = new URL(url);
517
+ const ref = parsed.searchParams.get('ref');
518
+ if (ref && ethers.utils.isAddress(ref)) {
519
+ return ref;
520
+ }
521
+ } catch {}
522
+ return null;
523
+ }
524
+
525
+ /**
526
+ * Format purchase summary
527
+ * @param {string|number} qvtxAmount QVTX amount to buy
528
+ * @param {string} [referrer] Optional referrer
529
+ * @returns {Promise<Object>} Purchase summary
530
+ */
531
+ async getPurchaseSummary(qvtxAmount, referrer = null) {
532
+ const amount = ethers.utils.parseEther(qvtxAmount.toString());
533
+ const refAddr = referrer || ethers.constants.AddressZero;
534
+
535
+ const [nativeCost, limits, discountPercent] = await Promise.all([
536
+ this.contract.calculateNativeCost(amount, refAddr),
537
+ this.getPurchaseLimits(),
538
+ this.contract.referralDiscountPercent()
539
+ ]);
540
+
541
+ const discount = referrer ? discountPercent.toNumber() / 100 : 0;
542
+
543
+ return {
544
+ qvtxAmount: qvtxAmount.toString(),
545
+ nativeCost: ethers.utils.formatEther(nativeCost),
546
+ referrer: referrer || 'None',
547
+ discount: `${discount}%`,
548
+ remainingSupply: limits.remainingSupply,
549
+ withinLimits: parseFloat(qvtxAmount) >= parseFloat(limits.minPurchase) &&
550
+ parseFloat(qvtxAmount) <= parseFloat(limits.maxPurchase)
551
+ };
552
+ }
553
+ }
554
+
555
+ // Factory functions
556
+ function createDirectPurchase(options) {
557
+ return new QVTXDirectPurchase(options);
558
+ }
559
+
560
+ // Export
561
+ module.exports = {
562
+ QVTXDirectPurchase,
563
+ createDirectPurchase,
564
+ PURCHASE_CONTRACTS,
565
+ STABLECOINS,
566
+ DirectPurchaseABI
567
+ };