Skip to content

Commit 5dafafe

Browse files
committed
fix: update election to include department
1 parent d08cf40 commit 5dafafe

File tree

10 files changed

+1292
-269
lines changed

10 files changed

+1292
-269
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,14 @@ forge build
9191
The VotsEngine contract is currently deployed and live on multiple testnets:
9292

9393
### Sepolia Testnet
94-
**VotsEngine Contract**: `0xda115D0e2F8e24c2F4E487bd9590AC56Fb750D06`
94+
**VotsEngine Contract**: `0x95935ceb316E5585E493602a1c2ADCEf832848c7`
9595

96-
**NFT Contract**: `0x9e1dbfAD78De1ef5BCbF8fA912BdA4B581663921`
96+
**NFT Contract**: `0x34bc2AbA1C4A49e22E83D99e36602FC332aD56C1`
9797

9898
**Network**: Sepolia Testnet
9999
- **Chain ID**: 11155111
100-
- **VotsEngine Block Explorer**: https://sepolia.etherscan.io/address/0xda115D0e2F8e24c2F4E487bd9590AC56Fb750D06
101-
- **NFT Contract Block Explorer**: https://sepolia.etherscan.io/address/0x9e1dbfAD78De1ef5BCbF8fA912BdA4B581663921
100+
- **VotsEngine Block Explorer**: https://sepolia.etherscan.io/address/0x95935ceb316E5585E493602a1c2ADCEf832848c7
101+
- **NFT Contract Block Explorer**: https://sepolia.etherscan.io/address/0x34bc2AbA1C4A49e22E83D99e36602FC332aD56C1
102102

103103
### Fuji Testnet (Avalanche)
104104
**VotsEngine Contract**: `0xed7eA5221041A1982d9d257c9617B1448032838d`
@@ -120,7 +120,7 @@ You can interact with the deployed contract using:
120120

121121
```javascript
122122
// Example using ethers.js with Sepolia
123-
const sepoliaContractAddress = "0xda115D0e2F8e24c2F4E487bd9590AC56Fb750D06....";
123+
const sepoliaContractAddress = "0x95935ceb316E5585E493602a1c2ADCEf832848c7....";
124124
const VotsEngineSepoliaClient = new ethers.Contract(sepoliaContractAddress, abi, sepoliaProvider);
125125

126126
// Example using ethers.js with Fuji
@@ -146,7 +146,7 @@ The VotsEngine system requires a **hybrid physical-digital infrastructure**:
146146
### Deploying the System
147147

148148
The VotsEngine is already deployed on multiple testnets:
149-
- **Sepolia**: `0xda115D0e2F8e24c2F4E487bd9590AC56Fb750D06....`
149+
- **Sepolia**: `0x95935ceb316E5585E493602a1c2ADCEf832848c7....`
150150
- **Fuji (Avalanche)**: `0xed7eA5221041A1982d9d257c9617B1448032838d`
151151

152152
For local development or custom deployments:
@@ -161,7 +161,7 @@ Or connect to the existing deployments:
161161
```javascript
162162
// Connect to Sepolia deployment
163163
const VotsEngineSepoliaClient = new ethers.Contract(
164-
"0xda115D0e2F8e24c2F4E487bd9590AC56Fb750D06....",
164+
"0x95935ceb316E5585E493602a1c2ADCEf832848c7....",
165165
VotsEngineABI,
166166
sepoliaProvider
167167
);

broadcast/DeployVotsEngine.s.sol/11155111/run-1752135320.json

Lines changed: 407 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/DeployVotsEngine.s.sol/11155111/run-1752136100.json

Lines changed: 407 additions & 0 deletions
Large diffs are not rendered by default.

broadcast/DeployVotsEngine.s.sol/11155111/run-latest.json

Lines changed: 161 additions & 161 deletions
Large diffs are not rendered by default.

src/Election.sol

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -279,17 +279,18 @@ contract Election is IElection, Ownable {
279279
/**
280280
* @dev Returns Returns a list of all voters
281281
*/
282-
function getAllVoters()
283-
public
284-
view
285-
onElectionEnded
286-
returns (ElectionVoter[] memory)
287-
{
282+
function getAllVoters() public view returns (ElectionVoter[] memory) {
288283
ElectionVoter[] memory all = new ElectionVoter[](
289284
_registeredVotersList.length
290285
);
291286
for (uint256 i = 0; i < _registeredVotersList.length; i++) {
292-
all[i] = _votersMap[_registeredVotersList[i]];
287+
ElectionVoter memory voter = _votersMap[_registeredVotersList[i]];
288+
all[i] = ElectionVoter({
289+
name: voter.name,
290+
level: voter.level,
291+
department: voter.department,
292+
voterState: VoterState.REGISTERED
293+
});
293294
}
294295
return all;
295296
}
@@ -818,6 +819,7 @@ contract Election is IElection, Ownable {
818819
ElectionVoter memory registeredVoter = ElectionVoter({
819820
name: voter.name,
820821
level: voter.level,
822+
department: voter.department,
821823
voterState: VoterState.REGISTERED
822824
});
823825
// add to votersList if the state is unknown

src/VotsElectionNFT.sol

Lines changed: 94 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ import {IVotsElectionNft} from "./interfaces/IVotsElectionNft.sol";
1414
* @notice NFT contract that mints tokens to election creators as proof of election creation
1515
* @dev This contract creates unique NFTs for each election created through the VotsEngine
1616
*/
17-
contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable {
17+
contract VotsElectionNft is
18+
IVotsElectionNft,
19+
ERC721,
20+
ERC721URIStorage,
21+
Ownable
22+
{
1823
using Strings for uint256;
1924

2025
// ====================================================================
@@ -36,7 +41,8 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
3641
mapping(uint256 => uint256) public electionTokenToNftToken;
3742

3843
// Mapping from NFT token ID to election data
39-
mapping(uint256 => IVotsElectionNft.ElectionNftData) public nftTokenToElectionData;
44+
mapping(uint256 => IVotsElectionNft.ElectionNftData)
45+
public nftTokenToElectionData;
4046

4147
// ====================================================================
4248
// Constructor
@@ -80,15 +86,16 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
8086
nftTokenId = _tokenIdCounter;
8187

8288
// Store election data
83-
IVotsElectionNft.ElectionNftData memory nftData = IVotsElectionNft.ElectionNftData({
84-
electionTokenId: electionTokenId,
85-
electionName: electionName,
86-
creator: creator,
87-
creationTimestamp: block.timestamp,
88-
electionDescription: electionDescription,
89-
startTime: startTime,
90-
endTime: endTime
91-
});
89+
IVotsElectionNft.ElectionNftData memory nftData = IVotsElectionNft
90+
.ElectionNftData({
91+
electionTokenId: electionTokenId,
92+
electionName: electionName,
93+
creator: creator,
94+
creationTimestamp: block.timestamp,
95+
electionDescription: electionDescription,
96+
startTime: startTime,
97+
endTime: endTime
98+
});
9299

93100
nftTokenToElectionData[nftTokenId] = nftData;
94101
electionTokenToNftToken[electionTokenId] = nftTokenId;
@@ -97,10 +104,18 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
97104
_safeMint(creator, nftTokenId);
98105

99106
// Set token URI
100-
string memory generatedTokenURI = _generateTokenURI(nftTokenId, nftData);
107+
string memory generatedTokenURI = _generateTokenURI(
108+
nftTokenId,
109+
nftData
110+
);
101111
_setTokenURI(nftTokenId, generatedTokenURI);
102112

103-
emit IVotsElectionNft.ElectionNftMinted(nftTokenId, electionTokenId, creator, electionName);
113+
emit IVotsElectionNft.ElectionNftMinted(
114+
nftTokenId,
115+
electionTokenId,
116+
creator,
117+
electionName
118+
);
104119

105120
return nftTokenId;
106121
}
@@ -111,11 +126,10 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
111126
* @param nftData The election data for this NFT
112127
* @return The complete token URI with embedded metadata
113128
*/
114-
function _generateTokenURI(uint256 nftTokenId, IVotsElectionNft.ElectionNftData memory nftData)
115-
internal
116-
pure
117-
returns (string memory)
118-
{
129+
function _generateTokenURI(
130+
uint256 nftTokenId,
131+
IVotsElectionNft.ElectionNftData memory nftData
132+
) internal pure returns (string memory) {
119133
// Create SVG image
120134
string memory svg = _generateSVG(nftData);
121135

@@ -163,46 +177,52 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
163177
* @param nftData The election data
164178
* @return SVG string
165179
*/
166-
function _generateSVG(IVotsElectionNft.ElectionNftData memory nftData) internal pure returns (string memory) {
167-
return string(
168-
abi.encodePacked(
169-
'<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">',
170-
"<defs>",
171-
'<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">',
172-
'<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />',
173-
'<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />',
174-
"</linearGradient>",
175-
"</defs>",
176-
'<rect width="400" height="400" fill="url(#grad1)" rx="20"/>',
177-
'<rect x="20" y="20" width="360" height="360" fill="none" stroke="white" stroke-width="2" rx="15"/>',
178-
'<text x="200" y="60" font-family="Arial, sans-serif" font-size="24" font-weight="bold" text-anchor="middle" fill="white">ELECTION CERTIFICATE</text>',
179-
'<text x="200" y="100" font-family="Arial, sans-serif" font-size="14" text-anchor="middle" fill="white">VotsEngine Creation Proof</text>',
180-
'<text x="50" y="140" font-family="Arial, sans-serif" font-size="14" fill="white">Election:</text>',
181-
'<text x="50" y="165" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">',
182-
_truncateString(nftData.electionName, 25),
183-
"</text>",
184-
'<text x="50" y="200" font-family="Arial, sans-serif" font-size="14" fill="white">Type:</text>',
185-
'<text x="50" y="225" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">',
186-
nftData.electionDescription,
187-
"</text>",
188-
'<text x="50" y="260" font-family="Arial, sans-serif" font-size="14" fill="white">Token ID:</text>',
189-
'<text x="50" y="285" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">#',
190-
nftData.electionTokenId.toString(),
191-
"</text>",
192-
'<text x="50" y="320" font-family="Arial, sans-serif" font-size="14" fill="white">Creator:</text>',
193-
'<text x="50" y="345" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">',
194-
_truncateAddress(nftData.creator),
195-
"</text>",
196-
'<text x="200" y="375" font-family="Arial, sans-serif" font-size="12" text-anchor="middle" fill="white">powered by YENIYAN-Group</text>',
197-
"</svg>"
198-
)
199-
);
180+
function _generateSVG(
181+
IVotsElectionNft.ElectionNftData memory nftData
182+
) internal pure returns (string memory) {
183+
return
184+
string(
185+
abi.encodePacked(
186+
'<svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">',
187+
"<defs>",
188+
'<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="100%">',
189+
'<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />',
190+
'<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />',
191+
"</linearGradient>",
192+
"</defs>",
193+
'<rect width="400" height="400" fill="url(#grad1)" rx="20"/>',
194+
'<rect x="20" y="20" width="360" height="360" fill="none" stroke="white" stroke-width="2" rx="15"/>',
195+
'<text x="200" y="60" font-family="Arial, sans-serif" font-size="24" font-weight="bold" text-anchor="middle" fill="white">ELECTION CERTIFICATE</text>',
196+
'<text x="200" y="100" font-family="Arial, sans-serif" font-size="14" text-anchor="middle" fill="white">VotsEngine Creation Proof</text>',
197+
'<text x="50" y="140" font-family="Arial, sans-serif" font-size="14" fill="white">Election:</text>',
198+
'<text x="50" y="165" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">',
199+
_truncateString(nftData.electionName, 25),
200+
"</text>",
201+
'<text x="50" y="200" font-family="Arial, sans-serif" font-size="14" fill="white">Description:</text>',
202+
'<text x="50" y="225" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">',
203+
_truncateString(nftData.electionDescription, 30),
204+
"</text>",
205+
'<text x="50" y="260" font-family="Arial, sans-serif" font-size="14" fill="white">Token ID:</text>',
206+
'<text x="50" y="285" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">#',
207+
nftData.electionTokenId.toString(),
208+
"</text>",
209+
'<text x="50" y="320" font-family="Arial, sans-serif" font-size="14" fill="white">Creator:</text>',
210+
'<text x="50" y="345" font-family="Arial, sans-serif" font-size="16" font-weight="bold" fill="white">',
211+
_truncateAddress(nftData.creator),
212+
"</text>",
213+
'<text x="200" y="375" font-family="Arial, sans-serif" font-size="12" text-anchor="middle" fill="white">powered by YENIYAN-Group</text>',
214+
"</svg>"
215+
)
216+
);
200217
}
201218

202219
/**
203220
* @dev Truncates a string to specified length with ellipsis
204221
*/
205-
function _truncateString(string memory str, uint256 maxLength) internal pure returns (string memory) {
222+
function _truncateString(
223+
string memory str,
224+
uint256 maxLength
225+
) internal pure returns (string memory) {
206226
bytes memory strBytes = bytes(str);
207227
if (strBytes.length <= maxLength) {
208228
return str;
@@ -219,7 +239,9 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
219239
/**
220240
* @dev Truncates an address for display
221241
*/
222-
function _truncateAddress(address addr) internal pure returns (string memory) {
242+
function _truncateAddress(
243+
address addr
244+
) internal pure returns (string memory) {
223245
string memory addrStr = Strings.toHexString(uint160(addr), 20);
224246
bytes memory addrBytes = bytes(addrStr);
225247

@@ -251,7 +273,9 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
251273
* @dev Returns election data for a given NFT token ID
252274
* @param nftTokenId The NFT token ID
253275
*/
254-
function getElectionData(uint256 nftTokenId) external view returns (IVotsElectionNft.ElectionNftData memory) {
276+
function getElectionData(
277+
uint256 nftTokenId
278+
) external view returns (IVotsElectionNft.ElectionNftData memory) {
255279
if (!_exists(nftTokenId)) {
256280
revert VotsElectionNft__TokenDoesNotExist();
257281
}
@@ -262,15 +286,19 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
262286
* @dev Returns NFT token ID for a given election token ID
263287
* @param electionTokenId The election token ID
264288
*/
265-
function getNftTokenByElectionId(uint256 electionTokenId) external view returns (uint256) {
289+
function getNftTokenByElectionId(
290+
uint256 electionTokenId
291+
) external view returns (uint256) {
266292
return electionTokenToNftToken[electionTokenId];
267293
}
268294

269295
/**
270296
* @dev Returns all NFTs owned by an address
271297
* @param owner The owner address
272298
*/
273-
function getOwnedTokens(address owner) external view returns (uint256[] memory) {
299+
function getOwnedTokens(
300+
address owner
301+
) external view returns (uint256[] memory) {
274302
uint256 balance = balanceOf(owner);
275303
uint256[] memory tokens = new uint256[](balance);
276304
uint256 currentIndex = 0;
@@ -291,7 +319,9 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
291319
* @dev Checks if an NFT exists for a given election
292320
* @param electionTokenId The election token ID
293321
*/
294-
function electionNftExists(uint256 electionTokenId) external view returns (bool) {
322+
function electionNftExists(
323+
uint256 electionTokenId
324+
) external view returns (bool) {
295325
return electionTokenToNftToken[electionTokenId] != 0;
296326
}
297327

@@ -303,11 +333,15 @@ contract VotsElectionNft is IVotsElectionNft, ERC721, ERC721URIStorage, Ownable
303333
// Override Functions
304334
// ====================================================================
305335

306-
function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
336+
function tokenURI(
337+
uint256 tokenId
338+
) public view override(ERC721, ERC721URIStorage) returns (string memory) {
307339
return super.tokenURI(tokenId);
308340
}
309341

310-
function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage) returns (bool) {
342+
function supportsInterface(
343+
bytes4 interfaceId
344+
) public view override(ERC721, ERC721URIStorage) returns (bool) {
311345
return super.supportsInterface(interfaceId);
312346
}
313347
}

src/interfaces/IElection.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ interface IElection {
6262
struct VoterInfoDTO {
6363
string name;
6464
string matricNo;
65+
string department;
6566
uint256 level;
6667
}
6768

@@ -70,6 +71,7 @@ interface IElection {
7071
*/
7172
struct ElectionVoter {
7273
string name;
74+
string department;
7375
uint256 level;
7476
VoterState voterState;
7577
}

test/unit/ElectionTest.t.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,36 +68,42 @@ contract ElectionTest is Test {
6868
IElection.VoterInfoDTO({
6969
name: "Voter1",
7070
matricNo: "VOT001",
71+
department: "Computer Science",
7172
level: 100
7273
});
7374
IElection.VoterInfoDTO voterTwo =
7475
IElection.VoterInfoDTO({
7576
name: "Voter2",
7677
matricNo: "VOT002",
78+
department: "Computer Science",
7779
level: 100
7880
});
7981
IElection.VoterInfoDTO voterThree =
8082
IElection.VoterInfoDTO({
8183
name: "Voter3",
8284
matricNo: "VOT003",
85+
department: "Computer Science",
8386
level: 100
8487
});
8588
IElection.VoterInfoDTO voterFour =
8689
IElection.VoterInfoDTO({
8790
name: "Voter4",
8891
matricNo: "VOT004",
92+
department: "Computer Science",
8993
level: 100
9094
});
9195
IElection.VoterInfoDTO voterFive =
9296
IElection.VoterInfoDTO({
9397
name: "Voter5",
9498
matricNo: "VOT005",
99+
department: "Computer Science",
95100
level: 100
96101
});
97102
IElection.VoterInfoDTO unknownVoter =
98103
IElection.VoterInfoDTO({
99104
name: "This Unknown",
100105
matricNo: "VOT007",
106+
department: "Computer Science",
101107
level: 100
102108
});
103109

0 commit comments

Comments
 (0)