Skip to content

Commit

Permalink
Add events to UniStaker
Browse files Browse the repository at this point in the history
  • Loading branch information
alexkeating committed Jan 30, 2024
1 parent fd2152d commit 9e5c475
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 6 deletions.
2 changes: 1 addition & 1 deletion lib/forge-std
Submodule forge-std updated 1 files
+1 −1 scripts/vm.py
30 changes: 30 additions & 0 deletions src/UniStaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,28 @@ import {ReentrancyGuard} from "openzeppelin/utils/ReentrancyGuard.sol";
contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
type DepositIdentifier is uint256;

event BeneficiaryAltered(
DepositIdentifier indexed depositId,
address indexed oldBeneficiary,
address indexed newBeneficiary
);
event DelegateeAltered(
DepositIdentifier indexed depositId, address oldDelegatee, address newDelegatee
);
event MoreStaked(DepositIdentifier indexed depositId, uint256 amount, uint256 balance);
event RewardClaimed(address indexed beneficiary, uint256 amount);
event RewardNotified(uint256 amount);
event Stake(
DepositIdentifier indexed depositId,
uint256 amount,
address indexed delegatee,
address indexed beneficiary
);
event SurrogateDeployed(address indexed delegatee, address indexed surrogate);
event StakeWithdrawn(
DepositIdentifier indexed depositId, uint256 amount, uint256 remainingAmount
);

/// @notice Thrown when an account attempts a call for which it lacks appropriate permission.
/// @param reason Human readable code explaining why the call is unauthorized.
/// @param caller The address that attempted the unauthorized call.
Expand Down Expand Up @@ -203,6 +225,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
totalDeposits[msg.sender] += _amount;
earningPower[deposit.beneficiary] += _amount;
deposit.balance += _amount;
emit MoreStaked(_depositId, _amount, totalDeposits[msg.sender]);
}

/// @notice For an existing deposit, change the address to which governance voting power is
Expand All @@ -217,6 +240,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
_revertIfNotDepositOwner(deposit);

DelegationSurrogate _oldSurrogate = surrogates[deposit.delegatee];
emit DelegateeAltered(_depositId, deposit.delegatee, _newDelegatee);
deposit.delegatee = _newDelegatee;
DelegationSurrogate _newSurrogate = _fetchOrDeploySurrogate(_newDelegatee);
_stakeTokenSafeTransferFrom(address(_oldSurrogate), address(_newSurrogate), deposit.balance);
Expand All @@ -240,6 +264,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
earningPower[deposit.beneficiary] -= deposit.balance;

_updateReward(_newBeneficiary);
emit BeneficiaryAltered(_depositId, deposit.beneficiary, _newBeneficiary);
deposit.beneficiary = _newBeneficiary;
earningPower[_newBeneficiary] += deposit.balance;
}
Expand All @@ -260,6 +285,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
totalDeposits[msg.sender] -= _amount;
earningPower[deposit.beneficiary] -= _amount;
_stakeTokenSafeTransferFrom(address(surrogates[deposit.delegatee]), deposit.owner, _amount);
emit StakeWithdrawn(_depositId, _amount, deposit.balance);
}

/// @notice Claim reward tokens the message sender has earned as a stake beneficiary. Tokens are
Expand All @@ -270,6 +296,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
uint256 _rewards = rewards[msg.sender];
if (_rewards == 0) return;
rewards[msg.sender] = 0;
emit RewardClaimed(msg.sender, _rewards);

SafeERC20.safeTransfer(REWARDS_TOKEN, msg.sender, _rewards);
}
Expand Down Expand Up @@ -300,6 +327,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {

finishAt = block.timestamp + REWARD_DURATION;
updatedAt = block.timestamp;
emit RewardNotified(_amount);
}

/// @notice Internal method which finds the existing surrogate contract—or deploys a new one if
Expand All @@ -315,6 +343,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
if (address(_surrogate) == address(0)) {
_surrogate = new DelegationSurrogate(STAKE_TOKEN, _delegatee);
surrogates[_delegatee] = _surrogate;
emit SurrogateDeployed(_delegatee, address(_surrogate));
}
}

Expand Down Expand Up @@ -359,6 +388,7 @@ contract UniStaker is INotifiableRewardReceiver, ReentrancyGuard {
delegatee: _delegatee,
beneficiary: _beneficiary
});
emit Stake(_depositId, _amount, _delegatee, _beneficiary);
}

// TODO: rename snapshotReward?
Expand Down
194 changes: 191 additions & 3 deletions test/UniStaker.t.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.23;

import {Test, console2} from "forge-std/Test.sol";
import {Vm, Test, console2} from "forge-std/Test.sol";
import {UniStaker, DelegationSurrogate, IERC20, IERC20Delegates} from "src/UniStaker.sol";
import {UniStakerHarness} from "test/harnesses/UniStakerHarness.sol";
import {ERC20VotesMock} from "test/mocks/MockERC20Votes.sol";
import {ERC20Fake} from "test/fakes/ERC20Fake.sol";

contract UniStakerTest is Test {
ERC20Fake rewardToken;
ERC20VotesMock govToken;
address rewardsNotifier;
UniStaker uniStaker;
UniStakerHarness uniStaker;

function setUp() public {
// Set the block timestamp to an arbitrary value to avoid introducing assumptions into tests
Expand All @@ -26,7 +27,7 @@ contract UniStakerTest is Test {
rewardsNotifier = address(0xaffab1ebeef);
vm.label(rewardsNotifier, "Rewards Notifier");

uniStaker = new UniStaker(rewardToken, govToken, rewardsNotifier);
uniStaker = new UniStakerHarness(rewardToken, govToken, rewardsNotifier);
vm.label(address(uniStaker), "UniStaker");
}

Expand Down Expand Up @@ -148,6 +149,57 @@ contract Stake is UniStakerTest {
assertEq(govToken.balanceOf(_depositor), 0);
}

function testFuzz_EmitsAnEventWhenStakingWithTwoParams(
address _depositor,
uint256 _amount,
address _delegatee
) public {
_amount = bound(_amount, 1, type(uint224).max);
_mintGovToken(_depositor, _amount);
UniStaker.DepositIdentifier depositId = uniStaker.exposed_useDepositId();

vm.assume(_delegatee != address(0));

vm.startPrank(_depositor);
govToken.approve(address(uniStaker), _amount);
vm.expectEmit();
emit UniStaker.Stake(
UniStaker.DepositIdentifier.wrap(UniStaker.DepositIdentifier.unwrap(depositId) + 1),
_amount,
_delegatee,
_depositor
);

uniStaker.stake(_amount, _delegatee);
vm.stopPrank();
}

function testFuzz_EmitsAnEventWhenStakingWithThreeParams(
address _depositor,
uint256 _amount,
address _delegatee,
address _beneficiary
) public {
_amount = bound(_amount, 1, type(uint224).max);
_mintGovToken(_depositor, _amount);
UniStaker.DepositIdentifier depositId = uniStaker.exposed_useDepositId();

vm.assume(_delegatee != address(0) && _beneficiary != address(0));

vm.startPrank(_depositor);
govToken.approve(address(uniStaker), _amount);
vm.expectEmit();
emit UniStaker.Stake(
UniStaker.DepositIdentifier.wrap(UniStaker.DepositIdentifier.unwrap(depositId) + 1),
_amount,
_delegatee,
_beneficiary
);

uniStaker.stake(_amount, _delegatee, _beneficiary);
vm.stopPrank();
}

function testFuzz_TransfersToAnExistingSurrogateWhenStakedToTheSameDelegatee(
address _depositor1,
uint256 _amount1,
Expand Down Expand Up @@ -621,6 +673,30 @@ contract StakeMore is UniStakerTest {
assertEq(_deposit.balance, _depositAmount + _addAmount);
}

function testFuzz_EmitsAnEventWhenStakingMore(
address _depositor,
uint256 _depositAmount,
uint256 _addAmount,
address _delegatee,
address _beneficiary
) public {
UniStaker.DepositIdentifier _depositId;
(_depositAmount, _depositId) =
_boundMintAndStake(_depositor, _depositAmount, _delegatee, _beneficiary);

_addAmount = _boundToRealisticStake(_addAmount);
_mintGovToken(_depositor, _addAmount);

vm.startPrank(_depositor);
govToken.approve(address(uniStaker), _addAmount);

vm.expectEmit();
emit UniStaker.MoreStaked(_depositId, _addAmount, _depositAmount + _addAmount);

uniStaker.stakeMore(_depositId, _addAmount);
vm.stopPrank();
}

function testFuzz_RevertIf_TheCallerIsNotTheDepositor(
address _depositor,
address _notDepositor,
Expand Down Expand Up @@ -721,6 +797,26 @@ contract AlterDelegatee is UniStakerTest {
assertEq(govToken.balanceOf(_afterSurrogate), _depositAmount);
}

function testFuzz_EmitsAnEventWhenADelegateeIsChanged(
address _depositor,
uint256 _depositAmount,
address _firstDelegatee,
address _beneficiary,
address _newDelegatee
) public {
vm.assume(_newDelegatee != address(0) && _newDelegatee != _firstDelegatee);

UniStaker.DepositIdentifier _depositId;
(_depositAmount, _depositId) =
_boundMintAndStake(_depositor, _depositAmount, _firstDelegatee, _beneficiary);

vm.expectEmit();
emit UniStaker.DelegateeAltered(_depositId, _firstDelegatee, _newDelegatee);

vm.prank(_depositor);
uniStaker.alterDelegatee(_depositId, _newDelegatee);
}

function testFuzz_RevertIf_TheCallerIsNotTheDepositor(
address _depositor,
address _notDepositor,
Expand Down Expand Up @@ -824,6 +920,26 @@ contract AlterBeneficiary is UniStakerTest {
assertEq(uniStaker.earningPower(_beneficiary), _depositAmount);
}

function testFuzz_EmitsAnEventWhenBeneficiaryAltered(
address _depositor,
uint256 _depositAmount,
address _delegatee,
address _firstBeneficiary,
address _newBeneficiary
) public {
vm.assume(_newBeneficiary != address(0) && _newBeneficiary != _firstBeneficiary);

UniStaker.DepositIdentifier _depositId;
(_depositAmount, _depositId) =
_boundMintAndStake(_depositor, _depositAmount, _delegatee, _firstBeneficiary);

vm.expectEmit();
emit UniStaker.BeneficiaryAltered(_depositId, _firstBeneficiary, _newBeneficiary);

vm.prank(_depositor);
uniStaker.alterBeneficiary(_depositId, _newBeneficiary);
}

function testFuzz_RevertIf_TheCallerIsNotTheDepositor(
address _depositor,
address _notDepositor,
Expand Down Expand Up @@ -1119,6 +1235,23 @@ contract Withdraw is UniStakerTest {
assertEq(uniStaker.earningPower(_beneficiary2), _depositAmount2 - _withdrawalAmount2);
}

function testFuzz_EmitsAnEventWhenThereIsAWithdrawl(
address _depositor,
uint256 _depositAmount,
address _delegatee,
uint256 _withdrawalAmount
) public {
UniStaker.DepositIdentifier _depositId;
(_depositAmount, _depositId) = _boundMintAndStake(_depositor, _depositAmount, _delegatee);
_withdrawalAmount = bound(_withdrawalAmount, 0, _depositAmount);

vm.expectEmit();
emit UniStaker.StakeWithdrawn(_depositId, _withdrawalAmount, _depositAmount - _withdrawalAmount);

vm.prank(_depositor);
uniStaker.withdraw(_depositId, _withdrawalAmount);
}

function testFuzz_RevertIf_TheWithdrawerIsNotTheDepositor(
address _depositor,
uint256 _amount,
Expand Down Expand Up @@ -1338,6 +1471,20 @@ contract NotifyRewardsAmount is UniStakerRewardsTest {
assertEq(uniStaker.rewardPerTokenStored(), uniStaker.rewardPerToken());
}

function testFuzz_EmitsAnEventWhenRewardsAreNotified(uint256 _amount) public {
_amount = _boundToRealisticReward(_amount);
rewardToken.mint(rewardsNotifier, _amount);

vm.startPrank(rewardsNotifier);
rewardToken.transfer(address(uniStaker), _amount);

vm.expectEmit();
emit UniStaker.RewardNotified(_amount);

uniStaker.notifyRewardsAmount(_amount);
vm.stopPrank();
}

function testFuzz_RevertIf_CallerIsNotTheRewardsNotifier(uint256 _amount, address _notNotifier)
public
{
Expand Down Expand Up @@ -1801,4 +1948,45 @@ contract ClaimReward is UniStakerRewardsTest {

assertEq(uniStaker.earned(_depositor), 0);
}

function testFuzz_EmitsAnEventWhenRewardsAreClaimed(
address _depositor,
address _delegatee,
uint256 _stakeAmount,
uint256 _rewardAmount,
uint256 _durationPercent
) public {
(_stakeAmount, _rewardAmount) = _boundToRealisticStakeAndReward(_stakeAmount, _rewardAmount);
_durationPercent = bound(_durationPercent, 1, 100);

// A user deposits staking tokens
_boundMintAndStake(_depositor, _stakeAmount, _delegatee);
// The contract is notified of a reward
_mintTransferAndNotifyReward(_rewardAmount);
// A portion of the duration passes
_jumpAheadByPercentOfRewardDuration(_durationPercent);

uint256 _earned = uniStaker.earned(_depositor);

vm.expectEmit();
emit UniStaker.RewardClaimed(_depositor, _earned);

vm.prank(_depositor);
uniStaker.claimReward();
}
}

contract _FetchOrDeploySurrogate is UniStakerRewardsTest {
function testFuzz_EmitsAnEventWhenASurrogateIsDeployed(address _delegatee) public {
vm.assume(_delegatee != address(0));
vm.recordLogs();
uniStaker.exposed_fetchOrDeploySurrogate(_delegatee);

Vm.Log[] memory logs = vm.getRecordedLogs();
DelegationSurrogate _surrogate = uniStaker.surrogates(_delegatee);

assertEq(logs[1].topics[0], keccak256("SurrogateDeployed(address,address)"));
assertEq(logs[1].topics[1], bytes32(uint256(uint160(_delegatee))));
assertEq(logs[1].topics[2], bytes32(uint256(uint160(address(_surrogate)))));
}
}
4 changes: 2 additions & 2 deletions test/V3FactoryOwner.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ contract SetAdmin is V3FactoryOwnerTest {
vm.assume(_newAdmin != address(0));
_deployFactoryOwnerWithPayoutAmount(0);

vm.expectEmit(true, true, true, true);
vm.expectEmit();
vm.prank(admin);
emit AdminSet(admin, _newAdmin);
factoryOwner.setAdmin(_newAdmin);
Expand Down Expand Up @@ -299,7 +299,7 @@ contract ClaimFees is V3FactoryOwnerTest {

vm.startPrank(_caller);
payoutToken.approve(address(factoryOwner), _payoutAmount);
vm.expectEmit(true, true, true, true);
vm.expectEmit();
emit FeesClaimed(address(pool), _caller, _recipient, _amount0, _amount1);
factoryOwner.claimFees(pool, _recipient, _amount0, _amount1);
vm.stopPrank();
Expand Down
Loading

0 comments on commit 9e5c475

Please sign in to comment.