Skip to main content

RegenStaker

Git Source

Inherits: RegenStakerBase

Author: Golem Foundation

Staking contract with voting delegation support via surrogates

*Extends RegenStakerBase to support IERC20Staking tokens with voting/delegation capabilities VARIANT COMPARISON: ═══════════════════════════════════ ┌──────────────────────────────┬──────────────┬─────────────────────────┐ │ Feature │ RegenStaker │ Without Surrogate │ ├──────────────────────────────┼──────────────┼─────────────────────────┤ │ Delegation Support │ ✓ Full │ ✗ None │ │ Surrogate Pattern │ ✓ Per User │ ✗ Contract = Surrogate │ │ Token Custody │ Surrogates │ Contract Directly │ │ Voting Power │ ✓ Delegated │ ✗ Locked in Contract │ │ Gas (First Delegatee) │ ~300k │ ~100k │ │ Gas (Subsequent) │ ~100k │ ~100k │ │ Complexity │ Higher │ Lower │ │ Use Case │ Governance │ Simple Staking │ └──────────────────────────────┴──────────────┴─────────────────────────┘ DELEGATION MECHANISM: ═══════════════════════════════════

  1. User stakes tokens and specifies delegatee
  2. Contract deploys/fetches DelegationSurrogateVotes for that delegatee
  3. Tokens transferred to surrogate (not main contract)
  4. Surrogate automatically delegates voting power to delegatee
  5. Delegatee can use voting power in governance
  6. On unstake, tokens withdrawn from surrogate back to user SURROGATE PATTERN:
  • One surrogate per delegatee (not per user)
  • Deployed on-demand via CREATE2 (deterministic addresses)
  • Surrogates are simple: hold tokens, delegate votes
  • Multiple users can share same surrogate (same delegatee) GAS CONSIDERATIONS:
  • First use of delegatee: ~250k-350k gas (deploys surrogate)
  • Subsequent uses: ~100k gas (reuses existing surrogate)
  • Pre-deploy surrogates for common delegatees during low gas WHEN TO USE:
  • Token: IERC20Staking (ERC20 + staking + delegation)
  • Need: Users want to delegate voting power while staked
  • Example: GLM token staking with governance delegation WHEN NOT TO USE:
  • Token: Simple ERC20 (no voting)
  • Don't need delegation
  • Want to minimize gas costs → Use RegenStakerWithoutDelegateSurrogateVotes instead SECURITY:
  • Surrogates deployed via CREATE2 (deterministic, verifiable)
  • Surrogates cannot be controlled by delegatee (just delegation target)
  • Tokens safe in surrogates (only contract can withdraw)*

Notes:

State Variables

_surrogates

Mapping of delegatee addresses to their surrogate contracts

One surrogate per delegatee, shared by all users delegating to that address

mapping(address => DelegationSurrogate) private _surrogates;

VOTING_TOKEN

The voting token interface for delegation operations

Immutable reference to the staking token with voting capabilities

IERC20Delegates public immutable VOTING_TOKEN;

Functions

constructor

Constructor for the RegenStaker contract.

constructor(
IERC20 _rewardsToken,
IERC20Staking _stakeToken,
IEarningPowerCalculator _earningPowerCalculator,
uint256 _maxBumpTip,
address _admin,
uint128 _rewardDuration,
uint128 _minimumStakeAmount,
IAddressSet _stakerAllowset,
IAddressSet _stakerBlockset,
AccessMode _stakerAccessMode,
IAddressSet _allocationMechanismAllowset
)
RegenStakerBase(
_rewardsToken,
IERC20(address(_stakeToken)),
_earningPowerCalculator,
_maxBumpTip,
_admin,
_rewardDuration,
_minimumStakeAmount,
_stakerAllowset,
_stakerBlockset,
_stakerAccessMode,
_allocationMechanismAllowset,
"RegenStaker"
);

Parameters

NameTypeDescription
_rewardsTokenIERC20Token used to reward contributors
_stakeTokenIERC20StakingToken used for staking (must implement IERC20Staking and IERC20Permit)
_earningPowerCalculatorIEarningPowerCalculatorEarning power calculator address
_maxBumpTipuint256Maximum bump tip in reward token base units
_adminaddressAdmin address (TRUSTED)
_rewardDurationuint128Duration for reward distribution in seconds
_minimumStakeAmountuint128Minimum stake required in stake token base units
_stakerAllowsetIAddressSetAllowset for ALLOWSET mode (can be address(0))
_stakerBlocksetIAddressSetBlockset for BLOCKSET mode (can be address(0))
_stakerAccessModeAccessModeStaker access mode (NONE, ALLOWSET, or BLOCKSET)
_allocationMechanismAllowsetIAddressSetAllowset of approved allocation mechanisms (SECURITY CRITICAL) Only audited and trusted allocation mechanisms should be in the allowset. Users contribute funds to these mechanisms and may lose funds if mechanisms are malicious.

surrogates

function surrogates(address _delegatee) public view override returns (DelegationSurrogate);

predictSurrogateAddress

Predicts the deterministic address of a surrogate for a delegatee

*Uses CREATE2 address calculation (EIP-1014) Formula: address = last 20 bytes of keccak256(0xff ++ deployer ++ salt ++ initCodeHash) COMPONENTS:

  • deployer: address(this) (RegenStaker contract)
  • salt: keccak256(delegatee address)
  • initCodeHash: keccak256(DelegationSurrogateVotes creation code + constructor args) USE CASES:
  • Predict address before deployment
  • Verify surrogate addresses off-chain
  • Pre-fund surrogates before first use*
function predictSurrogateAddress(address _delegatee) public view returns (address);

Parameters

NameTypeDescription
_delegateeaddressAddress that will receive delegated voting power

Returns

NameTypeDescription
<none>addresspredicted Predicted address of the surrogate contract

getDelegateeFromSurrogate

Returns the delegatee that a surrogate delegates to

Queries the voting token to check delegation Returns address(0) if surrogate is invalid or doesn't delegate

function getDelegateeFromSurrogate(address _surrogate) external view returns (address);

Parameters

NameTypeDescription
_surrogateaddressSurrogate contract address to query

Returns

NameTypeDescription
<none>addressdelegatee Address receiving the voting power (or address(0))

_fetchOrDeploySurrogate

Fetches existing surrogate or deploys new one for a delegatee

*Core function handling surrogate lifecycle FLOW:

  1. Check if surrogate exists for delegatee
  2. If exists: Return existing (cheap, ~5k gas)
  3. If not: Deploy new surrogate via CREATE2 (~300k gas)
  4. Store surrogate in mapping
  5. Emit SurrogateDeployed event GAS COSTS:
  • First use (deploy): ~250k-350k gas
  • Subsequent uses (fetch): ~5k gas (SLOAD) OPTIMIZATION: Pre-deploy surrogates for common delegatees during low gas periods:
// Off-peak gas optimization
regenStaker._fetchOrDeploySurrogate(popularDelegatee);

CREATE2 BENEFITS:

  • Deterministic addresses (can predict before deploy)
  • Prevents duplicate surrogates for same delegatee
  • Verifiable off-chain*
function _fetchOrDeploySurrogate(address _delegatee) internal override returns (DelegationSurrogate _surrogate);

Parameters

NameTypeDescription
_delegateeaddressAddress that will receive voting power

Returns

NameTypeDescription
_surrogateDelegationSurrogateAddress of surrogate contract (existing or newly deployed)

Events

SurrogateDeployed

Emitted when a new delegation surrogate is deployed

event SurrogateDeployed(address indexed delegatee, address indexed surrogate);

Parameters

NameTypeDescription
delegateeaddressAddress that receives voting power
surrogateaddressAddress of deployed surrogate contract