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.
Files changed (45) hide show
  1. package/.env.example +108 -0
  2. package/README.md +0 -0
  3. package/abis/QVTXBridge.json +273 -0
  4. package/abis/QVTXGovernance.json +267 -0
  5. package/abis/QVTXNFT.json +370 -0
  6. package/abis/QVTXRewards.json +155 -0
  7. package/abis/QVTXToken.json +311 -0
  8. package/abis/QVTXVesting.json +216 -0
  9. package/abis/index.js +15 -0
  10. package/bin/qvtx-developer-cli.js +99 -0
  11. package/config/index.js +108 -0
  12. package/config/networks.js +247 -0
  13. package/examples/basic-usage.js +39 -0
  14. package/examples/bridge-example.js +123 -0
  15. package/examples/governance-example.js +140 -0
  16. package/examples/nft-example.js +141 -0
  17. package/examples/staking-example.js +96 -0
  18. package/index.js +145 -0
  19. package/languages/blockchain_ai_sdk.js +0 -0
  20. package/languages/node_sdk.js +0 -0
  21. package/languages/solana_sdk.js +383 -0
  22. package/package.json +28 -3
  23. package/rewards/index.js +71 -0
  24. package/smart-contracts/QVTXBridge.sol +305 -0
  25. package/smart-contracts/QVTXGovernance.sol +325 -0
  26. package/smart-contracts/QVTXNFT.sol +338 -0
  27. package/smart-contracts/QVTXRewards.sol +102 -0
  28. package/smart-contracts/QVTXToken.sol +227 -0
  29. package/smart-contracts/QVTXVesting.sol +411 -0
  30. package/smart-contracts/interfaces/IERC20.sol +14 -0
  31. package/smart-contracts/interfaces/IERC20Metadata.sol +8 -0
  32. package/smart-contracts/interfaces/IERC721.sol +18 -0
  33. package/smart-contracts/interfaces/IERC721Metadata.sol +8 -0
  34. package/smart-contracts/interfaces/IERC721Receiver.sol +11 -0
  35. package/storage/index.js +112 -0
  36. package/templates/contract/ERC20Token.sol +116 -0
  37. package/templates/dapp/index.html +93 -0
  38. package/test/index.js +182 -0
  39. package/tools/build-tool.js +63 -0
  40. package/tools/create-template.js +116 -0
  41. package/tools/deploy-tool.js +55 -0
  42. package/tools/generate-docs.js +149 -0
  43. package/tools/init-project.js +138 -0
  44. package/tools/run-tests.js +64 -0
  45. package/types/index.d.ts +264 -0
@@ -0,0 +1,305 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ /**
5
+ * @title QVTX Bridge
6
+ * @dev Cross-chain bridge for QVTX token transfers
7
+ * @author QuantVestrix Tech Team (07-Tech)
8
+ */
9
+
10
+ interface IQVTXToken {
11
+ function bridgeMint(address to, uint256 amount, uint256 sourceChain) external;
12
+ function bridgeBurn(address from, uint256 amount, uint256 targetChain) external;
13
+ function balanceOf(address account) external view returns (uint256);
14
+ function transferFrom(address from, address to, uint256 amount) external returns (bool);
15
+ }
16
+
17
+ contract QVTXBridge {
18
+ // Bridge configuration
19
+ IQVTXToken public qvtxToken;
20
+ address public owner;
21
+ address public validator;
22
+
23
+ uint256 public currentChainId;
24
+ uint256 public nonce;
25
+ uint256 public bridgeFee = 0.001 ether; // Fee for bridging
26
+ uint256 public minBridgeAmount = 100 * 10**18; // Minimum 100 QVTX
27
+ uint256 public maxBridgeAmount = 10_000_000 * 10**18; // Maximum 10M QVTX
28
+
29
+ bool public paused;
30
+
31
+ // Supported chains
32
+ mapping(uint256 => bool) public supportedChains;
33
+ mapping(uint256 => address) public chainBridges; // chainId => bridge contract on that chain
34
+
35
+ // Bridge requests
36
+ struct BridgeRequest {
37
+ uint256 id;
38
+ address sender;
39
+ address recipient;
40
+ uint256 amount;
41
+ uint256 sourceChain;
42
+ uint256 targetChain;
43
+ uint256 timestamp;
44
+ bytes32 txHash;
45
+ BridgeStatus status;
46
+ }
47
+
48
+ enum BridgeStatus {
49
+ Pending,
50
+ Completed,
51
+ Refunded,
52
+ Failed
53
+ }
54
+
55
+ mapping(uint256 => BridgeRequest) public bridgeRequests;
56
+ mapping(bytes32 => bool) public processedHashes;
57
+ mapping(address => uint256[]) public userBridgeRequests;
58
+
59
+ // Daily limits
60
+ mapping(uint256 => uint256) public dailyVolume; // day => volume
61
+ uint256 public dailyLimit = 100_000_000 * 10**18; // 100M QVTX daily
62
+
63
+ // Events
64
+ event BridgeInitiated(
65
+ uint256 indexed requestId,
66
+ address indexed sender,
67
+ address indexed recipient,
68
+ uint256 amount,
69
+ uint256 sourceChain,
70
+ uint256 targetChain
71
+ );
72
+ event BridgeCompleted(
73
+ uint256 indexed requestId,
74
+ address indexed recipient,
75
+ uint256 amount,
76
+ uint256 sourceChain,
77
+ bytes32 sourceTxHash
78
+ );
79
+ event BridgeRefunded(uint256 indexed requestId, address indexed sender, uint256 amount);
80
+ event ChainAdded(uint256 indexed chainId, address bridgeContract);
81
+ event ChainRemoved(uint256 indexed chainId);
82
+ event ValidatorUpdated(address indexed newValidator);
83
+ event FeesWithdrawn(address indexed to, uint256 amount);
84
+ event Paused(bool status);
85
+
86
+ modifier onlyOwner() {
87
+ require(msg.sender == owner, "Bridge: not owner");
88
+ _;
89
+ }
90
+
91
+ modifier onlyValidator() {
92
+ require(msg.sender == validator || msg.sender == owner, "Bridge: not validator");
93
+ _;
94
+ }
95
+
96
+ modifier whenNotPaused() {
97
+ require(!paused, "Bridge: paused");
98
+ _;
99
+ }
100
+
101
+ constructor(address _qvtxToken, uint256 _chainId) {
102
+ qvtxToken = IQVTXToken(_qvtxToken);
103
+ currentChainId = _chainId;
104
+ owner = msg.sender;
105
+ validator = msg.sender;
106
+
107
+ // Add default supported chains
108
+ _addChain(1, address(0)); // Ethereum
109
+ _addChain(56, address(0)); // BSC
110
+ _addChain(137, address(0)); // Polygon
111
+ _addChain(42161, address(0)); // Arbitrum
112
+ _addChain(43114, address(0)); // Avalanche
113
+ _addChain(10, address(0)); // Optimism
114
+ }
115
+
116
+ // Initiate bridge transfer
117
+ function bridge(
118
+ address recipient,
119
+ uint256 amount,
120
+ uint256 targetChain
121
+ ) external payable whenNotPaused returns (uint256) {
122
+ require(supportedChains[targetChain], "Bridge: unsupported chain");
123
+ require(targetChain != currentChainId, "Bridge: same chain");
124
+ require(amount >= minBridgeAmount, "Bridge: below minimum");
125
+ require(amount <= maxBridgeAmount, "Bridge: exceeds maximum");
126
+ require(msg.value >= bridgeFee, "Bridge: insufficient fee");
127
+ require(recipient != address(0), "Bridge: zero recipient");
128
+
129
+ // Check daily limit
130
+ uint256 today = block.timestamp / 1 days;
131
+ require(dailyVolume[today] + amount <= dailyLimit, "Bridge: daily limit exceeded");
132
+ dailyVolume[today] += amount;
133
+
134
+ // Lock/burn tokens
135
+ qvtxToken.bridgeBurn(msg.sender, amount, targetChain);
136
+
137
+ // Create bridge request
138
+ nonce++;
139
+ uint256 requestId = nonce;
140
+
141
+ bridgeRequests[requestId] = BridgeRequest({
142
+ id: requestId,
143
+ sender: msg.sender,
144
+ recipient: recipient,
145
+ amount: amount,
146
+ sourceChain: currentChainId,
147
+ targetChain: targetChain,
148
+ timestamp: block.timestamp,
149
+ txHash: bytes32(0),
150
+ status: BridgeStatus.Pending
151
+ });
152
+
153
+ userBridgeRequests[msg.sender].push(requestId);
154
+
155
+ emit BridgeInitiated(requestId, msg.sender, recipient, amount, currentChainId, targetChain);
156
+
157
+ // Refund excess fee
158
+ if (msg.value > bridgeFee) {
159
+ payable(msg.sender).transfer(msg.value - bridgeFee);
160
+ }
161
+
162
+ return requestId;
163
+ }
164
+
165
+ // Complete bridge (called by validator on destination chain)
166
+ function completeBridge(
167
+ address recipient,
168
+ uint256 amount,
169
+ uint256 sourceChain,
170
+ bytes32 sourceTxHash,
171
+ bytes calldata signature
172
+ ) external onlyValidator whenNotPaused {
173
+ require(!processedHashes[sourceTxHash], "Bridge: already processed");
174
+ require(supportedChains[sourceChain], "Bridge: unsupported source chain");
175
+
176
+ // Verify signature
177
+ bytes32 messageHash = keccak256(abi.encodePacked(
178
+ recipient,
179
+ amount,
180
+ sourceChain,
181
+ currentChainId,
182
+ sourceTxHash
183
+ ));
184
+ require(_verifySignature(messageHash, signature), "Bridge: invalid signature");
185
+
186
+ processedHashes[sourceTxHash] = true;
187
+
188
+ // Mint tokens to recipient
189
+ qvtxToken.bridgeMint(recipient, amount, sourceChain);
190
+
191
+ // Create completion record
192
+ nonce++;
193
+ bridgeRequests[nonce] = BridgeRequest({
194
+ id: nonce,
195
+ sender: address(0),
196
+ recipient: recipient,
197
+ amount: amount,
198
+ sourceChain: sourceChain,
199
+ targetChain: currentChainId,
200
+ timestamp: block.timestamp,
201
+ txHash: sourceTxHash,
202
+ status: BridgeStatus.Completed
203
+ });
204
+
205
+ emit BridgeCompleted(nonce, recipient, amount, sourceChain, sourceTxHash);
206
+ }
207
+
208
+ // Refund failed bridge
209
+ function refund(uint256 requestId) external onlyValidator {
210
+ BridgeRequest storage request = bridgeRequests[requestId];
211
+ require(request.status == BridgeStatus.Pending, "Bridge: not pending");
212
+ require(request.sourceChain == currentChainId, "Bridge: wrong chain");
213
+
214
+ request.status = BridgeStatus.Refunded;
215
+
216
+ // Mint back to sender
217
+ qvtxToken.bridgeMint(request.sender, request.amount, currentChainId);
218
+
219
+ emit BridgeRefunded(requestId, request.sender, request.amount);
220
+ }
221
+
222
+ // Verify validator signature
223
+ function _verifySignature(bytes32 messageHash, bytes calldata signature) internal view returns (bool) {
224
+ bytes32 ethSignedHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash));
225
+ (bytes32 r, bytes32 s, uint8 v) = _splitSignature(signature);
226
+ address signer = ecrecover(ethSignedHash, v, r, s);
227
+ return signer == validator;
228
+ }
229
+
230
+ function _splitSignature(bytes calldata sig) internal pure returns (bytes32 r, bytes32 s, uint8 v) {
231
+ require(sig.length == 65, "Bridge: invalid signature length");
232
+ assembly {
233
+ r := calldataload(sig.offset)
234
+ s := calldataload(add(sig.offset, 32))
235
+ v := byte(0, calldataload(add(sig.offset, 64)))
236
+ }
237
+ }
238
+
239
+ // Get user bridge history
240
+ function getUserBridgeRequests(address user) external view returns (uint256[] memory) {
241
+ return userBridgeRequests[user];
242
+ }
243
+
244
+ // Get bridge request details
245
+ function getBridgeRequest(uint256 requestId) external view returns (BridgeRequest memory) {
246
+ return bridgeRequests[requestId];
247
+ }
248
+
249
+ // Check if chain is supported
250
+ function isChainSupported(uint256 chainId) external view returns (bool) {
251
+ return supportedChains[chainId];
252
+ }
253
+
254
+ // Admin functions
255
+ function addChain(uint256 chainId, address bridgeContract) external onlyOwner {
256
+ _addChain(chainId, bridgeContract);
257
+ }
258
+
259
+ function _addChain(uint256 chainId, address bridgeContract) internal {
260
+ supportedChains[chainId] = true;
261
+ chainBridges[chainId] = bridgeContract;
262
+ emit ChainAdded(chainId, bridgeContract);
263
+ }
264
+
265
+ function removeChain(uint256 chainId) external onlyOwner {
266
+ supportedChains[chainId] = false;
267
+ chainBridges[chainId] = address(0);
268
+ emit ChainRemoved(chainId);
269
+ }
270
+
271
+ function setValidator(address _validator) external onlyOwner {
272
+ require(_validator != address(0), "Bridge: zero address");
273
+ validator = _validator;
274
+ emit ValidatorUpdated(_validator);
275
+ }
276
+
277
+ function setBridgeFee(uint256 _fee) external onlyOwner {
278
+ bridgeFee = _fee;
279
+ }
280
+
281
+ function setLimits(uint256 _min, uint256 _max, uint256 _daily) external onlyOwner {
282
+ minBridgeAmount = _min;
283
+ maxBridgeAmount = _max;
284
+ dailyLimit = _daily;
285
+ }
286
+
287
+ function setPaused(bool _paused) external onlyOwner {
288
+ paused = _paused;
289
+ emit Paused(_paused);
290
+ }
291
+
292
+ function withdrawFees(address payable to) external onlyOwner {
293
+ uint256 balance = address(this).balance;
294
+ require(balance > 0, "Bridge: no fees");
295
+ to.transfer(balance);
296
+ emit FeesWithdrawn(to, balance);
297
+ }
298
+
299
+ function transferOwnership(address newOwner) external onlyOwner {
300
+ require(newOwner != address(0), "Bridge: zero address");
301
+ owner = newOwner;
302
+ }
303
+
304
+ receive() external payable {}
305
+ }
@@ -0,0 +1,325 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.19;
3
+
4
+ /**
5
+ * @title QVTX Governance
6
+ * @dev DAO governance contract for QuantVestrix ecosystem
7
+ * @author QuantVestrix Tech Team (07-Tech)
8
+ */
9
+
10
+ interface IQVTXToken {
11
+ function balanceOf(address account) external view returns (uint256);
12
+ function totalSupply() external view returns (uint256);
13
+ }
14
+
15
+ contract QVTXGovernance {
16
+ // Governance token
17
+ IQVTXToken public qvtxToken;
18
+ address public owner;
19
+
20
+ // Proposal structure
21
+ struct Proposal {
22
+ uint256 id;
23
+ address proposer;
24
+ string title;
25
+ string description;
26
+ bytes callData;
27
+ address target;
28
+ uint256 value;
29
+ uint256 forVotes;
30
+ uint256 againstVotes;
31
+ uint256 abstainVotes;
32
+ uint256 startBlock;
33
+ uint256 endBlock;
34
+ bool executed;
35
+ bool canceled;
36
+ mapping(address => Receipt) receipts;
37
+ }
38
+
39
+ struct Receipt {
40
+ bool hasVoted;
41
+ uint8 support; // 0 = against, 1 = for, 2 = abstain
42
+ uint256 votes;
43
+ }
44
+
45
+ struct ProposalInfo {
46
+ uint256 id;
47
+ address proposer;
48
+ string title;
49
+ string description;
50
+ uint256 forVotes;
51
+ uint256 againstVotes;
52
+ uint256 abstainVotes;
53
+ uint256 startBlock;
54
+ uint256 endBlock;
55
+ bool executed;
56
+ bool canceled;
57
+ ProposalState state;
58
+ }
59
+
60
+ enum ProposalState {
61
+ Pending,
62
+ Active,
63
+ Canceled,
64
+ Defeated,
65
+ Succeeded,
66
+ Queued,
67
+ Expired,
68
+ Executed
69
+ }
70
+
71
+ // Configuration
72
+ uint256 public proposalThreshold = 100_000 * 10**18; // 100k QVTX to propose
73
+ uint256 public quorumVotes = 4_000_000 * 10**18; // 4M QVTX (0.4% of supply)
74
+ uint256 public votingDelay = 1; // 1 block
75
+ uint256 public votingPeriod = 40320; // ~1 week (assuming 15s blocks)
76
+ uint256 public timelockDelay = 172800; // 2 days in seconds
77
+
78
+ // Storage
79
+ uint256 public proposalCount;
80
+ mapping(uint256 => Proposal) public proposals;
81
+ mapping(address => uint256) public latestProposalIds;
82
+ mapping(uint256 => uint256) public proposalTimelocks; // proposalId => execution timestamp
83
+
84
+ // Events
85
+ event ProposalCreated(
86
+ uint256 indexed id,
87
+ address indexed proposer,
88
+ string title,
89
+ address target,
90
+ uint256 startBlock,
91
+ uint256 endBlock
92
+ );
93
+ event VoteCast(
94
+ address indexed voter,
95
+ uint256 indexed proposalId,
96
+ uint8 support,
97
+ uint256 votes,
98
+ string reason
99
+ );
100
+ event ProposalCanceled(uint256 indexed id);
101
+ event ProposalQueued(uint256 indexed id, uint256 eta);
102
+ event ProposalExecuted(uint256 indexed id);
103
+ event ConfigUpdated(string param, uint256 value);
104
+
105
+ modifier onlyOwner() {
106
+ require(msg.sender == owner, "Governance: not owner");
107
+ _;
108
+ }
109
+
110
+ constructor(address _qvtxToken) {
111
+ qvtxToken = IQVTXToken(_qvtxToken);
112
+ owner = msg.sender;
113
+ }
114
+
115
+ // Create proposal
116
+ function propose(
117
+ string memory title,
118
+ string memory description,
119
+ address target,
120
+ uint256 value,
121
+ bytes memory callData
122
+ ) external returns (uint256) {
123
+ require(
124
+ qvtxToken.balanceOf(msg.sender) >= proposalThreshold,
125
+ "Governance: below proposal threshold"
126
+ );
127
+ require(
128
+ latestProposalIds[msg.sender] == 0 ||
129
+ getProposalState(latestProposalIds[msg.sender]) != ProposalState.Active,
130
+ "Governance: already has active proposal"
131
+ );
132
+
133
+ proposalCount++;
134
+ uint256 proposalId = proposalCount;
135
+
136
+ Proposal storage proposal = proposals[proposalId];
137
+ proposal.id = proposalId;
138
+ proposal.proposer = msg.sender;
139
+ proposal.title = title;
140
+ proposal.description = description;
141
+ proposal.target = target;
142
+ proposal.value = value;
143
+ proposal.callData = callData;
144
+ proposal.startBlock = block.number + votingDelay;
145
+ proposal.endBlock = proposal.startBlock + votingPeriod;
146
+
147
+ latestProposalIds[msg.sender] = proposalId;
148
+
149
+ emit ProposalCreated(
150
+ proposalId,
151
+ msg.sender,
152
+ title,
153
+ target,
154
+ proposal.startBlock,
155
+ proposal.endBlock
156
+ );
157
+
158
+ return proposalId;
159
+ }
160
+
161
+ // Cast vote
162
+ function castVote(uint256 proposalId, uint8 support) external {
163
+ _castVote(msg.sender, proposalId, support, "");
164
+ }
165
+
166
+ function castVoteWithReason(uint256 proposalId, uint8 support, string calldata reason) external {
167
+ _castVote(msg.sender, proposalId, support, reason);
168
+ }
169
+
170
+ function _castVote(
171
+ address voter,
172
+ uint256 proposalId,
173
+ uint8 support,
174
+ string memory reason
175
+ ) internal {
176
+ require(getProposalState(proposalId) == ProposalState.Active, "Governance: voting closed");
177
+ require(support <= 2, "Governance: invalid vote type");
178
+
179
+ Proposal storage proposal = proposals[proposalId];
180
+ Receipt storage receipt = proposal.receipts[voter];
181
+ require(!receipt.hasVoted, "Governance: already voted");
182
+
183
+ uint256 votes = qvtxToken.balanceOf(voter);
184
+ require(votes > 0, "Governance: no voting power");
185
+
186
+ if (support == 0) {
187
+ proposal.againstVotes += votes;
188
+ } else if (support == 1) {
189
+ proposal.forVotes += votes;
190
+ } else {
191
+ proposal.abstainVotes += votes;
192
+ }
193
+
194
+ receipt.hasVoted = true;
195
+ receipt.support = support;
196
+ receipt.votes = votes;
197
+
198
+ emit VoteCast(voter, proposalId, support, votes, reason);
199
+ }
200
+
201
+ // Queue successful proposal
202
+ function queue(uint256 proposalId) external {
203
+ require(
204
+ getProposalState(proposalId) == ProposalState.Succeeded,
205
+ "Governance: proposal not succeeded"
206
+ );
207
+
208
+ proposalTimelocks[proposalId] = block.timestamp + timelockDelay;
209
+
210
+ emit ProposalQueued(proposalId, proposalTimelocks[proposalId]);
211
+ }
212
+
213
+ // Execute queued proposal
214
+ function execute(uint256 proposalId) external payable {
215
+ require(
216
+ getProposalState(proposalId) == ProposalState.Queued,
217
+ "Governance: proposal not queued"
218
+ );
219
+ require(
220
+ block.timestamp >= proposalTimelocks[proposalId],
221
+ "Governance: timelock not passed"
222
+ );
223
+
224
+ Proposal storage proposal = proposals[proposalId];
225
+ proposal.executed = true;
226
+
227
+ (bool success, ) = proposal.target.call{value: proposal.value}(proposal.callData);
228
+ require(success, "Governance: execution failed");
229
+
230
+ emit ProposalExecuted(proposalId);
231
+ }
232
+
233
+ // Cancel proposal
234
+ function cancel(uint256 proposalId) external {
235
+ Proposal storage proposal = proposals[proposalId];
236
+ require(
237
+ msg.sender == proposal.proposer || msg.sender == owner,
238
+ "Governance: not authorized"
239
+ );
240
+ require(!proposal.executed, "Governance: already executed");
241
+
242
+ proposal.canceled = true;
243
+
244
+ emit ProposalCanceled(proposalId);
245
+ }
246
+
247
+ // Get proposal state
248
+ function getProposalState(uint256 proposalId) public view returns (ProposalState) {
249
+ require(proposalId > 0 && proposalId <= proposalCount, "Governance: invalid proposal");
250
+
251
+ Proposal storage proposal = proposals[proposalId];
252
+
253
+ if (proposal.canceled) {
254
+ return ProposalState.Canceled;
255
+ } else if (block.number <= proposal.startBlock) {
256
+ return ProposalState.Pending;
257
+ } else if (block.number <= proposal.endBlock) {
258
+ return ProposalState.Active;
259
+ } else if (proposal.forVotes <= proposal.againstVotes || proposal.forVotes < quorumVotes) {
260
+ return ProposalState.Defeated;
261
+ } else if (proposal.executed) {
262
+ return ProposalState.Executed;
263
+ } else if (proposalTimelocks[proposalId] == 0) {
264
+ return ProposalState.Succeeded;
265
+ } else if (block.timestamp < proposalTimelocks[proposalId]) {
266
+ return ProposalState.Queued;
267
+ } else if (block.timestamp >= proposalTimelocks[proposalId] + 14 days) {
268
+ return ProposalState.Expired;
269
+ } else {
270
+ return ProposalState.Queued;
271
+ }
272
+ }
273
+
274
+ // Get proposal info
275
+ function getProposal(uint256 proposalId) external view returns (ProposalInfo memory) {
276
+ Proposal storage proposal = proposals[proposalId];
277
+ return ProposalInfo({
278
+ id: proposal.id,
279
+ proposer: proposal.proposer,
280
+ title: proposal.title,
281
+ description: proposal.description,
282
+ forVotes: proposal.forVotes,
283
+ againstVotes: proposal.againstVotes,
284
+ abstainVotes: proposal.abstainVotes,
285
+ startBlock: proposal.startBlock,
286
+ endBlock: proposal.endBlock,
287
+ executed: proposal.executed,
288
+ canceled: proposal.canceled,
289
+ state: getProposalState(proposalId)
290
+ });
291
+ }
292
+
293
+ // Get vote receipt
294
+ function getReceipt(uint256 proposalId, address voter) external view returns (Receipt memory) {
295
+ return proposals[proposalId].receipts[voter];
296
+ }
297
+
298
+ // Admin functions
299
+ function setProposalThreshold(uint256 _threshold) external onlyOwner {
300
+ proposalThreshold = _threshold;
301
+ emit ConfigUpdated("proposalThreshold", _threshold);
302
+ }
303
+
304
+ function setQuorumVotes(uint256 _quorum) external onlyOwner {
305
+ quorumVotes = _quorum;
306
+ emit ConfigUpdated("quorumVotes", _quorum);
307
+ }
308
+
309
+ function setVotingPeriod(uint256 _period) external onlyOwner {
310
+ votingPeriod = _period;
311
+ emit ConfigUpdated("votingPeriod", _period);
312
+ }
313
+
314
+ function setTimelockDelay(uint256 _delay) external onlyOwner {
315
+ timelockDelay = _delay;
316
+ emit ConfigUpdated("timelockDelay", _delay);
317
+ }
318
+
319
+ function transferOwnership(address newOwner) external onlyOwner {
320
+ require(newOwner != address(0), "Governance: zero address");
321
+ owner = newOwner;
322
+ }
323
+
324
+ receive() external payable {}
325
+ }