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,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
|
+
};
|
package/rewards/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Rewards Module
|
|
5
|
+
* Cross-chain rewards distribution utilities
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
class QVTXRewards {
|
|
9
|
+
constructor(web3, contractAddress, abi) {
|
|
10
|
+
this.web3 = web3;
|
|
11
|
+
this.contractAddress = contractAddress;
|
|
12
|
+
this.abi = abi || require('./QVTXRewardsABI.json');
|
|
13
|
+
this.contract = null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async init() {
|
|
17
|
+
if (!this.web3) throw new Error('Web3 instance required');
|
|
18
|
+
this.contract = new this.web3.eth.Contract(this.abi, this.contractAddress);
|
|
19
|
+
return this;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async getStake(address) {
|
|
23
|
+
return await this.contract.methods.stakes(address).call();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getEarned(address) {
|
|
27
|
+
return await this.contract.methods.earned(address).call();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async getTotalStaked() {
|
|
31
|
+
return await this.contract.methods.totalStaked().call();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async getRewardRate() {
|
|
35
|
+
return await this.contract.methods.rewardRate().call();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async stake(amount, fromAddress, privateKey) {
|
|
39
|
+
const tx = this.contract.methods.stake(amount);
|
|
40
|
+
return await this._sendTransaction(tx, fromAddress, privateKey);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async withdraw(amount, fromAddress, privateKey) {
|
|
44
|
+
const tx = this.contract.methods.withdraw(amount);
|
|
45
|
+
return await this._sendTransaction(tx, fromAddress, privateKey);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async claimReward(fromAddress, privateKey) {
|
|
49
|
+
const tx = this.contract.methods.claimReward();
|
|
50
|
+
return await this._sendTransaction(tx, fromAddress, privateKey);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async _sendTransaction(tx, fromAddress, privateKey) {
|
|
54
|
+
const gas = await tx.estimateGas({ from: fromAddress });
|
|
55
|
+
const data = tx.encodeABI();
|
|
56
|
+
const nonce = await this.web3.eth.getTransactionCount(fromAddress);
|
|
57
|
+
const gasPrice = await this.web3.eth.getGasPrice();
|
|
58
|
+
|
|
59
|
+
const signedTx = await this.web3.eth.accounts.signTransaction({
|
|
60
|
+
to: this.contractAddress,
|
|
61
|
+
data,
|
|
62
|
+
gas: Math.ceil(gas * 1.2),
|
|
63
|
+
gasPrice,
|
|
64
|
+
nonce
|
|
65
|
+
}, privateKey);
|
|
66
|
+
|
|
67
|
+
return await this.web3.eth.sendSignedTransaction(signedTx.rawTransaction);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = QVTXRewards;
|