Skip to content

Commit

Permalink
Move permitAndStake and permitAndStakeMore into an extension cont…
Browse files Browse the repository at this point in the history
…ract
  • Loading branch information
garyghayrat authored and apbendi committed Nov 5, 2024
1 parent 63bbf23 commit a750e29
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 54 deletions.
52 changes: 0 additions & 52 deletions src/GovernanceStaker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -380,32 +380,6 @@ abstract contract GovernanceStaker is INotifiableRewardReceiver, Multicall, EIP7
_depositId = _stake(msg.sender, _amount, _delegatee, _beneficiary);
}

/// @notice Method to stake tokens to a new deposit. Before the staking operation occurs, a
/// signature is passed to the token contract's permit method to spend the would-be staked amount
/// of the token.
/// @param _amount Quantity of the staking token to stake.
/// @param _delegatee Address to assign the governance voting weight of the staked tokens.
/// @param _beneficiary Address that will accrue rewards for this stake.
/// @param _deadline The timestamp after which the permit signature should expire.
/// @param _v ECDSA signature component: Parity of the `y` coordinate of point `R`
/// @param _r ECDSA signature component: x-coordinate of `R`
/// @param _s ECDSA signature component: `s` value of the signature
/// @return _depositId Unique identifier for this deposit.
/// @dev Neither the delegatee nor the beneficiary may be the zero address. The deposit will be
/// owned by the message sender.
function permitAndStake(
uint256 _amount,
address _delegatee,
address _beneficiary,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external virtual returns (DepositIdentifier _depositId) {
try STAKE_TOKEN.permit(msg.sender, address(this), _amount, _deadline, _v, _r, _s) {} catch {}
_depositId = _stake(msg.sender, _amount, _delegatee, _beneficiary);
}

/// @notice Stake tokens to a new deposit on behalf of a user, using a signature to validate the
/// user's intent. The caller must pre-approve the staking contract to spend at least the
/// would-be staked amount of the token.
Expand Down Expand Up @@ -458,32 +432,6 @@ abstract contract GovernanceStaker is INotifiableRewardReceiver, Multicall, EIP7
_stakeMore(deposit, _depositId, _amount);
}

/// @notice Add more staking tokens to an existing deposit. A staker should call this method when
/// they have an existing deposit, and wish to stake more while retaining the same delegatee and
/// beneficiary. Before the staking operation occurs, a signature is passed to the token
/// contract's permit method to spend the would-be staked amount of the token.
/// @param _depositId Unique identifier of the deposit to which stake will be added.
/// @param _amount Quantity of stake to be added.
/// @param _deadline The timestamp after which the permit signature should expire.
/// @param _v ECDSA signature component: Parity of the `y` coordinate of point `R`
/// @param _r ECDSA signature component: x-coordinate of `R`
/// @param _s ECDSA signature component: `s` value of the signature
/// @dev The message sender must be the owner of the deposit.
function permitAndStakeMore(
DepositIdentifier _depositId,
uint256 _amount,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external virtual {
Deposit storage deposit = deposits[_depositId];
_revertIfNotDepositOwner(deposit, msg.sender);

try STAKE_TOKEN.permit(msg.sender, address(this), _amount, _deadline, _v, _r, _s) {} catch {}
_stakeMore(deposit, _depositId, _amount);
}

/// @notice Add more staking tokens to an existing deposit on behalf of a user, using a signature
/// to validate the user's intent. A staker should call this method when they have an existing
/// deposit, and wish to stake more while retaining the same delegatee and beneficiary.
Expand Down
65 changes: 65 additions & 0 deletions src/extensions/GovernanceStakerPermitAndStake.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.23;

import {GovernanceStaker} from "src/GovernanceStaker.sol";

/// @title GovernanceStakerPermitAndStake
/// @author [ScopeLift](https://scopelift.co)
/// @notice This contract extension adds permit functionality to the GovernanceStaker base contract,
/// allowing token approvals to happen via signatures rather than requiring a separate transaction.
/// The permit functionality is used in conjunction with staking operations, improving UX by
/// enabling users to approve and stake tokens in a single transaction.
/// Note that this extension requires the stake token to support EIP-2612 permit functionality.
abstract contract GovernanceStakerPermitAndStake is GovernanceStaker {
/// @notice Method to stake tokens to a new deposit. Before the staking operation occurs, a
/// signature is passed to the token contract's permit method to spend the would-be staked amount
/// of the token.
/// @param _amount Quantity of the staking token to stake.
/// @param _delegatee Address to assign the governance voting weight of the staked tokens.
/// @param _beneficiary Address that will accrue rewards for this stake.
/// @param _deadline The timestamp after which the permit signature should expire.
/// @param _v ECDSA signature component: Parity of the `y` coordinate of point `R`
/// @param _r ECDSA signature component: x-coordinate of `R`
/// @param _s ECDSA signature component: `s` value of the signature
/// @return _depositId Unique identifier for this deposit.
/// @dev Neither the delegatee nor the beneficiary may be the zero address. The deposit will be
/// owned by the message sender.
function permitAndStake(
uint256 _amount,
address _delegatee,
address _beneficiary,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external virtual returns (DepositIdentifier _depositId) {
try STAKE_TOKEN.permit(msg.sender, address(this), _amount, _deadline, _v, _r, _s) {} catch {}
_depositId = _stake(msg.sender, _amount, _delegatee, _beneficiary);
}

/// @notice Add more staking tokens to an existing deposit. A staker should call this method when
/// they have an existing deposit, and wish to stake more while retaining the same delegatee and
/// beneficiary. Before the staking operation occurs, a signature is passed to the token
/// contract's permit method to spend the would-be staked amount of the token.
/// @param _depositId Unique identifier of the deposit to which stake will be added.
/// @param _amount Quantity of stake to be added.
/// @param _deadline The timestamp after which the permit signature should expire.
/// @param _v ECDSA signature component: Parity of the `y` coordinate of point `R`
/// @param _r ECDSA signature component: x-coordinate of `R`
/// @param _s ECDSA signature component: `s` value of the signature
/// @dev The message sender must be the owner of the deposit.
function permitAndStakeMore(
DepositIdentifier _depositId,
uint256 _amount,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external virtual {
Deposit storage deposit = deposits[_depositId];
_revertIfNotDepositOwner(deposit, msg.sender);

try STAKE_TOKEN.permit(msg.sender, address(this), _amount, _deadline, _v, _r, _s) {} catch {}
_stakeMore(deposit, _depositId, _amount);
}
}
4 changes: 2 additions & 2 deletions test/harnesses/GovernanceStakerHarness.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ pragma solidity ^0.8.23;

import {DelegationSurrogateVotes} from "src/DelegationSurrogateVotes.sol";
import {GovernanceStaker} from "src/GovernanceStaker.sol";

import {GovernanceStakerPermitAndStake} from "src/extensions/GovernanceStakerPermitAndStake.sol";
import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {EIP712} from "openzeppelin/utils/cryptography/EIP712.sol";
import {IERC20Delegates} from "src/interfaces/IERC20Delegates.sol";
import {IEarningPowerCalculator} from "src/interfaces/IEarningPowerCalculator.sol";
import {DelegationSurrogate} from "src/DelegationSurrogate.sol";

contract GovernanceStakerHarness is GovernanceStaker {
contract GovernanceStakerHarness is GovernanceStaker, GovernanceStakerPermitAndStake {
constructor(
IERC20 _rewardsToken,
IERC20Delegates _stakeToken,
Expand Down

0 comments on commit a750e29

Please sign in to comment.