@@ -31,7 +31,7 @@ contract SmartnodesCoordinator is ReentrancyGuard {
3131 ISmartnodesCore private immutable i_smartnodesCore;
3232 uint8 private immutable i_requiredApprovalsPercentage;
3333
34- // Pack time-related variables
34+ // Packed time-related variables
3535 struct TimeConfig {
3636 uint128 updateTime;
3737 uint128 lastExecutionTime;
@@ -53,10 +53,9 @@ contract SmartnodesCoordinator is ReentrancyGuard {
5353 address [] public validators;
5454 address [] public currentRoundValidators;
5555 Proposal[] public currentProposals;
56-
5756 mapping (address => bool ) public isValidator;
58- mapping (address => uint256 ) public validatorVote; // Changed to uint256 for proposal index
59- mapping (uint256 => uint256 ) private proposalReadyBitmap ;
57+ mapping (address => uint256 ) public validatorVote;
58+ mapping (address => uint8 ) public hasSubmittedProposal ;
6059
6160 // ============= Events ==============
6261 event ProposalCreated (
@@ -143,6 +142,7 @@ contract SmartnodesCoordinator is ReentrancyGuard {
143142 function createProposal (
144143 bytes32 proposalHash
145144 ) external onlyEligibleValidator nonReentrant {
145+ address sender = msg .sender ;
146146 TimeConfig memory tc = timeConfig;
147147
148148 // Allow proposals only after 'updateTime' has passed since last executed proposal
@@ -155,7 +155,7 @@ contract SmartnodesCoordinator is ReentrancyGuard {
155155 uint256 proposalLength = currentProposals.length ;
156156
157157 for (uint8 i = 0 ; i < proposalLength; i++ ) {
158- if (currentProposals[i].creator == msg . sender ) {
158+ if (currentProposals[i].creator == sender) {
159159 hasExistingProposal = true ;
160160 break ;
161161 }
@@ -165,22 +165,22 @@ contract SmartnodesCoordinator is ReentrancyGuard {
165165 revert Coordinator__AlreadySubmittedProposal ();
166166 }
167167
168- uint8 proposalNum = uint8 (currentProposals.length ) + 1 ;
168+ uint8 proposalNum = uint8 (currentProposals.length ) + 1 ; // +1 to distinguish from 0 (no vote)
169169
170170 // Create new proposal
171171 currentProposals.push (
172172 Proposal ({
173- creator: msg . sender ,
173+ creator: sender,
174174 proposalNum: proposalNum,
175175 votes: 1 , // Creator automatically votes for their own proposal
176176 proposalHash: proposalHash
177177 })
178178 );
179179
180180 // Record creator's vote
181- validatorVote[msg . sender ] = proposalNum + 1 ; // +1 to distinguish from 0 (no vote)
182-
183- emit ProposalCreated (proposalNum, proposalHash, msg . sender );
181+ validatorVote[sender] = proposalNum;
182+ hasSubmittedProposal[sender] = proposalNum;
183+ emit ProposalCreated (proposalNum, proposalHash, sender);
184184 }
185185
186186 /**
@@ -235,6 +235,7 @@ contract SmartnodesCoordinator is ReentrancyGuard {
235235 if (proposal.creator != msg .sender ) {
236236 revert Coordinator__MustBeProposalCreator ();
237237 }
238+
238239 if (proposal.votes < _calculateRequiredVotes ()) {
239240 revert Coordinator__NotEnoughVotes ();
240241 }
@@ -378,7 +379,6 @@ contract SmartnodesCoordinator is ReentrancyGuard {
378379 _resetValidatorStates ();
379380 _selectNewRoundValidators ();
380381 delete currentProposals; // Clear proposals for new round
381-
382382 timeConfig.lastExecutionTime = uint128 (block .timestamp );
383383 nextProposalId++ ;
384384 }
@@ -389,7 +389,9 @@ contract SmartnodesCoordinator is ReentrancyGuard {
389389
390390 unchecked {
391391 for (uint256 i = 0 ; i < validatorCount; ++ i) {
392- delete validatorVote[vals[i]];
392+ address val = vals[i];
393+ delete validatorVote[val];
394+ delete hasSubmittedProposal[val];
393395 }
394396 }
395397 }
@@ -454,8 +456,7 @@ contract SmartnodesCoordinator is ReentrancyGuard {
454456 unchecked {
455457 for (uint256 i = 0 ; i < validatorCount; ++ i) {
456458 address validator = vals[i];
457- if (validatorVote[validator] == proposalId + 1 ) {
458- // +1 offset
459+ if (validatorVote[validator] == proposalId) {
459460 approvedValidators[approvedCount++ ] = validator;
460461 }
461462 }
@@ -506,6 +507,16 @@ contract SmartnodesCoordinator is ReentrancyGuard {
506507 }
507508
508509 // ============= View Functions =============
510+ function isProposalReady (uint8 proposalId ) external view returns (bool ) {
511+ if (proposalId == 0 || proposalId > currentProposals.length ) {
512+ revert Coordinator__InvalidProposalNumber ();
513+ }
514+
515+ Proposal storage proposal = currentProposals[proposalId - 1 ];
516+
517+ return (proposal.votes >= _calculateRequiredVotes ());
518+ }
519+
509520 function isRoundExpired () external view returns (bool ) {
510521 return _isCurrentRoundExpired ();
511522 }
0 commit comments