GOAT validator staking management system that automatically distributes rewards to multiple participants (Funders, Operators, Foundation) through smart contracts with flexible commission configuration.
GOAT Locking Wrapper Contract is a validator staking management system that serves as a middleware contract for the GOAT network, providing the following core values:
- Multi-party Collaboration: Supports three roles - Funder, Operator, and Foundation
- Automatic Distribution: Staking rewards are automatically distributed to all parties based on configured commission rates
- Dual-token Rewards: Supports both native tokens and ERC20 tokens (GOAT) as reward tokens
- Staking service providers managing multiple validators
- Separated funding and operation model for validator management
- Staking pools requiring protocol-level commission collection
- Multi-token reward distribution scenarios
┌─────────────────────────────────────────────────┐
│ ValidatorEntry (Main Contract) │
│ - Validator lifecycle management │
│ - Global commission rate configuration │
│ - Role management │
│ - Asset delegation/undelegation │
└──────────────┬────────────────┬─────────────────┘
│ │
│ Creates │ Interacts
│ and manages │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ IncentivePool │ │ IGoatLocking │
│ (per validator) │ │ (External) │
│ - Reward dist │ │ - Create val │
│ - Commission │ │ - Lock/unlock │
│ - Withdrawal │ │ - Claim rewards │
└──────────────────┘ └──────────────────┘
┌──────────┐ ┌──────────┐
│ Owner │ │Foundation│
│ Admin │ │ Protocol │
└──────────┘ └──────────┘
│ │
│ Config rates │
│ │
└─────────────────────┘
│
┌────────▼────────┐
│ ValidatorEntry │
└────────┬────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│Validator│ │Validator│ │Validator│
│ Pool 1 │ │ Pool 2 │ │ Pool N │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
┌────┼────┐ ┌────┼────┐ ┌────┼────┐
▼ ▼ ▼ ▼ ▼ ▼
┌────────┐ ┌──────┐ ┌────────┐ ┌──────┐ ┌────────┐ ┌──────┐
│Operator│ │Funder│ │Operator│ │Funder│ │Operator│ │Funder│
│Node Ops│ │Invest│ │Node Ops│ │Invest│ │Node Ops│ │Invest│
└────────┘ └──────┘ └────────┘ └──────┘ └────────┘ └──────┘
| Contract | Testnet Address | Mainnet Address |
|---|---|---|
| ValidatorEntry | 0xea95AF1A36DEe235aC290fa0Bec493271558D101 | 0x |
Add the delegate reference:
ILockingDelegate public lockingDelegate;Replace the existing locking calls with the delegate equivalents:
locking.lock{value: _eth}(validator, _locking);→lockingDelegate.delegate{value: _eth}(validator, _locking);locking.unlock(validator, _recipient, _locking);→lockingDelegate.undelegate(validator, _recipient, _locking);locking.claim(validator, distributor);→lockingDelegate.claimRewards(validator);
Here is a minimal example showing how a pool contract might wire the delegate and perform the migration sequence:
function setLockingDelegate(
address lockingDelegateAddr
) public {
lockingDelegate = ILockingDelegate(lockingDelegateAddr);
}
function migrateValidator(
uint256 operatorNativeAllowance,
uint256 operatorTokenAllowance,
uint256 allowanceUpdatePeriod
) public {
lockingDelegate.registerMigration(validator);
locking.changeValidatorOwner(validator, address(lockingDelegate));
lockingDelegate.migrate(
validator,
operator,
distributor,
address(this),
operatorNativeAllowance,
operatorTokenAllowance,
allowanceUpdatePeriod
);
}- Call
setLockingDelegate(<DELEGATE_CONTRACT_ADDRESS>)on the SequencerPool. - While the SequencerPool still owns the validator, call
registerMigration(<VALIDATOR_ADDRESS>)on the delegate contract. This pins the SequencerPool as thefunderthat is allowed to finalize the migration. - Call
changeValidatorOwner(<DELEGATE_CONTRACT_ADDRESS>)on the SequencerPool so the delegate temporarily owns the validator. - From the SequencerPool (the recorded funder), call
migrate(<VALIDATOR_ADDRESS>, <SEQUENCER_POOL_ADDRESS>, <DISTRIBUTOR_CONTRACT_ADDRESS>, <OPERATOR_ADDRESS>, <OPERATOR_NATIVE_ALLOWANCE>, <OPERATOR_GOAT_ALLOWANCE>, <ALLOWANCE_UPDATE_PERIOD>)on the delegate contract.
- The distributor receives its share automatically; no manual collection is required.
- Periodically call
claim()on the SequencerPool orclaimRewards(<VALIDATOR_ADDRESS>)on the delegate contract. - From the operator address, call
withdrawRewards(<VALIDATOR_ADDRESS>)on the delegate contract.