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
package/config/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Configuration Module
|
|
5
|
+
* Central configuration management
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const networks = require('./networks');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
|
|
12
|
+
// Load environment variables from .env if exists
|
|
13
|
+
function loadEnv(envPath) {
|
|
14
|
+
const dotenvPath = envPath || path.join(process.cwd(), '.env');
|
|
15
|
+
|
|
16
|
+
if (fs.existsSync(dotenvPath)) {
|
|
17
|
+
const content = fs.readFileSync(dotenvPath, 'utf8');
|
|
18
|
+
const lines = content.split('\n');
|
|
19
|
+
|
|
20
|
+
for (const line of lines) {
|
|
21
|
+
const trimmed = line.trim();
|
|
22
|
+
if (trimmed && !trimmed.startsWith('#')) {
|
|
23
|
+
const [key, ...valueParts] = trimmed.split('=');
|
|
24
|
+
const value = valueParts.join('=').trim();
|
|
25
|
+
if (key && !process.env[key]) {
|
|
26
|
+
process.env[key] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Default configuration
|
|
34
|
+
const DEFAULT_CONFIG = {
|
|
35
|
+
network: 'ethereum',
|
|
36
|
+
environment: 'mainnet',
|
|
37
|
+
debug: false,
|
|
38
|
+
timeout: 30000,
|
|
39
|
+
retries: 3,
|
|
40
|
+
gasMultiplier: 1.2
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Get configuration value
|
|
44
|
+
function get(key, defaultValue) {
|
|
45
|
+
const envKey = `QVTX_${key.toUpperCase()}`;
|
|
46
|
+
return process.env[envKey] || defaultValue;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Get private key (with validation)
|
|
50
|
+
function getPrivateKey() {
|
|
51
|
+
const key = process.env.PRIVATE_KEY;
|
|
52
|
+
if (!key) {
|
|
53
|
+
throw new Error('PRIVATE_KEY environment variable not set');
|
|
54
|
+
}
|
|
55
|
+
if (!key.startsWith('0x')) {
|
|
56
|
+
return '0x' + key;
|
|
57
|
+
}
|
|
58
|
+
return key;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Get RPC URL for network
|
|
62
|
+
function getRpcUrl(network, environment) {
|
|
63
|
+
const envKey = `${network.toUpperCase()}_RPC`;
|
|
64
|
+
if (process.env[envKey]) {
|
|
65
|
+
return process.env[envKey];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const networkConfig = networks.getNetwork(network, environment);
|
|
69
|
+
let rpc = networkConfig.rpc;
|
|
70
|
+
|
|
71
|
+
// Replace API key placeholders
|
|
72
|
+
if (rpc.includes('${INFURA_API_KEY}') && process.env.INFURA_API_KEY) {
|
|
73
|
+
rpc = rpc.replace('${INFURA_API_KEY}', process.env.INFURA_API_KEY);
|
|
74
|
+
}
|
|
75
|
+
if (rpc.includes('${ALCHEMY_API_KEY}') && process.env.ALCHEMY_API_KEY) {
|
|
76
|
+
rpc = rpc.replace('${ALCHEMY_API_KEY}', process.env.ALCHEMY_API_KEY);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return rpc;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Get contract address
|
|
83
|
+
function getContractAddress(contractName, network = 'ethereum') {
|
|
84
|
+
const envKey = `QVTX_${contractName.toUpperCase()}_${network.toUpperCase()}`;
|
|
85
|
+
return process.env[envKey] || null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Create configuration object
|
|
89
|
+
function createConfig(options = {}) {
|
|
90
|
+
return {
|
|
91
|
+
...DEFAULT_CONFIG,
|
|
92
|
+
...options,
|
|
93
|
+
getPrivateKey,
|
|
94
|
+
getRpcUrl: (net, env) => getRpcUrl(net || options.network, env || options.environment),
|
|
95
|
+
getContractAddress
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module.exports = {
|
|
100
|
+
loadEnv,
|
|
101
|
+
get,
|
|
102
|
+
getPrivateKey,
|
|
103
|
+
getRpcUrl,
|
|
104
|
+
getContractAddress,
|
|
105
|
+
createConfig,
|
|
106
|
+
DEFAULT_CONFIG,
|
|
107
|
+
...networks
|
|
108
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Network Configurations
|
|
5
|
+
* Supported blockchain networks and their settings
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const NETWORKS = {
|
|
9
|
+
// Ethereum
|
|
10
|
+
ethereum: {
|
|
11
|
+
mainnet: {
|
|
12
|
+
chainId: 1,
|
|
13
|
+
name: 'Ethereum Mainnet',
|
|
14
|
+
rpc: 'https://eth.llamarpc.com',
|
|
15
|
+
rpcAlternatives: [
|
|
16
|
+
'https://mainnet.infura.io/v3/${INFURA_API_KEY}',
|
|
17
|
+
'https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}',
|
|
18
|
+
'https://cloudflare-eth.com'
|
|
19
|
+
],
|
|
20
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
21
|
+
blockExplorer: 'https://etherscan.io',
|
|
22
|
+
avgBlockTime: 12
|
|
23
|
+
},
|
|
24
|
+
goerli: {
|
|
25
|
+
chainId: 5,
|
|
26
|
+
name: 'Goerli Testnet',
|
|
27
|
+
rpc: 'https://goerli.infura.io/v3/${INFURA_API_KEY}',
|
|
28
|
+
nativeCurrency: { name: 'Goerli Ether', symbol: 'ETH', decimals: 18 },
|
|
29
|
+
blockExplorer: 'https://goerli.etherscan.io',
|
|
30
|
+
faucet: 'https://goerlifaucet.com',
|
|
31
|
+
avgBlockTime: 12
|
|
32
|
+
},
|
|
33
|
+
sepolia: {
|
|
34
|
+
chainId: 11155111,
|
|
35
|
+
name: 'Sepolia Testnet',
|
|
36
|
+
rpc: 'https://sepolia.infura.io/v3/${INFURA_API_KEY}',
|
|
37
|
+
nativeCurrency: { name: 'Sepolia Ether', symbol: 'ETH', decimals: 18 },
|
|
38
|
+
blockExplorer: 'https://sepolia.etherscan.io',
|
|
39
|
+
faucet: 'https://sepoliafaucet.com',
|
|
40
|
+
avgBlockTime: 12
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// BNB Smart Chain
|
|
45
|
+
bsc: {
|
|
46
|
+
mainnet: {
|
|
47
|
+
chainId: 56,
|
|
48
|
+
name: 'BNB Smart Chain',
|
|
49
|
+
rpc: 'https://bsc-dataseed.binance.org/',
|
|
50
|
+
rpcAlternatives: [
|
|
51
|
+
'https://bsc-dataseed1.defibit.io/',
|
|
52
|
+
'https://bsc-dataseed1.ninicoin.io/'
|
|
53
|
+
],
|
|
54
|
+
nativeCurrency: { name: 'BNB', symbol: 'BNB', decimals: 18 },
|
|
55
|
+
blockExplorer: 'https://bscscan.com',
|
|
56
|
+
avgBlockTime: 3
|
|
57
|
+
},
|
|
58
|
+
testnet: {
|
|
59
|
+
chainId: 97,
|
|
60
|
+
name: 'BSC Testnet',
|
|
61
|
+
rpc: 'https://data-seed-prebsc-1-s1.binance.org:8545/',
|
|
62
|
+
nativeCurrency: { name: 'Test BNB', symbol: 'tBNB', decimals: 18 },
|
|
63
|
+
blockExplorer: 'https://testnet.bscscan.com',
|
|
64
|
+
faucet: 'https://testnet.binance.org/faucet-smart',
|
|
65
|
+
avgBlockTime: 3
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
// Polygon
|
|
70
|
+
polygon: {
|
|
71
|
+
mainnet: {
|
|
72
|
+
chainId: 137,
|
|
73
|
+
name: 'Polygon Mainnet',
|
|
74
|
+
rpc: 'https://polygon-rpc.com/',
|
|
75
|
+
rpcAlternatives: [
|
|
76
|
+
'https://rpc-mainnet.maticvigil.com/',
|
|
77
|
+
'https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}'
|
|
78
|
+
],
|
|
79
|
+
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
|
|
80
|
+
blockExplorer: 'https://polygonscan.com',
|
|
81
|
+
avgBlockTime: 2
|
|
82
|
+
},
|
|
83
|
+
mumbai: {
|
|
84
|
+
chainId: 80001,
|
|
85
|
+
name: 'Mumbai Testnet',
|
|
86
|
+
rpc: 'https://rpc-mumbai.maticvigil.com/',
|
|
87
|
+
nativeCurrency: { name: 'Test MATIC', symbol: 'MATIC', decimals: 18 },
|
|
88
|
+
blockExplorer: 'https://mumbai.polygonscan.com',
|
|
89
|
+
faucet: 'https://faucet.polygon.technology/',
|
|
90
|
+
avgBlockTime: 2
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// Arbitrum
|
|
95
|
+
arbitrum: {
|
|
96
|
+
mainnet: {
|
|
97
|
+
chainId: 42161,
|
|
98
|
+
name: 'Arbitrum One',
|
|
99
|
+
rpc: 'https://arb1.arbitrum.io/rpc',
|
|
100
|
+
rpcAlternatives: [
|
|
101
|
+
'https://arb-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}'
|
|
102
|
+
],
|
|
103
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
104
|
+
blockExplorer: 'https://arbiscan.io',
|
|
105
|
+
avgBlockTime: 0.25
|
|
106
|
+
},
|
|
107
|
+
goerli: {
|
|
108
|
+
chainId: 421613,
|
|
109
|
+
name: 'Arbitrum Goerli',
|
|
110
|
+
rpc: 'https://goerli-rollup.arbitrum.io/rpc',
|
|
111
|
+
nativeCurrency: { name: 'Goerli Ether', symbol: 'ETH', decimals: 18 },
|
|
112
|
+
blockExplorer: 'https://goerli.arbiscan.io',
|
|
113
|
+
faucet: 'https://faucet.arbitrum.io/',
|
|
114
|
+
avgBlockTime: 0.25
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
// Avalanche
|
|
119
|
+
avalanche: {
|
|
120
|
+
mainnet: {
|
|
121
|
+
chainId: 43114,
|
|
122
|
+
name: 'Avalanche C-Chain',
|
|
123
|
+
rpc: 'https://api.avax.network/ext/bc/C/rpc',
|
|
124
|
+
rpcAlternatives: [
|
|
125
|
+
'https://avalanche-mainnet.infura.io/v3/${INFURA_API_KEY}'
|
|
126
|
+
],
|
|
127
|
+
nativeCurrency: { name: 'AVAX', symbol: 'AVAX', decimals: 18 },
|
|
128
|
+
blockExplorer: 'https://snowtrace.io',
|
|
129
|
+
avgBlockTime: 2
|
|
130
|
+
},
|
|
131
|
+
fuji: {
|
|
132
|
+
chainId: 43113,
|
|
133
|
+
name: 'Avalanche Fuji',
|
|
134
|
+
rpc: 'https://api.avax-test.network/ext/bc/C/rpc',
|
|
135
|
+
nativeCurrency: { name: 'Test AVAX', symbol: 'AVAX', decimals: 18 },
|
|
136
|
+
blockExplorer: 'https://testnet.snowtrace.io',
|
|
137
|
+
faucet: 'https://faucet.avax.network/',
|
|
138
|
+
avgBlockTime: 2
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
// Optimism
|
|
143
|
+
optimism: {
|
|
144
|
+
mainnet: {
|
|
145
|
+
chainId: 10,
|
|
146
|
+
name: 'Optimism',
|
|
147
|
+
rpc: 'https://mainnet.optimism.io',
|
|
148
|
+
rpcAlternatives: [
|
|
149
|
+
'https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}'
|
|
150
|
+
],
|
|
151
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
152
|
+
blockExplorer: 'https://optimistic.etherscan.io',
|
|
153
|
+
avgBlockTime: 2
|
|
154
|
+
},
|
|
155
|
+
goerli: {
|
|
156
|
+
chainId: 420,
|
|
157
|
+
name: 'Optimism Goerli',
|
|
158
|
+
rpc: 'https://goerli.optimism.io',
|
|
159
|
+
nativeCurrency: { name: 'Goerli Ether', symbol: 'ETH', decimals: 18 },
|
|
160
|
+
blockExplorer: 'https://goerli-optimism.etherscan.io',
|
|
161
|
+
avgBlockTime: 2
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
// Solana (different structure)
|
|
166
|
+
solana: {
|
|
167
|
+
'mainnet-beta': {
|
|
168
|
+
name: 'Solana Mainnet',
|
|
169
|
+
rpc: 'https://api.mainnet-beta.solana.com',
|
|
170
|
+
rpcAlternatives: [
|
|
171
|
+
'https://solana-api.projectserum.com'
|
|
172
|
+
],
|
|
173
|
+
blockExplorer: 'https://explorer.solana.com'
|
|
174
|
+
},
|
|
175
|
+
devnet: {
|
|
176
|
+
name: 'Solana Devnet',
|
|
177
|
+
rpc: 'https://api.devnet.solana.com',
|
|
178
|
+
blockExplorer: 'https://explorer.solana.com?cluster=devnet',
|
|
179
|
+
faucet: 'https://solfaucet.com/'
|
|
180
|
+
},
|
|
181
|
+
testnet: {
|
|
182
|
+
name: 'Solana Testnet',
|
|
183
|
+
rpc: 'https://api.testnet.solana.com',
|
|
184
|
+
blockExplorer: 'https://explorer.solana.com?cluster=testnet'
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Helper functions
|
|
190
|
+
function getNetwork(name, environment = 'mainnet') {
|
|
191
|
+
const network = NETWORKS[name.toLowerCase()];
|
|
192
|
+
if (!network) {
|
|
193
|
+
throw new Error(`Unknown network: ${name}`);
|
|
194
|
+
}
|
|
195
|
+
const env = network[environment];
|
|
196
|
+
if (!env) {
|
|
197
|
+
throw new Error(`Unknown environment: ${environment} for network ${name}`);
|
|
198
|
+
}
|
|
199
|
+
return env;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getChainById(chainId) {
|
|
203
|
+
for (const [networkName, envs] of Object.entries(NETWORKS)) {
|
|
204
|
+
for (const [envName, config] of Object.entries(envs)) {
|
|
205
|
+
if (config.chainId === chainId) {
|
|
206
|
+
return { network: networkName, environment: envName, ...config };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getAllNetworks() {
|
|
214
|
+
return NETWORKS;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function getMainnets() {
|
|
218
|
+
const mainnets = {};
|
|
219
|
+
for (const [name, envs] of Object.entries(NETWORKS)) {
|
|
220
|
+
if (envs.mainnet || envs['mainnet-beta']) {
|
|
221
|
+
mainnets[name] = envs.mainnet || envs['mainnet-beta'];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return mainnets;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function getTestnets() {
|
|
228
|
+
const testnets = {};
|
|
229
|
+
for (const [name, envs] of Object.entries(NETWORKS)) {
|
|
230
|
+
for (const [envName, config] of Object.entries(envs)) {
|
|
231
|
+
if (envName !== 'mainnet' && envName !== 'mainnet-beta') {
|
|
232
|
+
if (!testnets[name]) testnets[name] = {};
|
|
233
|
+
testnets[name][envName] = config;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return testnets;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
module.exports = {
|
|
241
|
+
NETWORKS,
|
|
242
|
+
getNetwork,
|
|
243
|
+
getChainById,
|
|
244
|
+
getAllNetworks,
|
|
245
|
+
getMainnets,
|
|
246
|
+
getTestnets
|
|
247
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Developer Kit - Basic Usage Example
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const QVTX = require('qvtx-developer-kit');
|
|
8
|
+
|
|
9
|
+
async function main() {
|
|
10
|
+
// Initialize the SDK
|
|
11
|
+
const kit = new QVTX({
|
|
12
|
+
network: 'ethereum',
|
|
13
|
+
environment: 'mainnet'
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
console.log('QVTX Developer Kit v' + kit.getVersion());
|
|
17
|
+
console.log('Validator ID:', kit.validatorId);
|
|
18
|
+
|
|
19
|
+
// Display supported networks
|
|
20
|
+
console.log('\nSupported Networks:');
|
|
21
|
+
const networks = kit.getNetworks();
|
|
22
|
+
Object.entries(networks).forEach(([name, envs]) => {
|
|
23
|
+
console.log(` ${name}: ${Object.keys(envs).join(', ')}`);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Display token info
|
|
27
|
+
console.log('\nQVTX Token Info:');
|
|
28
|
+
const tokenInfo = kit.getTokenInfo();
|
|
29
|
+
console.log(` Name: ${tokenInfo.name}`);
|
|
30
|
+
console.log(` Symbol: ${tokenInfo.symbol}`);
|
|
31
|
+
console.log(` Decimals: ${tokenInfo.decimals}`);
|
|
32
|
+
|
|
33
|
+
// Initialize Web3 (requires Infura API key)
|
|
34
|
+
// const web3 = await kit.initWeb3('https://mainnet.infura.io/v3/YOUR_API_KEY');
|
|
35
|
+
// const blockNumber = await web3.eth.getBlockNumber();
|
|
36
|
+
// console.log('\nCurrent block:', blockNumber);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Cross-Chain Bridge Example
|
|
5
|
+
* Demonstrates how to bridge QVTX tokens between chains
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const QVTX = require('qvtx-developer-kit');
|
|
9
|
+
const { QVTXBridge: BridgeABI, QVTXToken: TokenABI } = require('qvtx-developer-kit/abis');
|
|
10
|
+
|
|
11
|
+
// Configuration
|
|
12
|
+
const CONFIG = {
|
|
13
|
+
PRIVATE_KEY: process.env.PRIVATE_KEY || 'your-private-key-here',
|
|
14
|
+
|
|
15
|
+
// Source chain (Ethereum)
|
|
16
|
+
SOURCE_RPC: process.env.SOURCE_RPC || 'https://mainnet.infura.io/v3/YOUR_KEY',
|
|
17
|
+
SOURCE_BRIDGE: process.env.SOURCE_BRIDGE || '0x...', // Bridge contract on Ethereum
|
|
18
|
+
SOURCE_TOKEN: process.env.SOURCE_TOKEN || '0x...', // QVTX token on Ethereum
|
|
19
|
+
|
|
20
|
+
// Target chain (BSC)
|
|
21
|
+
TARGET_CHAIN_ID: 56,
|
|
22
|
+
TARGET_RPC: process.env.TARGET_RPC || 'https://bsc-dataseed.binance.org/',
|
|
23
|
+
TARGET_BRIDGE: process.env.TARGET_BRIDGE || '0x...', // Bridge contract on BSC
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
async function main() {
|
|
27
|
+
console.log('QVTX Cross-Chain Bridge Example\n');
|
|
28
|
+
console.log('================================\n');
|
|
29
|
+
|
|
30
|
+
// Initialize SDK
|
|
31
|
+
const kit = new QVTX({
|
|
32
|
+
network: 'ethereum',
|
|
33
|
+
environment: 'mainnet'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Initialize Web3 for source chain
|
|
37
|
+
const web3 = await kit.initWeb3(CONFIG.SOURCE_RPC);
|
|
38
|
+
const account = web3.eth.accounts.privateKeyToAccount(CONFIG.PRIVATE_KEY);
|
|
39
|
+
web3.eth.accounts.wallet.add(account);
|
|
40
|
+
|
|
41
|
+
console.log('Wallet Address:', account.address);
|
|
42
|
+
console.log('Source Chain: Ethereum');
|
|
43
|
+
console.log('Target Chain: BSC (Chain ID:', CONFIG.TARGET_CHAIN_ID, ')');
|
|
44
|
+
|
|
45
|
+
// Initialize contracts
|
|
46
|
+
const bridgeContract = new web3.eth.Contract(BridgeABI.abi, CONFIG.SOURCE_BRIDGE);
|
|
47
|
+
const tokenContract = new web3.eth.Contract(TokenABI.abi, CONFIG.SOURCE_TOKEN);
|
|
48
|
+
|
|
49
|
+
// Check token balance
|
|
50
|
+
const balance = await tokenContract.methods.balanceOf(account.address).call();
|
|
51
|
+
console.log('\nQVTX Balance:', web3.utils.fromWei(balance, 'ether'), 'QVTX');
|
|
52
|
+
|
|
53
|
+
// Check bridge configuration
|
|
54
|
+
const bridgeFee = await bridgeContract.methods.bridgeFee().call();
|
|
55
|
+
const minAmount = await bridgeContract.methods.minBridgeAmount().call();
|
|
56
|
+
const maxAmount = await bridgeContract.methods.maxBridgeAmount().call();
|
|
57
|
+
const isTargetSupported = await bridgeContract.methods.isChainSupported(CONFIG.TARGET_CHAIN_ID).call();
|
|
58
|
+
|
|
59
|
+
console.log('\n--- Bridge Configuration ---');
|
|
60
|
+
console.log('Bridge Fee:', web3.utils.fromWei(bridgeFee, 'ether'), 'ETH');
|
|
61
|
+
console.log('Min Bridge Amount:', web3.utils.fromWei(minAmount, 'ether'), 'QVTX');
|
|
62
|
+
console.log('Max Bridge Amount:', web3.utils.fromWei(maxAmount, 'ether'), 'QVTX');
|
|
63
|
+
console.log('Target Chain Supported:', isTargetSupported);
|
|
64
|
+
|
|
65
|
+
// Check if bridge is paused
|
|
66
|
+
const isPaused = await bridgeContract.methods.paused().call();
|
|
67
|
+
if (isPaused) {
|
|
68
|
+
console.log('\nWARNING: Bridge is currently paused!');
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log('\n--- Bridge Operations ---\n');
|
|
73
|
+
|
|
74
|
+
// Example: Bridge tokens (uncomment to use)
|
|
75
|
+
/*
|
|
76
|
+
const bridgeAmount = web3.utils.toWei('100', 'ether'); // 100 QVTX
|
|
77
|
+
const recipient = account.address; // Same address on target chain
|
|
78
|
+
|
|
79
|
+
console.log('Bridging', web3.utils.fromWei(bridgeAmount, 'ether'), 'QVTX to BSC...');
|
|
80
|
+
|
|
81
|
+
// Step 1: Approve bridge contract to spend tokens
|
|
82
|
+
console.log('Step 1: Approving tokens...');
|
|
83
|
+
const approveTx = await tokenContract.methods.approve(CONFIG.SOURCE_BRIDGE, bridgeAmount)
|
|
84
|
+
.send({ from: account.address, gas: 100000 });
|
|
85
|
+
console.log('Approve TX:', approveTx.transactionHash);
|
|
86
|
+
|
|
87
|
+
// Step 2: Initiate bridge
|
|
88
|
+
console.log('Step 2: Initiating bridge transfer...');
|
|
89
|
+
const bridgeTx = await bridgeContract.methods.bridge(
|
|
90
|
+
recipient,
|
|
91
|
+
bridgeAmount,
|
|
92
|
+
CONFIG.TARGET_CHAIN_ID
|
|
93
|
+
).send({
|
|
94
|
+
from: account.address,
|
|
95
|
+
value: bridgeFee,
|
|
96
|
+
gas: 200000
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
console.log('Bridge TX:', bridgeTx.transactionHash);
|
|
100
|
+
|
|
101
|
+
// Get request ID from event
|
|
102
|
+
const requestId = bridgeTx.events.BridgeInitiated.returnValues.requestId;
|
|
103
|
+
console.log('Bridge Request ID:', requestId);
|
|
104
|
+
console.log('\nTokens are being bridged. They will appear on BSC within 5-15 minutes.');
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
// Check bridge history
|
|
108
|
+
const userRequests = await bridgeContract.methods.getUserBridgeRequests(account.address).call();
|
|
109
|
+
console.log('Your Bridge Requests:', userRequests.length);
|
|
110
|
+
|
|
111
|
+
if (userRequests.length > 0) {
|
|
112
|
+
console.log('\nRecent Bridge Requests:');
|
|
113
|
+
for (const requestId of userRequests.slice(-5)) {
|
|
114
|
+
const request = await bridgeContract.methods.getBridgeRequest(requestId).call();
|
|
115
|
+
const statusMap = ['Pending', 'Completed', 'Refunded', 'Failed'];
|
|
116
|
+
console.log(` #${requestId}: ${web3.utils.fromWei(request.amount, 'ether')} QVTX - ${statusMap[request.status]}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
console.log('\nBridge example completed!');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Governance Example
|
|
5
|
+
* Demonstrates how to create proposals and vote
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const QVTX = require('qvtx-developer-kit');
|
|
9
|
+
const { QVTXGovernance: GovernanceABI, QVTXToken: TokenABI } = require('qvtx-developer-kit/abis');
|
|
10
|
+
|
|
11
|
+
// Configuration
|
|
12
|
+
const CONFIG = {
|
|
13
|
+
PRIVATE_KEY: process.env.PRIVATE_KEY || 'your-private-key-here',
|
|
14
|
+
RPC_URL: process.env.RPC_URL || 'https://mainnet.infura.io/v3/YOUR_KEY',
|
|
15
|
+
GOVERNANCE_CONTRACT: process.env.GOVERNANCE_CONTRACT || '0x...',
|
|
16
|
+
TOKEN_CONTRACT: process.env.TOKEN_CONTRACT || '0x...'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function main() {
|
|
20
|
+
console.log('QVTX Governance Example\n');
|
|
21
|
+
console.log('=======================\n');
|
|
22
|
+
|
|
23
|
+
// Initialize SDK
|
|
24
|
+
const kit = new QVTX({
|
|
25
|
+
network: 'ethereum',
|
|
26
|
+
environment: 'mainnet'
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Initialize Web3
|
|
30
|
+
const web3 = await kit.initWeb3(CONFIG.RPC_URL);
|
|
31
|
+
const account = web3.eth.accounts.privateKeyToAccount(CONFIG.PRIVATE_KEY);
|
|
32
|
+
web3.eth.accounts.wallet.add(account);
|
|
33
|
+
|
|
34
|
+
console.log('Wallet Address:', account.address);
|
|
35
|
+
|
|
36
|
+
// Initialize contracts
|
|
37
|
+
const governance = new web3.eth.Contract(GovernanceABI.abi, CONFIG.GOVERNANCE_CONTRACT);
|
|
38
|
+
const token = new web3.eth.Contract(TokenABI.abi, CONFIG.TOKEN_CONTRACT);
|
|
39
|
+
|
|
40
|
+
// Check voting power (token balance)
|
|
41
|
+
const balance = await token.methods.balanceOf(account.address).call();
|
|
42
|
+
console.log('QVTX Balance (Voting Power):', web3.utils.fromWei(balance, 'ether'), 'QVTX');
|
|
43
|
+
|
|
44
|
+
// Get governance parameters
|
|
45
|
+
const proposalThreshold = await governance.methods.proposalThreshold().call();
|
|
46
|
+
const quorumVotes = await governance.methods.quorumVotes().call();
|
|
47
|
+
const votingPeriod = await governance.methods.votingPeriod().call();
|
|
48
|
+
const timelockDelay = await governance.methods.timelockDelay().call();
|
|
49
|
+
const proposalCount = await governance.methods.proposalCount().call();
|
|
50
|
+
|
|
51
|
+
console.log('\n--- Governance Parameters ---');
|
|
52
|
+
console.log('Proposal Threshold:', web3.utils.fromWei(proposalThreshold, 'ether'), 'QVTX');
|
|
53
|
+
console.log('Quorum Required:', web3.utils.fromWei(quorumVotes, 'ether'), 'QVTX');
|
|
54
|
+
console.log('Voting Period:', votingPeriod, 'blocks (~', Math.round(votingPeriod * 15 / 86400), 'days)');
|
|
55
|
+
console.log('Timelock Delay:', timelockDelay, 'seconds (~', Math.round(timelockDelay / 86400), 'days)');
|
|
56
|
+
console.log('Total Proposals:', proposalCount);
|
|
57
|
+
|
|
58
|
+
// Check if user can propose
|
|
59
|
+
const canPropose = BigInt(balance) >= BigInt(proposalThreshold);
|
|
60
|
+
console.log('\nCan Create Proposals:', canPropose ? 'Yes' : 'No (need more QVTX)');
|
|
61
|
+
|
|
62
|
+
console.log('\n--- Governance Operations ---\n');
|
|
63
|
+
|
|
64
|
+
// Example: Create proposal (uncomment to use)
|
|
65
|
+
/*
|
|
66
|
+
if (!canPropose) {
|
|
67
|
+
console.log('Insufficient QVTX to create proposal');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const proposalTitle = 'Increase Staking Rewards';
|
|
72
|
+
const proposalDescription = 'This proposal increases the staking reward rate from 100 to 150 tokens per block to incentivize more participation.';
|
|
73
|
+
const targetContract = CONFIG.GOVERNANCE_CONTRACT; // Or any contract to call
|
|
74
|
+
const value = 0; // ETH to send
|
|
75
|
+
const callData = '0x'; // Encoded function call
|
|
76
|
+
|
|
77
|
+
console.log('Creating proposal:', proposalTitle);
|
|
78
|
+
|
|
79
|
+
const proposeTx = await governance.methods.propose(
|
|
80
|
+
proposalTitle,
|
|
81
|
+
proposalDescription,
|
|
82
|
+
targetContract,
|
|
83
|
+
value,
|
|
84
|
+
callData
|
|
85
|
+
).send({
|
|
86
|
+
from: account.address,
|
|
87
|
+
gas: 500000
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const proposalId = proposeTx.events.ProposalCreated.returnValues.id;
|
|
91
|
+
console.log('Proposal Created! ID:', proposalId);
|
|
92
|
+
console.log('TX:', proposeTx.transactionHash);
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
// Example: Vote on proposal (uncomment to use)
|
|
96
|
+
/*
|
|
97
|
+
const proposalIdToVote = 1; // Replace with actual proposal ID
|
|
98
|
+
const support = 1; // 0 = Against, 1 = For, 2 = Abstain
|
|
99
|
+
const reason = 'I support increasing rewards to grow the ecosystem.';
|
|
100
|
+
|
|
101
|
+
// Check proposal state first
|
|
102
|
+
const state = await governance.methods.getProposalState(proposalIdToVote).call();
|
|
103
|
+
const stateNames = ['Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'];
|
|
104
|
+
console.log('Proposal State:', stateNames[state]);
|
|
105
|
+
|
|
106
|
+
if (state == 1) { // Active
|
|
107
|
+
console.log('Voting on proposal', proposalIdToVote, '...');
|
|
108
|
+
|
|
109
|
+
const voteTx = await governance.methods.castVoteWithReason(
|
|
110
|
+
proposalIdToVote,
|
|
111
|
+
support,
|
|
112
|
+
reason
|
|
113
|
+
).send({
|
|
114
|
+
from: account.address,
|
|
115
|
+
gas: 200000
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log('Vote Cast! TX:', voteTx.transactionHash);
|
|
119
|
+
}
|
|
120
|
+
*/
|
|
121
|
+
|
|
122
|
+
// View recent proposals
|
|
123
|
+
if (proposalCount > 0) {
|
|
124
|
+
console.log('Recent Proposals:\n');
|
|
125
|
+
const stateNames = ['Pending', 'Active', 'Canceled', 'Defeated', 'Succeeded', 'Queued', 'Expired', 'Executed'];
|
|
126
|
+
|
|
127
|
+
const startId = Math.max(1, proposalCount - 4);
|
|
128
|
+
for (let i = startId; i <= proposalCount; i++) {
|
|
129
|
+
const proposal = await governance.methods.getProposal(i).call();
|
|
130
|
+
console.log(` #${proposal.id}: ${proposal.title}`);
|
|
131
|
+
console.log(` State: ${stateNames[proposal.state]}`);
|
|
132
|
+
console.log(` For: ${web3.utils.fromWei(proposal.forVotes, 'ether')} | Against: ${web3.utils.fromWei(proposal.againstVotes, 'ether')} | Abstain: ${web3.utils.fromWei(proposal.abstainVotes, 'ether')}`);
|
|
133
|
+
console.log('');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log('Governance example completed!');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
main().catch(console.error);
|