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,411 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @title QVTX Vesting
|
|
6
|
+
* @dev Token vesting contract for team, advisors, and investors
|
|
7
|
+
* @author QuantVestrix Tech Team (07-Tech)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface IQVTXToken {
|
|
11
|
+
function transfer(address to, uint256 amount) external returns (bool);
|
|
12
|
+
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
|
13
|
+
function balanceOf(address account) external view returns (uint256);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
contract QVTXVesting {
|
|
17
|
+
IQVTXToken public qvtxToken;
|
|
18
|
+
address public owner;
|
|
19
|
+
|
|
20
|
+
struct VestingSchedule {
|
|
21
|
+
uint256 id;
|
|
22
|
+
address beneficiary;
|
|
23
|
+
uint256 totalAmount;
|
|
24
|
+
uint256 releasedAmount;
|
|
25
|
+
uint256 startTime;
|
|
26
|
+
uint256 cliffDuration;
|
|
27
|
+
uint256 vestingDuration;
|
|
28
|
+
uint256 slicePeriod;
|
|
29
|
+
bool revocable;
|
|
30
|
+
bool revoked;
|
|
31
|
+
VestingType vestingType;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
enum VestingType {
|
|
35
|
+
Team,
|
|
36
|
+
Advisor,
|
|
37
|
+
Investor,
|
|
38
|
+
Partnership,
|
|
39
|
+
Community,
|
|
40
|
+
Custom
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Storage
|
|
44
|
+
uint256 public scheduleCount;
|
|
45
|
+
mapping(uint256 => VestingSchedule) public vestingSchedules;
|
|
46
|
+
mapping(address => uint256[]) public beneficiarySchedules;
|
|
47
|
+
|
|
48
|
+
uint256 public totalVested;
|
|
49
|
+
uint256 public totalReleased;
|
|
50
|
+
|
|
51
|
+
// Preset vesting configurations
|
|
52
|
+
mapping(VestingType => VestingConfig) public presetConfigs;
|
|
53
|
+
|
|
54
|
+
struct VestingConfig {
|
|
55
|
+
uint256 cliffDuration;
|
|
56
|
+
uint256 vestingDuration;
|
|
57
|
+
uint256 slicePeriod;
|
|
58
|
+
bool revocable;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Events
|
|
62
|
+
event VestingScheduleCreated(
|
|
63
|
+
uint256 indexed scheduleId,
|
|
64
|
+
address indexed beneficiary,
|
|
65
|
+
uint256 amount,
|
|
66
|
+
VestingType vestingType
|
|
67
|
+
);
|
|
68
|
+
event TokensReleased(uint256 indexed scheduleId, address indexed beneficiary, uint256 amount);
|
|
69
|
+
event VestingRevoked(uint256 indexed scheduleId, uint256 refundAmount);
|
|
70
|
+
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
|
|
71
|
+
|
|
72
|
+
modifier onlyOwner() {
|
|
73
|
+
require(msg.sender == owner, "Vesting: not owner");
|
|
74
|
+
_;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
modifier scheduleExists(uint256 scheduleId) {
|
|
78
|
+
require(scheduleId > 0 && scheduleId <= scheduleCount, "Vesting: invalid schedule");
|
|
79
|
+
_;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
constructor(address _qvtxToken) {
|
|
83
|
+
qvtxToken = IQVTXToken(_qvtxToken);
|
|
84
|
+
owner = msg.sender;
|
|
85
|
+
|
|
86
|
+
// Initialize preset vesting configurations
|
|
87
|
+
// Team: 1 year cliff, 4 year vesting, monthly releases
|
|
88
|
+
presetConfigs[VestingType.Team] = VestingConfig({
|
|
89
|
+
cliffDuration: 365 days,
|
|
90
|
+
vestingDuration: 4 * 365 days,
|
|
91
|
+
slicePeriod: 30 days,
|
|
92
|
+
revocable: true
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Advisors: 6 month cliff, 2 year vesting, monthly releases
|
|
96
|
+
presetConfigs[VestingType.Advisor] = VestingConfig({
|
|
97
|
+
cliffDuration: 180 days,
|
|
98
|
+
vestingDuration: 2 * 365 days,
|
|
99
|
+
slicePeriod: 30 days,
|
|
100
|
+
revocable: true
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Investors: 3 month cliff, 18 month vesting, monthly releases
|
|
104
|
+
presetConfigs[VestingType.Investor] = VestingConfig({
|
|
105
|
+
cliffDuration: 90 days,
|
|
106
|
+
vestingDuration: 18 * 30 days,
|
|
107
|
+
slicePeriod: 30 days,
|
|
108
|
+
revocable: false
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Partnership: No cliff, 1 year vesting, quarterly releases
|
|
112
|
+
presetConfigs[VestingType.Partnership] = VestingConfig({
|
|
113
|
+
cliffDuration: 0,
|
|
114
|
+
vestingDuration: 365 days,
|
|
115
|
+
slicePeriod: 90 days,
|
|
116
|
+
revocable: false
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Community: No cliff, 6 month vesting, weekly releases
|
|
120
|
+
presetConfigs[VestingType.Community] = VestingConfig({
|
|
121
|
+
cliffDuration: 0,
|
|
122
|
+
vestingDuration: 180 days,
|
|
123
|
+
slicePeriod: 7 days,
|
|
124
|
+
revocable: false
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Create vesting schedule with preset config
|
|
129
|
+
function createVestingSchedule(
|
|
130
|
+
address beneficiary,
|
|
131
|
+
uint256 amount,
|
|
132
|
+
VestingType vestingType
|
|
133
|
+
) external onlyOwner returns (uint256) {
|
|
134
|
+
require(beneficiary != address(0), "Vesting: zero address");
|
|
135
|
+
require(amount > 0, "Vesting: zero amount");
|
|
136
|
+
|
|
137
|
+
VestingConfig memory config = presetConfigs[vestingType];
|
|
138
|
+
|
|
139
|
+
return _createSchedule(
|
|
140
|
+
beneficiary,
|
|
141
|
+
amount,
|
|
142
|
+
block.timestamp,
|
|
143
|
+
config.cliffDuration,
|
|
144
|
+
config.vestingDuration,
|
|
145
|
+
config.slicePeriod,
|
|
146
|
+
config.revocable,
|
|
147
|
+
vestingType
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Create custom vesting schedule
|
|
152
|
+
function createCustomVestingSchedule(
|
|
153
|
+
address beneficiary,
|
|
154
|
+
uint256 amount,
|
|
155
|
+
uint256 startTime,
|
|
156
|
+
uint256 cliffDuration,
|
|
157
|
+
uint256 vestingDuration,
|
|
158
|
+
uint256 slicePeriod,
|
|
159
|
+
bool revocable
|
|
160
|
+
) external onlyOwner returns (uint256) {
|
|
161
|
+
require(beneficiary != address(0), "Vesting: zero address");
|
|
162
|
+
require(amount > 0, "Vesting: zero amount");
|
|
163
|
+
require(vestingDuration > 0, "Vesting: zero duration");
|
|
164
|
+
require(slicePeriod > 0, "Vesting: zero slice period");
|
|
165
|
+
require(vestingDuration >= cliffDuration, "Vesting: cliff > duration");
|
|
166
|
+
|
|
167
|
+
return _createSchedule(
|
|
168
|
+
beneficiary,
|
|
169
|
+
amount,
|
|
170
|
+
startTime > 0 ? startTime : block.timestamp,
|
|
171
|
+
cliffDuration,
|
|
172
|
+
vestingDuration,
|
|
173
|
+
slicePeriod,
|
|
174
|
+
revocable,
|
|
175
|
+
VestingType.Custom
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function _createSchedule(
|
|
180
|
+
address beneficiary,
|
|
181
|
+
uint256 amount,
|
|
182
|
+
uint256 startTime,
|
|
183
|
+
uint256 cliffDuration,
|
|
184
|
+
uint256 vestingDuration,
|
|
185
|
+
uint256 slicePeriod,
|
|
186
|
+
bool revocable,
|
|
187
|
+
VestingType vestingType
|
|
188
|
+
) internal returns (uint256) {
|
|
189
|
+
// Transfer tokens to this contract
|
|
190
|
+
require(
|
|
191
|
+
qvtxToken.transferFrom(msg.sender, address(this), amount),
|
|
192
|
+
"Vesting: transfer failed"
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
scheduleCount++;
|
|
196
|
+
uint256 scheduleId = scheduleCount;
|
|
197
|
+
|
|
198
|
+
vestingSchedules[scheduleId] = VestingSchedule({
|
|
199
|
+
id: scheduleId,
|
|
200
|
+
beneficiary: beneficiary,
|
|
201
|
+
totalAmount: amount,
|
|
202
|
+
releasedAmount: 0,
|
|
203
|
+
startTime: startTime,
|
|
204
|
+
cliffDuration: cliffDuration,
|
|
205
|
+
vestingDuration: vestingDuration,
|
|
206
|
+
slicePeriod: slicePeriod,
|
|
207
|
+
revocable: revocable,
|
|
208
|
+
revoked: false,
|
|
209
|
+
vestingType: vestingType
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
beneficiarySchedules[beneficiary].push(scheduleId);
|
|
213
|
+
totalVested += amount;
|
|
214
|
+
|
|
215
|
+
emit VestingScheduleCreated(scheduleId, beneficiary, amount, vestingType);
|
|
216
|
+
|
|
217
|
+
return scheduleId;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Batch create vesting schedules
|
|
221
|
+
function batchCreateVestingSchedules(
|
|
222
|
+
address[] calldata beneficiaries,
|
|
223
|
+
uint256[] calldata amounts,
|
|
224
|
+
VestingType vestingType
|
|
225
|
+
) external onlyOwner returns (uint256[] memory) {
|
|
226
|
+
require(beneficiaries.length == amounts.length, "Vesting: length mismatch");
|
|
227
|
+
require(beneficiaries.length > 0, "Vesting: empty arrays");
|
|
228
|
+
|
|
229
|
+
uint256[] memory scheduleIds = new uint256[](beneficiaries.length);
|
|
230
|
+
|
|
231
|
+
for (uint256 i = 0; i < beneficiaries.length; i++) {
|
|
232
|
+
VestingConfig memory config = presetConfigs[vestingType];
|
|
233
|
+
scheduleIds[i] = _createSchedule(
|
|
234
|
+
beneficiaries[i],
|
|
235
|
+
amounts[i],
|
|
236
|
+
block.timestamp,
|
|
237
|
+
config.cliffDuration,
|
|
238
|
+
config.vestingDuration,
|
|
239
|
+
config.slicePeriod,
|
|
240
|
+
config.revocable,
|
|
241
|
+
vestingType
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return scheduleIds;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Release vested tokens
|
|
249
|
+
function release(uint256 scheduleId) external scheduleExists(scheduleId) {
|
|
250
|
+
VestingSchedule storage schedule = vestingSchedules[scheduleId];
|
|
251
|
+
require(!schedule.revoked, "Vesting: schedule revoked");
|
|
252
|
+
require(msg.sender == schedule.beneficiary, "Vesting: not beneficiary");
|
|
253
|
+
|
|
254
|
+
uint256 releasable = _computeReleasableAmount(schedule);
|
|
255
|
+
require(releasable > 0, "Vesting: nothing to release");
|
|
256
|
+
|
|
257
|
+
schedule.releasedAmount += releasable;
|
|
258
|
+
totalReleased += releasable;
|
|
259
|
+
|
|
260
|
+
require(
|
|
261
|
+
qvtxToken.transfer(schedule.beneficiary, releasable),
|
|
262
|
+
"Vesting: transfer failed"
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
emit TokensReleased(scheduleId, schedule.beneficiary, releasable);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Release all vested tokens for beneficiary
|
|
269
|
+
function releaseAll() external {
|
|
270
|
+
uint256[] memory scheduleIds = beneficiarySchedules[msg.sender];
|
|
271
|
+
require(scheduleIds.length > 0, "Vesting: no schedules");
|
|
272
|
+
|
|
273
|
+
uint256 totalReleasable = 0;
|
|
274
|
+
|
|
275
|
+
for (uint256 i = 0; i < scheduleIds.length; i++) {
|
|
276
|
+
VestingSchedule storage schedule = vestingSchedules[scheduleIds[i]];
|
|
277
|
+
if (!schedule.revoked) {
|
|
278
|
+
uint256 releasable = _computeReleasableAmount(schedule);
|
|
279
|
+
if (releasable > 0) {
|
|
280
|
+
schedule.releasedAmount += releasable;
|
|
281
|
+
totalReleasable += releasable;
|
|
282
|
+
emit TokensReleased(scheduleIds[i], msg.sender, releasable);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
require(totalReleasable > 0, "Vesting: nothing to release");
|
|
288
|
+
totalReleased += totalReleasable;
|
|
289
|
+
|
|
290
|
+
require(
|
|
291
|
+
qvtxToken.transfer(msg.sender, totalReleasable),
|
|
292
|
+
"Vesting: transfer failed"
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Revoke vesting schedule
|
|
297
|
+
function revoke(uint256 scheduleId) external onlyOwner scheduleExists(scheduleId) {
|
|
298
|
+
VestingSchedule storage schedule = vestingSchedules[scheduleId];
|
|
299
|
+
require(schedule.revocable, "Vesting: not revocable");
|
|
300
|
+
require(!schedule.revoked, "Vesting: already revoked");
|
|
301
|
+
|
|
302
|
+
// Release any vested tokens first
|
|
303
|
+
uint256 releasable = _computeReleasableAmount(schedule);
|
|
304
|
+
if (releasable > 0) {
|
|
305
|
+
schedule.releasedAmount += releasable;
|
|
306
|
+
totalReleased += releasable;
|
|
307
|
+
qvtxToken.transfer(schedule.beneficiary, releasable);
|
|
308
|
+
emit TokensReleased(scheduleId, schedule.beneficiary, releasable);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Calculate refund
|
|
312
|
+
uint256 refundAmount = schedule.totalAmount - schedule.releasedAmount;
|
|
313
|
+
schedule.revoked = true;
|
|
314
|
+
|
|
315
|
+
if (refundAmount > 0) {
|
|
316
|
+
totalVested -= refundAmount;
|
|
317
|
+
qvtxToken.transfer(owner, refundAmount);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
emit VestingRevoked(scheduleId, refundAmount);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Compute releasable amount
|
|
324
|
+
function _computeReleasableAmount(VestingSchedule storage schedule) internal view returns (uint256) {
|
|
325
|
+
uint256 vestedAmount = _computeVestedAmount(schedule);
|
|
326
|
+
return vestedAmount - schedule.releasedAmount;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Compute vested amount
|
|
330
|
+
function _computeVestedAmount(VestingSchedule storage schedule) internal view returns (uint256) {
|
|
331
|
+
if (block.timestamp < schedule.startTime + schedule.cliffDuration) {
|
|
332
|
+
return 0;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
uint256 timeFromStart = block.timestamp - schedule.startTime;
|
|
336
|
+
|
|
337
|
+
if (timeFromStart >= schedule.vestingDuration) {
|
|
338
|
+
return schedule.totalAmount;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Calculate vested amount based on slice periods
|
|
342
|
+
uint256 vestedSlices = timeFromStart / schedule.slicePeriod;
|
|
343
|
+
uint256 totalSlices = schedule.vestingDuration / schedule.slicePeriod;
|
|
344
|
+
|
|
345
|
+
return (schedule.totalAmount * vestedSlices) / totalSlices;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// View functions
|
|
349
|
+
function getVestingSchedule(uint256 scheduleId) external view returns (VestingSchedule memory) {
|
|
350
|
+
return vestingSchedules[scheduleId];
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function getBeneficiarySchedules(address beneficiary) external view returns (uint256[] memory) {
|
|
354
|
+
return beneficiarySchedules[beneficiary];
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function getReleasableAmount(uint256 scheduleId) external view scheduleExists(scheduleId) returns (uint256) {
|
|
358
|
+
VestingSchedule storage schedule = vestingSchedules[scheduleId];
|
|
359
|
+
if (schedule.revoked) return 0;
|
|
360
|
+
return _computeReleasableAmount(schedule);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function getVestedAmount(uint256 scheduleId) external view scheduleExists(scheduleId) returns (uint256) {
|
|
364
|
+
return _computeVestedAmount(vestingSchedules[scheduleId]);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function getTotalReleasable(address beneficiary) external view returns (uint256) {
|
|
368
|
+
uint256[] memory scheduleIds = beneficiarySchedules[beneficiary];
|
|
369
|
+
uint256 total = 0;
|
|
370
|
+
|
|
371
|
+
for (uint256 i = 0; i < scheduleIds.length; i++) {
|
|
372
|
+
VestingSchedule storage schedule = vestingSchedules[scheduleIds[i]];
|
|
373
|
+
if (!schedule.revoked) {
|
|
374
|
+
total += _computeReleasableAmount(schedule);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return total;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Admin functions
|
|
382
|
+
function updatePresetConfig(
|
|
383
|
+
VestingType vestingType,
|
|
384
|
+
uint256 cliffDuration,
|
|
385
|
+
uint256 vestingDuration,
|
|
386
|
+
uint256 slicePeriod,
|
|
387
|
+
bool revocable
|
|
388
|
+
) external onlyOwner {
|
|
389
|
+
presetConfigs[vestingType] = VestingConfig({
|
|
390
|
+
cliffDuration: cliffDuration,
|
|
391
|
+
vestingDuration: vestingDuration,
|
|
392
|
+
slicePeriod: slicePeriod,
|
|
393
|
+
revocable: revocable
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function transferOwnership(address newOwner) external onlyOwner {
|
|
398
|
+
require(newOwner != address(0), "Vesting: zero address");
|
|
399
|
+
emit OwnershipTransferred(owner, newOwner);
|
|
400
|
+
owner = newOwner;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Emergency withdraw (only unvested tokens)
|
|
404
|
+
function emergencyWithdraw() external onlyOwner {
|
|
405
|
+
uint256 balance = qvtxToken.balanceOf(address(this));
|
|
406
|
+
uint256 unvested = balance - (totalVested - totalReleased);
|
|
407
|
+
if (unvested > 0) {
|
|
408
|
+
qvtxToken.transfer(owner, unvested);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
interface IERC20 {
|
|
5
|
+
event Transfer(address indexed from, address indexed to, uint256 value);
|
|
6
|
+
event Approval(address indexed owner, address indexed spender, uint256 value);
|
|
7
|
+
|
|
8
|
+
function totalSupply() external view returns (uint256);
|
|
9
|
+
function balanceOf(address account) external view returns (uint256);
|
|
10
|
+
function transfer(address to, uint256 amount) external returns (bool);
|
|
11
|
+
function allowance(address owner, address spender) external view returns (uint256);
|
|
12
|
+
function approve(address spender, uint256 amount) external returns (bool);
|
|
13
|
+
function transferFrom(address from, address to, uint256 amount) external returns (bool);
|
|
14
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
interface IERC721 {
|
|
5
|
+
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
|
|
6
|
+
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
|
|
7
|
+
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
|
|
8
|
+
|
|
9
|
+
function balanceOf(address owner) external view returns (uint256);
|
|
10
|
+
function ownerOf(uint256 tokenId) external view returns (address);
|
|
11
|
+
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
|
|
12
|
+
function safeTransferFrom(address from, address to, uint256 tokenId) external;
|
|
13
|
+
function transferFrom(address from, address to, uint256 tokenId) external;
|
|
14
|
+
function approve(address to, uint256 tokenId) external;
|
|
15
|
+
function setApprovalForAll(address operator, bool approved) external;
|
|
16
|
+
function getApproved(uint256 tokenId) external view returns (address);
|
|
17
|
+
function isApprovedForAll(address owner, address operator) external view returns (bool);
|
|
18
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.19;
|
|
3
|
+
|
|
4
|
+
interface IERC721Metadata {
|
|
5
|
+
function name() external view returns (string memory);
|
|
6
|
+
function symbol() external view returns (string memory);
|
|
7
|
+
function tokenURI(uint256 tokenId) external view returns (string memory);
|
|
8
|
+
}
|
package/storage/index.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* QVTX Neural Storage Module
|
|
5
|
+
* Decentralized storage utilities with IPFS and Arweave support
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
|
|
10
|
+
class QVTXStorage {
|
|
11
|
+
constructor(config = {}) {
|
|
12
|
+
this.provider = config.provider || 'ipfs';
|
|
13
|
+
this.gateway = config.gateway || 'https://ipfs.io/ipfs/';
|
|
14
|
+
this.apiKey = config.apiKey || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Generate content hash
|
|
18
|
+
hash(content) {
|
|
19
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Upload to IPFS via Pinata or Infura
|
|
23
|
+
async uploadToIPFS(content, options = {}) {
|
|
24
|
+
if (!this.apiKey) {
|
|
25
|
+
throw new Error('IPFS API key required. Set apiKey in config.');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const axios = require('axios');
|
|
29
|
+
const FormData = require('form-data');
|
|
30
|
+
|
|
31
|
+
const form = new FormData();
|
|
32
|
+
|
|
33
|
+
if (typeof content === 'string') {
|
|
34
|
+
form.append('file', Buffer.from(content), {
|
|
35
|
+
filename: options.filename || 'data.json'
|
|
36
|
+
});
|
|
37
|
+
} else {
|
|
38
|
+
form.append('file', content);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const response = await axios.post('https://api.pinata.cloud/pinning/pinFileToIPFS', form, {
|
|
42
|
+
headers: {
|
|
43
|
+
...form.getHeaders(),
|
|
44
|
+
'Authorization': `Bearer ${this.apiKey}`
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
hash: response.data.IpfsHash,
|
|
50
|
+
url: this.gateway + response.data.IpfsHash,
|
|
51
|
+
size: response.data.PinSize
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Fetch from IPFS
|
|
56
|
+
async fetchFromIPFS(hash) {
|
|
57
|
+
const axios = require('axios');
|
|
58
|
+
const url = this.gateway + hash;
|
|
59
|
+
const response = await axios.get(url);
|
|
60
|
+
return response.data;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Create metadata for NFT
|
|
64
|
+
createNFTMetadata(name, description, image, attributes = []) {
|
|
65
|
+
return {
|
|
66
|
+
name,
|
|
67
|
+
description,
|
|
68
|
+
image,
|
|
69
|
+
attributes: attributes.map(attr => ({
|
|
70
|
+
trait_type: attr.trait,
|
|
71
|
+
value: attr.value
|
|
72
|
+
}))
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Store encrypted data
|
|
77
|
+
async storeEncrypted(data, password) {
|
|
78
|
+
const algorithm = 'aes-256-gcm';
|
|
79
|
+
const key = crypto.scryptSync(password, 'qvtx-salt', 32);
|
|
80
|
+
const iv = crypto.randomBytes(16);
|
|
81
|
+
|
|
82
|
+
const cipher = crypto.createCipheriv(algorithm, key, iv);
|
|
83
|
+
let encrypted = cipher.update(JSON.stringify(data), 'utf8', 'hex');
|
|
84
|
+
encrypted += cipher.final('hex');
|
|
85
|
+
|
|
86
|
+
const authTag = cipher.getAuthTag();
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
encrypted,
|
|
90
|
+
iv: iv.toString('hex'),
|
|
91
|
+
authTag: authTag.toString('hex')
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Retrieve encrypted data
|
|
96
|
+
async retrieveEncrypted(encryptedData, password) {
|
|
97
|
+
const algorithm = 'aes-256-gcm';
|
|
98
|
+
const key = crypto.scryptSync(password, 'qvtx-salt', 32);
|
|
99
|
+
const iv = Buffer.from(encryptedData.iv, 'hex');
|
|
100
|
+
const authTag = Buffer.from(encryptedData.authTag, 'hex');
|
|
101
|
+
|
|
102
|
+
const decipher = crypto.createDecipheriv(algorithm, key, iv);
|
|
103
|
+
decipher.setAuthTag(authTag);
|
|
104
|
+
|
|
105
|
+
let decrypted = decipher.update(encryptedData.encrypted, 'hex', 'utf8');
|
|
106
|
+
decrypted += decipher.final('utf8');
|
|
107
|
+
|
|
108
|
+
return JSON.parse(decrypted);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = QVTXStorage;
|