Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions protocol-contracts/staking/contracts/OperatorRewarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ contract OperatorRewarder {
/// @notice Emitted when an address is authorized to claim rewards on behalf of the receiver address.
event ClaimerAuthorized(address receiver, address claimer);

/// @notice Emitted when rewards are claimed for an account.
event RewardsClaimed(address indexed account, uint256 amount);

/// @notice Emitted when fees are claimed by the beneficiary.
event FeeClaimed(address indexed beneficiary, uint256 amount);

/// @notice Error for invalid claimer address.
error InvalidClaimer(address claimer);

Expand Down Expand Up @@ -152,6 +158,7 @@ contract OperatorRewarder {
_rewardsPaid[account] += SafeCast.toInt256(earned_);
_totalRewardsPaid += earned_;
_doTransferOut(account, earned_);
emit RewardsClaimed(account, earned_);
}
}

Expand Down Expand Up @@ -373,6 +380,7 @@ contract OperatorRewarder {
_lastClaimTotalAssetsPlusPaidRewards = totalAssetsPlusPaidRewards - unpaidFee_;
if (unpaidFee_ > 0) {
_doTransferOut(beneficiary(), unpaidFee_);
emit FeeClaimed(beneficiary(), unpaidFee_);
}
}

Expand Down
39 changes: 27 additions & 12 deletions protocol-contracts/staking/test/OperatorRewarder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ describe('OperatorRewarder', function () {
await expect(this.mock.earned(this.delegator1)).to.eventually.eq(ethers.parseEther('5'));
await expect(this.mock.connect(this.delegator1).claimRewards(this.delegator1))
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.delegator1, ethers.parseEther('5'));
.withArgs(this.mock, this.delegator1, ethers.parseEther('5'))
.to.emit(this.mock, 'RewardsClaimed')
.withArgs(this.delegator1, ethers.parseEther('5'));
await expect(this.mock.earned(this.delegator1)).to.eventually.eq(0);

// Historical reward: 10 (seconds) * 0.5 (reward rate) = 5
Expand All @@ -139,12 +141,16 @@ describe('OperatorRewarder', function () {

await expect(this.mock.connect(this.delegator1).claimRewards(this.delegator1))
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.delegator1, ethers.parseEther('2.75'));
.withArgs(this.mock, this.delegator1, ethers.parseEther('2.75'))
.to.emit(this.mock, 'RewardsClaimed')
.withArgs(this.delegator1, ethers.parseEther('2.75'));
await expect(this.mock.earned(this.delegator1)).to.eventually.eq(0);

await expect(this.mock.connect(this.delegator2).claimRewards(this.delegator2))
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.delegator2, ethers.parseEther('2.25'));
.withArgs(this.mock, this.delegator2, ethers.parseEther('2.25'))
.to.emit(this.mock, 'RewardsClaimed')
.withArgs(this.delegator2, ethers.parseEther('2.25'));
await expect(this.mock.earned(this.delegator2)).to.eventually.eq(0);

// Historical reward: (1+9) (seconds) * 0.5 (reward rate) = 5
Expand Down Expand Up @@ -178,12 +184,16 @@ describe('OperatorRewarder', function () {

await expect(this.mock.connect(this.delegator1).claimRewards(this.delegator1))
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.delegator1, ethers.parseEther('4.5'));
.withArgs(this.mock, this.delegator1, ethers.parseEther('4.5'))
.to.emit(this.mock, 'RewardsClaimed')
.withArgs(this.delegator1, ethers.parseEther('4.5'));
await expect(this.mock.earned(this.delegator1)).to.eventually.eq(0);

await expect(this.mock.connect(this.beneficiary).claimFee())
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.beneficiary, ethers.parseEther('0.5'));
.withArgs(this.mock, this.beneficiary, ethers.parseEther('0.5'))
.to.emit(this.mock, 'FeeClaimed')
.withArgs(this.beneficiary, ethers.parseEther('0.5'));
await expect(this.mock.unpaidFee()).to.eventually.eq(0);

// Historical reward: 10 (seconds) * 0.5 (reward rate) - 0.5 (10% fee) = 4.5
Expand All @@ -195,10 +205,9 @@ describe('OperatorRewarder', function () {
await this.operatorStaking.connect(this.delegator1).deposit(ethers.parseEther('1'), this.delegator1);
await time.increase(9);

await expect(this.mock.connect(this.delegator1).claimRewards(this.delegator1)).to.not.emit(
this.token,
'Transfer',
);
await expect(this.mock.connect(this.delegator1).claimRewards(this.delegator1))
.to.not.emit(this.token, 'Transfer')
.to.not.emit(this.mock, 'RewardsClaimed');

// Historical reward: 0 (no reward rate)
await expect(this.mock.historicalReward()).to.eventually.eq(ethers.parseEther('0'));
Expand Down Expand Up @@ -323,7 +332,9 @@ describe('OperatorRewarder', function () {
// 1 more second goes by
await expect(this.mock.connect(this.beneficiary).claimFee())
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.beneficiary, ethers.parseEther('1.05'));
.withArgs(this.mock, this.beneficiary, ethers.parseEther('1.05'))
.to.emit(this.mock, 'FeeClaimed')
.withArgs(this.beneficiary, ethers.parseEther('1.05'));
await expect(this.mock.unpaidFee()).to.eventually.eq(0);

// Historical reward: (20+1) (seconds) * 0.5 (reward rate) - 1.05 (10% fee) = 9.45
Expand Down Expand Up @@ -355,12 +366,16 @@ describe('OperatorRewarder', function () {
await timeIncreaseNoMine(10);
await expect(this.mock.connect(this.beneficiary).claimFee())
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.beneficiary, ethers.parseEther('0.5'));
.withArgs(this.mock, this.beneficiary, ethers.parseEther('0.5'))
.to.emit(this.mock, 'FeeClaimed')
.withArgs(this.beneficiary, ethers.parseEther('0.5'));

await timeIncreaseNoMine(5);
await expect(this.mock.connect(this.beneficiary).claimFee())
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.beneficiary, ethers.parseEther('0.25'));
.withArgs(this.mock, this.beneficiary, ethers.parseEther('0.25'))
.to.emit(this.mock, 'FeeClaimed')
.withArgs(this.beneficiary, ethers.parseEther('0.25'));
});

it('should not claim fee if not beneficiary', async function () {
Expand Down