qvtx-developer-kit 1.0.0 → 1.1.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/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 +15 -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/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 +28 -3
- package/rewards/index.js +71 -0
- package/smart-contracts/QVTXBridge.sol +305 -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 +264 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title QVTX ERC20 Token Template
|
|
6
|
+
* @dev Standard ERC20 implementation with minting and burning
|
|
7
|
+
*/
|
|
8
|
+
contract ERC20Token {
|
|
9
|
+
string public name;
|
|
10
|
+
string public symbol;
|
|
11
|
+
uint8 public constant decimals = 18;
|
|
12
|
+
uint256 public totalSupply;
|
|
13
|
+
address public owner;
|
|
14
|
+
|
|
15
|
+
mapping(address => uint256) private _balances;
|
|
16
|
+
mapping(address => mapping(address => uint256)) private _allowances;
|
|
17
|
+
|
|
18
|
+
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
19
|
+
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
20
|
+
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
|
21
|
+
|
|
22
|
+
modifier onlyOwner() {
|
|
23
|
+
require(msg.sender == owner, "ERC20: caller is not the owner");
|
|
24
|
+
_;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
constructor(string memory _name, string memory _symbol, uint256 _initialSupply) {
|
|
28
|
+
name = _name;
|
|
29
|
+
symbol = _symbol;
|
|
30
|
+
owner = msg.sender;
|
|
31
|
+
_mint(msg.sender, _initialSupply * 10 ** decimals);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function balanceOf(address account) public view returns (uint256) {
|
|
35
|
+
return _balances[account];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function transfer(address to, uint256 amount) public returns (bool) {
|
|
39
|
+
_transfer(msg.sender, to, amount);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function allowance(address tokenOwner, address spender) public view returns (uint256) {
|
|
44
|
+
return _allowances[tokenOwner][spender];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function approve(address spender, uint256 amount) public returns (bool) {
|
|
48
|
+
_approve(msg.sender, spender, amount);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
|
|
53
|
+
uint256 currentAllowance = _allowances[from][msg.sender];
|
|
54
|
+
require(currentAllowance >= amount, "ERC20: insufficient allowance");
|
|
55
|
+
unchecked {
|
|
56
|
+
_approve(from, msg.sender, currentAllowance - amount);
|
|
57
|
+
}
|
|
58
|
+
_transfer(from, to, amount);
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function mint(address to, uint256 amount) public onlyOwner {
|
|
63
|
+
_mint(to, amount);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function burn(uint256 amount) public {
|
|
67
|
+
_burn(msg.sender, amount);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function transferOwnership(address newOwner) public onlyOwner {
|
|
71
|
+
require(newOwner != address(0), "ERC20: new owner is zero address");
|
|
72
|
+
emit OwnershipTransferred(owner, newOwner);
|
|
73
|
+
owner = newOwner;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function _transfer(address from, address to, uint256 amount) internal {
|
|
77
|
+
require(from != address(0), "ERC20: transfer from zero address");
|
|
78
|
+
require(to != address(0), "ERC20: transfer to zero address");
|
|
79
|
+
require(_balances[from] >= amount, "ERC20: insufficient balance");
|
|
80
|
+
|
|
81
|
+
unchecked {
|
|
82
|
+
_balances[from] -= amount;
|
|
83
|
+
_balances[to] += amount;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
emit Transfer(from, to, amount);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function _mint(address account, uint256 amount) internal {
|
|
90
|
+
require(account != address(0), "ERC20: mint to zero address");
|
|
91
|
+
totalSupply += amount;
|
|
92
|
+
unchecked {
|
|
93
|
+
_balances[account] += amount;
|
|
94
|
+
}
|
|
95
|
+
emit Transfer(address(0), account, amount);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function _burn(address account, uint256 amount) internal {
|
|
99
|
+
require(account != address(0), "ERC20: burn from zero address");
|
|
100
|
+
require(_balances[account] >= amount, "ERC20: burn exceeds balance");
|
|
101
|
+
|
|
102
|
+
unchecked {
|
|
103
|
+
_balances[account] -= amount;
|
|
104
|
+
totalSupply -= amount;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
emit Transfer(account, address(0), amount);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function _approve(address tokenOwner, address spender, uint256 amount) internal {
|
|
111
|
+
require(tokenOwner != address(0), "ERC20: approve from zero address");
|
|
112
|
+
require(spender != address(0), "ERC20: approve to zero address");
|
|
113
|
+
_allowances[tokenOwner][spender] = amount;
|
|
114
|
+
emit Approval(tokenOwner, spender, amount);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>QVTX DApp Template</title>
|
|
7
|
+
<script src="https://cdn.jsdelivr.net/npm/web3@1.8.2/dist/web3.min.js"></script>
|
|
8
|
+
<style>
|
|
9
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
10
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0a0a0a; color: #fff; min-height: 100vh; }
|
|
11
|
+
.container { max-width: 800px; margin: 0 auto; padding: 40px 20px; }
|
|
12
|
+
h1 { font-size: 2.5rem; margin-bottom: 10px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
|
13
|
+
.subtitle { color: #888; margin-bottom: 40px; }
|
|
14
|
+
.card { background: #111; border-radius: 12px; padding: 24px; margin-bottom: 20px; border: 1px solid #222; }
|
|
15
|
+
.card h2 { font-size: 1.2rem; margin-bottom: 16px; color: #667eea; }
|
|
16
|
+
button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #fff; border: none; padding: 12px 24px; border-radius: 8px; cursor: pointer; font-size: 1rem; transition: transform 0.2s; }
|
|
17
|
+
button:hover { transform: scale(1.02); }
|
|
18
|
+
button:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
19
|
+
.account { font-family: monospace; font-size: 0.9rem; color: #888; word-break: break-all; }
|
|
20
|
+
.balance { font-size: 2rem; font-weight: bold; margin: 10px 0; }
|
|
21
|
+
.network { display: inline-block; padding: 4px 12px; background: #222; border-radius: 20px; font-size: 0.8rem; color: #667eea; }
|
|
22
|
+
</style>
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div class="container">
|
|
26
|
+
<h1>QVTX DApp</h1>
|
|
27
|
+
<p class="subtitle">Built with QuantVestrix Developer Kit</p>
|
|
28
|
+
|
|
29
|
+
<div class="card">
|
|
30
|
+
<h2>Wallet Connection</h2>
|
|
31
|
+
<button id="connectBtn" onclick="connectWallet()">Connect Wallet</button>
|
|
32
|
+
<div id="walletInfo" style="display:none; margin-top: 20px;">
|
|
33
|
+
<p class="account" id="account"></p>
|
|
34
|
+
<p class="balance" id="balance">0.00 ETH</p>
|
|
35
|
+
<span class="network" id="network"></span>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div class="card">
|
|
40
|
+
<h2>QVTX Token</h2>
|
|
41
|
+
<p>Name: QuantVestrix</p>
|
|
42
|
+
<p>Symbol: QVTX</p>
|
|
43
|
+
<p>Total Supply: 1,000,000,000</p>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<script>
|
|
48
|
+
let web3;
|
|
49
|
+
let account;
|
|
50
|
+
|
|
51
|
+
async function connectWallet() {
|
|
52
|
+
if (typeof window.ethereum === 'undefined') {
|
|
53
|
+
alert('Please install MetaMask!');
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
document.getElementById('connectBtn').disabled = true;
|
|
59
|
+
document.getElementById('connectBtn').textContent = 'Connecting...';
|
|
60
|
+
|
|
61
|
+
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
|
62
|
+
account = accounts[0];
|
|
63
|
+
web3 = new Web3(window.ethereum);
|
|
64
|
+
|
|
65
|
+
document.getElementById('account').textContent = account;
|
|
66
|
+
document.getElementById('walletInfo').style.display = 'block';
|
|
67
|
+
document.getElementById('connectBtn').textContent = 'Connected';
|
|
68
|
+
|
|
69
|
+
// Get balance
|
|
70
|
+
const balance = await web3.eth.getBalance(account);
|
|
71
|
+
const ethBalance = web3.utils.fromWei(balance, 'ether');
|
|
72
|
+
document.getElementById('balance').textContent = parseFloat(ethBalance).toFixed(4) + ' ETH';
|
|
73
|
+
|
|
74
|
+
// Get network
|
|
75
|
+
const chainId = await web3.eth.getChainId();
|
|
76
|
+
const networks = { 1: 'Ethereum', 56: 'BSC', 137: 'Polygon', 42161: 'Arbitrum', 43114: 'Avalanche' };
|
|
77
|
+
document.getElementById('network').textContent = networks[chainId] || 'Chain ' + chainId;
|
|
78
|
+
|
|
79
|
+
// Listen for account changes
|
|
80
|
+
window.ethereum.on('accountsChanged', (accounts) => {
|
|
81
|
+
if (accounts.length === 0) location.reload();
|
|
82
|
+
else location.reload();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(error);
|
|
87
|
+
document.getElementById('connectBtn').disabled = false;
|
|
88
|
+
document.getElementById('connectBtn').textContent = 'Connect Wallet';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
</script>
|
|
92
|
+
</body>
|
|
93
|
+
</html>
|
package/test/index.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Testing Utilities
|
|
5
|
+
* Helpers for testing QVTX applications
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { ethers } = require('ethers');
|
|
9
|
+
const Web3 = require('web3');
|
|
10
|
+
|
|
11
|
+
class QVTXTestUtils {
|
|
12
|
+
constructor(config = {}) {
|
|
13
|
+
this.rpcUrl = config.rpcUrl || 'http://127.0.0.1:8545';
|
|
14
|
+
this.web3 = new Web3(this.rpcUrl);
|
|
15
|
+
this.provider = new ethers.providers.JsonRpcProvider(this.rpcUrl);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Generate random test wallet
|
|
19
|
+
generateWallet() {
|
|
20
|
+
const wallet = ethers.Wallet.createRandom();
|
|
21
|
+
return {
|
|
22
|
+
address: wallet.address,
|
|
23
|
+
privateKey: wallet.privateKey,
|
|
24
|
+
mnemonic: wallet.mnemonic.phrase
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Generate multiple test wallets
|
|
29
|
+
generateWallets(count) {
|
|
30
|
+
return Array(count).fill(null).map(() => this.generateWallet());
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Fund test account (for local networks)
|
|
34
|
+
async fundAccount(address, amount = '10') {
|
|
35
|
+
const accounts = await this.web3.eth.getAccounts();
|
|
36
|
+
const funder = accounts[0];
|
|
37
|
+
|
|
38
|
+
await this.web3.eth.sendTransaction({
|
|
39
|
+
from: funder,
|
|
40
|
+
to: address,
|
|
41
|
+
value: this.web3.utils.toWei(amount, 'ether')
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return await this.web3.eth.getBalance(address);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Mine blocks (Hardhat/Ganache)
|
|
48
|
+
async mineBlocks(count) {
|
|
49
|
+
for (let i = 0; i < count; i++) {
|
|
50
|
+
await this.provider.send('evm_mine', []);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Increase time (Hardhat/Ganache)
|
|
55
|
+
async increaseTime(seconds) {
|
|
56
|
+
await this.provider.send('evm_increaseTime', [seconds]);
|
|
57
|
+
await this.provider.send('evm_mine', []);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Snapshot blockchain state
|
|
61
|
+
async snapshot() {
|
|
62
|
+
return await this.provider.send('evm_snapshot', []);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Revert to snapshot
|
|
66
|
+
async revert(snapshotId) {
|
|
67
|
+
await this.provider.send('evm_revert', [snapshotId]);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Deploy contract helper
|
|
71
|
+
async deployContract(abi, bytecode, args = [], signer) {
|
|
72
|
+
const factory = new ethers.ContractFactory(abi, bytecode, signer);
|
|
73
|
+
const contract = await factory.deploy(...args);
|
|
74
|
+
await contract.deployed();
|
|
75
|
+
return contract;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Parse wei to ether string
|
|
79
|
+
toEther(wei) {
|
|
80
|
+
return ethers.utils.formatEther(wei);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Parse ether to wei
|
|
84
|
+
toWei(ether) {
|
|
85
|
+
return ethers.utils.parseEther(ether.toString());
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Wait for transaction
|
|
89
|
+
async waitForTx(txPromise) {
|
|
90
|
+
const tx = await txPromise;
|
|
91
|
+
return await tx.wait();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Get event from receipt
|
|
95
|
+
getEvent(receipt, eventName) {
|
|
96
|
+
const event = receipt.events?.find(e => e.event === eventName);
|
|
97
|
+
return event ? event.args : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Assert transaction reverts
|
|
101
|
+
async expectRevert(txPromise, message) {
|
|
102
|
+
try {
|
|
103
|
+
await txPromise;
|
|
104
|
+
throw new Error('Expected transaction to revert');
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (message && !error.message.includes(message)) {
|
|
107
|
+
throw new Error(`Expected revert with "${message}" but got "${error.message}"`);
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Assert approximate equality (for BigNumbers)
|
|
114
|
+
assertApproxEqual(actual, expected, tolerance = '0.01') {
|
|
115
|
+
const actualBN = ethers.BigNumber.from(actual);
|
|
116
|
+
const expectedBN = ethers.BigNumber.from(expected);
|
|
117
|
+
const toleranceBN = expectedBN.mul(ethers.utils.parseEther(tolerance)).div(ethers.utils.parseEther('1'));
|
|
118
|
+
|
|
119
|
+
const diff = actualBN.sub(expectedBN).abs();
|
|
120
|
+
if (diff.gt(toleranceBN)) {
|
|
121
|
+
throw new Error(`Values not approximately equal: ${actualBN.toString()} vs ${expectedBN.toString()}`);
|
|
122
|
+
}
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Get gas used from receipt
|
|
127
|
+
getGasUsed(receipt) {
|
|
128
|
+
return receipt.gasUsed.toString();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Calculate gas cost in ETH
|
|
132
|
+
getGasCost(receipt) {
|
|
133
|
+
const gasUsed = receipt.gasUsed;
|
|
134
|
+
const gasPrice = receipt.effectiveGasPrice;
|
|
135
|
+
return ethers.utils.formatEther(gasUsed.mul(gasPrice));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Test fixtures
|
|
140
|
+
const fixtures = {
|
|
141
|
+
// Sample addresses
|
|
142
|
+
ZERO_ADDRESS: '0x0000000000000000000000000000000000000000',
|
|
143
|
+
DEAD_ADDRESS: '0x000000000000000000000000000000000000dEaD',
|
|
144
|
+
|
|
145
|
+
// Sample amounts
|
|
146
|
+
ONE_ETHER: ethers.utils.parseEther('1'),
|
|
147
|
+
TEN_ETHER: ethers.utils.parseEther('10'),
|
|
148
|
+
HUNDRED_ETHER: ethers.utils.parseEther('100'),
|
|
149
|
+
THOUSAND_ETHER: ethers.utils.parseEther('1000'),
|
|
150
|
+
MILLION_ETHER: ethers.utils.parseEther('1000000'),
|
|
151
|
+
|
|
152
|
+
// Time constants
|
|
153
|
+
ONE_MINUTE: 60,
|
|
154
|
+
ONE_HOUR: 3600,
|
|
155
|
+
ONE_DAY: 86400,
|
|
156
|
+
ONE_WEEK: 604800,
|
|
157
|
+
ONE_MONTH: 2592000,
|
|
158
|
+
ONE_YEAR: 31536000
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Mock contract responses for testing
|
|
162
|
+
class MockContract {
|
|
163
|
+
constructor(methods = {}) {
|
|
164
|
+
this.methods = {};
|
|
165
|
+
this.events = {};
|
|
166
|
+
|
|
167
|
+
for (const [name, returnValue] of Object.entries(methods)) {
|
|
168
|
+
this.methods[name] = (...args) => ({
|
|
169
|
+
call: async () => returnValue,
|
|
170
|
+
send: async () => ({ transactionHash: '0x' + '1'.repeat(64), events: {} }),
|
|
171
|
+
estimateGas: async () => 100000,
|
|
172
|
+
encodeABI: () => '0x'
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
QVTXTestUtils,
|
|
180
|
+
fixtures,
|
|
181
|
+
MockContract
|
|
182
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
|
|
7
|
+
async function run(options) {
|
|
8
|
+
console.log('\nQVTX Build Tool\n');
|
|
9
|
+
|
|
10
|
+
const production = options.production || false;
|
|
11
|
+
console.log(`Mode: ${production ? 'production' : 'development'}\n`);
|
|
12
|
+
|
|
13
|
+
// Check for hardhat - compile contracts
|
|
14
|
+
const hardhatConfig = path.join(process.cwd(), 'hardhat.config.js');
|
|
15
|
+
if (await fs.pathExists(hardhatConfig)) {
|
|
16
|
+
console.log('Compiling smart contracts...\n');
|
|
17
|
+
|
|
18
|
+
const child = spawn('npx', ['hardhat', 'compile'], {
|
|
19
|
+
stdio: 'inherit',
|
|
20
|
+
cwd: process.cwd(),
|
|
21
|
+
shell: true
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
child.on('close', async (code) => {
|
|
25
|
+
if (code === 0) {
|
|
26
|
+
console.log('\nContracts compiled successfully!');
|
|
27
|
+
|
|
28
|
+
// Copy artifacts
|
|
29
|
+
const artifactsDir = path.join(process.cwd(), 'artifacts');
|
|
30
|
+
const buildDir = path.join(process.cwd(), 'build');
|
|
31
|
+
|
|
32
|
+
if (await fs.pathExists(artifactsDir)) {
|
|
33
|
+
await fs.ensureDir(buildDir);
|
|
34
|
+
await fs.copy(artifactsDir, path.join(buildDir, 'artifacts'));
|
|
35
|
+
console.log('Artifacts copied to build/\n');
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
console.log(`\nBuild failed with code ${code}\n`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Check for package.json build script
|
|
45
|
+
const packagePath = path.join(process.cwd(), 'package.json');
|
|
46
|
+
if (await fs.pathExists(packagePath)) {
|
|
47
|
+
const pkg = await fs.readJson(packagePath);
|
|
48
|
+
if (pkg.scripts?.build) {
|
|
49
|
+
console.log('Running npm build script...\n');
|
|
50
|
+
const child = spawn('npm', ['run', 'build'], {
|
|
51
|
+
stdio: 'inherit',
|
|
52
|
+
cwd: process.cwd(),
|
|
53
|
+
shell: true
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log('No build configuration found');
|
|
60
|
+
console.log('Add a hardhat.config.js or build script to package.json\n');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
module.exports = { run };
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
async function run(type, name) {
|
|
7
|
+
const componentName = name || `qvtx-${type}`;
|
|
8
|
+
|
|
9
|
+
console.log(`\nCreating ${type}: ${componentName}\n`);
|
|
10
|
+
|
|
11
|
+
if (type === 'dapp') {
|
|
12
|
+
await createDapp(componentName);
|
|
13
|
+
} else if (type === 'contract') {
|
|
14
|
+
await createContract(componentName);
|
|
15
|
+
} else {
|
|
16
|
+
console.error(`Unknown type: ${type}. Use 'dapp' or 'contract'`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function createDapp(name) {
|
|
22
|
+
const targetDir = path.join(process.cwd(), name);
|
|
23
|
+
await fs.ensureDir(targetDir);
|
|
24
|
+
await fs.ensureDir(path.join(targetDir, 'src'));
|
|
25
|
+
await fs.ensureDir(path.join(targetDir, 'public'));
|
|
26
|
+
|
|
27
|
+
const indexHtml = `<!DOCTYPE html>
|
|
28
|
+
<html lang="en">
|
|
29
|
+
<head>
|
|
30
|
+
<meta charset="UTF-8">
|
|
31
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
32
|
+
<title>${name} - QVTX DApp</title>
|
|
33
|
+
<script src="https://cdn.jsdelivr.net/npm/web3@1.8.2/dist/web3.min.js"></script>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
<div id="app">
|
|
37
|
+
<h1>${name}</h1>
|
|
38
|
+
<p>Built with QVTX Developer Kit</p>
|
|
39
|
+
<button id="connectBtn">Connect Wallet</button>
|
|
40
|
+
<div id="account"></div>
|
|
41
|
+
</div>
|
|
42
|
+
<script src="src/app.js"></script>
|
|
43
|
+
</body>
|
|
44
|
+
</html>
|
|
45
|
+
`;
|
|
46
|
+
await fs.writeFile(path.join(targetDir, 'public/index.html'), indexHtml);
|
|
47
|
+
|
|
48
|
+
const appJs = `// QVTX DApp
|
|
49
|
+
let web3;
|
|
50
|
+
let account;
|
|
51
|
+
|
|
52
|
+
document.getElementById('connectBtn').addEventListener('click', connectWallet);
|
|
53
|
+
|
|
54
|
+
async function connectWallet() {
|
|
55
|
+
if (typeof window.ethereum !== 'undefined') {
|
|
56
|
+
try {
|
|
57
|
+
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
|
58
|
+
account = accounts[0];
|
|
59
|
+
web3 = new Web3(window.ethereum);
|
|
60
|
+
document.getElementById('account').textContent = 'Connected: ' + account;
|
|
61
|
+
console.log('Connected:', account);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('Connection failed:', error);
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
alert('Please install MetaMask!');
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
`;
|
|
70
|
+
await fs.writeFile(path.join(targetDir, 'src/app.js'), appJs);
|
|
71
|
+
|
|
72
|
+
console.log(`DApp created: ${targetDir}`);
|
|
73
|
+
console.log('\nTo run: open public/index.html in a browser\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function createContract(name) {
|
|
77
|
+
const contractsDir = path.join(process.cwd(), 'contracts');
|
|
78
|
+
await fs.ensureDir(contractsDir);
|
|
79
|
+
|
|
80
|
+
const contractName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
81
|
+
const contract = `// SPDX-License-Identifier: MIT
|
|
82
|
+
pragma solidity ^0.8.19;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @title ${contractName}
|
|
86
|
+
* @dev QVTX Smart Contract
|
|
87
|
+
*/
|
|
88
|
+
contract ${contractName} {
|
|
89
|
+
address public owner;
|
|
90
|
+
|
|
91
|
+
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
|
92
|
+
|
|
93
|
+
modifier onlyOwner() {
|
|
94
|
+
require(msg.sender == owner, "Not owner");
|
|
95
|
+
_;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
constructor() {
|
|
99
|
+
owner = msg.sender;
|
|
100
|
+
emit OwnershipTransferred(address(0), msg.sender);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function transferOwnership(address newOwner) public onlyOwner {
|
|
104
|
+
require(newOwner != address(0), "Invalid address");
|
|
105
|
+
emit OwnershipTransferred(owner, newOwner);
|
|
106
|
+
owner = newOwner;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
const filePath = path.join(contractsDir, `${contractName}.sol`);
|
|
111
|
+
await fs.writeFile(filePath, contract);
|
|
112
|
+
|
|
113
|
+
console.log(`Contract created: ${filePath}\n`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
module.exports = { run };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
async function run(contract, options) {
|
|
7
|
+
const network = options.network || 'ethereum';
|
|
8
|
+
const env = options.env || 'testnet';
|
|
9
|
+
|
|
10
|
+
console.log(`\nQVTX Deploy Tool`);
|
|
11
|
+
console.log(`================`);
|
|
12
|
+
console.log(`Network: ${network}`);
|
|
13
|
+
console.log(`Environment: ${env}`);
|
|
14
|
+
console.log(`Contract: ${contract || 'all'}\n`);
|
|
15
|
+
|
|
16
|
+
// Check for hardhat
|
|
17
|
+
const hardhatConfigPath = path.join(process.cwd(), 'hardhat.config.js');
|
|
18
|
+
if (!await fs.pathExists(hardhatConfigPath)) {
|
|
19
|
+
console.error('Error: hardhat.config.js not found');
|
|
20
|
+
console.log('Run "qvtx init" first or create a hardhat config\n');
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Check for contracts
|
|
25
|
+
const contractsDir = path.join(process.cwd(), 'contracts');
|
|
26
|
+
if (!await fs.pathExists(contractsDir)) {
|
|
27
|
+
console.error('Error: contracts/ directory not found\n');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const contracts = await fs.readdir(contractsDir);
|
|
32
|
+
const solFiles = contracts.filter(f => f.endsWith('.sol'));
|
|
33
|
+
|
|
34
|
+
if (solFiles.length === 0) {
|
|
35
|
+
console.error('Error: No Solidity contracts found\n');
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
console.log('Found contracts:');
|
|
40
|
+
solFiles.forEach(f => console.log(` - ${f}`));
|
|
41
|
+
console.log('');
|
|
42
|
+
|
|
43
|
+
// Check for private key
|
|
44
|
+
if (!process.env.PRIVATE_KEY) {
|
|
45
|
+
console.log('Warning: PRIVATE_KEY environment variable not set');
|
|
46
|
+
console.log('Set it with: export PRIVATE_KEY=your_private_key\n');
|
|
47
|
+
console.log('Deployment aborted - no private key\n');
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log('To deploy, run:');
|
|
52
|
+
console.log(` npx hardhat run scripts/deploy.js --network ${network}\n`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = { run };
|