Skip to content

Commit 4ad8007

Browse files
committed
Gave DAO 3% direct reward distribution
1 parent 5e5aa4a commit 4ad8007

File tree

3 files changed

+100
-64
lines changed

3 files changed

+100
-64
lines changed

src/SmartnodesToken.sol

Lines changed: 72 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
7171

7272
/** Constants */
7373
uint8 private constant VALIDATOR_REWARD_PERCENTAGE = 10;
74+
uint8 private constant DAO_REWARD_PERCENTAGE = 3;
7475
uint256 private constant BASE_EMISSION_RATE = 5832e18; // Base hourly emission rate
7576
uint256 private constant TAIL_EMISSION = 420e18; // Base hourly tail emission
7677
uint256 private constant REWARD_PERIOD = 365 days;
@@ -91,18 +92,17 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
9192
uint256 public s_validatorLockAmount;
9293
uint256 public s_userLockAmount;
9394

94-
uint256 public s_currentDistributionId;
95+
// Payment and rewards tracking (SNO + ETH)
9596
PaymentAmounts public s_totalUnclaimed;
9697
PaymentAmounts public s_totalEscrowed;
97-
uint256 public s_totalLocked;
98+
uint256 public s_totalLocked; // SNO
99+
uint256 public s_totalETHDeposited;
100+
uint256 public s_totalETHWithdrawn;
98101

102+
uint256 public s_currentDistributionId;
99103
uint256 public s_distributionInterval;
100104
uint256 public s_lastDistributionTime;
101105

102-
// ETH balance tracking
103-
uint256 public s_totalETHDeposited;
104-
uint256 public s_totalETHWithdrawn;
105-
106106
mapping(uint256 => MerkleDistribution) public s_distributions;
107107
mapping(uint256 => mapping(address => bool)) public s_claimed;
108108
mapping(address => LockedTokens) private s_lockedTokens;
@@ -120,8 +120,9 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
120120
}
121121

122122
modifier onlyDAO() {
123-
if (s_dao != address(0)) {
124-
if (msg.sender != s_dao) {
123+
address dao = s_dao;
124+
if (dao != address(0)) {
125+
if (msg.sender != dao) {
125126
revert Token__OnlyDAO();
126127
}
127128
}
@@ -162,6 +163,11 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
162163
uint256 totalSno,
163164
uint256 totalEth
164165
);
166+
event DAORewardsDistributed(
167+
uint256 indexed distributionId,
168+
uint256 snoAmount,
169+
uint256 ethAmount
170+
);
165171
event ETHDeposited(address indexed from, uint256 amount);
166172
event ETHWithdrawn(address indexed to, uint256 amount);
167173
event DistributionIntervalUpdated(uint256 oldInterval, uint256 newInterval);
@@ -315,7 +321,7 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
315321
ethShare += ethRemainder;
316322
}
317323

318-
_payValidator(validator, snoShare, ethShare);
324+
_payAccount(validator, snoShare, ethShare);
319325
}
320326

321327
emit ValidatorRewardsDistributed(
@@ -332,7 +338,7 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
332338
* @param snoAmount Amount of SNO tokens to mint/send
333339
* @param ethAmount Amount of ETH to transfer
334340
*/
335-
function _payValidator(
341+
function _payAccount(
336342
address validator,
337343
uint256 snoAmount,
338344
uint256 ethAmount
@@ -362,9 +368,9 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
362368
* @param _payments Additional payments to be added to the current emission rate
363369
* @param _approvedValidators List of validators that voted
364370
* @param _dustValidator Address of validator to receive dust rewards (usually the proposal executor)
365-
* @dev Workers receive 90% of the total reward, validators receive 10%
366-
* @dev Rewards are distributed with bias towards specified validator, then proportionally to workers based on their capacities
367-
* @dev This function is called by SmartnodesCore during state updates to distribute rewards.
371+
* @dev Workers receive 85% of the total reward, validators receive 10%, dao receives 5%
372+
* @dev Rewards are distributed proportionally to workers based on their capacities.
373+
* @dev This function is called periodically by SmartnodesCore during state updates to distribute rewards.
368374
*/
369375
function createMerkleDistribution(
370376
bytes32 _merkleRoot,
@@ -389,54 +395,71 @@ contract SmartnodesToken is ERC20, ERC20Permit, ERC20Votes, ReentrancyGuard {
389395

390396
uint256 distributionId = ++s_currentDistributionId;
391397

392-
if (_totalCapacity == 0) {
393-
// All rewards go to validators
394-
_distributeValidatorRewards(
395-
_approvedValidators,
396-
totalReward,
397-
distributionId,
398-
_dustValidator
399-
);
400-
return; // exit early
401-
}
402-
403-
// Split validator/worker share from the remaining pool
404-
PaymentAmounts memory validatorReward = PaymentAmounts({
398+
// Calculate reward distributions
399+
PaymentAmounts memory daoReward = PaymentAmounts({
405400
sno: uint128(
406-
(uint256(totalReward.sno) * VALIDATOR_REWARD_PERCENTAGE) / 100
401+
(uint256(totalReward.sno) * DAO_REWARD_PERCENTAGE) / 100
407402
),
408403
eth: uint128(
409-
(uint256(totalReward.eth) * VALIDATOR_REWARD_PERCENTAGE) / 100
404+
(uint256(totalReward.eth) * DAO_REWARD_PERCENTAGE) / 100
410405
)
411406
});
407+
PaymentAmounts memory validatorReward;
412408

413-
PaymentAmounts memory workerReward = PaymentAmounts({
414-
sno: totalReward.sno - validatorReward.sno,
415-
eth: totalReward.eth - validatorReward.eth
416-
});
409+
if (_totalCapacity == 0) {
410+
// If no workers, just give to validators
411+
validatorReward = PaymentAmounts({
412+
sno: totalReward.sno - daoReward.sno,
413+
eth: totalReward.eth - daoReward.eth
414+
});
415+
} else {
416+
// Split validator/worker share from the remaining pool
417+
validatorReward = PaymentAmounts({
418+
sno: uint128(
419+
(uint256(totalReward.sno) * VALIDATOR_REWARD_PERCENTAGE) /
420+
100
421+
),
422+
eth: uint128(
423+
(uint256(totalReward.eth) * VALIDATOR_REWARD_PERCENTAGE) /
424+
100
425+
)
426+
});
417427

418-
// Store merkle distribution (only worker rewards are stored for claiming)
419-
s_distributions[distributionId] = MerkleDistribution({
420-
merkleRoot: _merkleRoot,
421-
workerReward: workerReward,
422-
totalCapacity: _totalCapacity,
423-
active: true,
424-
timestamp: block.timestamp
425-
});
428+
PaymentAmounts memory workerReward = PaymentAmounts({
429+
sno: totalReward.sno - validatorReward.sno - daoReward.sno,
430+
eth: totalReward.eth - validatorReward.eth - daoReward.eth
431+
});
432+
433+
// Store merkle distribution (only worker rewards are stored for claiming)
434+
s_distributions[distributionId] = MerkleDistribution({
435+
merkleRoot: _merkleRoot,
436+
workerReward: workerReward,
437+
totalCapacity: _totalCapacity,
438+
active: true,
439+
timestamp: block.timestamp
440+
});
426441

427-
// Update total unclaimed (only worker rewards)
428-
s_totalUnclaimed.sno += workerReward.sno;
429-
s_totalUnclaimed.eth += workerReward.eth;
442+
// Update total unclaimed (only worker rewards)
443+
s_totalUnclaimed.sno += workerReward.sno;
444+
s_totalUnclaimed.eth += workerReward.eth;
430445

431-
emit MerkleDistributionCreated(
446+
emit MerkleDistributionCreated(
447+
distributionId,
448+
_merkleRoot,
449+
totalReward.sno,
450+
totalReward.eth,
451+
block.timestamp
452+
);
453+
}
454+
// Distribute DAO rewards
455+
_payAccount(s_dao, daoReward.sno, daoReward.eth);
456+
emit DAORewardsDistributed(
432457
distributionId,
433-
_merkleRoot,
434-
totalReward.sno,
435-
totalReward.eth,
436-
block.timestamp
458+
daoReward.sno,
459+
daoReward.eth
437460
);
438461

439-
// Distribute validator rewards immediately with bias
462+
// Distribute validator rewards
440463
_distributeValidatorRewards(
441464
_approvedValidators,
442465
validatorReward,

test/BaseTest.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ abstract contract BaseSmartnodesTest is Test {
1515
uint256 constant DEPLOYMENT_MULTIPLIER = 1;
1616
uint128 constant INTERVAL_SECONDS = 1 minutes;
1717
uint256 constant VALIDATOR_REWARD_PERCENTAGE = 10;
18+
uint256 constant DAO_REWARD_PERCENTAGE = 3;
1819
uint256 constant ADDITIONAL_SNO_PAYMENT = 1000e18;
1920
uint256 constant ADDITIONAL_ETH_PAYMENT = 5 ether;
2021
uint256 constant INITIAL_EMISSION_RATE = 5832e18;

test/TokenTest.t.sol

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -430,8 +430,14 @@ contract SmartnodesTokenTest is BaseSmartnodesTest {
430430
VALIDATOR_REWARD_PERCENTAGE) / 100;
431431
uint256 expectedValidatorEth = (totalEthReward *
432432
VALIDATOR_REWARD_PERCENTAGE) / 100;
433-
uint256 expectedWorkerSno = totalSnoReward - expectedValidatorSno;
434-
uint256 expectedWorkerEth = totalEthReward - expectedValidatorEth;
433+
uint256 expectedDaoSno = (totalSnoReward * DAO_REWARD_PERCENTAGE) / 100;
434+
uint256 expectedDaoEth = (totalEthReward * DAO_REWARD_PERCENTAGE) / 100;
435+
uint256 expectedWorkerSno = totalSnoReward -
436+
expectedValidatorSno -
437+
expectedDaoSno;
438+
uint256 expectedWorkerEth = totalEthReward -
439+
expectedValidatorEth -
440+
expectedDaoEth;
435441

436442
assertEq(
437443
workerReward.sno,
@@ -469,15 +475,18 @@ contract SmartnodesTokenTest is BaseSmartnodesTest {
469475
ADDITIONAL_SNO_PAYMENT;
470476
uint256 totalEthReward = ADDITIONAL_ETH_PAYMENT;
471477

472-
// Validator rewards (already paid out directly in contract)
473-
uint256 validatorSnoReward = (totalSnoReward *
478+
uint256 expectedValidatorSno = (totalSnoReward *
474479
VALIDATOR_REWARD_PERCENTAGE) / 100;
475-
uint256 validatorEthReward = (totalEthReward *
480+
uint256 expectedValidatorEth = (totalEthReward *
476481
VALIDATOR_REWARD_PERCENTAGE) / 100;
477-
478-
// Worker reward pools
479-
uint256 expectedWorkerSno = totalSnoReward - validatorSnoReward;
480-
uint256 expectedWorkerEth = totalEthReward - validatorEthReward;
482+
uint256 expectedDaoSno = (totalSnoReward * DAO_REWARD_PERCENTAGE) / 100;
483+
uint256 expectedDaoEth = (totalEthReward * DAO_REWARD_PERCENTAGE) / 100;
484+
uint256 expectedWorkerSno = totalSnoReward -
485+
expectedValidatorSno -
486+
expectedDaoSno;
487+
uint256 expectedWorkerEth = totalEthReward -
488+
expectedValidatorEth -
489+
expectedDaoEth;
481490

482491
uint256 totalCapacity = 0;
483492
for (uint256 i = 0; i < participants.length; i++) {
@@ -559,22 +568,25 @@ contract SmartnodesTokenTest is BaseSmartnodesTest {
559568

560569
// Pre-claim balances
561570
uint256 preClaimBalance = token.balanceOf(worker.addr);
562-
uint256 preClaimEth = worker.addr.balance;
563571

564572
// Claim rewards
565573
vm.prank(worker.addr);
566574
token.claimMerkleRewards(distributionId, worker.capacity, proof);
567575

568-
// Calculate expected rewards
569-
(, SmartnodesToken.PaymentAmounts memory workerReward, , , ) = token
570-
.s_distributions(distributionId);
571-
572576
uint256 validatorSnoReward = ((INITIAL_EMISSION_RATE *
573577
DEPLOYMENT_MULTIPLIER +
574578
ADDITIONAL_SNO_PAYMENT) * VALIDATOR_REWARD_PERCENTAGE) / 100;
579+
580+
uint256 daoSnoReward = ((INITIAL_EMISSION_RATE *
581+
DEPLOYMENT_MULTIPLIER +
582+
ADDITIONAL_SNO_PAYMENT) * DAO_REWARD_PERCENTAGE) / 100;
583+
575584
uint256 expectedWorkerSno = (INITIAL_EMISSION_RATE *
576585
DEPLOYMENT_MULTIPLIER +
577-
ADDITIONAL_SNO_PAYMENT) - validatorSnoReward;
586+
ADDITIONAL_SNO_PAYMENT) -
587+
validatorSnoReward -
588+
daoSnoReward;
589+
578590
uint256 expectedWorkerSnoShare = (expectedWorkerSno * worker.capacity) /
579591
totalCapacity;
580592

0 commit comments

Comments
 (0)