From 5c76dde41d2dbfe2e79b91808d73224df61197c1 Mon Sep 17 00:00:00 2001 From: Jay Date: Wed, 2 Oct 2024 17:10:53 -0400 Subject: [PATCH] test: dm unit refactor test: decreaseDelegatedShares current breaking tests from rounding it seems test: add helper assertions everywhere fix: added assertion helpers fix: rand(r) modifier test: completing undelegate withdrawal root undelegate, delegate new operator, and complete old withdrawal chore: fix gitattrs (#791) fix: compile warnings (#664) * fix: compile warnings * fix: rebase error * fix: rebase errors * fix: rebase error * fix: compile errors, fr this time docs: clarify strategy manager docs (#797) * Adding clarification to depositIntoStrategyWithSignature() per customer question in Slack feat: preprod rewards coordinator upgrade (#729) * feat: preprod deploy script * chore: update preprod config fix: update coverage workflow name (#834) Fix: Get Dev to Compile (#835) * fix: compile dev * fix: fixed compile (#830) --------- feat: storage report (#753) chore: move custom errors to sep branch chore: forge fmt src/contracts fix: ci and bindings chore: dmgr error tweaks chore: error tweaks for consistency and clarity feat: bump oz version (#755) * feat: bump oz version -> 0.4.9 - also moved remappings -> foundry.toml - removes remappings.txt * bindings --------- Co-authored-by: gpsanant test: custom errors passing (#783) * test: custom errors AVSDir * test: custom errors IPausable * test: custom errors Delegation * test: custom errors EigenPodManager * test: custom errors EigenPod * test: custom errors Pausable * test: custom errors RewardsCoordinator * test: custom errors IStrategy * test: custom errors StrategyManager * test: custom errors DelegationManager * test: custom errors refactor: review reconciliation refactor: review reconciliation refactor: review reconciliation chore: forge fmt src/contracts fix: compile feat: squashed slashing for devnet chore: forge fmt src/contracts fix: ci and bindings chore: dmgr error tweaks chore: error tweaks for consistency and clarity feat: bump oz version (#755) * feat: bump oz version -> 0.4.9 - also moved remappings -> foundry.toml - removes remappings.txt * bindings --------- Co-authored-by: gpsanant test: custom errors passing (#783) * test: custom errors AVSDir * test: custom errors IPausable * test: custom errors Delegation * test: custom errors EigenPodManager * test: custom errors EigenPod * test: custom errors Pausable * test: custom errors RewardsCoordinator * test: custom errors IStrategy * test: custom errors StrategyManager * test: custom errors DelegationManager * test: custom errors refactor: review reconciliation refactor: review reconciliation refactor: review reconciliation chore: forge fmt src/contracts feat: slashing * chore: pending delay calc cleanup * chore: storage pointer cleanup * eigenpods slashing updates (#745) * squash yet again * change again * update checkpoint struct * feat: AllocationManager Storage Simplification (#787) * feat: cleanup * feat: add helper func * fix: simplification * chore: clean up magnitude info usage and type conversions * refactor: review changes * fix: order struct params by size * fix: correct and simplify calc for slashed pending magnitude * fix: storage gap * feat: cleanup * chore: remove some type conversion bs and minor formatting * chore: clean up free magnitude logic * chore: rename pending deallocations and fix stack too deep * feat: slashing magnitudes cleanup (#786) * refactor: slashing magnitudes cleanup * refactor: assert `bipToSlash` is bounded * assert `bipsToSlash` is lte 100% and gt 0%. * refactor: move `isOperatorSlashable` -> AVSD * refactor: batch validate opsets on allocations - adds `AVSD.isOperatorSetBatch(operatorSets)` * feat: add pausing to ALM * refactor: remove single use helper - removes `_getLatestTotalMagnitude(operator, strategy)` * refactor: rename `ALLOCATION_DELAY_CONFIGURATION_DELAY` * refactor: remove `Slasher` * refactor: move constants + immutables to storage contracts * refactor: custom errors `RewardsCoordinatorStorage` * chore: dependency cleanup * fix: remove unused internal getter * chore: batch validate operator sets and minor cleanup * fix: fix stack too deep and compiler errors --------- Co-authored-by: wadealexc feat: dm cleanup (#788) Co-authored-by: wadealexc Revert "feat: dm cleanup (#788)" (#799) This reverts commit c27004ea11943e488af22e0b5f2b4b45ae105550. fix: compiles (#800) fix: refactor (#801) * fix: refactor * default was history * reline * rename rename test: generally compiling + AVSM unit tests compiling chore: forge fmt src/contracts add events, fix bugs, abstract better (#806) * fix bugs, add events, cleanup * wrap conditional * fmt * only one slash per timestamp test(wip): generally compiling update docs (#804) fix: revert change to event feat: am refactor add to wads to slash cleanup refactor: change totalMagnitude to maxMagnitude * condense slashOperator params * some struct field renaming remove unused eigenpod storage chore: storage report (#809) * chore: storage report * patch eigenpod --------- Co-authored-by: gpsanant feat: eip712 fixes (#808) * feat: use OZ SignatureChecker * feat: add `SignatureUtils` mixin * refactor: cleanup * feat: make storage report * storage report --------- Co-authored-by: gpsanant test: slashing tests passing (#812) fix: merge issues update events refactor: rename total magnitudes to max magnitudes * various formatting and cleanup * standardize allocation manager getter functions * update and improve commenting refactor: reorder functions to match interface fix: remove memory overwrite bug in delegation manager chore: forge fmt refactor: clean up getDepositedShare logic chore: remove old oz + forge update foundry-rs/forge-std feat: replace getSlashableMagnitudes with general purpose allocation info query Feat: SM/StrategyBase Unit Tests + Formatting (#813) refactor: delegation manager refactors test: AllocationManager progress feat: change event names feat: update doc fix: compile test: AllocationManager progress fix: tests progress add Strategy <> OperatorSet mapping in storage, and APIs and events (#814) * feat: add strategy to operator set mapping with corresponding functions and events * fix: update * fix: remove pagination of getStrategiesInOperatorSet * fix: update * fix: compiles * fix: add checks * fix: address -> IStrategy * fix: storage gap --------- Co-authored-by: gpsanant Slashing: DM Happy Path Test Cases (#815) * test: basic dm tests * test: start on share increase/decrease delegated shares tests * test: add DM unit tests passing except queue/complete * test: undelegate tests * test: queue withdrawals * test: completed DM happy path test cases * fix: compilation, rebase errors * chore: format Add view funcs (#816) * fix: add new view funcs * fix: update docs test: fix avsD tests (#817) chore: format fix: from scratch deploy feat: add shares to slashing event Slashing: Modify Allocations Happy Path Tests (#821) * refactor: add test contract per func * test: modify allocations * chore: format slashing: allocation delay happy path tests (#822) feat: wadSlashed (#820) Slashing: Clear Modification Queue Happy Path Tests (#823) test: basic allocation tests (#824) feat: inheritdoc refactor: alm test cleanup test: multiple allocations, only one slashed test: one pending alloc, slash test: revert bound refactor so tests pass Slashing: Add additional happy path AM test cases (#828) * test: happy path AM tests * chore: format Slashing: Get all tests passing (#829) * test: all tests passing * fix: mainnet integration test comment out Fix misset storage gaps (#831) * fix: misset storage gaps from #814 * fix: update gap to account for previous refactor fix: update coverage yml name (#833) Fix: Single Deallocation Queue (#827) * test: base regression * test: regression * fix: remove console * test: logs * test: add actual regression * fix: use a single deallocation queue * fix: comments * refactor: use deallocation queue everywhere * fix: address test comments * fix: test comment Feat: Update legacy withdrawal timestamp param to legacy withdrawal check (#836) * fix: make comment on timestamp clearer * chore: format Feat: Public Devnet Deploy (#838) * feat: slashing public devnet * fix: filepath * test: validation * fix: add test * fix: test fix: compile chore: format feat: add shares to queue event (#847) * feat: add shares to queue event * fix: submodule * chore: rename feat: named mapping params (#848) * feat: named mapping params * refactor: natspec * refactor: natspec * chore: forge fmt * refactor: nits chore: new storage (#851) Gpsanant/current stakes (#846) * feat: add getCurrent * chore: fmt * chore: storoage report * chore: new storage slashing: cleanup (#842) * chore: cleanup * chore: comments and add legacy view * chore: complete => clear * chore: reorder require check * fix: round up for slashed magnitudes * chore: nits and legacy complete withdrawals * feat: allow alloc delay 0 and fix tests * chore: clarify comment * chore: fmt * fix: fork test * chore: nits * test: roundup slashed magnitudes slashing: fully slashed strategies (#850) * feat: handling fully slashed scenarios * fix: unit test descriptions * fix: nits EigenPod/EPM Happy Path Test Cases (#857) * test: fix epm and ep unit tests * fix: remove unused contracts * test: get basic slash integration working * test: get ep integration tests passing * test: add rounding down test * chore: add TODO * chore: formatting * test: fix additional slash tests * chore: remove fork tests * fix: address comments * test: fix check on `withdrawSharesAsTokens` slashing: revert timestamps delegation (#861) * fix: back to blocks for withdrawals * refactor: queued legacy withdrawals are extended refactor: optimize pausable (#854) * refactor: optimize pausable * chore: forge fmt * refactor: nits * chore: make storage-report * fix: rebase * refactor: review changes - check pauser reg is non-zero on construction * fix: ci feat: small delegation refactors (#866) * refactor: small refactor to move duplicate expiry checks into signature utils * refactor: improve clarity of fully-slashed logic * refactor: improve readability of operator slashed amount calculation * todo: off-by-one error in test due to rounding issue * fix: correct amount of shares slashed * wip: comment out test so michael gets to it in his pr slashing: change queue withdrawal input to deposit shares (#869) * feat: queue withdraw uses deposit shares * fix: tests and removed old test files * chore: forge ftm * test: redelegate shares from rounding * chore: remove unused import feat: remove delegate to by signature (#871) feat: track staker withdrawals (#864) * feat: track staker withdrawals * chore: forge fmt * feat: track staker withdrawals * chore: forge fmt * fix: `pendingWithdrawals` arrangement * fix: ci Fix: Flaky DM Test (#875) * fix: test * fix: remove log feat: move operator set functionality to alm (#860) * feat: move operator set functionality to alm * feat: clarify registration states * fix: allow operators to deallocate from strategies that are not in operator set * fix: add enumeration for operator allocations * feat: remove deallocations on deregistration * chore: cleanup src/test (#863) * feat: add configurable avs registrar * feat: remove bloated getters * feat: use existing set method to get set values * test: avsd alm refactor (#867) * test: ALM compiling + but not passing * test(wip): avsd alm refactor * chore: update forge-std * fix: compile * test(wip): avsd passing * nit: cleanup * test: get alm tests passing * test: fix delegation tests * feat: add lib `Random` * test: add allocation enumeration checks to tests * test: reg/dereg + use `Random` * refactor: small refactor to reuse getters * test: `addStrategiesToOperatorSet` - also adds missing state mutation checks on previous tests * test: `removeStrategiesFromOperatorSet` * test: `createOperatorSets` * test: `setAVSRegistrar` --------- Co-authored-by: wadealexc * feat: add AVS metadata URI to ALM * chore: update comments * chore: address comments fix: change to sharesToWithdraw (#878) * fix: change to sharesToWithdraw * fix: test feat: add tasks to prepare chain state ready for slashing (#868) * feat: add tasks to prepare chain state ready for slashing * feat: add queueWithdrawal and completeWithdrawal tasks * fix: extract addresses from output after deploy * fix: upgrade scripts to account for AVS/ALM operatorSet changes * fix: add additional deposit * fix: add additional slashing between withdrawal queued and completed * fix: update slashing on top of alex/avsd-alm-refactor * chore: correct readme and formatting * fix: PascalCase contract names and update forge-std * feat: replace console with emit * feat: remove additional use of console --------- test: slashing todos (#880) * test(wip): todos * test: remove unused utils * feat: foundry `sparse_mode=true` * test(wip): todos * test(wip): todos * test(wip): todos * test(wip): todos * test(wip): todos * fix: cherry-pick errors * fix: forge-std * test(wip): todos feat: add getMinimumSlashableStake (#889) * feat: add getMinimumSlashableStake * fix: only account for deallocation when calculating minimum slashable refactor: pull beacon chain slashing out of slashing lib (#876) * fix: correctly update dsf when increasing delegation * fix: fix bug where we modify an array while iterating over it * chore: address review nits * refactor: minor refactors for codesize * refactor(nit): function ordering * fix: only check input lengths when receiving tokens * refactor: remove callstack indirection when completing a withdrawal * chore: update comment test: slashing test todos (#885) * test(wip): todos * chore: remove lcov * test(wip): remaining alm todos * test: final todos * test(wip): todos * chore: remove lcov feat: burn erc20s on slashing (#881) * refactor: pull beacon chain slashing out of slashing lib * feat: initial draft for sync slashing * fix: existing tests * fix: cumulative shares and fmt * fix: missing operator key in mapping * refactor: cumulative scaled shares * chore: cleanup * chore: storage report * fix: rename and beacon strategy * fix: rebase * fix: rounding * test: happy path test cases for burn erc20s * fix: address comments * test: timing regression test and comments * fix: slashable shares in queue amount * refactor: burn refactor (#897) * refactor: remove unused return values from _insert * also removes safe cast * refactor: pull unrelated operations out and condense library method usage * test: additional unit test with multiple withdrawals --------- test: DelegationManager unit tests (#886) * feat: initial draft for sync slashing * refactor: cumulative scaled shares * feat: initial draft for sync slashing * fix: cumulative shares and fmt * chore: cleanup src/test * fix: delegation tests * test: rebased and refactored tests fix: rebase tests test: delegation unit refactoring fix: rounding tests fix: continue fixing delegation tests * test: include fuzz underflow tests * fix: tests and rebase * chore: comment nit * fix: failing ci test: fix env requirement (#899) * fix: remove env required * fix: use envOr * fix: remove env from CI for being required fix: compile warnings (#900) fix: slashing local deploy (#898) * feat: local deploy * fix: transfer ownership * fix: comment test: slashing integration framework (#894) * feat: add `AVS` user * test(wip): slashing integration * test(wip): slashing integration * test(wip): slashing integration * test(wip): slashing integration * fix: make tracing useful * test(wip): slashing integration * fix: toStringWad * fix: eigenpods * test(wip): slashing integration * refactor: revert change * test(review): changes * fix: compile * test(review): changes * refactor: improve logging * refactor: review changes * fix: roll in `modifyAllocations` * fix: roll in `modifyAllocations` * refactor: review changes * refactor: add back pause constants --------- refactor: EigenPods and beacon chain slashing (#892) * refactor: eigenpod and beacon chain slashing * checkpoints are not deleted on completion, saving gas when creating a new checkpoint * refactor: pull bcsf out of delegationManager * chore: formatting * refactor: rename withdrawableRestakedExecutionLayerGwei * maintains old interface, only state variable is renamed * this is to reduce line length when using this variable * refactor: remove branching and slashing math from eigenpod * fix: clean up balance update conditions and ensure shares==0 is handled * refactor: remove input validation and explicitly document assumptions * fix: tests and roundup (#901) * chore: address feedback * chore: address feedback again * chore: cleanup EPM withdrawSharesAsTokens --------- refactor: remove deprecated methods (#903) * refactor: rename parameter to be more accurate * refactor: remove deprecated methods from dm * refactor: remove operator details usage * chore: nit word test: `withdrawSharesAsTokens` regression (#904) * fix: commented out integration * test: call into EP for withdrawing as tokens chore: remove unnecessary delegate checks (#908) * feat: remove checks on approver/operator sender on delegate * chore: update interface feat: user access management (#870) * feat: unified access management fix: test/compile chore: use helper func in modifier chore: remove extra * test: add UAM to unit tests * refactor: delegate -> appointee * refactor: setAdmin -> addAdmin * fix: prevent 0 admins * refactor: bitshift encode/decode * feat: short-circuit _checkCanCall * test: basic reversability * feat: admin 2-step fix: remove external call * chore: fix compile/test * feat: use opSet length as avs check * chore: format * refactor: getoperatorsetcount * fix: msg.sender in delegatoinapproverupdated * chore: format --- pkg/bindings/Checkpoints/binding.go | 203 + .../DelayedWithdrawalRouter/binding.go | 1969 +++++++++ pkg/bindings/MagnitudeCheckpoints/binding.go | 203 + pkg/bindings/SlashingConstants/binding.go | 327 ++ ...loy_RewardsCoordinator.holesky.config.json | 60 + script/deploy/devnet/Upgrade.s.sol | 24 + .../upgrade_preprod_rewardsCoordinator.s.sol | 44 + .../local/operatorSets/DeployStrategies.s.sol | 79 + .../mainnet/Deploy_Strategy_Factory.s.sol | 61 + .../harnesses/DelegationManagerHarness.sol | 69 + src/test/unit/DelegationUnit.t.sol | 3848 +++++++++++------ 11 files changed, 5482 insertions(+), 1405 deletions(-) create mode 100644 pkg/bindings/Checkpoints/binding.go create mode 100644 pkg/bindings/DelayedWithdrawalRouter/binding.go create mode 100644 pkg/bindings/MagnitudeCheckpoints/binding.go create mode 100644 pkg/bindings/SlashingConstants/binding.go create mode 100644 script/configs/holesky/Deploy_RewardsCoordinator.holesky.config.json create mode 100644 script/deploy/devnet/Upgrade.s.sol create mode 100644 script/deploy/holesky/upgrade_preprod_rewardsCoordinator.s.sol create mode 100644 script/deploy/local/operatorSets/DeployStrategies.s.sol create mode 100644 script/deploy/mainnet/Deploy_Strategy_Factory.s.sol create mode 100644 src/test/harnesses/DelegationManagerHarness.sol diff --git a/pkg/bindings/Checkpoints/binding.go b/pkg/bindings/Checkpoints/binding.go new file mode 100644 index 000000000..41a27afa8 --- /dev/null +++ b/pkg/bindings/Checkpoints/binding.go @@ -0,0 +1,203 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package Checkpoints + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// CheckpointsMetaData contains all meta data concerning the Checkpoints contract. +var CheckpointsMetaData = &bind.MetaData{ + ABI: "[]", + Bin: "0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea264697066735822122033cd598fba8494d1a6958a8af518adce5917043f9d96f1543c5e5350d9ce39b364736f6c634300080c0033", +} + +// CheckpointsABI is the input ABI used to generate the binding from. +// Deprecated: Use CheckpointsMetaData.ABI instead. +var CheckpointsABI = CheckpointsMetaData.ABI + +// CheckpointsBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use CheckpointsMetaData.Bin instead. +var CheckpointsBin = CheckpointsMetaData.Bin + +// DeployCheckpoints deploys a new Ethereum contract, binding an instance of Checkpoints to it. +func DeployCheckpoints(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Checkpoints, error) { + parsed, err := CheckpointsMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(CheckpointsBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Checkpoints{CheckpointsCaller: CheckpointsCaller{contract: contract}, CheckpointsTransactor: CheckpointsTransactor{contract: contract}, CheckpointsFilterer: CheckpointsFilterer{contract: contract}}, nil +} + +// Checkpoints is an auto generated Go binding around an Ethereum contract. +type Checkpoints struct { + CheckpointsCaller // Read-only binding to the contract + CheckpointsTransactor // Write-only binding to the contract + CheckpointsFilterer // Log filterer for contract events +} + +// CheckpointsCaller is an auto generated read-only Go binding around an Ethereum contract. +type CheckpointsCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// CheckpointsTransactor is an auto generated write-only Go binding around an Ethereum contract. +type CheckpointsTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// CheckpointsFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type CheckpointsFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// CheckpointsSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type CheckpointsSession struct { + Contract *Checkpoints // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// CheckpointsCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type CheckpointsCallerSession struct { + Contract *CheckpointsCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// CheckpointsTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type CheckpointsTransactorSession struct { + Contract *CheckpointsTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// CheckpointsRaw is an auto generated low-level Go binding around an Ethereum contract. +type CheckpointsRaw struct { + Contract *Checkpoints // Generic contract binding to access the raw methods on +} + +// CheckpointsCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type CheckpointsCallerRaw struct { + Contract *CheckpointsCaller // Generic read-only contract binding to access the raw methods on +} + +// CheckpointsTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type CheckpointsTransactorRaw struct { + Contract *CheckpointsTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewCheckpoints creates a new instance of Checkpoints, bound to a specific deployed contract. +func NewCheckpoints(address common.Address, backend bind.ContractBackend) (*Checkpoints, error) { + contract, err := bindCheckpoints(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Checkpoints{CheckpointsCaller: CheckpointsCaller{contract: contract}, CheckpointsTransactor: CheckpointsTransactor{contract: contract}, CheckpointsFilterer: CheckpointsFilterer{contract: contract}}, nil +} + +// NewCheckpointsCaller creates a new read-only instance of Checkpoints, bound to a specific deployed contract. +func NewCheckpointsCaller(address common.Address, caller bind.ContractCaller) (*CheckpointsCaller, error) { + contract, err := bindCheckpoints(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &CheckpointsCaller{contract: contract}, nil +} + +// NewCheckpointsTransactor creates a new write-only instance of Checkpoints, bound to a specific deployed contract. +func NewCheckpointsTransactor(address common.Address, transactor bind.ContractTransactor) (*CheckpointsTransactor, error) { + contract, err := bindCheckpoints(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &CheckpointsTransactor{contract: contract}, nil +} + +// NewCheckpointsFilterer creates a new log filterer instance of Checkpoints, bound to a specific deployed contract. +func NewCheckpointsFilterer(address common.Address, filterer bind.ContractFilterer) (*CheckpointsFilterer, error) { + contract, err := bindCheckpoints(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &CheckpointsFilterer{contract: contract}, nil +} + +// bindCheckpoints binds a generic wrapper to an already deployed contract. +func bindCheckpoints(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := CheckpointsMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Checkpoints *CheckpointsRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Checkpoints.Contract.CheckpointsCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Checkpoints *CheckpointsRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Checkpoints.Contract.CheckpointsTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Checkpoints *CheckpointsRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Checkpoints.Contract.CheckpointsTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Checkpoints *CheckpointsCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Checkpoints.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Checkpoints *CheckpointsTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Checkpoints.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Checkpoints *CheckpointsTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Checkpoints.Contract.contract.Transact(opts, method, params...) +} diff --git a/pkg/bindings/DelayedWithdrawalRouter/binding.go b/pkg/bindings/DelayedWithdrawalRouter/binding.go new file mode 100644 index 000000000..4943de658 --- /dev/null +++ b/pkg/bindings/DelayedWithdrawalRouter/binding.go @@ -0,0 +1,1969 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package DelayedWithdrawalRouter + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// IDelayedWithdrawalRouterDelayedWithdrawal is an auto generated low-level Go binding around an user-defined struct. +type IDelayedWithdrawalRouterDelayedWithdrawal struct { + Amount *big.Int + BlockCreated uint32 +} + +// IDelayedWithdrawalRouterUserDelayedWithdrawals is an auto generated low-level Go binding around an user-defined struct. +type IDelayedWithdrawalRouterUserDelayedWithdrawals struct { + DelayedWithdrawalsCompleted *big.Int + DelayedWithdrawals []IDelayedWithdrawalRouterDelayedWithdrawal +} + +// DelayedWithdrawalRouterMetaData contains all meta data concerning the DelayedWithdrawalRouter contract. +var DelayedWithdrawalRouterMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"constructor\",\"inputs\":[{\"name\":\"_eigenPodManager\",\"type\":\"address\",\"internalType\":\"contractIEigenPodManager\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"MAX_WITHDRAWAL_DELAY_BLOCKS\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"canClaimDelayedWithdrawal\",\"inputs\":[{\"name\":\"user\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"claimDelayedWithdrawals\",\"inputs\":[{\"name\":\"maxNumberOfDelayedWithdrawalsToClaim\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"claimDelayedWithdrawals\",\"inputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"maxNumberOfDelayedWithdrawalsToClaim\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"createDelayedWithdrawal\",\"inputs\":[{\"name\":\"podOwner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"recipient\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"eigenPodManager\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIEigenPodManager\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getClaimableUserDelayedWithdrawals\",\"inputs\":[{\"name\":\"user\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structIDelayedWithdrawalRouter.DelayedWithdrawal[]\",\"components\":[{\"name\":\"amount\",\"type\":\"uint224\",\"internalType\":\"uint224\"},{\"name\":\"blockCreated\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getUserDelayedWithdrawals\",\"inputs\":[{\"name\":\"user\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple[]\",\"internalType\":\"structIDelayedWithdrawalRouter.DelayedWithdrawal[]\",\"components\":[{\"name\":\"amount\",\"type\":\"uint224\",\"internalType\":\"uint224\"},{\"name\":\"blockCreated\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"initialize\",\"inputs\":[{\"name\":\"initOwner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"_pauserRegistry\",\"type\":\"address\",\"internalType\":\"contractIPauserRegistry\"},{\"name\":\"initPausedStatus\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"_withdrawalDelayBlocks\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"owner\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pause\",\"inputs\":[{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"pauseAll\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[{\"name\":\"index\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"paused\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pauserRegistry\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"contractIPauserRegistry\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"renounceOwnership\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setPauserRegistry\",\"inputs\":[{\"name\":\"newPauserRegistry\",\"type\":\"address\",\"internalType\":\"contractIPauserRegistry\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setWithdrawalDelayBlocks\",\"inputs\":[{\"name\":\"newValue\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"transferOwnership\",\"inputs\":[{\"name\":\"newOwner\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"unpause\",\"inputs\":[{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"userDelayedWithdrawalByIndex\",\"inputs\":[{\"name\":\"user\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"index\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIDelayedWithdrawalRouter.DelayedWithdrawal\",\"components\":[{\"name\":\"amount\",\"type\":\"uint224\",\"internalType\":\"uint224\"},{\"name\":\"blockCreated\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"userWithdrawals\",\"inputs\":[{\"name\":\"user\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"tuple\",\"internalType\":\"structIDelayedWithdrawalRouter.UserDelayedWithdrawals\",\"components\":[{\"name\":\"delayedWithdrawalsCompleted\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"delayedWithdrawals\",\"type\":\"tuple[]\",\"internalType\":\"structIDelayedWithdrawalRouter.DelayedWithdrawal[]\",\"components\":[{\"name\":\"amount\",\"type\":\"uint224\",\"internalType\":\"uint224\"},{\"name\":\"blockCreated\",\"type\":\"uint32\",\"internalType\":\"uint32\"}]}]}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"userWithdrawalsLength\",\"inputs\":[{\"name\":\"user\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"withdrawalDelayBlocks\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"DelayedWithdrawalCreated\",\"inputs\":[{\"name\":\"podOwner\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"index\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DelayedWithdrawalsClaimed\",\"inputs\":[{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amountClaimed\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"delayedWithdrawalsCompleted\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Initialized\",\"inputs\":[{\"name\":\"version\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"uint8\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OwnershipTransferred\",\"inputs\":[{\"name\":\"previousOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newOwner\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Paused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PauserRegistrySet\",\"inputs\":[{\"name\":\"pauserRegistry\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"contractIPauserRegistry\"},{\"name\":\"newPauserRegistry\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"contractIPauserRegistry\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"Unpaused\",\"inputs\":[{\"name\":\"account\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"newPausedStatus\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"WithdrawalDelayBlocksSet\",\"inputs\":[{\"name\":\"previousValue\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"newValue\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false}]", + Bin: "0x60a06040523480156200001157600080fd5b5060405162001f0e38038062001f0e8339810160408190526200003491620001a8565b6001600160a01b038116620000cb5760405162461bcd60e51b815260206004820152604c60248201527f44656c617965645769746864726177616c526f757465722e636f6e737472756360448201527f746f723a205f656967656e506f644d616e616765722063616e6e6f742062652060648201526b7a65726f206164647265737360a01b608482015260a4015b60405180910390fd5b6001600160a01b038116608052620000e2620000e9565b50620001da565b600054610100900460ff1615620001535760405162461bcd60e51b815260206004820152602760248201527f496e697469616c697a61626c653a20636f6e747261637420697320696e697469604482015266616c697a696e6760c81b6064820152608401620000c2565b60005460ff9081161015620001a6576000805460ff191660ff9081179091556040519081527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b565b600060208284031215620001bb57600080fd5b81516001600160a01b0381168114620001d357600080fd5b9392505050565b608051611d11620001fd600039600081816101fa0152610c000152611d116000f3fe60806040526004361061014b5760003560e01c806385594e58116100b6578063e4f4f8871161006f578063e4f4f887146103cc578063e5db06c014610405578063eb990c5914610425578063ecb7cb1b14610445578063f2fde38b14610472578063fabc1cbc1461049257600080fd5b806385594e5814610317578063886f1195146103445780638da5cb5b14610364578063c0db354c14610382578063ca661c0414610395578063d44e1b76146103ac57600080fd5b806350f73e7c1161010857806350f73e7c14610254578063595c6a67146102785780635ac86ab71461028d5780635c975abb146102cd578063715018a6146102e257806375608896146102f757600080fd5b806310d67a2f14610150578063136439dd146101725780631f39d87f146101925780633e1de008146101c85780634665bcda146101e85780634d50f9a414610234575b600080fd5b34801561015c57600080fd5b5061017061016b36600461196d565b6104b2565b005b34801561017e57600080fd5b5061017061018d366004611991565b61056e565b34801561019e57600080fd5b506101b26101ad36600461196d565b6106ad565b6040516101bf91906119c8565b60405180910390f35b3480156101d457600080fd5b506101b26101e336600461196d565b6108a8565b3480156101f457600080fd5b5061021c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101bf565b34801561024057600080fd5b5061017061024f366004611991565b6109ee565b34801561026057600080fd5b5061026a60c95481565b6040519081526020016101bf565b34801561028457600080fd5b506101706109ff565b34801561029957600080fd5b506102bd6102a8366004611a15565b609854600160ff9092169190911b9081161490565b60405190151581526020016101bf565b3480156102d957600080fd5b5060985461026a565b3480156102ee57600080fd5b50610170610ac6565b34801561030357600080fd5b506102bd610312366004611a38565b610ada565b34801561032357600080fd5b50610337610332366004611a38565b610b5d565b6040516101bf9190611a64565b34801561035057600080fd5b5060975461021c906001600160a01b031681565b34801561037057600080fd5b506033546001600160a01b031661021c565b610170610390366004611a72565b610bdd565b3480156103a157600080fd5b5061026a62034bc081565b3480156103b857600080fd5b506101706103c7366004611991565b610e9d565b3480156103d857600080fd5b5061026a6103e736600461196d565b6001600160a01b0316600090815260ca602052604090206001015490565b34801561041157600080fd5b50610170610420366004611a38565b610f31565b34801561043157600080fd5b50610170610440366004611aab565b610fc6565b34801561045157600080fd5b5061046561046036600461196d565b6110ee565b6040516101bf9190611af1565b34801561047e57600080fd5b5061017061048d36600461196d565b6111a8565b34801561049e57600080fd5b506101706104ad366004611991565b61121e565b609760009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610505573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105299190611b47565b6001600160a01b0316336001600160a01b0316146105625760405162461bcd60e51b815260040161055990611b64565b60405180910390fd5b61056b8161137a565b50565b60975460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa1580156105b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105da9190611bae565b6105f65760405162461bcd60e51b815260040161055990611bd0565b6098548181161461066f5760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e70617573653a20696e76616c696420617474656d70742060448201527f746f20756e70617573652066756e6374696f6e616c69747900000000000000006064820152608401610559565b609881905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d906020015b60405180910390a250565b6001600160a01b038116600090815260ca6020526040812080546001909101546060926106da8383611c2e565b90508060005b82811015610786576001600160a01b038716600090815260ca6020526040812060010161070d8388611c45565b8154811061071d5761071d611c5d565b6000918252602091829020604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff1691810182905260c95490925061076391611c45565b4310156107735781925050610786565b508061077e81611c73565b9150506106e0565b508060008167ffffffffffffffff8111156107a3576107a3611c8e565b6040519080825280602002602001820160405280156107e857816020015b60408051808201909152600080825260208201528152602001906001900390816107c15790505b509050811561089d5760005b8281101561089b576001600160a01b038916600090815260ca602052604090206001016108218289611c45565b8154811061083157610831611c5d565b6000918252602091829020604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff1691810191909152825183908390811061087d5761087d611c5d565b6020026020010181905250808061089390611c73565b9150506107f4565b505b979650505050505050565b6001600160a01b038116600090815260ca6020526040812080546001909101546060926108d58383611c2e565b905060008167ffffffffffffffff8111156108f2576108f2611c8e565b60405190808252806020026020018201604052801561093757816020015b60408051808201909152600080825260208201528152602001906001900390816109105790505b50905060005b828110156109e4576001600160a01b038716600090815260ca6020526040902060010161096a8287611c45565b8154811061097a5761097a611c5d565b6000918252602091829020604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff169181019190915282518390839081106109c6576109c6611c5d565b602002602001018190525080806109dc90611c73565b91505061093d565b5095945050505050565b6109f6611471565b61056b816114cb565b60975460405163237dfb4760e11b81523360048201526001600160a01b03909116906346fbf68e90602401602060405180830381865afa158015610a47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6b9190611bae565b610a875760405162461bcd60e51b815260040161055990611bd0565b600019609881905560405190815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a2565b610ace611471565b610ad86000611593565b565b6001600160a01b038216600090815260ca60205260408120548210801590610b54575060c9546001600160a01b038416600090815260ca60205260409020600101805484908110610b2d57610b2d611c5d565b600091825260209091200154610b509190600160e01b900463ffffffff16611c45565b4310155b90505b92915050565b60408051808201909152600080825260208201526001600160a01b038316600090815260ca60205260409020600101805483908110610b9e57610b9e611c5d565b6000918252602091829020604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff16918101919091529392505050565b60405163a38406a360e01b81526001600160a01b038084166004830152839133917f0000000000000000000000000000000000000000000000000000000000000000169063a38406a390602401602060405180830381865afa158015610c47573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c6b9190611b47565b6001600160a01b031614610ce75760405162461bcd60e51b815260206004820152603d60248201527f44656c617965645769746864726177616c526f757465722e6f6e6c794569676560448201527f6e506f643a206e6f7420706f644f776e6572277320456967656e506f640000006064820152608401610559565b60985460009060019081161415610d105760405162461bcd60e51b815260040161055990611ca4565b6001600160a01b038316610da65760405162461bcd60e51b815260206004820152605160248201527f44656c617965645769746864726177616c526f757465722e637265617465446560448201527f6c617965645769746864726177616c3a20726563697069656e742063616e6e6f60648201527074206265207a65726f206164647265737360781b608482015260a401610559565b346001600160e01b03811615610e96576040805180820182526001600160e01b03808416825263ffffffff43811660208085019182526001600160a01b038a16600081815260ca8352968720600190810180548083018255818a5293892088519551909616600160e01b029490961693909317939091019290925593525490917fb8f1b14c7caf74150801dcc9bc18d575cbeaf5b421943497e409df92c92e0f5991889188918691610e5791611c2e565b604080516001600160a01b0395861681529490931660208501526001600160e01b039091169183019190915260608201526080015b60405180910390a1505b5050505050565b60026065541415610ef05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610559565b600260655560985460009060019081161415610f1e5760405162461bcd60e51b815260040161055990611ca4565b610f2833836115e5565b50506001606555565b60026065541415610f845760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610559565b600260655560985460009060019081161415610fb25760405162461bcd60e51b815260040161055990611ca4565b610fbc83836115e5565b5050600160655550565b600054610100900460ff1615808015610fe65750600054600160ff909116105b806110005750303b158015611000575060005460ff166001145b6110635760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610559565b6000805460ff191660011790558015611086576000805461ff0019166101001790555b61108f85611593565b6110998484611750565b6110a2826114cb565b8015610e96576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15050505050565b6040805180820190915260008152606060208201526001600160a01b038216600090815260ca6020908152604080832081518083018352815481526001820180548451818702810187019095528085529195929486810194939192919084015b8282101561119a57600084815260209081902060408051808201909152908401546001600160e01b0381168252600160e01b900463ffffffff168183015282526001909201910161114e565b505050915250909392505050565b6111b0611471565b6001600160a01b0381166112155760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610559565b61056b81611593565b609760009054906101000a90046001600160a01b03166001600160a01b031663eab66d7a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611271573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112959190611b47565b6001600160a01b0316336001600160a01b0316146112c55760405162461bcd60e51b815260040161055990611b64565b6098541981196098541916146113435760405162461bcd60e51b815260206004820152603860248201527f5061757361626c652e756e70617573653a20696e76616c696420617474656d7060448201527f7420746f2070617573652066756e6374696f6e616c69747900000000000000006064820152608401610559565b609881905560405181815233907f3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c906020016106a2565b6001600160a01b0381166114085760405162461bcd60e51b815260206004820152604960248201527f5061757361626c652e5f73657450617573657252656769737472793a206e657760448201527f50617573657252656769737472792063616e6e6f7420626520746865207a65726064820152686f206164647265737360b81b608482015260a401610559565b609754604080516001600160a01b03928316815291831660208301527f6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6910160405180910390a1609780546001600160a01b0319166001600160a01b0392909216919091179055565b6033546001600160a01b03163314610ad85760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610559565b62034bc08111156115525760405162461bcd60e51b815260206004820152604560248201527f44656c617965645769746864726177616c526f757465722e5f7365745769746860448201527f64726177616c44656c6179426c6f636b733a206e657756616c756520746f6f206064820152646c6172676560d81b608482015260a401610559565b60c95460408051918252602082018390527f4ffb00400574147429ee377a5633386321e66d45d8b14676014b5fa393e61e9e910160405180910390a160c955565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b038216600090815260ca602052604081208054600190910154825b848110801561161e57508161161c8285611c45565b105b156116cb576001600160a01b038616600090815260ca602052604081206001016116488386611c45565b8154811061165857611658611c5d565b6000918252602091829020604080518082019091529101546001600160e01b0381168252600160e01b900463ffffffff1691810182905260c95490925061169e91611c45565b4310156116ab57506116cb565b80516116c0906001600160e01b031686611c45565b945050600101611607565b6116d58184611c45565b6001600160a01b038716600090815260ca602052604090205583156116fe576116fe868561183a565b7f6b7151500bd0b5cc211bcc47b3029831b769004df4549e8e1c9a69da05bb0943868561172b8487611c45565b604080516001600160a01b039094168452602084019290925290820152606001610e8c565b6097546001600160a01b031615801561177157506001600160a01b03821615155b6117f35760405162461bcd60e51b815260206004820152604760248201527f5061757361626c652e5f696e697469616c697a655061757365723a205f696e6960448201527f7469616c697a6550617573657228292063616e206f6e6c792062652063616c6c6064820152666564206f6e636560c81b608482015260a401610559565b609881905560405181815233907fab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d9060200160405180910390a26118368261137a565b5050565b8047101561188a5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a20696e73756666696369656e742062616c616e63650000006044820152606401610559565b6000826001600160a01b03168260405160006040518083038185875af1925050503d80600081146118d7576040519150601f19603f3d011682016040523d82523d6000602084013e6118dc565b606091505b50509050806119535760405162461bcd60e51b815260206004820152603a60248201527f416464726573733a20756e61626c6520746f2073656e642076616c75652c207260448201527f6563697069656e74206d617920686176652072657665727465640000000000006064820152608401610559565b505050565b6001600160a01b038116811461056b57600080fd5b60006020828403121561197f57600080fd5b813561198a81611958565b9392505050565b6000602082840312156119a357600080fd5b5035919050565b80516001600160e01b0316825260209081015163ffffffff16910152565b602080825282518282018190526000919060409081850190868401855b82811015611a08576119f88483516119aa565b92840192908501906001016119e5565b5091979650505050505050565b600060208284031215611a2757600080fd5b813560ff8116811461198a57600080fd5b60008060408385031215611a4b57600080fd5b8235611a5681611958565b946020939093013593505050565b60408101610b5782846119aa565b60008060408385031215611a8557600080fd5b8235611a9081611958565b91506020830135611aa081611958565b809150509250929050565b60008060008060808587031215611ac157600080fd5b8435611acc81611958565b93506020850135611adc81611958565b93969395505050506040820135916060013590565b602080825282518282015282810151604080840181905281516060850181905260009392830191849160808701905b8084101561089b57611b338286516119aa565b938501936001939093019290820190611b20565b600060208284031215611b5957600080fd5b815161198a81611958565b6020808252602a908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526939903ab73830bab9b2b960b11b606082015260800190565b600060208284031215611bc057600080fd5b8151801515811461198a57600080fd5b60208082526028908201527f6d73672e73656e646572206973206e6f74207065726d697373696f6e6564206160408201526739903830bab9b2b960c11b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b600082821015611c4057611c40611c18565b500390565b60008219821115611c5857611c58611c18565b500190565b634e487b7160e01b600052603260045260246000fd5b6000600019821415611c8757611c87611c18565b5060010190565b634e487b7160e01b600052604160045260246000fd5b60208082526019908201527f5061757361626c653a20696e646578206973207061757365640000000000000060408201526060019056fea264697066735822122005b0ecc66b0468e43c0d5b0ff9c7b1e449b7556e61ae26d108ff696ed83f730364736f6c634300080c0033", +} + +// DelayedWithdrawalRouterABI is the input ABI used to generate the binding from. +// Deprecated: Use DelayedWithdrawalRouterMetaData.ABI instead. +var DelayedWithdrawalRouterABI = DelayedWithdrawalRouterMetaData.ABI + +// DelayedWithdrawalRouterBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use DelayedWithdrawalRouterMetaData.Bin instead. +var DelayedWithdrawalRouterBin = DelayedWithdrawalRouterMetaData.Bin + +// DeployDelayedWithdrawalRouter deploys a new Ethereum contract, binding an instance of DelayedWithdrawalRouter to it. +func DeployDelayedWithdrawalRouter(auth *bind.TransactOpts, backend bind.ContractBackend, _eigenPodManager common.Address) (common.Address, *types.Transaction, *DelayedWithdrawalRouter, error) { + parsed, err := DelayedWithdrawalRouterMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(DelayedWithdrawalRouterBin), backend, _eigenPodManager) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &DelayedWithdrawalRouter{DelayedWithdrawalRouterCaller: DelayedWithdrawalRouterCaller{contract: contract}, DelayedWithdrawalRouterTransactor: DelayedWithdrawalRouterTransactor{contract: contract}, DelayedWithdrawalRouterFilterer: DelayedWithdrawalRouterFilterer{contract: contract}}, nil +} + +// DelayedWithdrawalRouter is an auto generated Go binding around an Ethereum contract. +type DelayedWithdrawalRouter struct { + DelayedWithdrawalRouterCaller // Read-only binding to the contract + DelayedWithdrawalRouterTransactor // Write-only binding to the contract + DelayedWithdrawalRouterFilterer // Log filterer for contract events +} + +// DelayedWithdrawalRouterCaller is an auto generated read-only Go binding around an Ethereum contract. +type DelayedWithdrawalRouterCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DelayedWithdrawalRouterTransactor is an auto generated write-only Go binding around an Ethereum contract. +type DelayedWithdrawalRouterTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DelayedWithdrawalRouterFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type DelayedWithdrawalRouterFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// DelayedWithdrawalRouterSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type DelayedWithdrawalRouterSession struct { + Contract *DelayedWithdrawalRouter // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DelayedWithdrawalRouterCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type DelayedWithdrawalRouterCallerSession struct { + Contract *DelayedWithdrawalRouterCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// DelayedWithdrawalRouterTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type DelayedWithdrawalRouterTransactorSession struct { + Contract *DelayedWithdrawalRouterTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// DelayedWithdrawalRouterRaw is an auto generated low-level Go binding around an Ethereum contract. +type DelayedWithdrawalRouterRaw struct { + Contract *DelayedWithdrawalRouter // Generic contract binding to access the raw methods on +} + +// DelayedWithdrawalRouterCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type DelayedWithdrawalRouterCallerRaw struct { + Contract *DelayedWithdrawalRouterCaller // Generic read-only contract binding to access the raw methods on +} + +// DelayedWithdrawalRouterTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type DelayedWithdrawalRouterTransactorRaw struct { + Contract *DelayedWithdrawalRouterTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewDelayedWithdrawalRouter creates a new instance of DelayedWithdrawalRouter, bound to a specific deployed contract. +func NewDelayedWithdrawalRouter(address common.Address, backend bind.ContractBackend) (*DelayedWithdrawalRouter, error) { + contract, err := bindDelayedWithdrawalRouter(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouter{DelayedWithdrawalRouterCaller: DelayedWithdrawalRouterCaller{contract: contract}, DelayedWithdrawalRouterTransactor: DelayedWithdrawalRouterTransactor{contract: contract}, DelayedWithdrawalRouterFilterer: DelayedWithdrawalRouterFilterer{contract: contract}}, nil +} + +// NewDelayedWithdrawalRouterCaller creates a new read-only instance of DelayedWithdrawalRouter, bound to a specific deployed contract. +func NewDelayedWithdrawalRouterCaller(address common.Address, caller bind.ContractCaller) (*DelayedWithdrawalRouterCaller, error) { + contract, err := bindDelayedWithdrawalRouter(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterCaller{contract: contract}, nil +} + +// NewDelayedWithdrawalRouterTransactor creates a new write-only instance of DelayedWithdrawalRouter, bound to a specific deployed contract. +func NewDelayedWithdrawalRouterTransactor(address common.Address, transactor bind.ContractTransactor) (*DelayedWithdrawalRouterTransactor, error) { + contract, err := bindDelayedWithdrawalRouter(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterTransactor{contract: contract}, nil +} + +// NewDelayedWithdrawalRouterFilterer creates a new log filterer instance of DelayedWithdrawalRouter, bound to a specific deployed contract. +func NewDelayedWithdrawalRouterFilterer(address common.Address, filterer bind.ContractFilterer) (*DelayedWithdrawalRouterFilterer, error) { + contract, err := bindDelayedWithdrawalRouter(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterFilterer{contract: contract}, nil +} + +// bindDelayedWithdrawalRouter binds a generic wrapper to an already deployed contract. +func bindDelayedWithdrawalRouter(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := DelayedWithdrawalRouterMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DelayedWithdrawalRouter.Contract.DelayedWithdrawalRouterCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.DelayedWithdrawalRouterTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.DelayedWithdrawalRouterTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _DelayedWithdrawalRouter.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.contract.Transact(opts, method, params...) +} + +// MAXWITHDRAWALDELAYBLOCKS is a free data retrieval call binding the contract method 0xca661c04. +// +// Solidity: function MAX_WITHDRAWAL_DELAY_BLOCKS() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) MAXWITHDRAWALDELAYBLOCKS(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "MAX_WITHDRAWAL_DELAY_BLOCKS") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// MAXWITHDRAWALDELAYBLOCKS is a free data retrieval call binding the contract method 0xca661c04. +// +// Solidity: function MAX_WITHDRAWAL_DELAY_BLOCKS() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) MAXWITHDRAWALDELAYBLOCKS() (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.MAXWITHDRAWALDELAYBLOCKS(&_DelayedWithdrawalRouter.CallOpts) +} + +// MAXWITHDRAWALDELAYBLOCKS is a free data retrieval call binding the contract method 0xca661c04. +// +// Solidity: function MAX_WITHDRAWAL_DELAY_BLOCKS() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) MAXWITHDRAWALDELAYBLOCKS() (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.MAXWITHDRAWALDELAYBLOCKS(&_DelayedWithdrawalRouter.CallOpts) +} + +// CanClaimDelayedWithdrawal is a free data retrieval call binding the contract method 0x75608896. +// +// Solidity: function canClaimDelayedWithdrawal(address user, uint256 index) view returns(bool) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) CanClaimDelayedWithdrawal(opts *bind.CallOpts, user common.Address, index *big.Int) (bool, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "canClaimDelayedWithdrawal", user, index) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// CanClaimDelayedWithdrawal is a free data retrieval call binding the contract method 0x75608896. +// +// Solidity: function canClaimDelayedWithdrawal(address user, uint256 index) view returns(bool) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) CanClaimDelayedWithdrawal(user common.Address, index *big.Int) (bool, error) { + return _DelayedWithdrawalRouter.Contract.CanClaimDelayedWithdrawal(&_DelayedWithdrawalRouter.CallOpts, user, index) +} + +// CanClaimDelayedWithdrawal is a free data retrieval call binding the contract method 0x75608896. +// +// Solidity: function canClaimDelayedWithdrawal(address user, uint256 index) view returns(bool) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) CanClaimDelayedWithdrawal(user common.Address, index *big.Int) (bool, error) { + return _DelayedWithdrawalRouter.Contract.CanClaimDelayedWithdrawal(&_DelayedWithdrawalRouter.CallOpts, user, index) +} + +// EigenPodManager is a free data retrieval call binding the contract method 0x4665bcda. +// +// Solidity: function eigenPodManager() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) EigenPodManager(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "eigenPodManager") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// EigenPodManager is a free data retrieval call binding the contract method 0x4665bcda. +// +// Solidity: function eigenPodManager() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) EigenPodManager() (common.Address, error) { + return _DelayedWithdrawalRouter.Contract.EigenPodManager(&_DelayedWithdrawalRouter.CallOpts) +} + +// EigenPodManager is a free data retrieval call binding the contract method 0x4665bcda. +// +// Solidity: function eigenPodManager() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) EigenPodManager() (common.Address, error) { + return _DelayedWithdrawalRouter.Contract.EigenPodManager(&_DelayedWithdrawalRouter.CallOpts) +} + +// GetClaimableUserDelayedWithdrawals is a free data retrieval call binding the contract method 0x1f39d87f. +// +// Solidity: function getClaimableUserDelayedWithdrawals(address user) view returns((uint224,uint32)[]) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) GetClaimableUserDelayedWithdrawals(opts *bind.CallOpts, user common.Address) ([]IDelayedWithdrawalRouterDelayedWithdrawal, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "getClaimableUserDelayedWithdrawals", user) + + if err != nil { + return *new([]IDelayedWithdrawalRouterDelayedWithdrawal), err + } + + out0 := *abi.ConvertType(out[0], new([]IDelayedWithdrawalRouterDelayedWithdrawal)).(*[]IDelayedWithdrawalRouterDelayedWithdrawal) + + return out0, err + +} + +// GetClaimableUserDelayedWithdrawals is a free data retrieval call binding the contract method 0x1f39d87f. +// +// Solidity: function getClaimableUserDelayedWithdrawals(address user) view returns((uint224,uint32)[]) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) GetClaimableUserDelayedWithdrawals(user common.Address) ([]IDelayedWithdrawalRouterDelayedWithdrawal, error) { + return _DelayedWithdrawalRouter.Contract.GetClaimableUserDelayedWithdrawals(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// GetClaimableUserDelayedWithdrawals is a free data retrieval call binding the contract method 0x1f39d87f. +// +// Solidity: function getClaimableUserDelayedWithdrawals(address user) view returns((uint224,uint32)[]) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) GetClaimableUserDelayedWithdrawals(user common.Address) ([]IDelayedWithdrawalRouterDelayedWithdrawal, error) { + return _DelayedWithdrawalRouter.Contract.GetClaimableUserDelayedWithdrawals(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// GetUserDelayedWithdrawals is a free data retrieval call binding the contract method 0x3e1de008. +// +// Solidity: function getUserDelayedWithdrawals(address user) view returns((uint224,uint32)[]) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) GetUserDelayedWithdrawals(opts *bind.CallOpts, user common.Address) ([]IDelayedWithdrawalRouterDelayedWithdrawal, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "getUserDelayedWithdrawals", user) + + if err != nil { + return *new([]IDelayedWithdrawalRouterDelayedWithdrawal), err + } + + out0 := *abi.ConvertType(out[0], new([]IDelayedWithdrawalRouterDelayedWithdrawal)).(*[]IDelayedWithdrawalRouterDelayedWithdrawal) + + return out0, err + +} + +// GetUserDelayedWithdrawals is a free data retrieval call binding the contract method 0x3e1de008. +// +// Solidity: function getUserDelayedWithdrawals(address user) view returns((uint224,uint32)[]) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) GetUserDelayedWithdrawals(user common.Address) ([]IDelayedWithdrawalRouterDelayedWithdrawal, error) { + return _DelayedWithdrawalRouter.Contract.GetUserDelayedWithdrawals(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// GetUserDelayedWithdrawals is a free data retrieval call binding the contract method 0x3e1de008. +// +// Solidity: function getUserDelayedWithdrawals(address user) view returns((uint224,uint32)[]) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) GetUserDelayedWithdrawals(user common.Address) ([]IDelayedWithdrawalRouterDelayedWithdrawal, error) { + return _DelayedWithdrawalRouter.Contract.GetUserDelayedWithdrawals(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) Owner() (common.Address, error) { + return _DelayedWithdrawalRouter.Contract.Owner(&_DelayedWithdrawalRouter.CallOpts) +} + +// Owner is a free data retrieval call binding the contract method 0x8da5cb5b. +// +// Solidity: function owner() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) Owner() (common.Address, error) { + return _DelayedWithdrawalRouter.Contract.Owner(&_DelayedWithdrawalRouter.CallOpts) +} + +// Paused is a free data retrieval call binding the contract method 0x5ac86ab7. +// +// Solidity: function paused(uint8 index) view returns(bool) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) Paused(opts *bind.CallOpts, index uint8) (bool, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "paused", index) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +// Paused is a free data retrieval call binding the contract method 0x5ac86ab7. +// +// Solidity: function paused(uint8 index) view returns(bool) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) Paused(index uint8) (bool, error) { + return _DelayedWithdrawalRouter.Contract.Paused(&_DelayedWithdrawalRouter.CallOpts, index) +} + +// Paused is a free data retrieval call binding the contract method 0x5ac86ab7. +// +// Solidity: function paused(uint8 index) view returns(bool) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) Paused(index uint8) (bool, error) { + return _DelayedWithdrawalRouter.Contract.Paused(&_DelayedWithdrawalRouter.CallOpts, index) +} + +// Paused0 is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) Paused0(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "paused0") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Paused0 is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) Paused0() (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.Paused0(&_DelayedWithdrawalRouter.CallOpts) +} + +// Paused0 is a free data retrieval call binding the contract method 0x5c975abb. +// +// Solidity: function paused() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) Paused0() (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.Paused0(&_DelayedWithdrawalRouter.CallOpts) +} + +// PauserRegistry is a free data retrieval call binding the contract method 0x886f1195. +// +// Solidity: function pauserRegistry() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) PauserRegistry(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "pauserRegistry") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// PauserRegistry is a free data retrieval call binding the contract method 0x886f1195. +// +// Solidity: function pauserRegistry() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) PauserRegistry() (common.Address, error) { + return _DelayedWithdrawalRouter.Contract.PauserRegistry(&_DelayedWithdrawalRouter.CallOpts) +} + +// PauserRegistry is a free data retrieval call binding the contract method 0x886f1195. +// +// Solidity: function pauserRegistry() view returns(address) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) PauserRegistry() (common.Address, error) { + return _DelayedWithdrawalRouter.Contract.PauserRegistry(&_DelayedWithdrawalRouter.CallOpts) +} + +// UserDelayedWithdrawalByIndex is a free data retrieval call binding the contract method 0x85594e58. +// +// Solidity: function userDelayedWithdrawalByIndex(address user, uint256 index) view returns((uint224,uint32)) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) UserDelayedWithdrawalByIndex(opts *bind.CallOpts, user common.Address, index *big.Int) (IDelayedWithdrawalRouterDelayedWithdrawal, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "userDelayedWithdrawalByIndex", user, index) + + if err != nil { + return *new(IDelayedWithdrawalRouterDelayedWithdrawal), err + } + + out0 := *abi.ConvertType(out[0], new(IDelayedWithdrawalRouterDelayedWithdrawal)).(*IDelayedWithdrawalRouterDelayedWithdrawal) + + return out0, err + +} + +// UserDelayedWithdrawalByIndex is a free data retrieval call binding the contract method 0x85594e58. +// +// Solidity: function userDelayedWithdrawalByIndex(address user, uint256 index) view returns((uint224,uint32)) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) UserDelayedWithdrawalByIndex(user common.Address, index *big.Int) (IDelayedWithdrawalRouterDelayedWithdrawal, error) { + return _DelayedWithdrawalRouter.Contract.UserDelayedWithdrawalByIndex(&_DelayedWithdrawalRouter.CallOpts, user, index) +} + +// UserDelayedWithdrawalByIndex is a free data retrieval call binding the contract method 0x85594e58. +// +// Solidity: function userDelayedWithdrawalByIndex(address user, uint256 index) view returns((uint224,uint32)) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) UserDelayedWithdrawalByIndex(user common.Address, index *big.Int) (IDelayedWithdrawalRouterDelayedWithdrawal, error) { + return _DelayedWithdrawalRouter.Contract.UserDelayedWithdrawalByIndex(&_DelayedWithdrawalRouter.CallOpts, user, index) +} + +// UserWithdrawals is a free data retrieval call binding the contract method 0xecb7cb1b. +// +// Solidity: function userWithdrawals(address user) view returns((uint256,(uint224,uint32)[])) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) UserWithdrawals(opts *bind.CallOpts, user common.Address) (IDelayedWithdrawalRouterUserDelayedWithdrawals, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "userWithdrawals", user) + + if err != nil { + return *new(IDelayedWithdrawalRouterUserDelayedWithdrawals), err + } + + out0 := *abi.ConvertType(out[0], new(IDelayedWithdrawalRouterUserDelayedWithdrawals)).(*IDelayedWithdrawalRouterUserDelayedWithdrawals) + + return out0, err + +} + +// UserWithdrawals is a free data retrieval call binding the contract method 0xecb7cb1b. +// +// Solidity: function userWithdrawals(address user) view returns((uint256,(uint224,uint32)[])) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) UserWithdrawals(user common.Address) (IDelayedWithdrawalRouterUserDelayedWithdrawals, error) { + return _DelayedWithdrawalRouter.Contract.UserWithdrawals(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// UserWithdrawals is a free data retrieval call binding the contract method 0xecb7cb1b. +// +// Solidity: function userWithdrawals(address user) view returns((uint256,(uint224,uint32)[])) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) UserWithdrawals(user common.Address) (IDelayedWithdrawalRouterUserDelayedWithdrawals, error) { + return _DelayedWithdrawalRouter.Contract.UserWithdrawals(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// UserWithdrawalsLength is a free data retrieval call binding the contract method 0xe4f4f887. +// +// Solidity: function userWithdrawalsLength(address user) view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) UserWithdrawalsLength(opts *bind.CallOpts, user common.Address) (*big.Int, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "userWithdrawalsLength", user) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// UserWithdrawalsLength is a free data retrieval call binding the contract method 0xe4f4f887. +// +// Solidity: function userWithdrawalsLength(address user) view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) UserWithdrawalsLength(user common.Address) (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.UserWithdrawalsLength(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// UserWithdrawalsLength is a free data retrieval call binding the contract method 0xe4f4f887. +// +// Solidity: function userWithdrawalsLength(address user) view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) UserWithdrawalsLength(user common.Address) (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.UserWithdrawalsLength(&_DelayedWithdrawalRouter.CallOpts, user) +} + +// WithdrawalDelayBlocks is a free data retrieval call binding the contract method 0x50f73e7c. +// +// Solidity: function withdrawalDelayBlocks() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCaller) WithdrawalDelayBlocks(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _DelayedWithdrawalRouter.contract.Call(opts, &out, "withdrawalDelayBlocks") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// WithdrawalDelayBlocks is a free data retrieval call binding the contract method 0x50f73e7c. +// +// Solidity: function withdrawalDelayBlocks() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) WithdrawalDelayBlocks() (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.WithdrawalDelayBlocks(&_DelayedWithdrawalRouter.CallOpts) +} + +// WithdrawalDelayBlocks is a free data retrieval call binding the contract method 0x50f73e7c. +// +// Solidity: function withdrawalDelayBlocks() view returns(uint256) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterCallerSession) WithdrawalDelayBlocks() (*big.Int, error) { + return _DelayedWithdrawalRouter.Contract.WithdrawalDelayBlocks(&_DelayedWithdrawalRouter.CallOpts) +} + +// ClaimDelayedWithdrawals is a paid mutator transaction binding the contract method 0xd44e1b76. +// +// Solidity: function claimDelayedWithdrawals(uint256 maxNumberOfDelayedWithdrawalsToClaim) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) ClaimDelayedWithdrawals(opts *bind.TransactOpts, maxNumberOfDelayedWithdrawalsToClaim *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "claimDelayedWithdrawals", maxNumberOfDelayedWithdrawalsToClaim) +} + +// ClaimDelayedWithdrawals is a paid mutator transaction binding the contract method 0xd44e1b76. +// +// Solidity: function claimDelayedWithdrawals(uint256 maxNumberOfDelayedWithdrawalsToClaim) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) ClaimDelayedWithdrawals(maxNumberOfDelayedWithdrawalsToClaim *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.ClaimDelayedWithdrawals(&_DelayedWithdrawalRouter.TransactOpts, maxNumberOfDelayedWithdrawalsToClaim) +} + +// ClaimDelayedWithdrawals is a paid mutator transaction binding the contract method 0xd44e1b76. +// +// Solidity: function claimDelayedWithdrawals(uint256 maxNumberOfDelayedWithdrawalsToClaim) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) ClaimDelayedWithdrawals(maxNumberOfDelayedWithdrawalsToClaim *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.ClaimDelayedWithdrawals(&_DelayedWithdrawalRouter.TransactOpts, maxNumberOfDelayedWithdrawalsToClaim) +} + +// ClaimDelayedWithdrawals0 is a paid mutator transaction binding the contract method 0xe5db06c0. +// +// Solidity: function claimDelayedWithdrawals(address recipient, uint256 maxNumberOfDelayedWithdrawalsToClaim) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) ClaimDelayedWithdrawals0(opts *bind.TransactOpts, recipient common.Address, maxNumberOfDelayedWithdrawalsToClaim *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "claimDelayedWithdrawals0", recipient, maxNumberOfDelayedWithdrawalsToClaim) +} + +// ClaimDelayedWithdrawals0 is a paid mutator transaction binding the contract method 0xe5db06c0. +// +// Solidity: function claimDelayedWithdrawals(address recipient, uint256 maxNumberOfDelayedWithdrawalsToClaim) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) ClaimDelayedWithdrawals0(recipient common.Address, maxNumberOfDelayedWithdrawalsToClaim *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.ClaimDelayedWithdrawals0(&_DelayedWithdrawalRouter.TransactOpts, recipient, maxNumberOfDelayedWithdrawalsToClaim) +} + +// ClaimDelayedWithdrawals0 is a paid mutator transaction binding the contract method 0xe5db06c0. +// +// Solidity: function claimDelayedWithdrawals(address recipient, uint256 maxNumberOfDelayedWithdrawalsToClaim) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) ClaimDelayedWithdrawals0(recipient common.Address, maxNumberOfDelayedWithdrawalsToClaim *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.ClaimDelayedWithdrawals0(&_DelayedWithdrawalRouter.TransactOpts, recipient, maxNumberOfDelayedWithdrawalsToClaim) +} + +// CreateDelayedWithdrawal is a paid mutator transaction binding the contract method 0xc0db354c. +// +// Solidity: function createDelayedWithdrawal(address podOwner, address recipient) payable returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) CreateDelayedWithdrawal(opts *bind.TransactOpts, podOwner common.Address, recipient common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "createDelayedWithdrawal", podOwner, recipient) +} + +// CreateDelayedWithdrawal is a paid mutator transaction binding the contract method 0xc0db354c. +// +// Solidity: function createDelayedWithdrawal(address podOwner, address recipient) payable returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) CreateDelayedWithdrawal(podOwner common.Address, recipient common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.CreateDelayedWithdrawal(&_DelayedWithdrawalRouter.TransactOpts, podOwner, recipient) +} + +// CreateDelayedWithdrawal is a paid mutator transaction binding the contract method 0xc0db354c. +// +// Solidity: function createDelayedWithdrawal(address podOwner, address recipient) payable returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) CreateDelayedWithdrawal(podOwner common.Address, recipient common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.CreateDelayedWithdrawal(&_DelayedWithdrawalRouter.TransactOpts, podOwner, recipient) +} + +// Initialize is a paid mutator transaction binding the contract method 0xeb990c59. +// +// Solidity: function initialize(address initOwner, address _pauserRegistry, uint256 initPausedStatus, uint256 _withdrawalDelayBlocks) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) Initialize(opts *bind.TransactOpts, initOwner common.Address, _pauserRegistry common.Address, initPausedStatus *big.Int, _withdrawalDelayBlocks *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "initialize", initOwner, _pauserRegistry, initPausedStatus, _withdrawalDelayBlocks) +} + +// Initialize is a paid mutator transaction binding the contract method 0xeb990c59. +// +// Solidity: function initialize(address initOwner, address _pauserRegistry, uint256 initPausedStatus, uint256 _withdrawalDelayBlocks) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) Initialize(initOwner common.Address, _pauserRegistry common.Address, initPausedStatus *big.Int, _withdrawalDelayBlocks *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.Initialize(&_DelayedWithdrawalRouter.TransactOpts, initOwner, _pauserRegistry, initPausedStatus, _withdrawalDelayBlocks) +} + +// Initialize is a paid mutator transaction binding the contract method 0xeb990c59. +// +// Solidity: function initialize(address initOwner, address _pauserRegistry, uint256 initPausedStatus, uint256 _withdrawalDelayBlocks) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) Initialize(initOwner common.Address, _pauserRegistry common.Address, initPausedStatus *big.Int, _withdrawalDelayBlocks *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.Initialize(&_DelayedWithdrawalRouter.TransactOpts, initOwner, _pauserRegistry, initPausedStatus, _withdrawalDelayBlocks) +} + +// Pause is a paid mutator transaction binding the contract method 0x136439dd. +// +// Solidity: function pause(uint256 newPausedStatus) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) Pause(opts *bind.TransactOpts, newPausedStatus *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "pause", newPausedStatus) +} + +// Pause is a paid mutator transaction binding the contract method 0x136439dd. +// +// Solidity: function pause(uint256 newPausedStatus) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) Pause(newPausedStatus *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.Pause(&_DelayedWithdrawalRouter.TransactOpts, newPausedStatus) +} + +// Pause is a paid mutator transaction binding the contract method 0x136439dd. +// +// Solidity: function pause(uint256 newPausedStatus) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) Pause(newPausedStatus *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.Pause(&_DelayedWithdrawalRouter.TransactOpts, newPausedStatus) +} + +// PauseAll is a paid mutator transaction binding the contract method 0x595c6a67. +// +// Solidity: function pauseAll() returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) PauseAll(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "pauseAll") +} + +// PauseAll is a paid mutator transaction binding the contract method 0x595c6a67. +// +// Solidity: function pauseAll() returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) PauseAll() (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.PauseAll(&_DelayedWithdrawalRouter.TransactOpts) +} + +// PauseAll is a paid mutator transaction binding the contract method 0x595c6a67. +// +// Solidity: function pauseAll() returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) PauseAll() (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.PauseAll(&_DelayedWithdrawalRouter.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) RenounceOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "renounceOwnership") +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) RenounceOwnership() (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.RenounceOwnership(&_DelayedWithdrawalRouter.TransactOpts) +} + +// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. +// +// Solidity: function renounceOwnership() returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) RenounceOwnership() (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.RenounceOwnership(&_DelayedWithdrawalRouter.TransactOpts) +} + +// SetPauserRegistry is a paid mutator transaction binding the contract method 0x10d67a2f. +// +// Solidity: function setPauserRegistry(address newPauserRegistry) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) SetPauserRegistry(opts *bind.TransactOpts, newPauserRegistry common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "setPauserRegistry", newPauserRegistry) +} + +// SetPauserRegistry is a paid mutator transaction binding the contract method 0x10d67a2f. +// +// Solidity: function setPauserRegistry(address newPauserRegistry) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) SetPauserRegistry(newPauserRegistry common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.SetPauserRegistry(&_DelayedWithdrawalRouter.TransactOpts, newPauserRegistry) +} + +// SetPauserRegistry is a paid mutator transaction binding the contract method 0x10d67a2f. +// +// Solidity: function setPauserRegistry(address newPauserRegistry) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) SetPauserRegistry(newPauserRegistry common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.SetPauserRegistry(&_DelayedWithdrawalRouter.TransactOpts, newPauserRegistry) +} + +// SetWithdrawalDelayBlocks is a paid mutator transaction binding the contract method 0x4d50f9a4. +// +// Solidity: function setWithdrawalDelayBlocks(uint256 newValue) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) SetWithdrawalDelayBlocks(opts *bind.TransactOpts, newValue *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "setWithdrawalDelayBlocks", newValue) +} + +// SetWithdrawalDelayBlocks is a paid mutator transaction binding the contract method 0x4d50f9a4. +// +// Solidity: function setWithdrawalDelayBlocks(uint256 newValue) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) SetWithdrawalDelayBlocks(newValue *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.SetWithdrawalDelayBlocks(&_DelayedWithdrawalRouter.TransactOpts, newValue) +} + +// SetWithdrawalDelayBlocks is a paid mutator transaction binding the contract method 0x4d50f9a4. +// +// Solidity: function setWithdrawalDelayBlocks(uint256 newValue) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) SetWithdrawalDelayBlocks(newValue *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.SetWithdrawalDelayBlocks(&_DelayedWithdrawalRouter.TransactOpts, newValue) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) TransferOwnership(opts *bind.TransactOpts, newOwner common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "transferOwnership", newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.TransferOwnership(&_DelayedWithdrawalRouter.TransactOpts, newOwner) +} + +// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. +// +// Solidity: function transferOwnership(address newOwner) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) TransferOwnership(newOwner common.Address) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.TransferOwnership(&_DelayedWithdrawalRouter.TransactOpts, newOwner) +} + +// Unpause is a paid mutator transaction binding the contract method 0xfabc1cbc. +// +// Solidity: function unpause(uint256 newPausedStatus) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactor) Unpause(opts *bind.TransactOpts, newPausedStatus *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.contract.Transact(opts, "unpause", newPausedStatus) +} + +// Unpause is a paid mutator transaction binding the contract method 0xfabc1cbc. +// +// Solidity: function unpause(uint256 newPausedStatus) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterSession) Unpause(newPausedStatus *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.Unpause(&_DelayedWithdrawalRouter.TransactOpts, newPausedStatus) +} + +// Unpause is a paid mutator transaction binding the contract method 0xfabc1cbc. +// +// Solidity: function unpause(uint256 newPausedStatus) returns() +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterTransactorSession) Unpause(newPausedStatus *big.Int) (*types.Transaction, error) { + return _DelayedWithdrawalRouter.Contract.Unpause(&_DelayedWithdrawalRouter.TransactOpts, newPausedStatus) +} + +// DelayedWithdrawalRouterDelayedWithdrawalCreatedIterator is returned from FilterDelayedWithdrawalCreated and is used to iterate over the raw logs and unpacked data for DelayedWithdrawalCreated events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterDelayedWithdrawalCreatedIterator struct { + Event *DelayedWithdrawalRouterDelayedWithdrawalCreated // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterDelayedWithdrawalCreatedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterDelayedWithdrawalCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterDelayedWithdrawalCreated) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterDelayedWithdrawalCreatedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterDelayedWithdrawalCreatedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterDelayedWithdrawalCreated represents a DelayedWithdrawalCreated event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterDelayedWithdrawalCreated struct { + PodOwner common.Address + Recipient common.Address + Amount *big.Int + Index *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterDelayedWithdrawalCreated is a free log retrieval operation binding the contract event 0xb8f1b14c7caf74150801dcc9bc18d575cbeaf5b421943497e409df92c92e0f59. +// +// Solidity: event DelayedWithdrawalCreated(address podOwner, address recipient, uint256 amount, uint256 index) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterDelayedWithdrawalCreated(opts *bind.FilterOpts) (*DelayedWithdrawalRouterDelayedWithdrawalCreatedIterator, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "DelayedWithdrawalCreated") + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterDelayedWithdrawalCreatedIterator{contract: _DelayedWithdrawalRouter.contract, event: "DelayedWithdrawalCreated", logs: logs, sub: sub}, nil +} + +// WatchDelayedWithdrawalCreated is a free log subscription operation binding the contract event 0xb8f1b14c7caf74150801dcc9bc18d575cbeaf5b421943497e409df92c92e0f59. +// +// Solidity: event DelayedWithdrawalCreated(address podOwner, address recipient, uint256 amount, uint256 index) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchDelayedWithdrawalCreated(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterDelayedWithdrawalCreated) (event.Subscription, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "DelayedWithdrawalCreated") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterDelayedWithdrawalCreated) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "DelayedWithdrawalCreated", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseDelayedWithdrawalCreated is a log parse operation binding the contract event 0xb8f1b14c7caf74150801dcc9bc18d575cbeaf5b421943497e409df92c92e0f59. +// +// Solidity: event DelayedWithdrawalCreated(address podOwner, address recipient, uint256 amount, uint256 index) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParseDelayedWithdrawalCreated(log types.Log) (*DelayedWithdrawalRouterDelayedWithdrawalCreated, error) { + event := new(DelayedWithdrawalRouterDelayedWithdrawalCreated) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "DelayedWithdrawalCreated", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DelayedWithdrawalRouterDelayedWithdrawalsClaimedIterator is returned from FilterDelayedWithdrawalsClaimed and is used to iterate over the raw logs and unpacked data for DelayedWithdrawalsClaimed events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterDelayedWithdrawalsClaimedIterator struct { + Event *DelayedWithdrawalRouterDelayedWithdrawalsClaimed // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterDelayedWithdrawalsClaimedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterDelayedWithdrawalsClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterDelayedWithdrawalsClaimed) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterDelayedWithdrawalsClaimedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterDelayedWithdrawalsClaimedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterDelayedWithdrawalsClaimed represents a DelayedWithdrawalsClaimed event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterDelayedWithdrawalsClaimed struct { + Recipient common.Address + AmountClaimed *big.Int + DelayedWithdrawalsCompleted *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterDelayedWithdrawalsClaimed is a free log retrieval operation binding the contract event 0x6b7151500bd0b5cc211bcc47b3029831b769004df4549e8e1c9a69da05bb0943. +// +// Solidity: event DelayedWithdrawalsClaimed(address recipient, uint256 amountClaimed, uint256 delayedWithdrawalsCompleted) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterDelayedWithdrawalsClaimed(opts *bind.FilterOpts) (*DelayedWithdrawalRouterDelayedWithdrawalsClaimedIterator, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "DelayedWithdrawalsClaimed") + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterDelayedWithdrawalsClaimedIterator{contract: _DelayedWithdrawalRouter.contract, event: "DelayedWithdrawalsClaimed", logs: logs, sub: sub}, nil +} + +// WatchDelayedWithdrawalsClaimed is a free log subscription operation binding the contract event 0x6b7151500bd0b5cc211bcc47b3029831b769004df4549e8e1c9a69da05bb0943. +// +// Solidity: event DelayedWithdrawalsClaimed(address recipient, uint256 amountClaimed, uint256 delayedWithdrawalsCompleted) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchDelayedWithdrawalsClaimed(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterDelayedWithdrawalsClaimed) (event.Subscription, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "DelayedWithdrawalsClaimed") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterDelayedWithdrawalsClaimed) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "DelayedWithdrawalsClaimed", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseDelayedWithdrawalsClaimed is a log parse operation binding the contract event 0x6b7151500bd0b5cc211bcc47b3029831b769004df4549e8e1c9a69da05bb0943. +// +// Solidity: event DelayedWithdrawalsClaimed(address recipient, uint256 amountClaimed, uint256 delayedWithdrawalsCompleted) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParseDelayedWithdrawalsClaimed(log types.Log) (*DelayedWithdrawalRouterDelayedWithdrawalsClaimed, error) { + event := new(DelayedWithdrawalRouterDelayedWithdrawalsClaimed) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "DelayedWithdrawalsClaimed", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DelayedWithdrawalRouterInitializedIterator is returned from FilterInitialized and is used to iterate over the raw logs and unpacked data for Initialized events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterInitializedIterator struct { + Event *DelayedWithdrawalRouterInitialized // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterInitializedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterInitialized) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterInitializedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterInitializedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterInitialized represents a Initialized event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterInitialized struct { + Version uint8 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterInitialized is a free log retrieval operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterInitialized(opts *bind.FilterOpts) (*DelayedWithdrawalRouterInitializedIterator, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterInitializedIterator{contract: _DelayedWithdrawalRouter.contract, event: "Initialized", logs: logs, sub: sub}, nil +} + +// WatchInitialized is a free log subscription operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchInitialized(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterInitialized) (event.Subscription, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "Initialized") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterInitialized) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "Initialized", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseInitialized is a log parse operation binding the contract event 0x7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498. +// +// Solidity: event Initialized(uint8 version) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParseInitialized(log types.Log) (*DelayedWithdrawalRouterInitialized, error) { + event := new(DelayedWithdrawalRouterInitialized) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "Initialized", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DelayedWithdrawalRouterOwnershipTransferredIterator is returned from FilterOwnershipTransferred and is used to iterate over the raw logs and unpacked data for OwnershipTransferred events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterOwnershipTransferredIterator struct { + Event *DelayedWithdrawalRouterOwnershipTransferred // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterOwnershipTransferredIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterOwnershipTransferredIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterOwnershipTransferred represents a OwnershipTransferred event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterOwnershipTransferred struct { + PreviousOwner common.Address + NewOwner common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOwnershipTransferred is a free log retrieval operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, previousOwner []common.Address, newOwner []common.Address) (*DelayedWithdrawalRouterOwnershipTransferredIterator, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterOwnershipTransferredIterator{contract: _DelayedWithdrawalRouter.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +// WatchOwnershipTransferred is a free log subscription operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterOwnershipTransferred, previousOwner []common.Address, newOwner []common.Address) (event.Subscription, error) { + + var previousOwnerRule []interface{} + for _, previousOwnerItem := range previousOwner { + previousOwnerRule = append(previousOwnerRule, previousOwnerItem) + } + var newOwnerRule []interface{} + for _, newOwnerItem := range newOwner { + newOwnerRule = append(newOwnerRule, newOwnerItem) + } + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "OwnershipTransferred", previousOwnerRule, newOwnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterOwnershipTransferred) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOwnershipTransferred is a log parse operation binding the contract event 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0. +// +// Solidity: event OwnershipTransferred(address indexed previousOwner, address indexed newOwner) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParseOwnershipTransferred(log types.Log) (*DelayedWithdrawalRouterOwnershipTransferred, error) { + event := new(DelayedWithdrawalRouterOwnershipTransferred) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DelayedWithdrawalRouterPausedIterator is returned from FilterPaused and is used to iterate over the raw logs and unpacked data for Paused events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterPausedIterator struct { + Event *DelayedWithdrawalRouterPaused // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterPausedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterPaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterPaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterPausedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterPausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterPaused represents a Paused event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterPaused struct { + Account common.Address + NewPausedStatus *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterPaused is a free log retrieval operation binding the contract event 0xab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d. +// +// Solidity: event Paused(address indexed account, uint256 newPausedStatus) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterPaused(opts *bind.FilterOpts, account []common.Address) (*DelayedWithdrawalRouterPausedIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "Paused", accountRule) + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterPausedIterator{contract: _DelayedWithdrawalRouter.contract, event: "Paused", logs: logs, sub: sub}, nil +} + +// WatchPaused is a free log subscription operation binding the contract event 0xab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d. +// +// Solidity: event Paused(address indexed account, uint256 newPausedStatus) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchPaused(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterPaused, account []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "Paused", accountRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterPaused) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "Paused", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParsePaused is a log parse operation binding the contract event 0xab40a374bc51de372200a8bc981af8c9ecdc08dfdaef0bb6e09f88f3c616ef3d. +// +// Solidity: event Paused(address indexed account, uint256 newPausedStatus) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParsePaused(log types.Log) (*DelayedWithdrawalRouterPaused, error) { + event := new(DelayedWithdrawalRouterPaused) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "Paused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DelayedWithdrawalRouterPauserRegistrySetIterator is returned from FilterPauserRegistrySet and is used to iterate over the raw logs and unpacked data for PauserRegistrySet events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterPauserRegistrySetIterator struct { + Event *DelayedWithdrawalRouterPauserRegistrySet // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterPauserRegistrySetIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterPauserRegistrySet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterPauserRegistrySet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterPauserRegistrySetIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterPauserRegistrySetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterPauserRegistrySet represents a PauserRegistrySet event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterPauserRegistrySet struct { + PauserRegistry common.Address + NewPauserRegistry common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterPauserRegistrySet is a free log retrieval operation binding the contract event 0x6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6. +// +// Solidity: event PauserRegistrySet(address pauserRegistry, address newPauserRegistry) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterPauserRegistrySet(opts *bind.FilterOpts) (*DelayedWithdrawalRouterPauserRegistrySetIterator, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "PauserRegistrySet") + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterPauserRegistrySetIterator{contract: _DelayedWithdrawalRouter.contract, event: "PauserRegistrySet", logs: logs, sub: sub}, nil +} + +// WatchPauserRegistrySet is a free log subscription operation binding the contract event 0x6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6. +// +// Solidity: event PauserRegistrySet(address pauserRegistry, address newPauserRegistry) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchPauserRegistrySet(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterPauserRegistrySet) (event.Subscription, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "PauserRegistrySet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterPauserRegistrySet) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "PauserRegistrySet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParsePauserRegistrySet is a log parse operation binding the contract event 0x6e9fcd539896fca60e8b0f01dd580233e48a6b0f7df013b89ba7f565869acdb6. +// +// Solidity: event PauserRegistrySet(address pauserRegistry, address newPauserRegistry) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParsePauserRegistrySet(log types.Log) (*DelayedWithdrawalRouterPauserRegistrySet, error) { + event := new(DelayedWithdrawalRouterPauserRegistrySet) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "PauserRegistrySet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DelayedWithdrawalRouterUnpausedIterator is returned from FilterUnpaused and is used to iterate over the raw logs and unpacked data for Unpaused events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterUnpausedIterator struct { + Event *DelayedWithdrawalRouterUnpaused // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterUnpausedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterUnpaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterUnpaused) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterUnpausedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterUnpausedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterUnpaused represents a Unpaused event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterUnpaused struct { + Account common.Address + NewPausedStatus *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterUnpaused is a free log retrieval operation binding the contract event 0x3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c. +// +// Solidity: event Unpaused(address indexed account, uint256 newPausedStatus) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterUnpaused(opts *bind.FilterOpts, account []common.Address) (*DelayedWithdrawalRouterUnpausedIterator, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "Unpaused", accountRule) + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterUnpausedIterator{contract: _DelayedWithdrawalRouter.contract, event: "Unpaused", logs: logs, sub: sub}, nil +} + +// WatchUnpaused is a free log subscription operation binding the contract event 0x3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c. +// +// Solidity: event Unpaused(address indexed account, uint256 newPausedStatus) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchUnpaused(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterUnpaused, account []common.Address) (event.Subscription, error) { + + var accountRule []interface{} + for _, accountItem := range account { + accountRule = append(accountRule, accountItem) + } + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "Unpaused", accountRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterUnpaused) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "Unpaused", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseUnpaused is a log parse operation binding the contract event 0x3582d1828e26bf56bd801502bc021ac0bc8afb57c826e4986b45593c8fad389c. +// +// Solidity: event Unpaused(address indexed account, uint256 newPausedStatus) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParseUnpaused(log types.Log) (*DelayedWithdrawalRouterUnpaused, error) { + event := new(DelayedWithdrawalRouterUnpaused) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "Unpaused", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// DelayedWithdrawalRouterWithdrawalDelayBlocksSetIterator is returned from FilterWithdrawalDelayBlocksSet and is used to iterate over the raw logs and unpacked data for WithdrawalDelayBlocksSet events raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterWithdrawalDelayBlocksSetIterator struct { + Event *DelayedWithdrawalRouterWithdrawalDelayBlocksSet // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *DelayedWithdrawalRouterWithdrawalDelayBlocksSetIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterWithdrawalDelayBlocksSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(DelayedWithdrawalRouterWithdrawalDelayBlocksSet) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *DelayedWithdrawalRouterWithdrawalDelayBlocksSetIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *DelayedWithdrawalRouterWithdrawalDelayBlocksSetIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// DelayedWithdrawalRouterWithdrawalDelayBlocksSet represents a WithdrawalDelayBlocksSet event raised by the DelayedWithdrawalRouter contract. +type DelayedWithdrawalRouterWithdrawalDelayBlocksSet struct { + PreviousValue *big.Int + NewValue *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterWithdrawalDelayBlocksSet is a free log retrieval operation binding the contract event 0x4ffb00400574147429ee377a5633386321e66d45d8b14676014b5fa393e61e9e. +// +// Solidity: event WithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) FilterWithdrawalDelayBlocksSet(opts *bind.FilterOpts) (*DelayedWithdrawalRouterWithdrawalDelayBlocksSetIterator, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.FilterLogs(opts, "WithdrawalDelayBlocksSet") + if err != nil { + return nil, err + } + return &DelayedWithdrawalRouterWithdrawalDelayBlocksSetIterator{contract: _DelayedWithdrawalRouter.contract, event: "WithdrawalDelayBlocksSet", logs: logs, sub: sub}, nil +} + +// WatchWithdrawalDelayBlocksSet is a free log subscription operation binding the contract event 0x4ffb00400574147429ee377a5633386321e66d45d8b14676014b5fa393e61e9e. +// +// Solidity: event WithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) WatchWithdrawalDelayBlocksSet(opts *bind.WatchOpts, sink chan<- *DelayedWithdrawalRouterWithdrawalDelayBlocksSet) (event.Subscription, error) { + + logs, sub, err := _DelayedWithdrawalRouter.contract.WatchLogs(opts, "WithdrawalDelayBlocksSet") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(DelayedWithdrawalRouterWithdrawalDelayBlocksSet) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "WithdrawalDelayBlocksSet", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseWithdrawalDelayBlocksSet is a log parse operation binding the contract event 0x4ffb00400574147429ee377a5633386321e66d45d8b14676014b5fa393e61e9e. +// +// Solidity: event WithdrawalDelayBlocksSet(uint256 previousValue, uint256 newValue) +func (_DelayedWithdrawalRouter *DelayedWithdrawalRouterFilterer) ParseWithdrawalDelayBlocksSet(log types.Log) (*DelayedWithdrawalRouterWithdrawalDelayBlocksSet, error) { + event := new(DelayedWithdrawalRouterWithdrawalDelayBlocksSet) + if err := _DelayedWithdrawalRouter.contract.UnpackLog(event, "WithdrawalDelayBlocksSet", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/pkg/bindings/MagnitudeCheckpoints/binding.go b/pkg/bindings/MagnitudeCheckpoints/binding.go new file mode 100644 index 000000000..9a5a2ef52 --- /dev/null +++ b/pkg/bindings/MagnitudeCheckpoints/binding.go @@ -0,0 +1,203 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package MagnitudeCheckpoints + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// MagnitudeCheckpointsMetaData contains all meta data concerning the MagnitudeCheckpoints contract. +var MagnitudeCheckpointsMetaData = &bind.MetaData{ + ABI: "[]", + Bin: "0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220d403c7152d652d1a100c83daf55d72392c025180800bf29f79d386c0af19a05d64736f6c634300080c0033", +} + +// MagnitudeCheckpointsABI is the input ABI used to generate the binding from. +// Deprecated: Use MagnitudeCheckpointsMetaData.ABI instead. +var MagnitudeCheckpointsABI = MagnitudeCheckpointsMetaData.ABI + +// MagnitudeCheckpointsBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use MagnitudeCheckpointsMetaData.Bin instead. +var MagnitudeCheckpointsBin = MagnitudeCheckpointsMetaData.Bin + +// DeployMagnitudeCheckpoints deploys a new Ethereum contract, binding an instance of MagnitudeCheckpoints to it. +func DeployMagnitudeCheckpoints(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *MagnitudeCheckpoints, error) { + parsed, err := MagnitudeCheckpointsMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(MagnitudeCheckpointsBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &MagnitudeCheckpoints{MagnitudeCheckpointsCaller: MagnitudeCheckpointsCaller{contract: contract}, MagnitudeCheckpointsTransactor: MagnitudeCheckpointsTransactor{contract: contract}, MagnitudeCheckpointsFilterer: MagnitudeCheckpointsFilterer{contract: contract}}, nil +} + +// MagnitudeCheckpoints is an auto generated Go binding around an Ethereum contract. +type MagnitudeCheckpoints struct { + MagnitudeCheckpointsCaller // Read-only binding to the contract + MagnitudeCheckpointsTransactor // Write-only binding to the contract + MagnitudeCheckpointsFilterer // Log filterer for contract events +} + +// MagnitudeCheckpointsCaller is an auto generated read-only Go binding around an Ethereum contract. +type MagnitudeCheckpointsCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MagnitudeCheckpointsTransactor is an auto generated write-only Go binding around an Ethereum contract. +type MagnitudeCheckpointsTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MagnitudeCheckpointsFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type MagnitudeCheckpointsFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// MagnitudeCheckpointsSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type MagnitudeCheckpointsSession struct { + Contract *MagnitudeCheckpoints // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MagnitudeCheckpointsCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type MagnitudeCheckpointsCallerSession struct { + Contract *MagnitudeCheckpointsCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// MagnitudeCheckpointsTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type MagnitudeCheckpointsTransactorSession struct { + Contract *MagnitudeCheckpointsTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// MagnitudeCheckpointsRaw is an auto generated low-level Go binding around an Ethereum contract. +type MagnitudeCheckpointsRaw struct { + Contract *MagnitudeCheckpoints // Generic contract binding to access the raw methods on +} + +// MagnitudeCheckpointsCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type MagnitudeCheckpointsCallerRaw struct { + Contract *MagnitudeCheckpointsCaller // Generic read-only contract binding to access the raw methods on +} + +// MagnitudeCheckpointsTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type MagnitudeCheckpointsTransactorRaw struct { + Contract *MagnitudeCheckpointsTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewMagnitudeCheckpoints creates a new instance of MagnitudeCheckpoints, bound to a specific deployed contract. +func NewMagnitudeCheckpoints(address common.Address, backend bind.ContractBackend) (*MagnitudeCheckpoints, error) { + contract, err := bindMagnitudeCheckpoints(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &MagnitudeCheckpoints{MagnitudeCheckpointsCaller: MagnitudeCheckpointsCaller{contract: contract}, MagnitudeCheckpointsTransactor: MagnitudeCheckpointsTransactor{contract: contract}, MagnitudeCheckpointsFilterer: MagnitudeCheckpointsFilterer{contract: contract}}, nil +} + +// NewMagnitudeCheckpointsCaller creates a new read-only instance of MagnitudeCheckpoints, bound to a specific deployed contract. +func NewMagnitudeCheckpointsCaller(address common.Address, caller bind.ContractCaller) (*MagnitudeCheckpointsCaller, error) { + contract, err := bindMagnitudeCheckpoints(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &MagnitudeCheckpointsCaller{contract: contract}, nil +} + +// NewMagnitudeCheckpointsTransactor creates a new write-only instance of MagnitudeCheckpoints, bound to a specific deployed contract. +func NewMagnitudeCheckpointsTransactor(address common.Address, transactor bind.ContractTransactor) (*MagnitudeCheckpointsTransactor, error) { + contract, err := bindMagnitudeCheckpoints(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &MagnitudeCheckpointsTransactor{contract: contract}, nil +} + +// NewMagnitudeCheckpointsFilterer creates a new log filterer instance of MagnitudeCheckpoints, bound to a specific deployed contract. +func NewMagnitudeCheckpointsFilterer(address common.Address, filterer bind.ContractFilterer) (*MagnitudeCheckpointsFilterer, error) { + contract, err := bindMagnitudeCheckpoints(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &MagnitudeCheckpointsFilterer{contract: contract}, nil +} + +// bindMagnitudeCheckpoints binds a generic wrapper to an already deployed contract. +func bindMagnitudeCheckpoints(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := MagnitudeCheckpointsMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_MagnitudeCheckpoints *MagnitudeCheckpointsRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _MagnitudeCheckpoints.Contract.MagnitudeCheckpointsCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_MagnitudeCheckpoints *MagnitudeCheckpointsRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _MagnitudeCheckpoints.Contract.MagnitudeCheckpointsTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_MagnitudeCheckpoints *MagnitudeCheckpointsRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _MagnitudeCheckpoints.Contract.MagnitudeCheckpointsTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_MagnitudeCheckpoints *MagnitudeCheckpointsCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _MagnitudeCheckpoints.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_MagnitudeCheckpoints *MagnitudeCheckpointsTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _MagnitudeCheckpoints.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_MagnitudeCheckpoints *MagnitudeCheckpointsTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _MagnitudeCheckpoints.Contract.contract.Transact(opts, method, params...) +} diff --git a/pkg/bindings/SlashingConstants/binding.go b/pkg/bindings/SlashingConstants/binding.go new file mode 100644 index 000000000..0a9cb4c71 --- /dev/null +++ b/pkg/bindings/SlashingConstants/binding.go @@ -0,0 +1,327 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package SlashingConstants + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// SlashingConstantsMetaData contains all meta data concerning the SlashingConstants contract. +var SlashingConstantsMetaData = &bind.MetaData{ + ABI: "[{\"type\":\"function\",\"name\":\"DEALLOCATION_DELAY\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint32\",\"internalType\":\"uint32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"INITIAL_TOTAL_MAGNITUDE\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"PRECISION_FACTOR\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"PRECISION_FACTOR_SQUARED\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"}]", + Bin: "0x61010561003a600b82828239805160001a60731461002d57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe730000000000000000000000000000000000000000301460806040526004361060515760003560e01c806321afdf8e1460565780632981eb7714607e5780639a543ca414609b578063ccd34cd51460c1575b600080fd5b606b6ec097ce7bc90715b34b9f100000000081565b6040519081526020015b60405180910390f35b60876217124081565b60405163ffffffff90911681526020016075565b60a9670de0b6b3a764000081565b60405167ffffffffffffffff90911681526020016075565b606b670de0b6b3a76400008156fea264697066735822122051573b877db53ce7fd9202510ed740ac5b29774096c961210de6c69c249f537264736f6c634300080c0033", +} + +// SlashingConstantsABI is the input ABI used to generate the binding from. +// Deprecated: Use SlashingConstantsMetaData.ABI instead. +var SlashingConstantsABI = SlashingConstantsMetaData.ABI + +// SlashingConstantsBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use SlashingConstantsMetaData.Bin instead. +var SlashingConstantsBin = SlashingConstantsMetaData.Bin + +// DeploySlashingConstants deploys a new Ethereum contract, binding an instance of SlashingConstants to it. +func DeploySlashingConstants(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *SlashingConstants, error) { + parsed, err := SlashingConstantsMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SlashingConstantsBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &SlashingConstants{SlashingConstantsCaller: SlashingConstantsCaller{contract: contract}, SlashingConstantsTransactor: SlashingConstantsTransactor{contract: contract}, SlashingConstantsFilterer: SlashingConstantsFilterer{contract: contract}}, nil +} + +// SlashingConstants is an auto generated Go binding around an Ethereum contract. +type SlashingConstants struct { + SlashingConstantsCaller // Read-only binding to the contract + SlashingConstantsTransactor // Write-only binding to the contract + SlashingConstantsFilterer // Log filterer for contract events +} + +// SlashingConstantsCaller is an auto generated read-only Go binding around an Ethereum contract. +type SlashingConstantsCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SlashingConstantsTransactor is an auto generated write-only Go binding around an Ethereum contract. +type SlashingConstantsTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SlashingConstantsFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type SlashingConstantsFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// SlashingConstantsSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type SlashingConstantsSession struct { + Contract *SlashingConstants // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SlashingConstantsCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type SlashingConstantsCallerSession struct { + Contract *SlashingConstantsCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// SlashingConstantsTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type SlashingConstantsTransactorSession struct { + Contract *SlashingConstantsTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// SlashingConstantsRaw is an auto generated low-level Go binding around an Ethereum contract. +type SlashingConstantsRaw struct { + Contract *SlashingConstants // Generic contract binding to access the raw methods on +} + +// SlashingConstantsCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type SlashingConstantsCallerRaw struct { + Contract *SlashingConstantsCaller // Generic read-only contract binding to access the raw methods on +} + +// SlashingConstantsTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type SlashingConstantsTransactorRaw struct { + Contract *SlashingConstantsTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewSlashingConstants creates a new instance of SlashingConstants, bound to a specific deployed contract. +func NewSlashingConstants(address common.Address, backend bind.ContractBackend) (*SlashingConstants, error) { + contract, err := bindSlashingConstants(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &SlashingConstants{SlashingConstantsCaller: SlashingConstantsCaller{contract: contract}, SlashingConstantsTransactor: SlashingConstantsTransactor{contract: contract}, SlashingConstantsFilterer: SlashingConstantsFilterer{contract: contract}}, nil +} + +// NewSlashingConstantsCaller creates a new read-only instance of SlashingConstants, bound to a specific deployed contract. +func NewSlashingConstantsCaller(address common.Address, caller bind.ContractCaller) (*SlashingConstantsCaller, error) { + contract, err := bindSlashingConstants(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &SlashingConstantsCaller{contract: contract}, nil +} + +// NewSlashingConstantsTransactor creates a new write-only instance of SlashingConstants, bound to a specific deployed contract. +func NewSlashingConstantsTransactor(address common.Address, transactor bind.ContractTransactor) (*SlashingConstantsTransactor, error) { + contract, err := bindSlashingConstants(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &SlashingConstantsTransactor{contract: contract}, nil +} + +// NewSlashingConstantsFilterer creates a new log filterer instance of SlashingConstants, bound to a specific deployed contract. +func NewSlashingConstantsFilterer(address common.Address, filterer bind.ContractFilterer) (*SlashingConstantsFilterer, error) { + contract, err := bindSlashingConstants(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &SlashingConstantsFilterer{contract: contract}, nil +} + +// bindSlashingConstants binds a generic wrapper to an already deployed contract. +func bindSlashingConstants(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := SlashingConstantsMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_SlashingConstants *SlashingConstantsRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SlashingConstants.Contract.SlashingConstantsCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_SlashingConstants *SlashingConstantsRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SlashingConstants.Contract.SlashingConstantsTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_SlashingConstants *SlashingConstantsRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SlashingConstants.Contract.SlashingConstantsTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_SlashingConstants *SlashingConstantsCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _SlashingConstants.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_SlashingConstants *SlashingConstantsTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _SlashingConstants.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_SlashingConstants *SlashingConstantsTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _SlashingConstants.Contract.contract.Transact(opts, method, params...) +} + +// DEALLOCATIONDELAY is a free data retrieval call binding the contract method 0x2981eb77. +// +// Solidity: function DEALLOCATION_DELAY() view returns(uint32) +func (_SlashingConstants *SlashingConstantsCaller) DEALLOCATIONDELAY(opts *bind.CallOpts) (uint32, error) { + var out []interface{} + err := _SlashingConstants.contract.Call(opts, &out, "DEALLOCATION_DELAY") + + if err != nil { + return *new(uint32), err + } + + out0 := *abi.ConvertType(out[0], new(uint32)).(*uint32) + + return out0, err + +} + +// DEALLOCATIONDELAY is a free data retrieval call binding the contract method 0x2981eb77. +// +// Solidity: function DEALLOCATION_DELAY() view returns(uint32) +func (_SlashingConstants *SlashingConstantsSession) DEALLOCATIONDELAY() (uint32, error) { + return _SlashingConstants.Contract.DEALLOCATIONDELAY(&_SlashingConstants.CallOpts) +} + +// DEALLOCATIONDELAY is a free data retrieval call binding the contract method 0x2981eb77. +// +// Solidity: function DEALLOCATION_DELAY() view returns(uint32) +func (_SlashingConstants *SlashingConstantsCallerSession) DEALLOCATIONDELAY() (uint32, error) { + return _SlashingConstants.Contract.DEALLOCATIONDELAY(&_SlashingConstants.CallOpts) +} + +// INITIALTOTALMAGNITUDE is a free data retrieval call binding the contract method 0x9a543ca4. +// +// Solidity: function INITIAL_TOTAL_MAGNITUDE() view returns(uint64) +func (_SlashingConstants *SlashingConstantsCaller) INITIALTOTALMAGNITUDE(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _SlashingConstants.contract.Call(opts, &out, "INITIAL_TOTAL_MAGNITUDE") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// INITIALTOTALMAGNITUDE is a free data retrieval call binding the contract method 0x9a543ca4. +// +// Solidity: function INITIAL_TOTAL_MAGNITUDE() view returns(uint64) +func (_SlashingConstants *SlashingConstantsSession) INITIALTOTALMAGNITUDE() (uint64, error) { + return _SlashingConstants.Contract.INITIALTOTALMAGNITUDE(&_SlashingConstants.CallOpts) +} + +// INITIALTOTALMAGNITUDE is a free data retrieval call binding the contract method 0x9a543ca4. +// +// Solidity: function INITIAL_TOTAL_MAGNITUDE() view returns(uint64) +func (_SlashingConstants *SlashingConstantsCallerSession) INITIALTOTALMAGNITUDE() (uint64, error) { + return _SlashingConstants.Contract.INITIALTOTALMAGNITUDE(&_SlashingConstants.CallOpts) +} + +// PRECISIONFACTOR is a free data retrieval call binding the contract method 0xccd34cd5. +// +// Solidity: function PRECISION_FACTOR() view returns(uint256) +func (_SlashingConstants *SlashingConstantsCaller) PRECISIONFACTOR(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SlashingConstants.contract.Call(opts, &out, "PRECISION_FACTOR") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// PRECISIONFACTOR is a free data retrieval call binding the contract method 0xccd34cd5. +// +// Solidity: function PRECISION_FACTOR() view returns(uint256) +func (_SlashingConstants *SlashingConstantsSession) PRECISIONFACTOR() (*big.Int, error) { + return _SlashingConstants.Contract.PRECISIONFACTOR(&_SlashingConstants.CallOpts) +} + +// PRECISIONFACTOR is a free data retrieval call binding the contract method 0xccd34cd5. +// +// Solidity: function PRECISION_FACTOR() view returns(uint256) +func (_SlashingConstants *SlashingConstantsCallerSession) PRECISIONFACTOR() (*big.Int, error) { + return _SlashingConstants.Contract.PRECISIONFACTOR(&_SlashingConstants.CallOpts) +} + +// PRECISIONFACTORSQUARED is a free data retrieval call binding the contract method 0x21afdf8e. +// +// Solidity: function PRECISION_FACTOR_SQUARED() view returns(uint256) +func (_SlashingConstants *SlashingConstantsCaller) PRECISIONFACTORSQUARED(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _SlashingConstants.contract.Call(opts, &out, "PRECISION_FACTOR_SQUARED") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// PRECISIONFACTORSQUARED is a free data retrieval call binding the contract method 0x21afdf8e. +// +// Solidity: function PRECISION_FACTOR_SQUARED() view returns(uint256) +func (_SlashingConstants *SlashingConstantsSession) PRECISIONFACTORSQUARED() (*big.Int, error) { + return _SlashingConstants.Contract.PRECISIONFACTORSQUARED(&_SlashingConstants.CallOpts) +} + +// PRECISIONFACTORSQUARED is a free data retrieval call binding the contract method 0x21afdf8e. +// +// Solidity: function PRECISION_FACTOR_SQUARED() view returns(uint256) +func (_SlashingConstants *SlashingConstantsCallerSession) PRECISIONFACTORSQUARED() (*big.Int, error) { + return _SlashingConstants.Contract.PRECISIONFACTORSQUARED(&_SlashingConstants.CallOpts) +} diff --git a/script/configs/holesky/Deploy_RewardsCoordinator.holesky.config.json b/script/configs/holesky/Deploy_RewardsCoordinator.holesky.config.json new file mode 100644 index 000000000..ecd86e029 --- /dev/null +++ b/script/configs/holesky/Deploy_RewardsCoordinator.holesky.config.json @@ -0,0 +1,60 @@ +{ + "chainInfo": { + "chainId": 17000 + }, + "multisig_addresses": { + "pauserMultisig": "0x53410249ec7d3a3F9F1ba3912D50D6A3Df6d10A7", + "communityMultisig": "0xCb8d2f9e55Bc7B1FA9d089f9aC80C583D2BDD5F7", + "operationsMultisig": "0xfaEF7338b7490b9E272d80A1a39f4657cAf2b97d", + "executorMultisig": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "timelock": "0xcF19CE0561052a7A7Ff21156730285997B350A7D" + }, + "strategies": { + "numStrategies": 0, + "MAX_PER_DEPOSIT": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "MAX_TOTAL_DEPOSITS": 115792089237316195423570985008687907853269984665640564039457584007913129639935, + "strategiesToDeploy": [] + }, + "strategyManager": { + "init_strategy_whitelister": "0x28Ade60640fdBDb2609D8d8734D1b5cBeFc0C348", + "init_paused_status": 0 + }, + "delegationManager": { + "init_paused_status": 0, + "init_minWithdrawalDelayBlocks": 10 + }, + "rewardsCoordinator": { + "init_paused_status": 0, + "CALCULATION_INTERVAL_SECONDS": 604800, + "MAX_REWARDS_DURATION": 6048000, + "MAX_RETROACTIVE_LENGTH": 7776000, + "MAX_FUTURE_LENGTH": 2592000, + "GENESIS_REWARDS_TIMESTAMP": 1710979200, + "rewards_updater_address": "0x18a0f92Ad9645385E8A8f3db7d0f6CF7aBBb0aD4", + "activation_delay": 7200, + "calculation_interval_seconds": 604800, + "global_operator_commission_bips": 1000, + "OPERATOR_SET_GENESIS_REWARDS_TIMESTAMP": 1720656000, + "OPERATOR_SET_MAX_RETROACTIVE_LENGTH": 2592000 + }, + "avsDirectory": { + "init_paused_status": 0 + }, + "slasher": { + "init_paused_status": 0 + }, + "eigenPod": { + "MAX_RESTAKED_BALANCE_GWEI_PER_VALIDATOR": 32000000000, + "GENESIS_TIME": 1695902400 + }, + "eigenPodManager": { + "init_paused_status": 0, + "deneb_fork_timestamp": "1707305664" + }, + "delayedWithdrawalRouter": { + "init_paused_status": 0, + "init_withdrawalDelayBlocks": 10 + }, + "ethPOSDepositAddress": "0x4242424242424242424242424242424242424242", + "beaconOracleAddress": "0x4C116BB629bff7A8373c2378bBd919f8349B8f25" +} \ No newline at end of file diff --git a/script/deploy/devnet/Upgrade.s.sol b/script/deploy/devnet/Upgrade.s.sol new file mode 100644 index 000000000..b9b430512 --- /dev/null +++ b/script/deploy/devnet/Upgrade.s.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import "../../utils/ExistingDeploymentParser.sol"; + +// # To load the variables in the .env file +// source .env + +// Generic upgrade script, DOES NOT UPDATE IMPLEMENTATION IN OUTPUT FILE +// forge script script/deploy/devnet/Upgrade.s.sol --rpc-url $RPC_URL --private-key $PRIVATE_KEY +contract Upgrade is ExistingDeploymentParser { + + function run() external { + // EDIT this for your script + _parseDeployedContracts("script/output/holesky/pre_preprod_slashing.holesky.json"); + + vm.startBroadcast(); + AVSDirectory newAVSDirectoryImplementation = new AVSDirectory(delegationManager, eigenLayerPauserReg); + eigenLayerProxyAdmin.upgrade(ITransparentUpgradeableProxy(payable(address(avsDirectory))), address(newAVSDirectoryImplementation)); + vm.stopBroadcast(); + + } +} \ No newline at end of file diff --git a/script/deploy/holesky/upgrade_preprod_rewardsCoordinator.s.sol b/script/deploy/holesky/upgrade_preprod_rewardsCoordinator.s.sol new file mode 100644 index 000000000..90e12a73f --- /dev/null +++ b/script/deploy/holesky/upgrade_preprod_rewardsCoordinator.s.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.12; + +import "./Deploy_Test_RewardsCoordinator.s.sol"; + +/** + * @notice Script used for the first deployment of EigenLayer core contracts to Holesky + * anvil --fork-url $RPC_HOLESKY + * + * Holesky testnet: Deploy/Upgrade RewardsCoordinator + * forge script script/deploy/holesky/upgrade_preprod_rewardsCoordinator.s.sol --rpc-url $RPC_HOLESKY --private-key $PRIVATE_KEY --broadcast -vvvv + * + */ +contract Upgrade_Preprod_RewardsCoordinator is Deploy_Test_RewardsCoordinator { + function run() external virtual override { + _parseInitialDeploymentParams("script/configs/holesky/eigenlayer_preprod.config.json"); + _parseDeployedContracts("script/configs/holesky/eigenlayer_addresses_preprod.config.json"); + + // Overwrite testAddress and multisigs to be EOAowner + testAddress = msg.sender; + executorMultisig = testAddress; + operationsMultisig = testAddress; + pauserMultisig = testAddress; + communityMultisig = testAddress; + STRATEGY_MANAGER_WHITELISTER = testAddress; + + // START RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.startBroadcast(); + + emit log_named_address("Deployer Address", msg.sender); + + _upgradeRewardsCoordinator(); + + // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.stopBroadcast(); + + // Sanity Checks + _verifyContractPointers(); + _verifyImplementations(); + _verifyContractsInitialized(false); + _verifyInitializationParams(); + + } +} diff --git a/script/deploy/local/operatorSets/DeployStrategies.s.sol b/script/deploy/local/operatorSets/DeployStrategies.s.sol new file mode 100644 index 000000000..e2f05e806 --- /dev/null +++ b/script/deploy/local/operatorSets/DeployStrategies.s.sol @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol"; +import "../../../utils/ExistingDeploymentParser.sol"; + +// # To load the variables in the .env file +// source .env + +// # To deploy and verify our contract +contract DeployStrategies is ExistingDeploymentParser { + + function run() external { + _parseDeployedContracts("script/output/devnet/M2_from_scratch_deployment_data.json"); + + vm.startBroadcast(); + StrategyDeployer strategyDeployer = new StrategyDeployer( + executorMultisig, + address(baseStrategyImplementation) + ); + uint256 batches = 1; + uint256 batchSize = 100; + + IStrategy[] memory strategies = new IStrategy[](batchSize * batches); + + for (uint256 i = 0; i < batches; i++) { + IStrategy[] memory strategiesJustDeployed = strategyDeployer.createManyStrategies(batchSize); + for (uint256 j = 0; j < batchSize; j++) { + strategies[i * batchSize + j] = strategiesJustDeployed[j]; + } + strategyManager.addStrategiesToDepositWhitelist(strategiesJustDeployed); + } + + vm.stopBroadcast(); + + address[] memory strategyAddresses; + assembly { + strategyAddresses := strategies + } + string memory deployed_strategies = vm.serializeAddress("", "strategies", strategyAddresses); + + vm.writeJson(deployed_strategies, "script/output/devnet/deployed_strategies.json"); + } +} + + +contract StrategyDeployer { + address immutable beneficiary; + address immutable baseStrategyImplementation; + + constructor(address _beneficiary, address _baseStrategyImplementation) { + beneficiary = _beneficiary; + baseStrategyImplementation = _baseStrategyImplementation; + } + + function createManyStrategies(uint256 numStrategies) external returns(IStrategy[] memory) { + IStrategy[] memory strategies = new IStrategy[](numStrategies); + for (uint256 i = 0; i < numStrategies; i++) { + // create a strategy + strategies[i] = + StrategyBaseTVLLimits( + address( + new TransparentUpgradeableProxy( + address(baseStrategyImplementation), + address(1), + abi.encodeWithSelector( + StrategyBaseTVLLimits.initialize.selector, + type(uint256).max, + type(uint256).max, + new ERC20PresetFixedSupply("Test", "TST", uint256(type(uint128).max), beneficiary), + address(1) + ) + ) + ) + ); + } + return strategies; + } +} \ No newline at end of file diff --git a/script/deploy/mainnet/Deploy_Strategy_Factory.s.sol b/script/deploy/mainnet/Deploy_Strategy_Factory.s.sol new file mode 100644 index 000000000..027d7ecdc --- /dev/null +++ b/script/deploy/mainnet/Deploy_Strategy_Factory.s.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "@openzeppelin/contracts/utils/Create2.sol"; +import "../../utils/ExistingDeploymentParser.sol"; + +import "../../../src/contracts/strategies/StrategyFactory.sol"; + +/** + * @notice Script used for the first deployment of EigenLayer core contracts to Holesky + * FORK LOCAL + * anvil --fork-url $RPC_MAINNET + * forge script script/deploy/mainnet/Deploy_Strategy_Factory.s.sol:MainnetStrategyFactoryDeploy --rpc-url http://127.0.0.1:8545 --private-key $PRIVATE_KEY --broadcast -vvvv + * + * MAINNET + * forge script script/deploy/mainnet/Deploy_Strategy_Factory.s.sol:MainnetStrategyFactoryDeploy --rpc-url $RPC_MAINNET --private-key $PRIVATE_KEY --verify --broadcast -vvvv + * + */ + +contract MainnetStrategyFactoryDeploy is ExistingDeploymentParser { + function run() external virtual { + // Use rewards config + _parseInitialDeploymentParams( + "script/configs/mainnet/v0.3.0-mainnet-rewards.config.json" + ); + _parseDeployedContracts( + "script/configs/mainnet/Mainnet_curent_deployment.config.json" + ); + + // START RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.startBroadcast(); + + emit log_named_address("Deployer Address", msg.sender); + + _deployStrategyFactory(); + + // STOP RECORDING TRANSACTIONS FOR DEPLOYMENT + vm.stopBroadcast(); + + // Sanity Checks + _verifyContractPointers(); + _verifyImplementations(); + _verifyContractsInitialized(false); + _verifyInitializationParams(); + + logAndOutputContractAddresses("script/output/mainnet/v0.3.2-mainnet-strategy-factory.output.json"); + } + + /** + * @notice Deploy StrategyFactory for Mainnet + */ + + function _deployStrategyFactory() internal { + strategyFactoryImplementation = new StrategyFactory( + strategyManager, eigenLayerPauserReg + ); + + + + } +} \ No newline at end of file diff --git a/src/test/harnesses/DelegationManagerHarness.sol b/src/test/harnesses/DelegationManagerHarness.sol new file mode 100644 index 000000000..640297995 --- /dev/null +++ b/src/test/harnesses/DelegationManagerHarness.sol @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.27; + +import "../../contracts/core/DelegationManager.sol"; +import "forge-std/Test.sol"; + +contract DelegationManagerHarness is DelegationManager { + + constructor( + IAVSDirectory _avsDirectory, + IStrategyManager _strategyManager, + IEigenPodManager _eigenPodManager, + IAllocationManager _allocationManager, + IPauserRegistry _pauserRegistry, + IPermissionController _permissionController, + uint32 _MIN_WITHDRAWAL_DELAY + ) + DelegationManager( + _avsDirectory, + _strategyManager, + _eigenPodManager, + _allocationManager, + _pauserRegistry, + _permissionController, + _MIN_WITHDRAWAL_DELAY + ) + {} + + function getSlashingFactor( + address staker, + IStrategy strategy, + uint64 operatorMaxMagnitude + ) external view returns (uint256) { + return _getSlashingFactor(staker, strategy, operatorMaxMagnitude); + } + + function getSlashingFactors( + address staker, + address operator, + IStrategy[] memory strategies + ) external view returns (uint256[] memory) { + return _getSlashingFactors(staker, operator, strategies); + } + + function getSlashingFactorsAtBlock( + address staker, + address operator, + IStrategy[] memory strategies, + uint32 blockNumber + ) external view returns (uint256[] memory) { + return _getSlashingFactorsAtBlock(staker, operator, strategies, blockNumber); + } + + function getSlashedSharesInQueue( + address operator, + IStrategy strategy, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) external view returns (uint256) { + return _getSlashedSharesInQueue(operator, strategy, prevMaxMagnitude, newMaxMagnitude); + } + + function getDepositScalingFactor( + address staker, + IStrategy strategy + ) external view returns (uint256) { + return _depositScalingFactor[staker][strategy]._scalingFactor; + } +} diff --git a/src/test/unit/DelegationUnit.t.sol b/src/test/unit/DelegationUnit.t.sol index 734253ac2..073330ad4 100644 --- a/src/test/unit/DelegationUnit.t.sol +++ b/src/test/unit/DelegationUnit.t.sol @@ -9,6 +9,7 @@ import "src/contracts/strategies/StrategyBase.sol"; import "src/test/utils/EigenLayerUnitTestSetup.sol"; import "src/contracts/libraries/SlashingLib.sol"; import "src/test/utils/SingleItemArrayLib.sol"; +import "src/test/harnesses/DelegationManagerHarness.sol"; // TODO: add upgrade tests for completing withdrawals queued before upgrade in integration tests // TODO: add slashing cases for withdrawing as shares (can also be in integration tests) @@ -22,72 +23,59 @@ import "src/test/utils/SingleItemArrayLib.sol"; contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManagerEvents, IDelegationManagerErrors { using SlashingLib for *; using SingleItemArrayLib for *; + using Math for *; - // Contract under test - DelegationManager delegationManager; - DelegationManager delegationManagerImplementation; + /// ----------------------------------------------------------------------- + /// Contracts and Mocks + /// ----------------------------------------------------------------------- - // Helper to use in storage - DepositScalingFactor dsf; - - // Mocks + DelegationManagerHarness delegationManager; + DelegationManagerHarness delegationManagerImplementation; StrategyBase strategyImplementation; StrategyBase strategyMock; - IERC20 tokenMock; uint256 tokenMockInitialSupply = 10e50; - uint32 constant MIN_WITHDRAWAL_DELAY_BLOCKS = 126_000; // 17.5 days in blocks + /// ----------------------------------------------------------------------- + /// Constants + /// ----------------------------------------------------------------------- + uint32 constant MIN_WITHDRAWAL_DELAY_BLOCKS = 126_000; // 17.5 days in blocks + IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); + uint8 internal constant PAUSED_NEW_DELEGATION = 0; + uint8 internal constant PAUSED_ENTER_WITHDRAWAL_QUEUE = 1; + uint8 internal constant PAUSED_EXIT_WITHDRAWAL_QUEUE = 2; // Fuzz bound checks uint256 constant MIN_FUZZ_SHARES = 10_000; uint256 constant MIN_FUZZ_MAGNITUDE = 10_000; - uint256 constant APPROX_REL_DIFF = 1e14; // 0.01% relative difference for assertion checks. Needed due to rounding errors - + uint256 constant APPROX_REL_DIFF = 1e6; // 0.0.0000000001000000% relative difference for assertion checks. Needed due to rounding errors // Max shares in a strategy, see StrategyBase.sol uint256 constant MAX_STRATEGY_SHARES = 1e38 - 1; uint256 constant MAX_ETH_SUPPLY = 120_400_000 ether; + // Helper to use in storage + DepositScalingFactor dsf; + + /// ----------------------------------------------------------------------- + /// Defaults & Mappings for Stack too deep errors + /// ----------------------------------------------------------------------- // Delegation signer uint256 delegationSignerPrivateKey = uint256(0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80); + address defaultApprover = cheats.addr(delegationSignerPrivateKey); uint256 stakerPrivateKey = uint256(123_456_789); - - // empty string reused across many tests - string emptyStringForMetadataURI; - - // "empty" / zero salt, reused across many tests - bytes32 emptySalt; - - // reused in various tests. in storage to help handle stack-too-deep errors - address defaultStaker = cheats.addr(uint256(123_456_789)); + address defaultStaker = cheats.addr(stakerPrivateKey); address defaultOperator = address(this); - address defaultApprover = cheats.addr(delegationSignerPrivateKey); + address defaultOperator2 = address(0x123); address defaultAVS = address(this); - - // 604800 seconds in week / 12 = 50,400 blocks - uint256 minWithdrawalDelayBlocks = 50400; - - IStrategy public constant beaconChainETHStrategy = IStrategy(0xbeaC0eeEeeeeEEeEeEEEEeeEEeEeeeEeeEEBEaC0); - - // Index for flag that pauses new delegations when set. - uint8 internal constant PAUSED_NEW_DELEGATION = 0; - - // Index for flag that pauses queuing new withdrawals when set. - uint8 internal constant PAUSED_ENTER_WITHDRAWAL_QUEUE = 1; - - // Index for flag that pauses completing existing withdrawals when set. - uint8 internal constant PAUSED_EXIT_WITHDRAWAL_QUEUE = 2; - - // the number of 12-second blocks in 30 days (60 * 60 * 24 * 30 / 12 = 216,000) - uint256 public constant MAX_WITHDRAWAL_DELAY_BLOCKS = 216000; + string emptyStringForMetadataURI; + ISignatureUtils.SignatureWithExpiry emptyApproverSignatureAndExpiry; + bytes32 emptySalt; /// @notice mappings used to handle duplicate entries in fuzzed address array input mapping(address => uint256) public totalSharesForStrategyInArray; mapping(IStrategy => uint256) public totalSharesDecreasedForStrategy; mapping(IStrategy => uint256) public delegatedSharesBefore; - mapping(address => uint256) public stakerDepositShares; - // Keep track of queued withdrawals mapping(address => IDelegationManagerTypes.Withdrawal[]) public stakerQueuedWithdrawals; @@ -95,7 +83,7 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag // Setup EigenLayerUnitTestSetup.setUp(); - delegationManager = DelegationManager( + delegationManager = DelegationManagerHarness( address(new TransparentUpgradeableProxy(address(emptyContract), address(eigenLayerProxyAdmin), "")) ); @@ -103,11 +91,11 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag strategyManagerMock = StrategyManagerMock(payable(address(new StrategyManagerMock(delegationManager)))); // Deploy DelegationManager implmentation and upgrade proxy - delegationManagerImplementation = new DelegationManager( - IAVSDirectory(address(avsDirectoryMock)), - IStrategyManager(address(strategyManagerMock)), - IEigenPodManager(address(eigenPodManagerMock)), - IAllocationManager(address(allocationManagerMock)), + delegationManagerImplementation = new DelegationManagerHarness( + IAVSDirectory(address(avsDirectoryMock)), + IStrategyManager(address(strategyManagerMock)), + IEigenPodManager(address(eigenPodManagerMock)), + IAllocationManager(address(allocationManagerMock)), pauserRegistry, IPermissionController(address(permissionController)), MIN_WITHDRAWAL_DELAY_BLOCKS @@ -151,7 +139,7 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag */ /** - * @notice internal function to deploy mock tokens and strategies and have the staker deposit into them. + * @notice internal function to deploy mock tokens and strategies and have the staker deposit into them. * Since we are mocking the strategyManager we call strategyManagerMock.setDeposits so that when * DelegationManager calls getDeposits, we can have these share amounts returned. */ @@ -205,8 +193,9 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag // If depositing beaconShares, then for last index of shareAmount, set shares into EPM instead if (strategies[i] == beaconChainETHStrategy) { eigenPodManagerMock.setPodOwnerShares(staker, int256(sharesAmounts[i])); + } else { + strategyManagerMock.addDeposit(staker, strategies[i], sharesAmounts[i]); } - strategyManagerMock.addDeposit(staker, strategies[i], sharesAmounts[i]); } } @@ -291,12 +280,12 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag /** * @notice Using this helper function to fuzz withdrawalAmounts since fuzzing two dynamic sized arrays of equal lengths - * reject too many inputs. + * reject too many inputs. */ function _fuzzDepositWithdrawalAmounts( Randomness r, uint32 numStrategies - )internal returns ( + ) internal returns ( uint256[] memory depositAmounts, uint256[] memory withdrawalAmounts, uint64[] memory prevMagnitudes, @@ -352,7 +341,7 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag scaledShares: scaledSharesArray }); bytes32 withdrawalRoot = delegationManager.calculateWithdrawalRoot(withdrawal); - + return (queuedWithdrawalParams, withdrawal, withdrawalRoot); } @@ -391,7 +380,7 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag scaledShares: scaledSharesArray }); bytes32 withdrawalRoot = delegationManager.calculateWithdrawalRoot(withdrawal); - + return (queuedWithdrawalParams, withdrawal, withdrawalRoot); } @@ -417,8 +406,11 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag uint64 maxMagnitude = allocationManagerMock.getMaxMagnitudes(operator, strategyArray)[0]; uint256 slashingFactor = _getSlashingFactor(staker, strategy, maxMagnitude); - DepositScalingFactor memory _dsf = DepositScalingFactor(delegationManager.depositScalingFactor(staker, strategy)); - uint256 sharesToWithdraw = _dsf.calcWithdrawable(depositSharesToWithdraw, slashingFactor); + uint256 sharesToWithdraw = _calcWithdrawableShares( + depositSharesToWithdraw, + delegationManager.depositScalingFactor(staker, strategy), + slashingFactor + ); uint256 scaledShares = SlashingLib.scaleForQueueWithdrawal({ sharesToWithdraw: sharesToWithdraw, @@ -438,10 +430,12 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag require(strategies.length == depositSharesToWithdraw.length, "DelegationManagerUnitTests: length mismatch"); uint256[] memory withdrawnShares = new uint256[](strategies.length); for (uint256 i = 0; i < strategies.length; i++) { - DepositScalingFactor memory _dsf = DepositScalingFactor({ - _scalingFactor: delegationManager.depositScalingFactor(staker, strategies[i]) - }); - withdrawnShares[i] = _dsf.calcWithdrawable(depositSharesToWithdraw[i], maxMagnitudes[i]); + uint256 slashingFactor = _getSlashingFactor(staker, strategies[i], maxMagnitudes[i]); + withdrawnShares[i] = _calcWithdrawableShares( + depositSharesToWithdraw[i], + delegationManager.depositScalingFactor(staker, strategies[i]), + slashingFactor + ); } return withdrawnShares; } @@ -461,7 +455,7 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag /** * Deploy and deposit staker into a single strategy, then set up a queued withdrawal for the staker - * Assumptions: + * Assumptions: * - operator is already a registered operator. * - withdrawalAmount <= depositAmount */ @@ -559,7 +553,7 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag /** * Deploy and deposit staker into strategies, then set up a queued withdrawal for the staker - * Assumptions: + * Assumptions: * - operator is already a registered operator. * - for each i, withdrawalAmount[i] <= depositAmount[i] (see filterFuzzedDepositWithdrawInputs above) */ @@ -602,14 +596,47 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag allocationManagerMock.setMaxMagnitude(operator, strategy, magnitude); } - function _setOperatorMagnitudes( - address operator, - IStrategy[] memory strategies, - uint64[] memory magnitudes - ) internal { - allocationManagerMock.setMaxMagnitudes(operator, strategies, magnitudes); + function _setNewBeaconChainSlashingFactor( + address staker, + int256 beaconShares, + uint256 sharesDecrease + ) internal returns ( + uint64 prevBeaconChainSlashingFactor, + uint256 wadSlashed, + uint64 newBeaconChainScalingFactor + ) { + uint256 newRestakedBalanceWei = uint256(beaconShares) - sharesDecrease; + uint256 proportionRemainingWad = newRestakedBalanceWei.divWadRoundUp(uint256(beaconShares)); + wadSlashed = uint256(WAD) - proportionRemainingWad; + prevBeaconChainSlashingFactor = eigenPodManagerMock.beaconChainSlashingFactor(staker); + + newBeaconChainScalingFactor = uint64(prevBeaconChainSlashingFactor.mulWad(proportionRemainingWad)); + eigenPodManagerMock.setBeaconChainSlashingFactor(staker, newBeaconChainScalingFactor); + } + + function _decreaseBeaconChainShares( + address staker, + int256 beaconShares, + uint256 sharesDecrease + ) internal returns (uint64 prevBeaconChainSlashingFactor, uint256 wadSlashed, uint64 newBeaconChainSlashingFactor) { + ( + prevBeaconChainSlashingFactor, + wadSlashed, + newBeaconChainSlashingFactor + ) = _setNewBeaconChainSlashingFactor(staker, beaconShares, sharesDecrease); + cheats.prank(address(eigenPodManagerMock)); + delegationManager.decreaseDelegatedShares({ + staker: staker, + curDepositShares: uint256(beaconShares), + prevBeaconChainSlashingFactor: prevBeaconChainSlashingFactor, + wadSlashed: wadSlashed + }); } + /// ----------------------------------------------------------------------- + /// Event helpers + /// ----------------------------------------------------------------------- + struct RegisterAsOperatorEmitStruct { address operator; address delegationApprover; @@ -682,13 +709,12 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag function _undelegate_expectEmit_singleStrat( UndelegateEmitStruct memory params ) internal { + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit StakerUndelegated(params.staker, params.operator); if (params.forceUndelegated) { cheats.expectEmit(true, true, true, true, address(delegationManager)); emit StakerForceUndelegated(params.staker, params.operator); } - - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit StakerUndelegated(params.staker, params.operator); if (address(params.strategy) != address(0)) { cheats.expectEmit(true, true, true, true, address(delegationManager)); @@ -713,6 +739,17 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag emit OperatorSharesIncreased(params.operator, params.staker, params.strategy, params.sharesToIncrease); } + struct DecreaseDelegatedSharesEmitStruct { + address staker; + address operator; + uint256 sharesToDecrease; + } + + function _decreaseDelegatedShares_expectEmit(DecreaseDelegatedSharesEmitStruct memory params) internal { + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit OperatorSharesDecreased(params.operator, params.staker, beaconChainETHStrategy, params.sharesToDecrease); + } + struct QueueWithdrawalsEmitStruct { address staker; address operator; @@ -725,18 +762,20 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag for (uint256 i = 0; i < params.queuedWithdrawalParams.length; i++) { uint256[] memory sharesToWithdraw = new uint256[](params.queuedWithdrawalParams[i].strategies.length); for (uint256 j = 0; j < params.queuedWithdrawalParams[i].strategies.length; j++) { - DepositScalingFactor memory _dsf = DepositScalingFactor({ - _scalingFactor: delegationManager.depositScalingFactor(defaultStaker, params.queuedWithdrawalParams[i].strategies[j]) - }); + uint256 depositScalingFactor = delegationManager.depositScalingFactor(defaultStaker, params.queuedWithdrawalParams[i].strategies[j]); uint256 newMaxMagnitude = allocationManagerMock.getMaxMagnitudes(params.operator, params.queuedWithdrawalParams[i].strategies)[j]; - sharesToWithdraw[j] = _dsf.calcWithdrawable(params.queuedWithdrawalParams[i].depositShares[j], newMaxMagnitude); + sharesToWithdraw[j] = _calcWithdrawableShares( + params.queuedWithdrawalParams[i].depositShares[j], + depositScalingFactor, + newMaxMagnitude + ); cheats.expectEmit(true, true, true, true, address(delegationManager)); emit OperatorSharesDecreased( params.operator, params.staker, params.queuedWithdrawalParams[i].strategies[j], - _dsf.calcWithdrawable(params.queuedWithdrawalParams[i].depositShares[j], newMaxMagnitude) + sharesToWithdraw[j] ); } cheats.expectEmit(true, true, true, true, address(delegationManager)); @@ -744,6 +783,333 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag } } + struct CompleteQueuedWithdrawalEmitStruct { + Withdrawal withdrawal; + IERC20[] tokens; + bool receiveAsTokens; + } + + function _completeQueuedWithdrawal_expectEmit(CompleteQueuedWithdrawalEmitStruct memory params) internal { + if (!params.receiveAsTokens) { + address operator = delegationManager.delegatedTo(params.withdrawal.staker); + uint64[] memory prevSlashingFactors = new uint64[](params.withdrawal.strategies.length); + prevSlashingFactors = allocationManagerMock.getMaxMagnitudes(operator, params.withdrawal.strategies); + + // receiving as shares so check for OperatorSharesIncrease and DepositScalingFactor updated + for (uint256 i = 0; i < params.withdrawal.strategies.length; i++) { + // scale shares for complete withdrawal to get expected withdrawn shares + prevSlashingFactors[i] = uint64(delegationManager.getSlashingFactor( + params.withdrawal.staker, + params.withdrawal.strategies[i], + prevSlashingFactors[i] + )); + + uint256 sharesToWithdraw = SlashingLib.scaleForCompleteWithdrawal({ + scaledShares: params.withdrawal.scaledShares[i], + slashingFactor: prevSlashingFactors[i] + }); + + // Get updated deposit scaling factor + dsf = DepositScalingFactor({ + _scalingFactor: delegationManager.depositScalingFactor(params.withdrawal.staker, params.withdrawal.strategies[i]) + }); + uint256 curDepositShares; + if (params.withdrawal.strategies[i] == beaconChainETHStrategy) { + curDepositShares = uint256(eigenPodManagerMock.stakerDepositShares(params.withdrawal.staker, address(0))); + } else { + curDepositShares = strategyManagerMock.stakerDepositShares(params.withdrawal.staker, params.withdrawal.strategies[i]); + } + dsf.update(curDepositShares, sharesToWithdraw, prevSlashingFactors[i]); + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit DepositScalingFactorUpdated(params.withdrawal.staker, params.withdrawal.strategies[i], dsf.scalingFactor()); + + if (operator != address(0)) { + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit OperatorSharesIncreased( + operator, + params.withdrawal.staker, + params.withdrawal.strategies[i], + sharesToWithdraw + ); + } + } + } + + emit SlashingWithdrawalCompleted( + delegationManager.calculateWithdrawalRoot(params.withdrawal) + ); + } + + struct CompleteQueuedWithdrawalsEmitStruct { + Withdrawal[] withdrawals; + IERC20[][] tokens; + bool[] receiveAsTokens; + } + + function _completeQueuedWithdrawals_expectEmit( + CompleteQueuedWithdrawalsEmitStruct memory params + ) internal { + for (uint256 i = 0; i < params.withdrawals.length; i++) { + _completeQueuedWithdrawal_expectEmit( + CompleteQueuedWithdrawalEmitStruct({ + withdrawal: params.withdrawals[i], + tokens: params.tokens[i], + receiveAsTokens: params.receiveAsTokens[i] + }) + ); + } + } + + struct BurnOperatorSharesEmitStruct { + address operator; + IStrategy strategy; + uint256 sharesToDecrease; + uint256 sharesToBurn; + } + + function _burnOperatorShares_expectEmit(BurnOperatorSharesEmitStruct memory params) internal { + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit OperatorSharesDecreased(params.operator, address(0), params.strategy, params.sharesToDecrease); + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit OperatorSharesBurned(params.operator, params.strategy, params.sharesToBurn); + } + + /// ----------------------------------------------------------------------- + /// Slashing Lib helpers + /// Logic is essentially copied from SlashingLib to test the calculations + /// and to avoid using the same library in the tests + /// ----------------------------------------------------------------------- + + /// @notice Calculates the exact withdrawable shares + function _calcWithdrawableShares( + uint256 depositShares, + uint256 depositScalingFactor, + uint256 slashingFactor + ) internal pure returns (uint256) { + return depositShares + .mulWad(depositScalingFactor) + .mulWad(slashingFactor); + } + + /// @notice Calculates the new deposit scaling factor after a deposit + function _calcDepositScalingFactor( + uint256 prevDsf, + uint256 prevDepositShares, + uint256 addedDepositShares, + uint256 slashingFactor + ) internal pure returns (uint256) { + if (prevDepositShares == 0) return uint256(WAD).divWad(slashingFactor); + + uint256 currWithdrawableShares = _calcWithdrawableShares( + prevDepositShares, + prevDsf, + slashingFactor + ); + + uint256 newWithdrawableShares = currWithdrawableShares + addedDepositShares; + + uint256 newDsf = newWithdrawableShares + .divWad(prevDepositShares + addedDepositShares) + .divWad(slashingFactor); + + return newDsf; + } + + function _calcSlashedAmount( + uint256 operatorShares, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) internal pure returns (uint256 slashedAmount, uint256 operatorSharesAfterSlash) { + operatorSharesAfterSlash = operatorShares.mulDiv(newMaxMagnitude, prevMaxMagnitude, Math.Rounding.Up); + slashedAmount = operatorShares - operatorSharesAfterSlash; + } + + /// ----------------------------------------------------------------------- + /// Helper Assertions + /// ----------------------------------------------------------------------- + + /// @notice Asserts for depositShares, withdrawableShares, and depositScalingFactor after a deposit + function _assertDeposit( + address staker, + address operator, + IStrategy strategy, + uint256 operatorSharesBefore, + uint256 withdrawableSharesBefore, + uint256 depositSharesBefore, + uint256 prevDsf, + uint256 depositAmount + ) internal view { + ( + uint256[] memory withdrawableShares, + uint256[] memory depositShares + ) = delegationManager.getWithdrawableShares(staker, strategy.toArray()); + // Check deposit shares added correctly + assertEq( + depositShares[0], + depositSharesBefore + depositAmount, + "depositShares should be equal to depositSharesBefore + depositAmount" + ); + // Check withdrawable shares are increased, with rounding error + assertApproxEqRel( + withdrawableShares[0], + withdrawableSharesBefore + depositAmount, + APPROX_REL_DIFF, + "withdrawableShares should be equal to existingDepositShares - depositShares" + ); + // Check the new dsf is accurate + uint256 expectedWithdrawableShares; + uint256 expectedDsf; + { + uint64 maxMagnitude = allocationManagerMock.getMaxMagnitude(operator, strategy); + uint256 slashingFactor = _getSlashingFactor(staker, strategy, maxMagnitude); + expectedDsf = _calcDepositScalingFactor( + prevDsf, + depositSharesBefore, + depositAmount, + slashingFactor + ); + expectedWithdrawableShares = _calcWithdrawableShares( + depositSharesBefore + depositAmount, + expectedDsf, + slashingFactor + ); + } + // Check the new dsf is accurate + assertEq( + expectedDsf, + delegationManager.depositScalingFactor(staker, strategy), + "depositScalingFactor should be equal to expectedDsf" + ); + // Check new operatorShares increased correctly + if (operator != address(0)) { + assertEq( + operatorSharesBefore + depositAmount, + delegationManager.operatorShares(operator, strategy), + "OperatorShares not increased correctly" + ); + } + // Check the newly calculated withdrawable shares are correct + assertEq( + withdrawableShares[0], + expectedWithdrawableShares, + "withdrawableShares should be equal to expectedWithdrawableShares" + ); + } + + /// @notice Asserts for depositShares, and operatorShares decremented properly after a withdrawal + function _assertWithdrawal( + address staker, + address operator, + IStrategy strategy, + uint256 operatorSharesBefore, + uint256 depositSharesBefore, + uint256 depositSharesWithdrawn, + uint256 depositScalingFactor, + uint256 slashingFactor + ) internal view { + ( + uint256[] memory withdrawableShares, + uint256[] memory depositShares + ) = delegationManager.getWithdrawableShares(staker, strategy.toArray()); + // Check deposit shares decreased correctly + assertEq( + depositShares[0], + depositSharesBefore - depositSharesWithdrawn, + "depositShares should be equal to depositSharesBefore - depositSharesWithdrawn" + ); + // Check withdrawable shares are decreased, with rounding error + uint256 expectedWithdrawableShares = _calcWithdrawableShares( + depositSharesBefore - depositSharesWithdrawn, + depositScalingFactor, + slashingFactor + ); + assertEq( + withdrawableShares[0], + expectedWithdrawableShares, + "withdrawableShares should be equal to expectedWithdrawableShares" + ); + // Check operatorShares decreased properly + uint256 expectedWithdrawnShares = _calcWithdrawableShares( + depositSharesWithdrawn, + depositScalingFactor, + slashingFactor + ); + assertEq( + operatorSharesBefore - expectedWithdrawnShares, + delegationManager.operatorShares(operator, strategy), + "OperatorShares not decreased correctly" + ); + } + + function _assertOperatorSharesAfterSlash( + address operator, + IStrategy strategy, + uint256 operatorSharesBefore, + uint64 prevMaxMagnitude, + uint64 newMaxMagnitude + ) internal view returns (uint256 sharesToDecrement, uint256 operatorSharesAfterSlash) { + (sharesToDecrement, operatorSharesAfterSlash) = _calcSlashedAmount({ + operatorShares: operatorSharesBefore, + prevMaxMagnitude: prevMaxMagnitude, + newMaxMagnitude: newMaxMagnitude + }); + + assertEq( + operatorSharesAfterSlash, + delegationManager.operatorShares(operator, strategy), + "OperatorShares should be equal to operatorSharesAfterSlash" + ); + assertEq( + delegationManager.operatorShares(operator, strategy) + sharesToDecrement, + operatorSharesBefore, + "OperatorShares + sharesToDecrement should be equal to operatorSharesBefore" + ); + } + + function _assertSharesAfterSlash( + address staker, + IStrategy strategy, + uint256 withdrawableSharesBefore, + uint256 expectedWithdrawableShares, + uint256 prevMaxMagnitude, + uint256 currMaxMagnitude + ) internal view { + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategy.toArray()); + + assertApproxEqRel( + uint256(withdrawableSharesBefore).mulDiv(currMaxMagnitude, prevMaxMagnitude), + withdrawableShares[0], + APPROX_REL_DIFF, + "withdrawableShares should be equal to withdrawableSharesBefore * currMaxMagnitude / prevMaxMagnitude" + ); + + assertEq( + withdrawableShares[0], + expectedWithdrawableShares, + "withdrawableShares should be equal to expectedWithdrawableShares" + ); + } + + function _assertSharesAfterBeaconSlash( + address staker, + uint256 withdrawableSharesBefore, + uint256 expectedWithdrawableShares, + uint256 prevBeaconChainScalingFactor + ) internal view { + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, beaconChainETHStrategy.toArray()); + uint256 currBeaconSlashingFactor = eigenPodManagerMock.beaconChainSlashingFactor(defaultStaker); + assertEq( + withdrawableShares[0], + expectedWithdrawableShares, + "withdrawableShares should be equal to expectedWithdrawableShares" + ); + assertApproxEqRel( + uint256(withdrawableSharesBefore).mulDiv(currBeaconSlashingFactor, prevBeaconChainScalingFactor), + withdrawableShares[0], + APPROX_REL_DIFF, + "withdrawableShares should be equal to withdrawableSharesBefore * currBeaconSlashingFactor / prevBeaconChainSlashingFactor" + ); + } /// @notice Due to rounding, withdrawable shares and operator shares may not align even if the operator /// only has the single staker with deposits. @@ -752,29 +1118,12 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag uint256 operatorShares, string memory errorMessage ) internal pure { - assertLe( + assertApproxEqRel( withdrawableShares, operatorShares, - "withdrawableShares should be less than or equal to operatorShares" + APPROX_REL_DIFF, + errorMessage ); - if (withdrawableShares < 1e18) { - // Note that the amount of "drift"/difference between staker withdrawable shares - // and operator shares is a result of the nested floor division in the calculation - // of withdrawable shares in calcWithdrawable. This is expected behavior. - assertApproxEqAbs( - withdrawableShares, - operatorShares, - 1, - errorMessage - ); - } else { - assertApproxEqRel( - withdrawableShares, - operatorShares, - APPROX_REL_DIFF, - errorMessage - ); - } assertLe( withdrawableShares, operatorShares, @@ -839,6 +1188,60 @@ contract DelegationManagerUnitTests is EigenLayerUnitTestSetup, IDelegationManag } } } + + /** + * @notice Assertion checks after queuing a withdrawal. Reads withdrawals set in storage in test + * - Asserts exact match of Withdrawal struct exists in storage + * - Asserts Withdrawal root is pending + */ + function _assertQueuedWithdrawalExists( + address staker, + Withdrawal memory withdrawal + ) internal view { + bytes32 withdrawalRootToCheck = delegationManager.calculateWithdrawalRoot(withdrawal); + assertTrue( + delegationManager.pendingWithdrawals(withdrawalRootToCheck), + "withdrawalRoot not pending" + ); + + (Withdrawal[] memory withdrawals, ) = delegationManager.getQueuedWithdrawals(staker); + for (uint256 i = 0; i < withdrawals.length; ++i) { + assertEq( + withdrawals[i].staker, + withdrawal.staker + ); + assertEq( + withdrawals[i].withdrawer, + withdrawal.withdrawer + ); + assertEq( + withdrawals[i].delegatedTo, + withdrawal.delegatedTo + ); + assertEq( + withdrawals[i].nonce, + withdrawal.nonce + ); + assertEq( + withdrawals[i].startBlock, + withdrawal.startBlock + ); + assertEq( + withdrawals[i].scaledShares.length, + withdrawal.scaledShares.length + ); + for (uint256 j = 0; j < withdrawal.scaledShares.length; ++j) { + assertEq( + withdrawals[i].scaledShares[j], + withdrawal.scaledShares[j] + ); + assertEq( + address(withdrawals[i].strategies[j]), + address(withdrawal.strategies[j]) + ); + } + } + } } contract DelegationManagerUnitTests_Initialization_Setters is DelegationManagerUnitTests { @@ -886,11 +1289,7 @@ contract DelegationManagerUnitTests_RegisterModifyOperator is DelegationManagerU delegationManager.pause(2 ** PAUSED_NEW_DELEGATION); cheats.expectRevert(IPausable.CurrentlyPaused.selector); - delegationManager.registerAsOperator( - address(0), - 0, - emptyStringForMetadataURI - ); + delegationManager.registerAsOperator(address(0), 0, emptyStringForMetadataURI); } // @notice Verifies that someone cannot successfully call `DelegationManager.registerAsOperator(delegationApprover)` again after registering for the first time @@ -1002,7 +1401,8 @@ contract DelegationManagerUnitTests_RegisterModifyOperator is DelegationManagerU /// @notice Add test for registerAsOperator where the operator has existing deposits in strategies /// Assert: /// depositShares == operatorShares == withdrawableShares - function testFuzz_registerAsOperator_withDeposits(Randomness r) public { + /// check operatorDetails hash encode matches the operatorDetails hash stored (call view function) + function testFuzz_registerAsOperator_withDeposits(Randomness r) public rand(r) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); // Set staker shares in StrategyManager IStrategy[] memory strategiesToReturn = new IStrategy[](1); @@ -1180,7 +1580,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { function testFuzz_Revert_WhenDelegateWhileDelegated( Randomness r, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry - ) public { + ) public rand(r) { address staker = r.Address(); address operator = r.Address(); bytes32 salt = r.Bytes32(); @@ -1196,7 +1596,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { } /// @notice Verifies that `staker` cannot delegate to an unregistered `operator` - function testFuzz_Revert_WhenDelegateToUnregisteredOperator(Randomness r) public { + function testFuzz_Revert_WhenDelegateToUnregisteredOperator(Randomness r) public rand(r) { address staker = r.Address(); address operator = r.Address(); assertFalse(delegationManager.isOperator(operator), "incorrect test input?"); @@ -1212,15 +1612,18 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) * via the `staker` calling `DelegationManager.delegateTo` * The function should pass with any `operatorSignature` input (since it should be unused) - * Properly emits a `StakerDelegated` event - * Staker is correctly delegated after the call (i.e. correct storage update) - * Reverts if the staker is already delegated (to the operator or to anyone else) - * Reverts if the ‘operator’ is not actually registered as an operator + * Assertion checks + * - Properly emitted events from `delegateTo` + * - depositShares incremented for staker correctly + * - withdrawableShares are correct + * - depositScalingFactor is updated correctly + * - operatorShares increase by depositShares amount + * - defaultOperator is an operator, staker is delegated to defaultOperator, staker is not an operator */ function testFuzz_OperatorWhoAcceptsAllStakers_StrategyManagerShares( Randomness r, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); @@ -1250,9 +1653,17 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { }) ); delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); - uint256 operatorSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); - assertEq(operatorSharesBefore + shares, operatorSharesAfter, "operator shares not increased correctly"); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); assertTrue(delegationManager.isOperator(defaultOperator), "staker not registered as operator"); assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); @@ -1269,17 +1680,18 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { /** * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) * but it should revert as the strategy has been fully slashed for the operator. + * Assertion checks + * - staker is not delegated to defaultOperator afterwards + * - staker is not delegated + * - staker is not registered as an operator + * - salt is not spent */ function testFuzz_Revert_OperatorWhoAcceptsAllStakers_AlreadySlashed100Percent_StrategyManagerShares( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); - // Set empty sig+salt - ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry; - bytes32 salt; - _registerOperatorWithBaseDetails(defaultOperator); // Set staker shares in StrategyManager @@ -1293,7 +1705,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { // delegate from the `staker` to the operator cheats.prank(staker); cheats.expectRevert(FullySlashed.selector); - delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); + delegationManager.delegateTo(defaultOperator, emptyApproverSignatureAndExpiry, emptySalt); assertTrue(delegationManager.delegatedTo(staker) != defaultOperator, "staker should not be delegated to the operator"); assertFalse(delegationManager.isDelegated(staker), "staker should not be delegated"); @@ -1302,7 +1714,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { assertFalse( delegationManager.delegationApproverSaltIsSpent( delegationManager.delegationApprover(defaultOperator), - salt + emptySalt ), "salt somehow spent too early?" ); @@ -1312,18 +1724,20 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) * but it should revert as the beaconChainStrategy has been fully slashed for the operator. * The function should pass with any `operatorSignature` input (since it should be unused) - * Properly emits a `StakerDelegated` event - * Staker is correctly delegated after the call (i.e. correct storage update) - * OperatorSharesIncreased event should only be emitted if beaconShares is > 0. Since a staker can have negative shares nothing should happen in that case + * Assertion checks + * - beaconChainETHStrategy shares are unchanged for the operator + * - staker is not delegated to defaultOperator afterwards + * - staker is not delegated + * - staker is not registered as an operator + * - salt is not spent */ function testFuzz_Revert_OperatorWhoAcceptsAllStakers_AlreadySlashed100Percent_BeaconChainStrategyShares( Randomness r, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); - int256 beaconShares = r.Int256(); - cheats.assume(beaconShares > 0); + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); _registerOperatorWithBaseDetails(defaultOperator); @@ -1347,7 +1761,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { cheats.expectRevert(FullySlashed.selector); delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); uint256 beaconSharesAfter = delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy); - + assertEq( beaconSharesBefore, beaconSharesAfter, @@ -1366,26 +1780,27 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { ); } - /// @notice Same test as above, except operator has a magnitude < WAD for the given strategies + /** + * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) + * and the strategy has already been slashed for the operator. + * Assertion Checks + * - Properly emitted events from `delegateTo` + * - depositShares incremented for staker correctly + * - withdrawableShares are correct + * - depositScalingFactor is updated correctly + * - operatorShares increase by depositShares amount + * - defaultOperator is an operator, staker is delegated to defaultOperator, staker is not an operator + * - That the staker withdrawableShares is <= operatorShares (less due to rounding from non-WAD maxMagnitude) + */ function testFuzz_OperatorWhoAcceptsAllStakers_AlreadySlashed_StrategyManagerShares( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); - uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); + uint256 shares = r.Uint256(1 gwei, MAX_STRATEGY_SHARES); uint64 maxMagnitude = r.Uint64(1, WAD); - // Set empty sig+salt - ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry; - bytes32 salt; - _registerOperatorWithBaseDetails(defaultOperator); - - // Set staker shares in StrategyManager strategyManagerMock.addDeposit(staker, strategyMock, shares); - (IStrategy[] memory strategiesToReturn, ) = strategyManagerMock.getDeposits(staker); - uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); - - // Set the operators magnitude _setOperatorMagnitude(defaultOperator, strategyMock, maxMagnitude); // Expected staker scaling factor @@ -1402,10 +1817,18 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { depositScalingFactor: stakerScalingFactor }) ); - delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); - uint256 operatorSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); + delegationManager.delegateTo(defaultOperator, emptyApproverSignatureAndExpiry, emptySalt); - assertEq(operatorSharesBefore + shares, operatorSharesAfter, "operator shares not increased correctly"); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); assertTrue(delegationManager.isOperator(defaultOperator), "staker not registered as operator"); assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); @@ -1413,31 +1836,39 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { assertFalse( delegationManager.delegationApproverSaltIsSpent( delegationManager.delegationApprover(defaultOperator), - salt + emptySalt ), "salt somehow spent too early?" ); - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategiesToReturn); - _assertWithdrawableAndOperatorShares(withdrawableShares[0], operatorSharesAfter, "withdrawableShares not set correctly"); + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategyMock.toArray()); + _assertWithdrawableAndOperatorShares( + withdrawableShares[0], + delegationManager.operatorShares(defaultOperator, strategyMock), + "withdrawableShares not set correctly" + ); } /** * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) - * via the `staker` calling `DelegationManager.delegateTo` + * via the `staker` calling `DelegationManager.delegateTo`. `staker` holds beaconChainETHStrategy Shares * The function should pass with any `operatorSignature` input (since it should be unused) - * Properly emits a `StakerDelegated` event - * Staker is correctly delegated after the call (i.e. correct storage update) - * OperatorSharesIncreased event should only be emitted if beaconShares is > 0. Since a staker can have negative shares nothing should happen in that case + * Assertion Checks + * - Properly emitted events from `delegateTo` + * - depositShares incremented for staker correctly + * - withdrawableShares are correct + * - depositScalingFactor is updated correctly + * - operatorShares increase by depositShares amount + * - defaultOperator is an operator, staker is delegated to defaultOperator, staker is not an operator + * - That the staker withdrawableShares is <= operatorShares (less due to rounding from non-WAD maxMagnitude) */ function testFuzz_OperatorWhoAcceptsAllStakers_BeaconChainStrategyShares( Randomness r, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); - int256 beaconShares = r.Int256(); - cheats.assume(beaconShares > 0); + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); _registerOperatorWithBaseDetails(defaultOperator); // Set the operators magnitude @@ -1453,7 +1884,6 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { ); // Set staker shares in BeaconChainStrategy eigenPodManagerMock.setPodOwnerShares(staker, beaconShares); - uint256 beaconSharesBefore = delegationManager.operatorShares(staker, beaconChainETHStrategy); // delegate from the `staker` to the operator cheats.startPrank(staker); @@ -1462,25 +1892,22 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { staker: staker, operator: defaultOperator, strategy: beaconChainETHStrategy, - depositShares: beaconShares > 0 ? uint256(beaconShares) : 0, + depositShares: uint256(beaconShares), depositScalingFactor: WAD }) ); delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); - uint256 beaconSharesAfter = delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy); - if (beaconShares <= 0) { - assertEq( - beaconSharesBefore, - beaconSharesAfter, - "operator beaconchain shares should not have increased with negative shares" - ); - } else { - assertEq( - beaconSharesBefore + uint256(beaconShares), - beaconSharesAfter, - "operator beaconchain shares not increased correctly" - ); - } + + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); assertTrue(delegationManager.isOperator(defaultOperator), "staker not registered as operator"); assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); @@ -1492,20 +1919,32 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { ), "salt somehow spent too early?" ); + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, beaconChainETHStrategy.toArray()); + _assertWithdrawableAndOperatorShares( + withdrawableShares[0], + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), + "withdrawableShares not set correctly" + ); } - /// @notice Same test as above, except operator has a magnitude < WAD for the given strategies + /** + * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) + * and the strategy has already been slashed for the operator. `staker` holds beaconChainETHStrategy Shares + * Assertion Checks + * - Properly emitted events from `delegateTo` + * - depositShares incremented for staker correctly + * - withdrawableShares are correct + * - depositScalingFactor is updated correctly + * - operatorShares increase by depositShares amount + * - defaultOperator is an operator, staker is delegated to defaultOperator, staker is not an operator + * - That the staker withdrawableShares is <= operatorShares (less due to rounding from non-WAD maxMagnitude) + */ function testFuzz_OperatorWhoAcceptsAllStakers_AlreadySlashed_BeaconChainStrategyShares( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); uint64 maxMagnitude = r.Uint64(1, WAD); - // Bound and ensure beaconShares rounded down to gwei - int256 beaconShares = int256(bound(r.Uint256(), 1 gwei, MAX_ETH_SUPPLY)); - - // Set empty sig+salt - ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry; - bytes32 salt; + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); // Register and set operator's magnitude _registerOperatorWithBaseDetails(defaultOperator); @@ -1526,21 +1965,18 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { depositScalingFactor: uint256(WAD).divWad(maxMagnitude) }) ); - delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); - uint256 beaconSharesAfter = delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy); - if (beaconShares <= 0) { - assertEq( - beaconSharesBefore, - beaconSharesAfter, - "operator beaconchain shares should not have increased with negative shares" - ); - } else { - assertEq( - beaconSharesBefore + uint256(beaconShares), - beaconSharesAfter, - "operator beaconchain shares not increased correctly" - ); - } + delegationManager.delegateTo(defaultOperator, emptyApproverSignatureAndExpiry, emptySalt); + + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); assertTrue(delegationManager.isOperator(defaultOperator), "staker not registered as operator"); assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); @@ -1548,36 +1984,41 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { assertFalse( delegationManager.delegationApproverSaltIsSpent( delegationManager.delegationApprover(defaultOperator), - salt + emptySalt ), "salt somehow spent too early?" ); - IStrategy[] memory strategiesToReturn = beaconChainETHStrategy.toArray(); - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategiesToReturn); - if (beaconShares > 0) { - _assertWithdrawableAndOperatorShares( - withdrawableShares[0], - beaconSharesAfter, - "withdrawableShares not set correctly" - ); - } else { - assertEq(withdrawableShares[0], 0, "staker shares not set correctly"); - } + ( + uint256[] memory withdrawableShares, + ) = delegationManager.getWithdrawableShares(staker, beaconChainETHStrategy.toArray()); + _assertWithdrawableAndOperatorShares( + withdrawableShares[0], + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), + "withdrawableShares not set correctly" + ); } /** * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) * via the `staker` calling `DelegationManager.delegateTo` * Similar to tests above but now with staker who has both EigenPod and StrategyManager shares. + * Assertion Checks for strategyMock and beaconChainETHStrategy + * - Properly emitted events from `delegateTo` + * - depositShares incremented for staker correctly + * - withdrawableShares are correct + * - depositScalingFactor is updated correctly + * - operatorShares increase by depositShares amount + * - defaultOperator is an operator, staker is delegated to defaultOperator, staker is not an operator + * - That the staker withdrawableShares is <= operatorShares (less due to rounding from non-WAD maxMagnitude) */ function testFuzz_OperatorWhoAcceptsAllStakers_BeaconChainAndStrategyManagerShares( Randomness r, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); - int256 beaconShares = int256(bound(r.Uint256(), 1 gwei, MAX_ETH_SUPPLY)); + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); _registerOperatorWithBaseDetails(defaultOperator); @@ -1597,17 +2038,11 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { IStrategy[] memory strategiesToReturn, uint256[] memory sharesToReturn ) = delegationManager.getDepositedShares(staker); - uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); - uint256 beaconSharesBefore = delegationManager.operatorShares(staker, beaconChainETHStrategy); + uint256[] memory depositScalingFactors = new uint256[](2); + depositScalingFactors[0] = uint256(WAD); + depositScalingFactors[1] = uint256(WAD); // delegate from the `staker` to the operator cheats.startPrank(staker); - uint256[] memory depositScalingFactors = new uint256[](1); - depositScalingFactors[0] = uint256(WAD); - if (beaconShares > 0) { - depositScalingFactors = new uint256[](2); - depositScalingFactors[0] = uint256(WAD); - depositScalingFactors[1] = uint256(WAD); - } _delegateTo_expectEmit( DelegateToEmitStruct({ staker: staker, @@ -1619,33 +2054,39 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { ); delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); cheats.stopPrank(); - uint256 operatorSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); - uint256 beaconSharesAfter = delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy); + + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategiesToReturn); _assertWithdrawableAndOperatorShares( withdrawableShares[0], - operatorSharesAfter, + delegationManager.operatorShares(defaultOperator, strategyMock), "withdrawableShares not set correctly" ); - if (beaconShares <= 0) { - assertEq( - beaconSharesBefore, - beaconSharesAfter, - "operator beaconchain shares should not have increased with negative shares" - ); - } else { - _assertWithdrawableAndOperatorShares( - withdrawableShares[1], - beaconSharesAfter, - "withdrawableShares not set correctly" - ); - assertEq( - beaconSharesBefore + uint256(beaconShares), - beaconSharesAfter, - "operator beaconchain shares not increased correctly" - ); - } - assertEq(operatorSharesBefore + shares, operatorSharesAfter, "operator shares not increased correctly"); + _assertWithdrawableAndOperatorShares( + withdrawableShares[1], + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), + "withdrawableShares not set correctly" + ); + assertTrue(delegationManager.isOperator(defaultOperator), "staker not registered as operator"); assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); @@ -1660,26 +2101,28 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { } /** - * @notice `staker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) - * via the `staker` calling `DelegationManager.delegateTo` + * @notice `defaultStaker` delegates to an operator who does not require any signature verification (i.e. the operator’s `delegationApprover` address is set to the zero address) + * via the `defaultStaker` calling `DelegationManager.delegateTo` * Similar to tests above but now with staker who has both EigenPod and StrategyManager shares. + * The operator has been slashed prior to deposit for both strategies. + * Assertion Checks for strategyMock and beaconChainETHStrategy + * - Properly emitted events from `delegateTo` + * - depositShares incremented for staker correctly + * - withdrawableShares are correct + * - depositScalingFactor is updated correctly + * - operatorShares increase by depositShares amount + * - defaultOperator is an operator, defaultStaker is delegated to defaultOperator, defaultStaker is not an operator + * - That the defaultStaker withdrawableShares is <= operatorShares (less due to rounding from non-WAD maxMagnitude) */ function testFuzz_OperatorWhoAcceptsAllStakers_AlreadySlashed_BeaconChainAndStrategyManagerShares( Randomness r - ) public { + ) public rand(r) { // 1. register operator and setup values, magnitudes uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); - int256 beaconShares = int256(bound(r.Uint256(), 1 gwei, MAX_ETH_SUPPLY)); + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); uint64 maxMagnitudeBeacon = r.Uint64(1, WAD); uint64 maxMagnitudeStrategy = r.Uint64(1, WAD); - _registerOperatorWithBaseDetails(defaultOperator); - - // Set empty sig+salt - ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry; - bytes32 salt; - - // Set the operators magnitude to be 50% _setOperatorMagnitude(defaultOperator, beaconChainETHStrategy, maxMagnitudeBeacon); _setOperatorMagnitude(defaultOperator, strategyMock, maxMagnitudeStrategy); @@ -1690,18 +2133,12 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { IStrategy[] memory strategiesToReturn, uint256[] memory sharesToReturn ) = delegationManager.getDepositedShares(defaultStaker); - uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); - uint256 beaconSharesBefore = delegationManager.operatorShares(defaultStaker, beaconChainETHStrategy); + // 3. delegate from the `staker` to the operator with expected emitted events cheats.startPrank(defaultStaker); - - uint256[] memory depositScalingFactors = new uint256[](1); + uint256[] memory depositScalingFactors = new uint256[](2); depositScalingFactors[0] = uint256(WAD).divWad(maxMagnitudeStrategy); - if (beaconShares > 0) { - depositScalingFactors = new uint256[](2); - depositScalingFactors[0] = uint256(WAD).divWad(maxMagnitudeStrategy); - depositScalingFactors[1] = uint256(WAD).divWad(maxMagnitudeBeacon); - } + depositScalingFactors[1] = uint256(WAD).divWad(maxMagnitudeBeacon); _delegateTo_expectEmit( DelegateToEmitStruct({ staker: defaultStaker, @@ -1711,13 +2148,30 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { depositScalingFactors: depositScalingFactors }) ); - delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); + delegationManager.delegateTo(defaultOperator, emptyApproverSignatureAndExpiry, emptySalt); cheats.stopPrank(); // 4. Assert correct end state values - uint256 operatorSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); - uint256 beaconSharesAfter = delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy); - assertEq(operatorSharesBefore + shares, operatorSharesAfter, "operator shares not increased correctly"); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); assertTrue(delegationManager.isOperator(defaultOperator), "defaultStaker not registered as operator"); assertEq(delegationManager.delegatedTo(defaultStaker), defaultOperator, "defaultStaker delegated to the wrong address"); assertFalse(delegationManager.isOperator(defaultStaker), "staker incorrectly registered as operator"); @@ -1725,35 +2179,21 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { assertFalse( delegationManager.delegationApproverSaltIsSpent( delegationManager.delegationApprover(defaultOperator), - salt + emptySalt ), "salt somehow spent too early?" ); (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategiesToReturn); _assertWithdrawableAndOperatorShares( withdrawableShares[0], - operatorSharesAfter, + delegationManager.operatorShares(defaultOperator, strategyMock), "withdrawable strategy shares not set correctly" ); - if (beaconShares > 0) { - _assertWithdrawableAndOperatorShares( - withdrawableShares[1], - beaconSharesAfter, - "withdrawable beacon shares not set correctly" - ); - assertEq( - beaconSharesBefore + uint256(beaconShares), - beaconSharesAfter, - "operator beaconchain shares not increased correctly" - ); - } else { - assertEq(withdrawableShares[0], 0, "staker beacon chain shares not set correctly"); - assertEq( - beaconSharesBefore, - beaconSharesAfter, - "operator beaconchain shares should not have increased with negative shares" - ); - } + _assertWithdrawableAndOperatorShares( + withdrawableShares[1], + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), + "withdrawable beacon shares not set correctly" + ); } /** @@ -1764,17 +2204,10 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { function testFuzz_OperatorWhoAcceptsAllStakers_ZeroDelegatableShares( Randomness r, ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); - uint64 operatorMagnitude = r.Uint64(1, WAD); - - // register *this contract* as an operator - // filter inputs, since this will fail when the staker is already registered as an operator - cheats.assume(staker != defaultOperator); - _registerOperatorWithBaseDetails(defaultOperator); - _setOperatorMagnitude(defaultOperator, strategyMock, operatorMagnitude); // verify that the salt hasn't been used before assertFalse( @@ -1809,16 +2242,13 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { * @notice Like `testDelegateToOperatorWhoRequiresECDSASignature` but using an invalid expiry on purpose and checking that reversion occurs */ function testFuzz_Revert_WhenOperatorWhoRequiresECDSASignature_ExpiredDelegationApproverSignature( - address staker, - bytes32 salt, - uint256 expiry - ) public filterFuzzedAddressInputs(staker) { + Randomness r + ) public rand(r) { + address staker = r.Address(); + bytes32 salt = r.Bytes32(); + uint256 expiry = r.Uint256(0, block.timestamp - 1); // roll to a very late timestamp skip(type(uint256).max / 2); - // filter to only *invalid* `expiry` values - expiry = bound(expiry, 0, block.timestamp - 1); - // filter inputs, since this will fail when the staker is already registered as an operator - cheats.assume(staker != defaultOperator); _registerOperatorWithDelegationApprover(defaultOperator); @@ -1843,17 +2273,11 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { * and checking that reversion occurs with the same salt */ function testFuzz_Revert_WhenOperatorWhoRequiresECDSASignature_PreviouslyUsedSalt( - address staker, - bytes32 salt, - uint256 expiry - ) public filterFuzzedAddressInputs(staker) { - // filter to only valid `expiry` values - cheats.assume(expiry >= block.timestamp); - - // filter inputs, since this will fail when the staker is already registered as an operator - // staker also must not be the delegationApprover so that signature verification process takes place - cheats.assume(staker != defaultOperator); - cheats.assume(staker != defaultApprover); + Randomness r + ) public rand(r) { + address staker = r.Address(); + bytes32 salt = r.Bytes32(); + uint256 expiry = r.Uint256(block.timestamp + 1, type(uint256).max); _registerOperatorWithDelegationApprover(defaultOperator); @@ -1895,13 +2319,10 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { * @notice Like `testDelegateToOperatorWhoRequiresECDSASignature` but using an incorrect signature on purpose and checking that reversion occurs */ function testFuzz_Revert_WhenOperatorWhoRequiresECDSASignature_WithBadSignature( - address staker, - uint256 expiry - ) public filterFuzzedAddressInputs(staker) { - // filter to only valid `expiry` values - expiry = bound(expiry, block.timestamp + 1, type(uint256).max); - // filter inputs, since this will fail when the staker is already registered as an operator - cheats.assume(staker != defaultOperator && staker != defaultApprover); + Randomness r + ) public rand(r) { + address staker = r.Address(); + uint256 expiry = r.Uint256(block.timestamp + 1, type(uint256).max); _registerOperatorWithDelegationApprover(defaultOperator); @@ -1938,15 +2359,10 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { * Reverts if the staker is already delegated (to the operator or to anyone else) * Reverts if the ‘operator’ is not actually registered as an operator */ - function testFuzz_OperatorWhoRequiresECDSASignature( - address staker, - bytes32 salt, - uint256 expiry - ) public filterFuzzedAddressInputs(staker) { - // filter to only valid `expiry` values - cheats.assume(expiry >= block.timestamp); - // filter inputs, since this will fail when the staker is already registered as an operator - cheats.assume(staker != defaultOperator); + function testFuzz_OperatorWhoRequiresECDSASignature(Randomness r) public rand(r) { + address staker = r.Address(); + bytes32 salt = r.Bytes32(); + uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); _registerOperatorWithDelegationApprover(defaultOperator); @@ -2011,7 +2427,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { */ function testFuzz_OperatorWhoRequiresECDSASignature_StrategyManagerShares( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); @@ -2038,8 +2454,6 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { // Set staker shares in StrategyManager strategyManagerMock.addDeposit(staker, strategyMock, shares); - (IStrategy[] memory strategiesToReturn, ) = strategyManagerMock.getDeposits(staker); - uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); // delegate from the `staker` to the operator cheats.startPrank(staker); _delegateTo_expectEmit_singleStrat( @@ -2053,13 +2467,25 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { ); delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); cheats.stopPrank(); - uint256 operatorSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); - assertEq(operatorSharesBefore + shares, operatorSharesAfter, "operator shares not increased correctly"); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategiesToReturn); - _assertWithdrawableAndOperatorShares(withdrawableShares[0], operatorSharesAfter, "withdrawableShares not set correctly"); + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategyMock.toArray()); + _assertWithdrawableAndOperatorShares( + withdrawableShares[0], + delegationManager.operatorShares(defaultOperator, strategyMock), + "withdrawableShares not set correctly" + ); if (staker == delegationManager.delegationApprover(defaultOperator)) { // verify that the salt is still marked as unused (since it wasn't checked or used) @@ -2094,11 +2520,11 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { */ function testFuzz_OperatorWhoRequiresECDSASignature_BeaconChainStrategyShares( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); - int256 beaconShares = int256(bound(r.Uint256(), 1 gwei, MAX_ETH_SUPPLY)); + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); _registerOperatorWithDelegationApprover(defaultOperator); @@ -2121,8 +2547,6 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { // Set staker shares in BeaconChainStrategy eigenPodManagerMock.setPodOwnerShares(staker, beaconShares); - (IStrategy[] memory strategiesToReturn, ) = delegationManager.getDepositedShares(staker); - uint256 beaconSharesBefore = delegationManager.operatorShares(staker, beaconChainETHStrategy); // delegate from the `staker` to the operator cheats.startPrank(staker); _delegateTo_expectEmit_singleStrat( @@ -2136,33 +2560,32 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { ); delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); cheats.stopPrank(); - uint256 beaconSharesAfter = delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy); - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategiesToReturn); - if (beaconShares <= 0) { - assertEq( - beaconSharesBefore, - beaconSharesAfter, - "operator beaconchain shares should not have increased with negative shares" - ); - } else { - _assertWithdrawableAndOperatorShares( - withdrawableShares[0], - beaconSharesAfter, - "withdrawableShares not set correctly" - ); - assertEq( - beaconSharesBefore + uint256(beaconShares), - beaconSharesAfter, - "operator beaconchain shares not increased correctly" - ); - } - assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); - assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); - assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); - if (staker == delegationManager.delegationApprover(defaultOperator)) { - // verify that the salt is still marked as unused (since it wasn't checked or used) - assertFalse( - delegationManager.delegationApproverSaltIsSpent( + + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); + ( + uint256[] memory withdrawableShares, + ) = delegationManager.getWithdrawableShares(staker, beaconChainETHStrategy.toArray()); + _assertWithdrawableAndOperatorShares( + withdrawableShares[0], + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), + "withdrawableShares not set correctly" + ); + assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); + assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); + assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); + if (staker == delegationManager.delegationApprover(defaultOperator)) { + // verify that the salt is still marked as unused (since it wasn't checked or used) + assertFalse( + delegationManager.delegationApproverSaltIsSpent( delegationManager.delegationApprover(defaultOperator), salt ), @@ -2193,11 +2616,11 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { */ function testFuzz_OperatorWhoRequiresECDSASignature_BeaconChainAndStrategyManagerShares( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); - int256 beaconShares = int256(bound(r.Uint256(), 1 gwei, MAX_ETH_SUPPLY)); + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); // filter inputs, since this will fail when the staker is already registered as an operator @@ -2221,23 +2644,17 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { ); // Set staker shares in BeaconChainStrategy and StrategyMananger - uint256[] memory depositScalingFactors = new uint256[](1); + uint256[] memory depositScalingFactors = new uint256[](2); depositScalingFactors[0] = uint256(WAD); + depositScalingFactors[1] = uint256(WAD); strategyManagerMock.addDeposit(staker, strategyMock, shares); eigenPodManagerMock.setPodOwnerShares(staker, beaconShares); ( IStrategy[] memory strategiesToReturn, uint256[] memory sharesToReturn ) = delegationManager.getDepositedShares(staker); - uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); - uint256 beaconSharesBefore = delegationManager.operatorShares(staker, beaconChainETHStrategy); // delegate from the `staker` to the operator cheats.startPrank(staker); - if (beaconShares > 0) { - depositScalingFactors = new uint256[](2); - depositScalingFactors[0] = uint256(WAD); - depositScalingFactors[1] = uint256(WAD); - } _delegateTo_expectEmit( DelegateToEmitStruct({ staker: staker, @@ -2250,27 +2667,26 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { delegationManager.delegateTo(defaultOperator, approverSignatureAndExpiry, salt); cheats.stopPrank(); - if (beaconShares <= 0) { - assertEq( - beaconSharesBefore, - delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), - "operator beaconchain shares should not have increased with negative shares" - ); - } else { - assertEq( - beaconSharesBefore + uint256(beaconShares), - delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), - "operator beaconchain shares not increased correctly" - ); - } - assertEq( - operatorSharesBefore + shares, - delegationManager.operatorShares(defaultOperator, strategyMock), - "operator shares not increased correctly" - ); - assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); - assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); - assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategiesToReturn); _assertWithdrawableAndOperatorShares( withdrawableShares[0], @@ -2282,7 +2698,9 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), "withdrawableShares for beacon strategy not set correctly" ); - + assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); + assertEq(delegationManager.delegatedTo(staker), defaultOperator, "staker delegated to the wrong address"); + assertFalse(delegationManager.isOperator(staker), "staker incorrectly registered as operator"); if (staker == delegationManager.delegationApprover(defaultOperator)) { // verify that the salt is still marked as unused (since it wasn't checked or used) assertFalse( @@ -2310,7 +2728,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { */ function testFuzz_Revert_WhenOperatorWhoRequiresEIP1271Signature_ExpiredDelegationApproverSignature( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); uint256 expiry = r.Uint256(0, block.timestamp - 1); uint256 currTimestamp = r.Uint256(block.timestamp, type(uint256).max); @@ -2338,7 +2756,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { */ function testFuzz_Revert_WhenOperatorWhoRequiresEIP1271Signature_PreviouslyUsedSalt( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); @@ -2374,7 +2792,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { */ function testFuzz_Revert_WhenOperatorWhoRequiresEIP1271Signature_NonCompliantWallet( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); @@ -2400,7 +2818,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { */ function testFuzz_Revert_WhenOperatorWhoRequiresEIP1271Signature_IsValidSignatureFails( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); @@ -2438,7 +2856,7 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { * Reverts if the staker is already delegated (to the operator or to anyone else) * Reverts if the ‘operator’ is not actually registered as an operator */ - function testFuzz_OperatorWhoRequiresEIP1271Signature(Randomness r) public { + function testFuzz_OperatorWhoRequiresEIP1271Signature(Randomness r) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); uint256 expiry = r.Uint256(block.timestamp, type(uint256).max); @@ -2496,12 +2914,13 @@ contract DelegationManagerUnitTests_delegateTo is DelegationManagerUnitTests { } } -contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTests { +contract DelegationManagerUnitTests_increaseDelegatedShares is DelegationManagerUnitTests { using SingleItemArrayLib for *; using SlashingLib for *; + using Math for *; /// @notice Verifies that `DelegationManager.increaseDelegatedShares` reverts if not called by the StrategyManager nor EigenPodManager - function testFuzz_Revert_increaseDelegatedShares_invalidCaller(Randomness r) public { + function testFuzz_Revert_increaseDelegatedShares_invalidCaller(Randomness r) public rand(r) { address invalidCaller = r.Address(); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); cheats.expectRevert(IDelegationManagerErrors.OnlyStrategyManagerOrEigenPodManager.selector); @@ -2514,7 +2933,7 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest */ function testFuzz_Revert_increaseDelegatedShares_slashedOperator100Percent( Randomness r - ) public { + ) public rand(r) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); address staker = r.Address(); @@ -2556,7 +2975,7 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest */ function testFuzz_Revert_increaseDelegatedShares_slashedOperator100PercentWithExistingStaker( Randomness r - ) public { + ) public rand(r) { address staker = r.Address(); uint64 initialMagnitude = r.Uint64(1, WAD); uint256 existingShares = r.Uint256(1, MAX_STRATEGY_SHARES); @@ -2578,6 +2997,16 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest cheats.prank(address(strategyManagerMock)); delegationManager.increaseDelegatedShares(staker, strategyMock, 0, existingShares); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: existingShares + }); (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategiesDeposited); _assertWithdrawableAndOperatorShares( withdrawableShares[0], @@ -2605,8 +3034,8 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest ); } - // @notice Verifies that there is no change in shares if the staker is not delegated - function testFuzz_increaseDelegatedShares_noop(Randomness r) public { + /// @notice Verifies that there is no change in shares if the staker is not delegated + function testFuzz_increaseDelegatedShares_noop(Randomness r) public rand(r) { address staker = r.Address(); _registerOperatorWithBaseDetails(defaultOperator); assertFalse(delegationManager.isDelegated(staker), "bad test setup"); @@ -2619,9 +3048,13 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest /** * @notice Verifies that `DelegationManager.increaseDelegatedShares` properly increases the delegated `shares` that the operator * who the `staker` is delegated to has in the strategy + * Asserts: + * - depositScalingFactor, depositShares, withdrawableShares, operatorShares after deposit + * - correct operator shares after deposit + * @dev Checks that there is no change if the staker is not delegated */ - function testFuzz_increaseDelegatedShares(Randomness r) public { + function testFuzz_increaseDelegatedShares(Randomness r) public rand(r) { address staker = r.Address(); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); bool delegateFromStakerToOperator = r.Boolean(); @@ -2632,7 +3065,7 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest if (delegateFromStakerToOperator) { _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); } - uint256 _delegatedSharesBefore = delegationManager.operatorShares( + uint256 delegatedSharesBefore = delegationManager.operatorShares( delegationManager.delegatedTo(staker), strategyMock ); @@ -2643,7 +3076,7 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest _increaseDelegatedShares_expectEmit( IncreaseDelegatedSharesEmitStruct({ staker: staker, - operator: defaultOperator, + operator: delegationManager.delegatedTo(staker), strategy: strategyMock, sharesToIncrease: shares, depositScalingFactor: WAD @@ -2652,29 +3085,29 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest } cheats.prank(address(strategyManagerMock)); delegationManager.increaseDelegatedShares(staker, strategyMock, 0, shares); + _assertDeposit({ + staker: staker, + operator: delegationManager.delegatedTo(staker), + strategy: strategyMock, + operatorSharesBefore: delegatedSharesBefore, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); // Assert correct end state values - uint256 delegatedSharesAfter = delegationManager.operatorShares( - delegationManager.delegatedTo(staker), - strategyMock - ); - IStrategy[] memory strategy = new IStrategy[](1); - strategy[0] = strategyMock; - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategy); + uint256 delegatedSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategyMock.toArray()); if (delegationManager.isDelegated(staker)) { - assertEq( - delegatedSharesAfter, - _delegatedSharesBefore + shares, - "delegated shares did not increment correctly" - ); - assertEq( + _assertWithdrawableAndOperatorShares( withdrawableShares[0], delegatedSharesAfter, "Invalid withdrawable shares" ); } else { - assertEq(delegatedSharesAfter, _delegatedSharesBefore, "delegated shares incremented incorrectly"); - assertEq(_delegatedSharesBefore, 0, "nonzero shares delegated to zero address!"); + assertEq(delegatedSharesAfter, delegatedSharesBefore, "delegated shares incremented incorrectly"); + assertEq(delegatedSharesBefore, 0, "nonzero shares delegated to zero address!"); } } @@ -2683,7 +3116,7 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest * who the `staker` is delegated to has in the strategy * @dev Checks that there is no change if the staker is not delegated */ - function testFuzz_increaseDelegatedShares_slashedOperator(Randomness r) public { + function testFuzz_increaseDelegatedShares_slashedOperator(Randomness r) public rand(r) { address staker = r.Address(); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); uint64 magnitude = r.Uint64(1, WAD); @@ -2695,57 +3128,54 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest // Set operator magnitude _setOperatorMagnitude(defaultOperator, strategyMock, magnitude); - // delegate from the `staker` to the operator *if `delegateFromStakerToOperator` is 'true'* if (delegateFromStakerToOperator) { _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); } - uint256 _delegatedSharesBefore = delegationManager.operatorShares( + uint256 delegatedSharesBefore = delegationManager.operatorShares( delegationManager.delegatedTo(staker), strategyMock ); + strategyManagerMock.addDeposit(staker, strategyMock, shares); if (delegationManager.isDelegated(staker)) { - uint256 slashingFactor = _getSlashingFactor(staker, strategyMock, magnitude); - dsf.update(0, shares, slashingFactor); _increaseDelegatedShares_expectEmit( IncreaseDelegatedSharesEmitStruct({ staker: staker, operator: defaultOperator, strategy: strategyMock, sharesToIncrease: shares, - depositScalingFactor: dsf.scalingFactor() + depositScalingFactor: uint256(WAD).divWad(magnitude) }) ); } - - strategyManagerMock.addDeposit(staker, strategyMock, shares); cheats.prank(address(strategyManagerMock)); delegationManager.increaseDelegatedShares(staker, strategyMock, 0, shares); + _assertDeposit({ + staker: staker, + operator: delegationManager.delegatedTo(staker), + strategy: strategyMock, + operatorSharesBefore: delegatedSharesBefore, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); + // Assert correct values - uint256 delegatedSharesAfter = delegationManager.operatorShares( - delegationManager.delegatedTo(staker), - strategyMock - ); - IStrategy[] memory strategy = new IStrategy[](1); - strategy[0] = strategyMock; - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategy); + uint256 delegatedSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategyMock.toArray()); if (delegationManager.isDelegated(staker)) { - assertEq( - delegatedSharesAfter, - _delegatedSharesBefore + shares, - "delegated shares did not increment correctly" - ); _assertWithdrawableAndOperatorShares( withdrawableShares[0], delegatedSharesAfter, "Invalid withdrawable shares" ); } else { - assertEq(delegatedSharesAfter, _delegatedSharesBefore, "delegated shares incremented incorrectly"); - assertEq(_delegatedSharesBefore, 0, "nonzero shares delegated to zero address!"); + assertEq(delegatedSharesAfter, delegatedSharesBefore, "delegated shares incremented incorrectly"); + assertEq(delegatedSharesBefore, 0, "nonzero shares delegated to zero address!"); } } @@ -2753,7 +3183,9 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest * @notice Verifies that `DelegationManager.increaseDelegatedShares` doesn't revert when operator slashed 100% for a strategy * and the staker has deposits in a separate strategy */ - function testFuzz_increaseDelegatedShares_slashedOperator100Percent(Randomness r) public { + function testFuzz_increaseDelegatedShares_slashedOperator100Percent( + Randomness r + ) public rand(r) { address staker = r.Address(); uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); uint64 magnitude = r.Uint64(1, WAD); @@ -2777,7 +3209,7 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest // delegate from the `staker` to the operator _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); - uint256 _delegatedSharesBefore = delegationManager.operatorShares( + uint256 delegatedSharesBefore = delegationManager.operatorShares( delegationManager.delegatedTo(staker), strategy ); @@ -2798,22 +3230,28 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest cheats.prank(address(strategyManagerMock)); delegationManager.increaseDelegatedShares(staker, strategy, 0, shares); + _assertDeposit({ + staker: staker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: delegatedSharesBefore, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); + // Assert correct end state values + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategyArray); uint256 delegatedSharesAfter = delegationManager.operatorShares( delegationManager.delegatedTo(staker), strategy ); - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(staker, strategyArray); _assertWithdrawableAndOperatorShares( withdrawableShares[0], delegatedSharesAfter, "Invalid withdrawable shares" ); - assertEq( - delegatedSharesAfter, - _delegatedSharesBefore + shares, - "delegated shares did not increment correctly" - ); } /** @@ -2862,410 +3300,201 @@ contract DelegationManagerUnitTests_ShareAdjustment is DelegationManagerUnitTest "drift should be 4.418e13 from previous tests" ); } +} - /// @notice Verifies that `DelegationManager.burnOperatorShares` reverts if not called by the AllocationManager - function testFuzz_Revert_burnOperatorShares_invalidCaller(Randomness r) public { - address invalidCaller = r.Address(); +contract DelegationManagerUnitTests_decreaseDelegatedShares is DelegationManagerUnitTests { + using SingleItemArrayLib for *; + using SlashingLib for *; + using Math for *; - cheats.startPrank(invalidCaller); - cheats.expectRevert(IDelegationManagerErrors.OnlyAllocationManager.selector); - delegationManager.burnOperatorShares(defaultOperator, strategyMock, 0, 0); + function testFuzz_Revert_decreaseDelegatedShares_onlyEPM(Randomness r) public rand(r) { + address invalidCaller = r.Address(); + address staker = r.Address(); + uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); + uint64 prevBeaconChainSlashingFactor = uint64(r.Uint256(0, WAD)); + cheats.expectRevert(IDelegationManagerErrors.OnlyEigenPodManager.selector); + cheats.prank(invalidCaller); + delegationManager.decreaseDelegatedShares(staker, 0, prevBeaconChainSlashingFactor, shares); } - /// @notice Verifies that there is no change in shares if the staker is not delegatedd - function testFuzz_Revert_burnOperatorShares_noop() public { + function testFuzz_decreaseDelegatedShares_noop(Randomness r) public rand(r) { + address staker = r.Address(); + uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); + uint64 prevBeaconChainSlashingFactor = uint64(r.Uint256(0, WAD)); + + // Register operator _registerOperatorWithBaseDetails(defaultOperator); + assertFalse(delegationManager.isDelegated(staker), "bad test setup"); - cheats.prank(address(allocationManagerMock)); - delegationManager.burnOperatorShares(defaultOperator, strategyMock, WAD, WAD/2); + cheats.prank(address(eigenPodManagerMock)); + delegationManager.decreaseDelegatedShares(staker, 0, prevBeaconChainSlashingFactor, shares); assertEq(delegationManager.operatorShares(defaultOperator, strategyMock), 0, "shares should not have changed"); } - /** - * @notice Verifies that `DelegationManager.burnOperatorShares` properly decreases the delegated `shares` that the operator - * who the `defaultStaker` is delegated to has in the strategies - */ - function testFuzz_burnOperatorShares_slashedOperator(Randomness r) public { - // sanity-filtering on fuzzed input length & staker - IStrategy[] memory strategies = r.StrategyArray(16); - uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); - uint64 prevMaxMagnitude = r.Uint64(2, WAD); - uint64 newMaxMagnitude = r.Uint64(1, prevMaxMagnitude); - bool hasBeaconChainStrategy = r.Boolean(); - if (hasBeaconChainStrategy) { - // Set last strategy in array as beacon chain strategy - strategies[strategies.length - 1] = beaconChainETHStrategy; - } + function testFuzz_decreaseDelegatedShares(Randomness r) public rand(r) { + int256 beaconShares = int256(r.Uint256(1, MAX_ETH_SUPPLY)); + uint256 sharesDecrease = r.Uint256(0, uint256(beaconShares) - 1); + uint64 maxMagnitude = r.Uint64(1, WAD); + uint64 beaconChainScalingFactor = r.Uint64(0, WAD); - // Register operator + // 1. Setup staker and delegate to operator _registerOperatorWithBaseDetails(defaultOperator); + _setOperatorMagnitude(defaultOperator, beaconChainETHStrategy, maxMagnitude); + eigenPodManagerMock.setPodOwnerShares(defaultStaker, beaconShares); + eigenPodManagerMock.setBeaconChainSlashingFactor(defaultStaker, beaconChainScalingFactor); + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); - // Set the staker deposits in the strategies - uint256[] memory sharesToSet = new uint256[](strategies.length); - uint256[] memory depositScalingFactors = new uint256[](strategies.length); - for(uint256 i = 0; i < strategies.length; i++) { - strategies[i] = IStrategy(random().Address()); - sharesToSet[i] = shares; - depositScalingFactors[i] = uint256(WAD).divWad(uint256(prevMaxMagnitude)); - _setOperatorMagnitude(defaultOperator, strategies[i], prevMaxMagnitude); - } - - // Okay to set beacon chain shares in SM mock, wont' be called by DM - strategyManagerMock.setDeposits(defaultStaker, strategies, sharesToSet); - if (hasBeaconChainStrategy) { - eigenPodManagerMock.setPodOwnerShares(defaultStaker, int256(uint256(shares))); - } + (uint256[] memory withdrawableSharesBefore,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); - // events expected emitted for each strategy - _delegateTo_expectEmit( - DelegateToEmitStruct({ + // 2. Perform beaconChain slash + decreaseDelegatedShares() + ( + uint64 prevBeaconChainSlashingFactor, + uint256 wadSlashed, + uint64 newBeaconChainScalingFactor + ) = _setNewBeaconChainSlashingFactor(defaultStaker, beaconShares, sharesDecrease); + assertEq( + beaconChainScalingFactor, + prevBeaconChainSlashingFactor, + "Bad test setup" + ); + uint256 depositScalingFactor = uint256(WAD).divWad(maxMagnitude.mulWad(beaconChainScalingFactor)); + // expected operatorShares decreased for event + uint256 operatorSharesToDecrease = _calcWithdrawableShares({ + depositShares: uint256(beaconShares), + depositScalingFactor: depositScalingFactor, + slashingFactor: maxMagnitude.mulWad(prevBeaconChainSlashingFactor) + }).mulWad(wadSlashed); + // expected events + _decreaseDelegatedShares_expectEmit( + DecreaseDelegatedSharesEmitStruct({ staker: defaultStaker, operator: defaultOperator, - strategies: strategies, - depositShares: sharesToSet, - depositScalingFactors: depositScalingFactors + sharesToDecrease: operatorSharesToDecrease }) ); - // delegate from the `staker` to the operator - _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); - address delegatedTo = delegationManager.delegatedTo(defaultStaker); - - // check shares before call to `burnOperatorShares` - for (uint256 i = 0; i < strategies.length; ++i) { - // store delegated shares in a mapping - delegatedSharesBefore[strategies[i]] = delegationManager.operatorShares(delegatedTo, strategies[i]); - // also construct an array which we'll use in another loop - totalSharesForStrategyInArray[address(strategies[i])] += shares; - } + cheats.prank(address(eigenPodManagerMock)); + delegationManager.decreaseDelegatedShares(defaultStaker, uint256(beaconShares), prevBeaconChainSlashingFactor, wadSlashed); - // for each strategy in `strategies`, decrease delegated shares by `shares` - { - cheats.startPrank(address(allocationManagerMock)); - for (uint256 i = 0; i < strategies.length; ++i) { - uint256 currentShares = delegationManager.operatorShares(defaultOperator, strategies[i]); - uint256 sharesToDecrease = SlashingLib.calcSlashedAmount({ - operatorShares: currentShares, - prevMaxMagnitude: prevMaxMagnitude, - newMaxMagnitude: newMaxMagnitude - }); + // 3. Assert correct values + uint256 expectedWithdrawableShares = _calcWithdrawableShares({ + depositShares: uint256(beaconShares), + depositScalingFactor: depositScalingFactor, + slashingFactor: maxMagnitude.mulWad(newBeaconChainScalingFactor) + }); + _assertSharesAfterBeaconSlash({ + staker: defaultStaker, + withdrawableSharesBefore: uint256(beaconShares), + expectedWithdrawableShares: expectedWithdrawableShares, + prevBeaconChainScalingFactor: prevBeaconChainSlashingFactor + }); + // Assert correct end state values + (uint256[] memory withdrawableSharesAfter, ) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased( - defaultOperator, - address(0), - strategies[i], - sharesToDecrease - ); - delegationManager.burnOperatorShares(defaultOperator, strategies[i], prevMaxMagnitude, newMaxMagnitude); + assertEq( + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy) + operatorSharesToDecrease, + uint256(beaconShares), + "operator shares not decreased correctly" + ); - // Also update maxMagnitude in ALM mock - allocationManagerMock.setMaxMagnitude(defaultOperator, strategies[i], newMaxMagnitude); + _assertWithdrawableAndOperatorShares( + withdrawableSharesAfter[0], + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), + "Invalid withdrawable shares" + ); + } - totalSharesDecreasedForStrategy[strategies[i]] += sharesToDecrease; - } - cheats.stopPrank(); - } + function testFuzz_decreaseDelegatedShares_entireBalance(Randomness r) public rand(r) { + int256 beaconShares = int256(r.Uint256(1, MAX_ETH_SUPPLY)); + uint64 maxMagnitude = r.Uint64(1, WAD); + uint64 beaconChainScalingFactor = r.Uint64(0, WAD); - // check shares after call to `burnOperatorShares` - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategies); - for (uint256 i = 0; i < strategies.length; ++i) { - uint256 delegatedSharesAfter = delegationManager.operatorShares(delegatedTo, strategies[i]); - assertEq( - delegatedSharesAfter, - delegatedSharesBefore[strategies[i]] - totalSharesDecreasedForStrategy[strategies[i]], - "delegated shares did not decrement correctly" - ); + // 1. Setup staker and delegate to operator + _registerOperatorWithBaseDetails(defaultOperator); + _setOperatorMagnitude(defaultOperator, beaconChainETHStrategy, maxMagnitude); + eigenPodManagerMock.setPodOwnerShares(defaultStaker, beaconShares); + eigenPodManagerMock.setBeaconChainSlashingFactor(defaultStaker, beaconChainScalingFactor); + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); - _assertWithdrawableAndOperatorShares( - withdrawableShares[i], - delegatedSharesAfter, - "withdrawable and operator shares not decremented correctly" - ); - } - } + (uint256[] memory withdrawableSharesBefore,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); + // 2. Perform beaconChain slash + decreaseDelegatedShares() + ( + uint64 prevBeaconChainSlashingFactor, + uint256 wadSlashed, + uint64 newBeaconChainScalingFactor + ) = _setNewBeaconChainSlashingFactor(defaultStaker, beaconShares, uint256(beaconShares)); + assertEq( + beaconChainScalingFactor, + prevBeaconChainSlashingFactor, + "Bad test setup" + ); + uint256 depositScalingFactor = uint256(WAD).divWad(maxMagnitude.mulWad(beaconChainScalingFactor)); + // expected operatorShares decreased for event + uint256 operatorSharesToDecrease = _calcWithdrawableShares({ + depositShares: uint256(beaconShares), + depositScalingFactor: depositScalingFactor, + slashingFactor: maxMagnitude.mulWad(prevBeaconChainSlashingFactor) + }).mulWad(wadSlashed); + // expected events + _decreaseDelegatedShares_expectEmit( + DecreaseDelegatedSharesEmitStruct({ + staker: defaultStaker, + operator: defaultOperator, + sharesToDecrease: operatorSharesToDecrease + }) + ); + cheats.prank(address(eigenPodManagerMock)); + delegationManager.decreaseDelegatedShares(defaultStaker, uint256(beaconShares), prevBeaconChainSlashingFactor, wadSlashed); - /** - * @notice This test demonstrates that the rate that withdrawable shares decrease from slashing is at LEAST - * greater than or equal to the rate that the operator shares decrease from slashing. - * We want this property otherwise undelegating/queue withdrawing all shares as a staker could lead to a underflow revert. - * Note: If the SlashingLib.calcSlashedAmount function were to round down (overslash) then this test would fail. - */ - function test_burnOperatorShares_slashedRepeatedly() public { - uint64 initialMagnitude = 90009; - uint256 shares = 40000000004182209037560531097078597505; - - // register *this contract* as an operator - _registerOperatorWithBaseDetails(defaultOperator); - _setOperatorMagnitude(defaultOperator, strategyMock, initialMagnitude); - - // Set the staker deposits in the strategies - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = strategyMock; - strategyManagerMock.addDeposit(defaultStaker, strategyMock, shares); - - // delegate from the `defaultStaker` to the operator - _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); - - // Set operator magnitude - uint64 newOperatorMagnitude = initialMagnitude; - - for (uint256 i = 0; i < 100; ++i) { - uint64 slashMagnitude = 100; - newOperatorMagnitude -= slashMagnitude; - _setOperatorMagnitude(defaultOperator, strategyMock, newOperatorMagnitude); - - cheats.prank(address(allocationManagerMock)); - delegationManager.burnOperatorShares( - defaultOperator, - strategyMock, - newOperatorMagnitude + slashMagnitude, - newOperatorMagnitude - ); - - uint256 operatorSharesAfterSlash = delegationManager.operatorShares(defaultOperator, strategyMock); - ( - uint256[] memory withdrawableShares, - uint256[] memory depositShares - ) = delegationManager.getWithdrawableShares(defaultStaker, strategies); - assertEq(depositShares[0], shares, "staker deposit shares not reset correctly"); - assertLe( - withdrawableShares[0], - operatorSharesAfterSlash, - "withdrawable should always be <= operatorShares even after rounding" - ); - } - } -} - -contract DelegationManagerUnitTests_Redelegate is DelegationManagerUnitTests { - - ISignatureUtils.SignatureWithExpiry emptySig; - - // @notice Verifies that redelegating is not possible when the "delegation paused" switch is flipped - function testFuzz_Revert_redelegate_delegatePaused(Randomness r) public { - address staker = r.Address(); - address newOperator = r.Address(); - - // register *this contract* as an operator and delegate from the `staker` to them - _registerOperatorWithBaseDetails(defaultOperator); - _registerOperatorWithBaseDetails(newOperator); - _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); - - // set the pausing flag - cheats.prank(pauser); - delegationManager.pause(2 ** PAUSED_NEW_DELEGATION); - - cheats.prank(staker); - cheats.expectRevert(IPausable.CurrentlyPaused.selector); - delegationManager.redelegate(newOperator, emptySig, emptySalt); - } - - // @notice Verifies that redelegating is not possible when the "undelegation paused" switch is flipped - function testFuzz_Revert_redelegate_undelegatePaused(Randomness r) public { - address staker = r.Address(); - address newOperator = r.Address(); - - // register *this contract* as an operator and delegate from the `staker` to them - _registerOperatorWithBaseDetails(defaultOperator); - _registerOperatorWithBaseDetails(newOperator); - _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); - - // set the pausing flag - cheats.prank(pauser); - delegationManager.pause(2 ** PAUSED_ENTER_WITHDRAWAL_QUEUE); - - cheats.prank(staker); - cheats.expectRevert(IPausable.CurrentlyPaused.selector); - delegationManager.redelegate(newOperator, emptySig, emptySalt); - } - - function testFuzz_Revert_redelegate_notDelegated(Randomness r) public { - address undelegatedStaker = r.Address(); - assertFalse(delegationManager.isDelegated(undelegatedStaker), "bad test setup"); - - _registerOperatorWithBaseDetails(defaultOperator); - - cheats.prank(undelegatedStaker); - cheats.expectRevert(NotActivelyDelegated.selector); - delegationManager.redelegate(defaultOperator, emptySig, emptySalt); - } - - // @notice Verifies that an operator cannot undelegate from themself (this should always be forbidden) - function testFuzz_Revert_redelegate_stakerIsOperator(Randomness r) public { - address operator = r.Address(); - _registerOperatorWithBaseDetails(operator); - _registerOperatorWithBaseDetails(defaultOperator); - - cheats.prank(operator); - cheats.expectRevert(OperatorsCannotUndelegate.selector); - delegationManager.redelegate(defaultOperator, emptySig, emptySalt); - } - - /// @notice Verifies that `staker` cannot redelegate to an unregistered `operator` - function testFuzz_Revert_RedelegateToUnregisteredOperator(Randomness r) public { - address staker = r.Address(); - address operator = r.Address(); - assertFalse(delegationManager.isOperator(operator), "incorrect test input?"); - - _registerOperatorWithBaseDetails(defaultOperator); - _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); - - // try to delegate and check that the call reverts - cheats.prank(staker); - cheats.expectRevert(OperatorNotRegistered.selector); - delegationManager.redelegate(operator, emptySig, emptySalt); - } - - function testFuzz_Revert_Redelegate_ExpiredSignature( - Randomness r - ) public { - // roll to a very late timestamp - skip(type(uint256).max / 2); - - address staker = r.Address(); - address newOperator = r.Address(); - uint expiry = r.Uint256(0, block.timestamp - 1); - bytes32 salt = r.Bytes32(); - - _registerOperatorWithBaseDetails(defaultOperator); - _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); - - _registerOperatorWithDelegationApprover(newOperator); - - // calculate the delegationSigner's signature - ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry = _getApproverSignature( - delegationSignerPrivateKey, - staker, - newOperator, - salt, - expiry - ); - - // delegate from the `staker` to the operator - cheats.startPrank(staker); - cheats.expectRevert(ISignatureUtils.SignatureExpired.selector); - delegationManager.redelegate(newOperator, approverSignatureAndExpiry, salt); - cheats.stopPrank(); - } - - function testFuzz_Revert_Redelegate_SpentSalt( - Randomness r - ) public { - address staker = r.Address(); - address newOperator = r.Address(); - uint expiry = r.Uint256(block.timestamp, block.timestamp + 100); - bytes32 salt = r.Bytes32(); - - _registerOperatorWithBaseDetails(defaultOperator); - _registerOperatorWithDelegationApprover(newOperator); - - // verify that the salt hasn't been used before - assertFalse( - delegationManager.delegationApproverSaltIsSpent( - delegationManager.delegationApprover(newOperator), - salt - ), - "salt somehow spent too early?" - ); - // calculate the delegationSigner's signature - ISignatureUtils.SignatureWithExpiry memory approverSignatureAndExpiry = _getApproverSignature( - delegationSignerPrivateKey, - staker, - newOperator, - salt, - expiry - ); - - // Spend salt by delegating normally first - cheats.startPrank(staker); - delegationManager.delegateTo(newOperator, approverSignatureAndExpiry, salt); - assertTrue( - delegationManager.delegationApproverSaltIsSpent( - delegationManager.delegationApprover(newOperator), - salt - ), - "salt somehow spent not spent?" + // 3. Assert correct values + uint256 expectedWithdrawableShares = _calcWithdrawableShares({ + depositShares: uint256(beaconShares), + depositScalingFactor: depositScalingFactor, + slashingFactor: maxMagnitude.mulWad(newBeaconChainScalingFactor) + }); + assertEq( + expectedWithdrawableShares, + 0, + "All shares should be slashed" ); - - // redelegate to a different operator - delegationManager.redelegate(defaultOperator, emptySig, emptySalt); - - // Now try to redelegate to the original operator using the invalid signature - cheats.expectRevert(SaltSpent.selector); - delegationManager.redelegate(newOperator, approverSignatureAndExpiry, salt); - cheats.stopPrank(); - } - - /** - * @notice Verifies that the `redelegate` function properly queues a withdrawal for all shares of the staker - * ... and delegates to a new operator - */ - function testFuzz_redelegate_noSlashing(Randomness r) public { - uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); - IStrategy[] memory strategyArray = r.StrategyArray(1); - IStrategy strategy = strategyArray[0]; - - // Set the staker deposits in the strategies - strategyManagerMock.addDeposit(defaultStaker, strategy, shares); - - // register *this contract* as an operator and delegate from the `staker` to them - address newOperator = r.Address(); - _registerOperatorWithBaseDetails(defaultOperator); - _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); - _registerOperatorWithBaseDetails(newOperator); - - // Format queued withdrawal - ( - , - Withdrawal memory withdrawal, - bytes32 withdrawalRoot - ) = _setUpQueueWithdrawalsSingleStrat({ + _assertSharesAfterBeaconSlash({ staker: defaultStaker, - withdrawer: defaultStaker, - strategy: strategy, - depositSharesToWithdraw: shares + withdrawableSharesBefore: uint256(beaconShares), + expectedWithdrawableShares: expectedWithdrawableShares, + prevBeaconChainScalingFactor: prevBeaconChainSlashingFactor }); - - // Redelegate the staker - _undelegate_expectEmit_singleStrat( - UndelegateEmitStruct({ - staker: defaultStaker, - operator: defaultOperator, - strategy: strategy, - depositSharesQueued: shares, - operatorSharesDecreased: shares, - withdrawal: withdrawal, - withdrawalRoot: withdrawalRoot, - depositScalingFactor: WAD, - forceUndelegated: false - }) - ); - _delegateTo_expectEmit_singleStrat( - DelegateToSingleStratEmitStruct({ - staker: defaultStaker, - operator: newOperator, - strategy: strategyMock, - depositShares: 0, - depositScalingFactor: WAD - }) + // Assert correct end state values + (uint256[] memory withdrawableSharesAfter, ) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); + _assertWithdrawableAndOperatorShares( + withdrawableSharesAfter[0], + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy), + "Invalid withdrawable shares" ); - cheats.prank(defaultStaker); - delegationManager.redelegate(newOperator, emptySig, emptySalt); - - // Checks - delegation status assertEq( - delegationManager.delegatedTo(defaultStaker), - newOperator, - "undelegated staker should be delegated to new operator" + delegationManager.operatorShares(defaultOperator, beaconChainETHStrategy) + operatorSharesToDecrease, + uint256(beaconShares), + "operator shares not decreased correctly" ); - assertTrue(delegationManager.isDelegated(defaultStaker), "staker should still be delegated"); - - // Checks - operator & staker shares - assertEq(delegationManager.operatorShares(defaultOperator, strategyMock), 0, "operator shares not decreased correctly"); - assertEq(delegationManager.operatorShares(newOperator, strategyMock), 0, "operator shares should not have been added"); - (uint256[] memory stakerWithdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); - assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); } } @@ -3275,13 +3504,8 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { using Math for uint256; // @notice Verifies that undelegating is not possible when the "undelegation paused" switch is flipped - function testFuzz_Revert_undelegate_paused(Randomness r) public { + function testFuzz_Revert_undelegate_paused(Randomness r) public rand(r) { address staker = r.Address(); - - // register *this contract* as an operator and delegate from the `staker` to them - _registerOperatorWithBaseDetails(defaultOperator); - _delegateToOperatorWhoAcceptsAllStakers(staker, defaultOperator); - // set the pausing flag cheats.prank(pauser); delegationManager.pause(2 ** PAUSED_ENTER_WITHDRAWAL_QUEUE); @@ -3291,7 +3515,7 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { delegationManager.undelegate(staker); } - function testFuzz_Revert_undelegate_notDelegated(Randomness r) public { + function testFuzz_Revert_undelegate_notDelegated(Randomness r) public rand(r) { address undelegatedStaker = r.Address(); assertFalse(delegationManager.isDelegated(undelegatedStaker), "bad test setup"); @@ -3301,7 +3525,7 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { } // @notice Verifies that an operator cannot undelegate from themself (this should always be forbidden) - function testFuzz_Revert_undelegate_stakerIsOperator(Randomness r) public { + function testFuzz_Revert_undelegate_stakerIsOperator(Randomness r) public rand(r) { address operator = r.Address(); _registerOperatorWithBaseDetails(operator); @@ -3313,7 +3537,9 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { /** * @notice verifies that `DelegationManager.undelegate` reverts if trying to undelegate an operator from themselves */ - function testFuzz_Revert_undelegate_operatorCannotForceUndelegateThemself(Randomness r) public { + function testFuzz_Revert_undelegate_operatorCannotForceUndelegateThemself( + Randomness r + ) public rand(r) { address delegationApprover = r.Address(); bool callFromOperatorOrApprover = r.Boolean(); @@ -3346,14 +3572,9 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { * @notice Verifies that the `undelegate` function has proper access controls (can only be called by the operator who the `staker` has delegated * to or the operator's `delegationApprover`), or the staker themselves */ - function testFuzz_undelegate_revert_invalidCaller( - address invalidCaller - ) public filterFuzzedAddressInputs(invalidCaller) { - address staker = address(0x123); - // filter out addresses that are actually allowed to call the function - cheats.assume(invalidCaller != staker); - cheats.assume(invalidCaller != defaultOperator); - cheats.assume(invalidCaller != defaultApprover); + function testFuzz_Revert_undelegate_invalidCaller(Randomness r) public rand(r) { + address invalidCaller = r.Address(); + address staker = r.Address(); _registerOperatorWithDelegationApprover(defaultOperator); _delegateToOperatorWhoRequiresSig(staker, defaultOperator); @@ -3370,7 +3591,7 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { * Properly undelegates the staker, i.e. the staker becomes “delegated to” the zero address, and `isDelegated(staker)` returns ‘false’ * Emits a `StakerUndelegated` event */ - function testFuzz_undelegate_noDelegateableShares(Randomness r) public { + function testFuzz_undelegate_noDelegateableShares(Randomness r) public rand(r) { address staker = r.Address(); // register *this contract* as an operator and delegate from the `staker` to them @@ -3394,7 +3615,7 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { /** * @notice Verifies that the `undelegate` function allows for a force undelegation */ - function testFuzz_undelegate_forceUndelegation_noDelegateableShares(Randomness r) public { + function testFuzz_undelegate_forceUndelegation_noDelegateableShares(Randomness r) public rand(r) { address staker = r.Address(); bytes32 salt = r.Bytes32(); bool callFromOperatorOrApprover = r.Boolean(); @@ -3435,36 +3656,10 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { assertFalse(delegationManager.isDelegated(staker), "staker not undelegated"); } - function testFuzz_undelegate_UAM(Randomness r) public rand(r) { - address staker = r.Address(); - bytes32 salt = r.Bytes32(); - _registerOperatorWithBaseDetails(defaultOperator); - _delegateToOperatorWhoRequiresSig(staker, defaultOperator, salt); - - cheats.prank(defaultOperator); - permissionController.setAppointee( - defaultOperator, - address(this), - address(delegationManager), - IDelegationManager.undelegate.selector - ); - - // Undelegate - delegationManager.undelegate(staker); - - // Checks - assertEq( - delegationManager.delegatedTo(staker), - address(0), - "undelegated staker should be delegated to zero address" - ); - assertFalse(delegationManager.isDelegated(staker), "staker not undelegated"); - } - /** * @notice Verifies that the `undelegate` function properly queues a withdrawal for all shares of the staker */ - function testFuzz_undelegate_nonSlashedOperator(Randomness r) public { + function testFuzz_undelegate_nonSlashedOperator(Randomness r) public rand(r) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); IStrategy[] memory strategyArray = r.StrategyArray(1); IStrategy strategy = strategyArray[0]; @@ -3475,7 +3670,18 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { // register *this contract* as an operator and delegate from the `staker` to them _registerOperatorWithBaseDetails(defaultOperator); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); - + + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); + // Format queued withdrawal ( , @@ -3512,22 +3718,27 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { "undelegated staker should be delegated to zero address" ); assertFalse(delegationManager.isDelegated(defaultStaker), "staker not undelegated"); - // Checks - operator & staker shares - assertEq(delegationManager.operatorShares(defaultOperator, strategyMock), 0, "operator shares not decreased correctly"); - (uint256[] memory stakerWithdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); - assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: shares, + depositSharesBefore: shares, + depositSharesWithdrawn: shares, + depositScalingFactor: uint256(WAD), + slashingFactor: uint256(WAD) + }); } /** * @notice Verifies that the `undelegate` function properly queues a withdrawal for all shares of the staker * @notice The operator should have its shares slashed prior to the staker's deposit */ - function testFuzz_undelegate_preSlashedOperator(Randomness r) public { + function testFuzz_undelegate_preSlashedOperator(Randomness r) public rand(r) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); uint64 operatorMagnitude = r.Uint64(1, WAD); - IStrategy[] memory strategyArray = r.StrategyArray(1); - IStrategy strategy = strategyArray[0]; + IStrategy strategy = IStrategy(r.Address()); // register *this contract* as an operator & set its slashed magnitude _registerOperatorWithBaseDetails(defaultOperator); @@ -3538,6 +3749,16 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { // delegate from the `staker` to them _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: uint256(WAD).divWad(operatorMagnitude), + depositAmount: shares + }); // Format queued withdrawal ( @@ -3554,10 +3775,11 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { // Calculate operatorShares decreased, may be off of shares due to rounding uint256 depositScalingFactor = delegationManager.depositScalingFactor(defaultStaker, strategy); assertTrue(depositScalingFactor > WAD, "bad test setup"); - DepositScalingFactor memory dsf = DepositScalingFactor({ - _scalingFactor: depositScalingFactor - }); - uint256 operatorSharesDecreased = dsf.calcWithdrawable(shares, operatorMagnitude); + uint256 operatorSharesDecreased = _calcWithdrawableShares( + shares, + depositScalingFactor, + operatorMagnitude + ); assertLe(operatorSharesDecreased, shares, "operatorSharesDecreased should be <= shares"); // Undelegate the staker @@ -3586,12 +3808,17 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { assertFalse(delegationManager.isDelegated(defaultStaker), "staker not undelegated"); // Checks - operator & staker shares - assertEq( - delegationManager.operatorShares(defaultOperator, strategy), - shares - operatorSharesDecreased, - "operator shares not decreased correctly" - ); - (uint256[] memory stakerWithdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: shares, + depositSharesBefore: shares, + depositSharesWithdrawn: shares, + depositScalingFactor: uint256(WAD).divWad(operatorMagnitude), + slashingFactor: uint256(operatorMagnitude) + }); + (uint256[] memory stakerWithdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategy.toArray()); assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); } @@ -3599,12 +3826,11 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { * @notice Verifies that the `undelegate` function properly queues a withdrawal for all shares of the staker * @notice The operator should have its shares slashed prior to the staker's deposit */ - function testFuzz_undelegate_slashedWhileStaked(Randomness r) public { + function testFuzz_undelegate_slashedWhileStaked(Randomness r) public rand(r) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); uint64 prevMaxMagnitude = r.Uint64(2, WAD); uint64 newMaxMagnitude = r.Uint64(1, prevMaxMagnitude - 1); - IStrategy[] memory strategyArray = r.StrategyArray(1); - IStrategy strategy = strategyArray[0]; + IStrategy strategy = IStrategy(r.Address()); // register *this contract* as an operator _registerOperatorWithBaseDetails(defaultOperator); @@ -3615,6 +3841,17 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { // delegate from the `defaultStaker` to the operator _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); + assertEq( delegationManager.operatorShares(defaultOperator, strategy), shares, @@ -3622,66 +3859,77 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { ); // Set operator magnitude - uint256 operatorSharesAfterSlash; - uint256 sharesToDecrement; { - sharesToDecrement = SlashingLib.calcSlashedAmount({ - operatorShares: shares, + (uint256[] memory withdrawableSharesBefore, ) = delegationManager.getWithdrawableShares(defaultStaker, strategy.toArray()); + uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategy); + _setOperatorMagnitude(defaultOperator, strategy, newMaxMagnitude); + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares(defaultOperator, strategy, prevMaxMagnitude, newMaxMagnitude); + (, uint256 operatorSharesAfterSlash) = _assertOperatorSharesAfterSlash({ + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: operatorSharesBefore, prevMaxMagnitude: prevMaxMagnitude, newMaxMagnitude: newMaxMagnitude }); - _setOperatorMagnitude(defaultOperator, strategy, newMaxMagnitude); - cheats.prank(address(allocationManagerMock)); - delegationManager.burnOperatorShares(defaultOperator, strategy, prevMaxMagnitude, newMaxMagnitude); - operatorSharesAfterSlash = delegationManager.operatorShares(defaultOperator, strategy); + uint256 expectedWithdrawable = _calcWithdrawableShares( + shares, + uint256(WAD).divWad(prevMaxMagnitude), + _getSlashingFactor(defaultStaker, strategy, newMaxMagnitude) + ); + _assertSharesAfterSlash({ + staker: defaultStaker, + strategy: strategy, + withdrawableSharesBefore: withdrawableSharesBefore[0], + expectedWithdrawableShares: expectedWithdrawable, + prevMaxMagnitude: prevMaxMagnitude, + currMaxMagnitude: newMaxMagnitude + }); + + // Get withdrawable shares + (uint256[] memory withdrawableSharesAfter, uint256[] memory depositSharesAfter) = delegationManager.getWithdrawableShares(defaultStaker, strategy.toArray()); + _assertWithdrawableAndOperatorShares(withdrawableSharesAfter[0], operatorSharesAfterSlash, "Invalid withdrawable shares"); + assertEq(depositSharesAfter[0], shares, "Invalid deposit shares"); assertEq( - shares - sharesToDecrement, - operatorSharesAfterSlash + delegationManager.depositScalingFactor(defaultStaker, strategy), + uint256(WAD).divWad(prevMaxMagnitude), + "bad test setup" ); } - uint256 depositScalingFactor = delegationManager.depositScalingFactor(defaultStaker, strategy); - assertEq(depositScalingFactor, uint256(WAD).divWad(prevMaxMagnitude), "bad test setup"); - assertEq( - sharesToDecrement + operatorSharesAfterSlash, - shares, - "operatorShares should decrement correctly" - ); - - // Get withdrawable shares - (uint256[] memory withdrawableShares, uint256[] memory depositShares) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); - _assertWithdrawableAndOperatorShares(withdrawableShares[0], operatorSharesAfterSlash, "Invalid withdrawable shares"); - assertEq(depositShares[0], shares, "Invalid deposit shares"); - // Format queued withdrawal - ( - , - IDelegationManagerTypes.Withdrawal memory withdrawal, - bytes32 withdrawalRoot - ) = _setUpQueueWithdrawalsSingleStrat({ - staker: defaultStaker, - withdrawer: defaultStaker, - strategy: strategy, - depositSharesToWithdraw: shares - }); - - // Undelegate the staker - _undelegate_expectEmit_singleStrat( - UndelegateEmitStruct({ + (uint256[] memory withdrawableShares, uint256[] memory depositShares) = delegationManager.getWithdrawableShares(defaultStaker, strategy.toArray()); + uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategy); + { + ( + , + IDelegationManagerTypes.Withdrawal memory withdrawal, + bytes32 withdrawalRoot + ) = _setUpQueueWithdrawalsSingleStrat({ staker: defaultStaker, - operator: defaultOperator, - strategy: strategy, - depositSharesQueued: shares, - operatorSharesDecreased: withdrawableShares[0], - withdrawal: withdrawal, - withdrawalRoot: withdrawalRoot, - depositScalingFactor: WAD, - forceUndelegated: false - }) - ); - cheats.prank(defaultStaker); - delegationManager.undelegate(defaultStaker); + withdrawer: defaultStaker, + strategy: strategy, + depositSharesToWithdraw: shares + }); + + // Undelegate the staker + _undelegate_expectEmit_singleStrat( + UndelegateEmitStruct({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + depositSharesQueued: shares, + operatorSharesDecreased: withdrawableShares[0], + withdrawal: withdrawal, + withdrawalRoot: withdrawalRoot, + depositScalingFactor: WAD, + forceUndelegated: false + }) + ); + cheats.prank(defaultStaker); + delegationManager.undelegate(defaultStaker); + } // Checks - delegation status assertEq( @@ -3692,13 +3940,18 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { assertFalse(delegationManager.isDelegated(defaultStaker), "staker not undelegated"); // Checks - operator & staker shares - assertEq( - delegationManager.operatorShares(defaultOperator, strategy), - operatorSharesAfterSlash - withdrawableShares[0], // Note that this is not exactly 0 due to rounding - "operator shares not decreased correctly" - ); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: operatorSharesBefore, + depositSharesBefore: shares, + depositSharesWithdrawn: shares, + depositScalingFactor: uint256(WAD).divWad(prevMaxMagnitude), + slashingFactor: uint256(newMaxMagnitude) + }); - (withdrawableShares, depositShares) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); + (withdrawableShares, depositShares) = delegationManager.getWithdrawableShares(defaultStaker, strategy.toArray()); assertEq(withdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); assertEq(depositShares[0], 0, "staker deposit shares not reset correctly"); } @@ -3707,19 +3960,29 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { * @notice Verifies that the `undelegate` function properly undelegates a staker even though their shares * were slashed entirely. */ - function testFuzz_undelegate_slashedOperator100PercentWhileStaked(Randomness r) public { + function testFuzz_undelegate_slashedOperator100PercentWhileStaked(Randomness r) public rand(r) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); IStrategy[] memory strategyArray = r.StrategyArray(1); IStrategy strategy = strategyArray[0]; // register *this contract* as an operator _registerOperatorWithBaseDetails(defaultOperator); - + // Set the staker deposits in the strategies strategyManagerMock.addDeposit(defaultStaker, strategy, shares); // delegate from the `defaultStaker` to the operator _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); // Set operator magnitude uint64 operatorMagnitude = 0; @@ -3789,6 +4052,17 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { assertFalse(delegationManager.isDelegated(defaultStaker), "staker not undelegated"); // Checks - operator & staker shares + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + operatorSharesBefore: 0, + depositSharesBefore: shares, + depositSharesWithdrawn: shares, + depositScalingFactor: uint256(WAD), + slashingFactor: 0 + }); + assertEq(delegationManager.operatorShares(defaultOperator, strategy), 0, "operator shares not decreased correctly"); ( uint256[] memory stakerWithdrawableShares, @@ -3798,9 +4072,8 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { assertEq(depositShares[0], 0, "staker deposit shares not reset correctly"); } - function testFuzz_undelegate_slashedOperatorCloseTo100(Randomness r) public { - uint32 numStakers = r.Uint32(1, 8); - address[] memory stakers = r.StakerArray(numStakers); + function testFuzz_undelegate_slashedOperatorCloseTo100(Randomness r) public rand(r) { + address[] memory stakers = r.StakerArray(r.Uint32(1, 8)); uint64 prevMaxMagnitude = r.Uint64(2, WAD); uint64 newMaxMagnitude = 1; @@ -3809,10 +4082,8 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { _setOperatorMagnitude(defaultOperator, strategyMock, prevMaxMagnitude); // 2. Stakers deposits in the strategyMock - IStrategy[] memory strategies = new IStrategy[](1); - strategies[0] = strategyMock; { - for (uint256 i = 0; i < numStakers; ++i) { + for (uint256 i = 0; i < stakers.length; ++i) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); strategyManagerMock.addDeposit( stakers[i], @@ -3826,13 +4097,23 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { // 3. Delegate from the `stakers` to the operator { uint256 totalWithdrawable = 0; - for (uint256 i = 0; i < numStakers; ++i) { - _delegateToOperatorWhoAcceptsAllStakers(stakers[i], defaultOperator); - ( - uint256[] memory withdrawableSharesBefore, - uint256[] memory depositSharesBefore - ) = delegationManager.getWithdrawableShares(stakers[i], strategies); - assertEq(depositSharesBefore[0], stakerDepositShares[stakers[i]], "deposit shares not set correctly"); + for (uint256 i = 0; i < stakers.length; ++i) { + { + uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); + _delegateToOperatorWhoAcceptsAllStakers(stakers[i], defaultOperator); + _assertDeposit({ + staker: stakers[i], + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: operatorSharesBefore, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: stakerDepositShares[stakers[i]] + }); + } + + (uint256[] memory withdrawableSharesBefore, ) = delegationManager.getWithdrawableShares(stakers[i], strategyMock.toArray()); totalWithdrawable += withdrawableSharesBefore[0]; } assertLe( @@ -3841,18 +4122,24 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { } // 4. Slash operator - Set operator magnitude and call burnOperatorShares - uint256 operatorSharesAfterSlash; { + uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); _setOperatorMagnitude(defaultOperator, strategyMock, newMaxMagnitude); cheats.prank(address(allocationManagerMock)); delegationManager.burnOperatorShares(defaultOperator, strategyMock, prevMaxMagnitude, newMaxMagnitude); - operatorSharesAfterSlash = delegationManager.operatorShares(defaultOperator, strategyMock); + _assertOperatorSharesAfterSlash({ + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: operatorSharesBefore, + prevMaxMagnitude: prevMaxMagnitude, + newMaxMagnitude: newMaxMagnitude + }); } // 5. Undelegate the stakers with expected events uint256 totalOperatorSharesDecreased = 0; - for (uint256 i = 0; i < numStakers; ++i) { + for (uint256 i = 0; i < stakers.length; ++i) { ( , IDelegationManagerTypes.Withdrawal memory withdrawal, @@ -3863,10 +4150,11 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { strategy: strategyMock, depositSharesToWithdraw: stakerDepositShares[stakers[i]] }); - dsf = DepositScalingFactor({ - _scalingFactor: delegationManager.depositScalingFactor(stakers[i], strategyMock) - }); - uint256 operatorSharesDecreased = dsf.calcWithdrawable(stakerDepositShares[stakers[i]], newMaxMagnitude); + uint256 operatorSharesDecreased = _calcWithdrawableShares( + stakerDepositShares[stakers[i]], + delegationManager.depositScalingFactor(stakers[i], strategyMock), + newMaxMagnitude + ); _undelegate_expectEmit_singleStrat( UndelegateEmitStruct({ staker: stakers[i], @@ -3894,16 +4182,11 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { "undelegated staker should be delegated to zero address" ); assertFalse(delegationManager.isDelegated(defaultStaker), "staker not undelegated"); - assertEq( - delegationManager.operatorShares(defaultOperator, strategyMock) + totalOperatorSharesDecreased, - operatorSharesAfterSlash, - "operator shares not decreased correctly" - ); - for (uint256 i = 0; i < numStakers; ++i) { + for (uint256 i = 0; i < stakers.length; ++i) { ( uint256[] memory stakerWithdrawableShares, uint256[] memory stakerDepositShares - ) = delegationManager.getWithdrawableShares(stakers[i], strategies); + ) = delegationManager.getWithdrawableShares(stakers[i], strategyMock.toArray()); assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); assertEq(stakerDepositShares[0], 0, "staker deposit shares not reset correctly"); } @@ -3913,7 +4196,7 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { * @notice Given an operator with slashed magnitude, delegate, undelegate, and then delegate back to the same operator with * completing withdrawals as shares. This should result in the operatorShares after the second delegation being <= the shares from the first delegation. */ - function testFuzz_undelegate_delegateAgainWithRounding(Randomness r) public { + function testFuzz_undelegate_delegateAgainWithRounding(Randomness r) public rand(r) { uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); // set magnitude to 66% to ensure rounding when calculating `toShares` uint64 operatorMagnitude = 333333333333333333; @@ -3921,18 +4204,22 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { // register *this contract* as an operator & set its slashed magnitude _registerOperatorWithBaseDetails(defaultOperator); _setOperatorMagnitude(defaultOperator, strategyMock, operatorMagnitude); - + // Set the staker deposits in the strategies - IStrategy[] memory strategies = strategyMock.toArray(); - { - uint256[] memory sharesToSet = shares.toArrayU256(); - strategyManagerMock.setDeposits(defaultStaker, strategies, sharesToSet); - } + strategyManagerMock.addDeposit(defaultStaker, strategyMock, shares); // delegate from the `staker` to them _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); - uint256 depositScalingFactor = delegationManager.depositScalingFactor(defaultStaker, strategyMock); - assertEq(depositScalingFactor, uint256(WAD).divWad(uint256(operatorMagnitude)), "first deposit should result in k value of (1 / magnitude)"); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: shares + }); uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); // Format queued withdrawal @@ -3947,9 +4234,12 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { depositSharesToWithdraw: shares }); - DepositScalingFactor memory _dsf = DepositScalingFactor(depositScalingFactor); uint256 slashingFactor = _getSlashingFactor(defaultStaker, strategyMock, operatorMagnitude); - uint256 operatorSharesDecreased = _dsf.calcWithdrawable(shares, slashingFactor); + uint256 operatorSharesDecreased = _calcWithdrawableShares( + shares, + delegationManager.depositScalingFactor(defaultStaker, strategyMock), + slashingFactor + ); // Undelegate the staker cheats.prank(defaultStaker); @@ -3975,16 +4265,25 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { "undelegated staker should be delegated to zero address" ); assertFalse(delegationManager.isDelegated(defaultStaker), "staker not undelegated"); - // Checks - operator & staker shares - (uint256[] memory stakerWithdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategies); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: operatorSharesBefore, + depositSharesBefore: shares, + depositSharesWithdrawn: shares, + depositScalingFactor: uint256(WAD).divWad(operatorMagnitude), + slashingFactor: operatorMagnitude + }); + (uint256[] memory stakerWithdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategyMock.toArray()); assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); // // Re-delegate the staker to the operator again. The shares should have increased but may be less than from before due to rounding _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); // complete withdrawal as shares, should add back delegated shares to operator due to delegating again IERC20[] memory tokens = new IERC20[](1); - tokens[0] = IERC20(strategies[0].underlyingToken()); + tokens[0] = IERC20(strategyMock.underlyingToken()); cheats.roll(withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); cheats.prank(defaultStaker); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, false); @@ -3997,11 +4296,11 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { * @notice This function tests to ensure that a delegator can re-delegate to an operator after undelegating. * Asserts the shares after re-delegating are the same as originally. No slashing is done in this test. */ - function testFuzz_undelegate_redelegateWithSharesBack(Randomness r) public { + function testFuzz_undelegate_redelegateWithSharesBack(Randomness r) public rand(r) { address staker = r.Address(); address operator = r.Address(); uint256 strategyShares = r.Uint256(1, MAX_STRATEGY_SHARES); - int256 beaconShares = int256(r.Uint256(1, MAX_ETH_SUPPLY)); + int256 beaconShares = int256(r.Uint256(1 gwei, MAX_ETH_SUPPLY)); bool completeAsShares = r.Boolean(); // 1. Set staker shares @@ -4013,6 +4312,27 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { // 2. register operator and delegate staker to operator _registerOperatorWithBaseDetails(operator); _delegateToOperatorWhoAcceptsAllStakers(staker, operator); + _assertDeposit({ + staker: staker, + operator: operator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: uint256(WAD), + depositAmount: strategyShares + }); + _assertDeposit({ + staker: staker, + operator: operator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: uint256(WAD), + depositAmount: uint256(beaconShares) + }); + // 3. Setup queued withdrawals from `undelegate` // queued withdrawals done for single strat as this is how undelegate queue withdraws ( @@ -4052,7 +4372,7 @@ contract DelegationManagerUnitTests_Undelegate is DelegationManagerUnitTests { cheats.stopPrank(); } else { // complete withdrawal first and then delegate - cheats.prank(staker); + cheats.startPrank(staker); delegationManager.completeQueuedWithdrawal(strategyWithdrawal, strategyTokens, false); delegationManager.completeQueuedWithdrawal(beaconWithdrawal, beaconTokens, false); cheats.stopPrank(); @@ -4150,22 +4470,30 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes /** * @notice Verifies that `DelegationManager.queueWithdrawals` properly queues a withdrawal for the `withdrawer` - * from the `strategy` for the `sharesAmount`. + * from the `strategy` for the `sharesAmount`. * - Asserts that staker is delegated to the operator * - Asserts that shares for delegatedTo operator are decreased by `sharesAmount` * - Asserts that staker cumulativeWithdrawalsQueued nonce is incremented * - Checks that event was emitted with correct withdrawalRoot and withdrawal */ - function testFuzz_queueWithdrawal_SingleStrat_nonSlashedOperator(Randomness r) public { + function testFuzz_queueWithdrawal_SingleStrat_nonSlashedOperator(Randomness r) public rand(r) { uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); uint256 withdrawalAmount = r.Uint256(1, depositAmount); - uint256[] memory sharesAmounts = new uint256[](1); - sharesAmounts[0] = depositAmount; bool depositBeaconChainShares = r.Boolean(); // sharesAmounts is single element so returns single strategy - IStrategy[] memory strategies = _deployAndDepositIntoStrategies(defaultStaker, sharesAmounts, depositBeaconChainShares); + IStrategy[] memory strategies = _deployAndDepositIntoStrategies(defaultStaker, depositAmount.toArrayU256(), depositBeaconChainShares); _registerOperatorWithBaseDetails(defaultOperator); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[0], + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: uint256(WAD), + depositAmount: depositAmount + }); ( QueuedWithdrawalParams[] memory queuedWithdrawalParams, Withdrawal memory withdrawal, @@ -4193,11 +4521,19 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes cheats.prank(defaultStaker); delegationManager.queueWithdrawals(queuedWithdrawalParams); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[0], + operatorSharesBefore: delegatedSharesBefore, + depositSharesBefore: depositAmount, + depositSharesWithdrawn: withdrawalAmount, + depositScalingFactor: uint256(WAD), + slashingFactor: uint256(WAD) + }); + _assertQueuedWithdrawalExists(defaultStaker, withdrawal); uint256 nonceAfter = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); - uint256 delegatedSharesAfter = delegationManager.operatorShares(defaultOperator, strategies[0]); - _assertQueuedWithdrawalExists(defaultStaker); assertEq(nonceBefore + 1, nonceAfter, "staker nonce should have incremented"); - assertEq(delegatedSharesBefore - withdrawalAmount, delegatedSharesAfter, "delegated shares not decreased correctly"); } /** @@ -4208,19 +4544,28 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes * - Asserts that staker cumulativeWithdrawalsQueued nonce is incremented * - Checks that event was emitted with correct withdrawalRoot and withdrawal */ - function testFuzz_queueWithdrawal_SingleStrat_preSlashedOperator(Randomness r) public { + function testFuzz_queueWithdrawal_SingleStrat_preSlashedOperator(Randomness r) public rand(r) { uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); uint256 withdrawalAmount = r.Uint256(1, depositAmount); - uint64 prevMaxMagnitude = r.Uint64(2, WAD); - uint64 newMaxMagnitude = r.Uint64(1, prevMaxMagnitude - 1); + uint64 maxMagnitude = r.Uint64(1, WAD); // Slash the operator _registerOperatorWithBaseDetails(defaultOperator); - _setOperatorMagnitude(defaultOperator, strategyMock, newMaxMagnitude); + _setOperatorMagnitude(defaultOperator, strategyMock, maxMagnitude); // Deposit for staker & delegate strategyManagerMock.addDeposit(defaultStaker, strategyMock, depositAmount); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: depositAmount + }); ( QueuedWithdrawalParams[] memory queuedWithdrawalParams, @@ -4233,10 +4578,6 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes depositSharesToWithdraw: withdrawalAmount }); - dsf = DepositScalingFactor({ - _scalingFactor: delegationManager.depositScalingFactor(defaultStaker, strategyMock) - }); - uint256 expectedWithdrawnShares = dsf.calcWithdrawable(withdrawalAmount, newMaxMagnitude); assertEq(delegationManager.delegatedTo(defaultStaker), defaultOperator, "staker should be delegated to operator"); uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); uint256 delegatedSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); @@ -4254,16 +4595,20 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes cheats.prank(defaultStaker); delegationManager.queueWithdrawals(queuedWithdrawalParams); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: delegatedSharesBefore, + depositSharesBefore: depositAmount, + depositSharesWithdrawn: withdrawalAmount, + depositScalingFactor: delegationManager.depositScalingFactor(defaultStaker, strategyMock), + slashingFactor: uint256(maxMagnitude) + }); + _assertQueuedWithdrawalExists(defaultStaker, withdrawal); + uint256 nonceAfter = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); - uint256 delegatedSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); assertEq(nonceBefore + 1, nonceAfter, "staker nonce should have incremented"); - _assertQueuedWithdrawalExists(defaultStaker); - assertApproxEqRel( - delegatedSharesBefore - expectedWithdrawnShares, - delegatedSharesAfter, - 1e16, // max delta of 1%, given shares amount > 1000 - "delegated shares not decreased correctly" - ); } /** @@ -4274,7 +4619,7 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes * - Asserts that staker cumulativeWithdrawalsQueued nonce is incremented * - Checks that event was emitted with correct withdrawalRoot and withdrawal */ - function testFuzz_queueWithdrawal_SingleStrat_slashedWhileStaked(Randomness r) public { + function testFuzz_queueWithdrawal_SingleStrat_slashedWhileStaked(Randomness r) public rand(r) { uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); uint256 withdrawalAmount = r.Uint256(1, depositAmount); uint64 prevMaxMagnitude = r.Uint64(2, WAD); @@ -4284,59 +4629,78 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes _registerOperatorWithBaseDetails(defaultOperator); _setOperatorMagnitude(defaultOperator, strategyMock, prevMaxMagnitude); - // Deposit for staker & delegate strategyManagerMock.addDeposit(defaultStaker, strategyMock, depositAmount); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: depositAmount + }); // Slash the operator - _setOperatorMagnitude(defaultOperator, strategyMock, newMaxMagnitude); + uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); _setOperatorMagnitude(defaultOperator, strategyMock, newMaxMagnitude); cheats.prank(address(allocationManagerMock)); delegationManager.burnOperatorShares(defaultOperator, strategyMock, prevMaxMagnitude, newMaxMagnitude); - - ( - QueuedWithdrawalParams[] memory queuedWithdrawalParams, - Withdrawal memory withdrawal, - bytes32 withdrawalRoot - ) = _setUpQueueWithdrawalsSingleStrat({ - staker: defaultStaker, - withdrawer: defaultStaker, + // Assertions on amount burned + (uint256 operatorSharesSlashed, ) = _assertOperatorSharesAfterSlash({ + operator: defaultOperator, strategy: strategyMock, - depositSharesToWithdraw: withdrawalAmount + operatorSharesBefore: operatorSharesBefore, + prevMaxMagnitude: prevMaxMagnitude, + newMaxMagnitude: newMaxMagnitude }); - - assertEq(delegationManager.delegatedTo(defaultStaker), defaultOperator, "staker should be delegated to operator"); uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); - uint256 delegatedSharesBefore = delegationManager.operatorShares(defaultOperator, strategyMock); - // queueWithdrawals - _queueWithdrawals_expectEmit( - QueueWithdrawalsEmitStruct({ + { + ( + QueuedWithdrawalParams[] memory queuedWithdrawalParams, + Withdrawal memory withdrawal, + bytes32 withdrawalRoot + ) = _setUpQueueWithdrawalsSingleStrat({ staker: defaultStaker, - operator: defaultOperator, - queuedWithdrawalParams: queuedWithdrawalParams, - withdrawal: withdrawal, - withdrawalRoot: withdrawalRoot - }) - ); - cheats.prank(defaultStaker); - delegationManager.queueWithdrawals(queuedWithdrawalParams); + withdrawer: defaultStaker, + strategy: strategyMock, + depositSharesToWithdraw: withdrawalAmount + }); - uint256 delegatedSharesAfter = delegationManager.operatorShares(defaultOperator, strategyMock); + // queueWithdrawals + _queueWithdrawals_expectEmit( + QueueWithdrawalsEmitStruct({ + staker: defaultStaker, + operator: defaultOperator, + queuedWithdrawalParams: queuedWithdrawalParams, + withdrawal: withdrawal, + withdrawalRoot: withdrawalRoot + }) + ); + cheats.prank(defaultStaker); + delegationManager.queueWithdrawals(queuedWithdrawalParams); + _assertQueuedWithdrawalExists(defaultStaker, withdrawal); + } uint256 slashingFactor = _getSlashingFactor(defaultStaker, strategyMock, newMaxMagnitude); - dsf = DepositScalingFactor(delegationManager.depositScalingFactor(defaultStaker, strategyMock)); - - uint256 sharesWithdrawn = dsf.calcWithdrawable(withdrawalAmount, slashingFactor); - uint256 nonceAfter = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); - assertEq(nonceBefore + 1, nonceAfter, "staker nonce should have incremented"); - _assertQueuedWithdrawalExists(defaultStaker); assertEq( - delegatedSharesBefore - sharesWithdrawn, - delegatedSharesAfter, - "delegated shares not decreased correctly" + nonceBefore + 1, + delegationManager.cumulativeWithdrawalsQueued(defaultStaker), + "staker nonce should have incremented" ); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: depositAmount - operatorSharesSlashed, + depositSharesBefore: depositAmount, + depositSharesWithdrawn: withdrawalAmount, + depositScalingFactor: uint256(WAD).divWad(prevMaxMagnitude), + slashingFactor: slashingFactor + }); } /** @@ -4346,13 +4710,25 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes * - Asserts that staker withdrawableShares after is 0 * - Checks that event was emitted with correct withdrawalRoot and withdrawal */ - function testFuzz_queueWithdrawal_SingleStrat_slashed100PercentWhileStaked(Randomness r) public { + function testFuzz_queueWithdrawal_SingleStrat_slashed100PercentWhileStaked( + Randomness r + ) public rand(r) { uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); // Register operator, deposit for staker & delegate _registerOperatorWithBaseDetails(defaultOperator); strategyManagerMock.addDeposit(defaultStaker, strategyMock, depositAmount); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: depositAmount + }); ( IDelegationManagerTypes.QueuedWithdrawalParams[] memory queuedWithdrawalParams, @@ -4370,6 +4746,13 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes _setOperatorMagnitude(defaultOperator, strategyMock, operatorMagnitude); cheats.prank(address(allocationManagerMock)); delegationManager.burnOperatorShares(defaultOperator, strategyMock, WAD, 0); + _assertOperatorSharesAfterSlash({ + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: depositAmount, + prevMaxMagnitude: WAD, + newMaxMagnitude: operatorMagnitude + }); assertEq(delegationManager.delegatedTo(defaultStaker), defaultOperator, "staker should be delegated to operator"); // queueWithdrawals should result in an empty withdrawal @@ -4385,14 +4768,23 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes cheats.prank(defaultStaker); delegationManager.queueWithdrawals(queuedWithdrawalParams); - (IStrategy[] memory strategies, ) = delegationManager.getDepositedShares(defaultStaker); - (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategies); + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategyMock.toArray()); assertEq( withdrawableShares[0], 0, "withdrawable shares should be 0 after being slashed fully" ); - _assertQueuedWithdrawalExists(defaultStaker); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategyMock, + operatorSharesBefore: 0, + depositSharesBefore: depositAmount, + depositSharesWithdrawn: 0, + depositScalingFactor: uint256(WAD), + slashingFactor: 0 + }); + _assertQueuedWithdrawalExists(defaultStaker, withdrawal); } /** @@ -4407,7 +4799,7 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes */ function testFuzz_queueWithdrawal_MultipleStrats_nonSlashedOperator( Randomness r - ) public { + ) public rand(r) { uint32 numStrategies = r.Uint32(1, 32); bool depositBeaconChainShares = r.Boolean(); @@ -4420,6 +4812,19 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes _registerOperatorWithBaseDetails(defaultOperator); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + for (uint256 i = 0; i < strategies.length; ++i) { + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: 0, + depositAmount: depositAmounts[i] + }); + } + ( QueuedWithdrawalParams[] memory queuedWithdrawalParams, Withdrawal memory withdrawal, @@ -4453,17 +4858,16 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes // Post queueWithdrawal state values for (uint256 i = 0; i < strategies.length; i++) { - assertEq( - delegatedSharesBefore[i] - withdrawalAmounts[i], // Shares before - withdrawal amount - delegationManager.operatorShares(defaultOperator, strategies[i]), // Shares after - "delegated shares not decreased correctly" - ); - // assert staker deposit shares decremented correctly - assertEq( - strategyManagerMock.stakerDepositShares(defaultStaker, strategies[i]), - depositAmounts[i] - withdrawalAmounts[i], - "staker deposit shares not decreased correctly" - ); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: delegatedSharesBefore[i], + depositSharesBefore: depositAmounts[i], + depositSharesWithdrawn: withdrawalAmounts[i], + depositScalingFactor: uint256(WAD), + slashingFactor: uint256(WAD) + }); } assertEq( delegationManager.delegatedTo(defaultStaker), @@ -4472,7 +4876,7 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes ); uint256 nonceAfter = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); assertEq(nonceBefore + 1, nonceAfter, "staker nonce should have incremented"); - _assertQueuedWithdrawalExists(defaultStaker); + _assertQueuedWithdrawalExists(defaultStaker, withdrawal); } /** @@ -4485,75 +4889,81 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes * - Asserts that staker cumulativeWithdrawalsQueued nonce is incremented * - Checks that event was emitted with correct withdrawalRoot and withdrawal */ - function testFuzz_queueWithdrawal_MultipleStrats_preSlashedOperator(Randomness r) public { + function testFuzz_queueWithdrawal_MultipleStrats_preSlashedOperator( + Randomness r + ) public rand(r) { // 1. Setup // - fuzz numbers of strategies, deposit and withdraw amounts, and prev/new magnitudes for each strategy respectively // - deposit into strategies, delegate to operator - uint32 numStrats = r.Uint32(1, 32); bool depositBeaconChainShares = r.Boolean(); - IStrategy[] memory strategies = r.StrategyArray(numStrats); + IStrategy[] memory strategies = r.StrategyArray(r.Uint32(1, 32)); if (depositBeaconChainShares) { - strategies[numStrats - 1] = beaconChainETHStrategy; + strategies[strategies.length - 1] = beaconChainETHStrategy; } ( uint256[] memory depositAmounts, uint256[] memory withdrawalAmounts, uint64[] memory prevMaxMagnitudes, - ) = _fuzzDepositWithdrawalAmounts({ r: r, numStrategies: numStrats }); + ) = _fuzzDepositWithdrawalAmounts({ r: r, numStrategies: uint32(strategies.length) }); _registerOperatorWithBaseDetails(defaultOperator); allocationManagerMock.setMaxMagnitudes(defaultOperator, strategies, prevMaxMagnitudes); _depositIntoStrategies(defaultStaker, strategies, depositAmounts); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); - - uint256[] memory expectedWithdrawnShares = _getWithdrawableShares({ - staker: defaultStaker, - strategies: strategies, - maxMagnitudes: prevMaxMagnitudes, - depositSharesToWithdraw: withdrawalAmounts - }); - - - // 2. Setup and call queued withdrawals - ( - IDelegationManagerTypes.QueuedWithdrawalParams[] memory queuedWithdrawalParams, - IDelegationManagerTypes.Withdrawal memory withdrawal, - bytes32 withdrawalRoot - ) = _setUpQueueWithdrawals({ - staker: defaultStaker, - withdrawer: defaultStaker, - strategies: strategies, - depositWithdrawalAmounts: withdrawalAmounts - }); - uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); - // expected events emitted - _queueWithdrawals_expectEmit( - QueueWithdrawalsEmitStruct({ + // Check deposit state for all strategies after delegating + for (uint256 i = 0; i < strategies.length; ++i) { + _assertDeposit({ staker: defaultStaker, operator: defaultOperator, - queuedWithdrawalParams: queuedWithdrawalParams, - withdrawal: withdrawal, - withdrawalRoot: withdrawalRoot - }) - ); - // 3. call `DelegationManager.queueWithdrawals` - _queueWithdrawals(defaultStaker, queuedWithdrawalParams, withdrawal); + strategy: strategies[i], + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: depositAmounts[i] + }); + } + uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); + + // 2. Setup and call queued withdrawals + { + ( + IDelegationManagerTypes.QueuedWithdrawalParams[] memory queuedWithdrawalParams, + IDelegationManagerTypes.Withdrawal memory withdrawal, + bytes32 withdrawalRoot + ) = _setUpQueueWithdrawals({ + staker: defaultStaker, + withdrawer: defaultStaker, + strategies: strategies, + depositWithdrawalAmounts: withdrawalAmounts + }); + // expected events emitted + _queueWithdrawals_expectEmit( + QueueWithdrawalsEmitStruct({ + staker: defaultStaker, + operator: defaultOperator, + queuedWithdrawalParams: queuedWithdrawalParams, + withdrawal: withdrawal, + withdrawalRoot: withdrawalRoot + }) + ); + // 3. call `DelegationManager.queueWithdrawals` + _queueWithdrawals(defaultStaker, queuedWithdrawalParams, withdrawal); + } // 4. Post queueWithdrawal state values // Post queueWithdrawal state values for (uint256 i = 0; i < strategies.length; i++) { - // assert operator shares decreased properly - assertEq( - depositAmounts[i] - expectedWithdrawnShares[i], // Shares before - withdrawal amount - delegationManager.operatorShares(defaultOperator, strategies[i]), // Shares after - "delegated shares not decreased correctly" - ); - // assert staker deposit shares decremented correctly - assertEq( - strategyManagerMock.stakerDepositShares(defaultStaker, strategies[i]), - depositAmounts[i] - withdrawalAmounts[i], - "staker deposit shares not decreased correctly" - ); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: depositAmounts[i], + depositSharesBefore: depositAmounts[i], + depositSharesWithdrawn: withdrawalAmounts[i], + depositScalingFactor: uint256(WAD).divWad(prevMaxMagnitudes[i]), + slashingFactor: prevMaxMagnitudes[i] + }); } assertEq( delegationManager.delegatedTo(defaultStaker), @@ -4579,46 +4989,63 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes * - Asserts that staker cumulativeWithdrawalsQueued nonce is incremented * - Checks that event was emitted with correct withdrawalRoot and withdrawal */ - function testFuzz_queueWithdrawal_MultipleStrats_slashedWhileStaked(Randomness r) public { + function testFuzz_queueWithdrawal_MultipleStrats_slashedWhileStaked( + Randomness r + ) public rand(r) { // 1. Setup // - fuzz numbers of strategies, deposit and withdraw amounts, and prev/new magnitudes for each strategy respectively // - deposit into strategies, delegate to operator uint32 numStrats = r.Uint32(1, 32); - IStrategy[] memory strategies = r.StrategyArray(numStrats); + IStrategy[] memory strategies = r.StrategyArray(r.Uint32(1, 32)); bool depositBeaconChainShares = r.Boolean(); if (depositBeaconChainShares) { - strategies[numStrats - 1] = beaconChainETHStrategy; + strategies[strategies.length - 1] = beaconChainETHStrategy; } ( uint256[] memory depositAmounts, uint256[] memory withdrawalAmounts, uint64[] memory prevMaxMagnitudes, uint64[] memory newMaxMagnitudes - ) = _fuzzDepositWithdrawalAmounts({ r: r, numStrategies: numStrats }); + ) = _fuzzDepositWithdrawalAmounts({ r: r, numStrategies: uint32(strategies.length) }); _registerOperatorWithBaseDetails(defaultOperator); allocationManagerMock.setMaxMagnitudes(defaultOperator, strategies, prevMaxMagnitudes); _depositIntoStrategies(defaultStaker, strategies, depositAmounts); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); - - uint256[] memory expectedWithdrawnShares = _getWithdrawableShares({ - staker: defaultStaker, - strategies: strategies, - maxMagnitudes: newMaxMagnitudes, - depositSharesToWithdraw: withdrawalAmounts - }); + // Check deposit state for all strategies after delegating + for (uint256 i = 0; i < strategies.length; ++i) { + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: depositAmounts[i] + }); + } // 2. Slash operator while staker is delegated and staked allocationManagerMock.setMaxMagnitudes(defaultOperator, strategies, newMaxMagnitudes); cheats.startPrank(address(allocationManagerMock)); + uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); + uint256[] memory slashedOperatorShares = new uint256[](strategies.length); for (uint256 i = 0; i < strategies.length; i++) { + uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategies[i]); delegationManager.burnOperatorShares(defaultOperator, strategies[i], prevMaxMagnitudes[i], newMaxMagnitudes[i]); - delegatedSharesBefore[strategies[i]] = delegationManager.operatorShares(defaultOperator, strategies[i]); + // Assert correct amount of shares slashed from operator + (slashedOperatorShares[i], ) = _assertOperatorSharesAfterSlash({ + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: operatorSharesBefore, + prevMaxMagnitude: prevMaxMagnitudes[i], + newMaxMagnitude: newMaxMagnitudes[i] + }); } cheats.stopPrank(); // 3. Setup and call queued withdrawals - uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); - { + { ( IDelegationManagerTypes.QueuedWithdrawalParams[] memory queuedWithdrawalParams, IDelegationManagerTypes.Withdrawal memory withdrawal, @@ -4641,22 +5068,21 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes ); // 4. call `DelegationManager.queueWithdrawals` _queueWithdrawals(defaultStaker, queuedWithdrawalParams, withdrawal); + _assertQueuedWithdrawalExists(defaultStaker, withdrawal); } // 5. Post queueWithdrawal state values for (uint256 i = 0; i < strategies.length; i++) { - // assert operator shares decreased properly - assertEq( - delegatedSharesBefore[strategies[i]] - expectedWithdrawnShares[i], // Shares before - withdrawal amount - delegationManager.operatorShares(defaultOperator, strategies[i]), // Shares after - "delegated shares not decreased correctly" - ); - // assert staker deposit shares decremented correctly - assertEq( - strategyManagerMock.stakerDepositShares(defaultStaker, strategies[i]), - depositAmounts[i] - withdrawalAmounts[i], - "staker deposit shares not decreased correctly" - ); + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: depositAmounts[i] - slashedOperatorShares[i], + depositSharesBefore: depositAmounts[i], + depositSharesWithdrawn: withdrawalAmounts[i], + depositScalingFactor: uint256(WAD).divWad(prevMaxMagnitudes[i]), + slashingFactor: newMaxMagnitudes[i] + }); } assertEq( delegationManager.delegatedTo(defaultStaker), @@ -4668,7 +5094,6 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes delegationManager.cumulativeWithdrawalsQueued(defaultStaker), "staker nonce should have incremented" ); - _assertQueuedWithdrawalExists(defaultStaker); } /** @@ -4676,7 +5101,9 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes * - Asserts that the strategy with 0 newMaxMagnitude has 0 delegated shares before and after withdrawal * - Asserts that the staker withdrawn shares for the strategy with 0 newMaxMagnitude is 0 */ - function testFuzz_queueWithdrawal_MultipleStrats__slashed100PercentWhileStaked(Randomness r) public { + function testFuzz_queueWithdrawal_MultipleStrats__slashed100PercentWhileStaked( + Randomness r + ) public rand(r) { // 1. Setup // - fuzz numbers of strategies, deposit and withdraw amounts, and prev/new magnitudes for each strategy respectively // - deposit into strategies, delegate to operator @@ -4700,6 +5127,19 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes allocationManagerMock.setMaxMagnitudes(defaultOperator, strategies, prevMaxMagnitudes); _depositIntoStrategies(defaultStaker, strategies, depositAmounts); _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + // Check deposit state for all strategies after delegating + for (uint256 i = 0; i < strategies.length; ++i) { + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: depositAmounts[i] + }); + } uint256[] memory expectedWithdrawnShares = _getWithdrawableShares({ staker: defaultStaker, @@ -4709,16 +5149,39 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes }); // 2. Slash operator while staker is delegated and staked + uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); + uint256[] memory slashedOperatorShares = new uint256[](strategies.length); allocationManagerMock.setMaxMagnitudes(defaultOperator, strategies, newMaxMagnitudes); cheats.startPrank(address(allocationManagerMock)); for (uint256 i = 0; i < strategies.length; i++) { + uint256 operatorSharesBefore = delegationManager.operatorShares(defaultOperator, strategies[i]); delegationManager.burnOperatorShares(defaultOperator, strategies[i], prevMaxMagnitudes[i], newMaxMagnitudes[i]); - delegatedSharesBefore[strategies[i]] = delegationManager.operatorShares(defaultOperator, strategies[i]); + + // Assertions on amount burned + (slashedOperatorShares[i], ) = _assertOperatorSharesAfterSlash({ + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: operatorSharesBefore, + prevMaxMagnitude: prevMaxMagnitudes[i], + newMaxMagnitude: newMaxMagnitudes[i] + }); + // additional assertion checks for strategy that was slashed 100% + if (zeroMagnitudeIndex == i) { + assertEq( + slashedOperatorShares[i], + operatorSharesBefore, + "expected slashed operator shares to be full amount" + ); + assertEq( + delegationManager.operatorShares(defaultOperator, strategies[i]), + 0, + "expected operator shares to be 0" + ); + } } cheats.stopPrank(); // 3. Setup and call queued withdrawals - uint256 nonceBefore = delegationManager.cumulativeWithdrawalsQueued(defaultStaker); { ( IDelegationManagerTypes.QueuedWithdrawalParams[] memory queuedWithdrawalParams, @@ -4742,44 +5205,39 @@ contract DelegationManagerUnitTests_queueWithdrawals is DelegationManagerUnitTes ); // 4. call `DelegationManager.queueWithdrawals` _queueWithdrawals(defaultStaker, queuedWithdrawalParams, withdrawal); + _assertQueuedWithdrawalExists(defaultStaker, withdrawal); } // 5. Post queueWithdrawal state values for (uint256 i = 0; i < strategies.length; i++) { - // assert operator shares decreased properly - assertEq( - delegatedSharesBefore[strategies[i]] - expectedWithdrawnShares[i], // Shares before - withdrawal amount - delegationManager.operatorShares(defaultOperator, strategies[i]), // Shares after - "delegated shares not decreased correctly" - ); - // assert staker deposit shares decremented correctly - assertEq( - strategyManagerMock.stakerDepositShares(defaultStaker, strategies[i]), - depositAmounts[i] - withdrawalAmounts[i], - "staker deposit shares not decreased correctly" - ); + if (zeroMagnitudeIndex == i) { + assertEq( + newMaxMagnitudes[i], + 0, + "expected new max magnitude to be 0" + ); + } + _assertWithdrawal({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategies[i], + operatorSharesBefore: depositAmounts[i] - slashedOperatorShares[i], + depositSharesBefore: depositAmounts[i], + depositSharesWithdrawn: withdrawalAmounts[i], + depositScalingFactor: uint256(WAD).divWad(prevMaxMagnitudes[i]), + slashingFactor: zeroMagnitudeIndex == i ? 0 : newMaxMagnitudes[i] + }); } assertEq( delegationManager.delegatedTo(defaultStaker), defaultOperator, "staker should be delegated to operator" ); - assertEq( - expectedWithdrawnShares[zeroMagnitudeIndex], - delegatedSharesBefore[strategies[zeroMagnitudeIndex]], - "expected withdrawn shares and delegated shares before withdrawal should be 0" - ); - assertEq( - delegatedSharesBefore[strategies[zeroMagnitudeIndex]], - 0, - "expected withdrawn shares and delegated shares before withdrawal should be 0" - ); assertEq( nonceBefore + 1, delegationManager.cumulativeWithdrawalsQueued(defaultStaker), "staker nonce should have incremented" ); - _assertQueuedWithdrawalExists(defaultStaker); } } @@ -4857,9 +5315,9 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage cheats.prank(defaultStaker); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, true); } - - function test_Revert_WhenWithdrawerNotCaller(address invalidCaller) filterFuzzedAddressInputs(invalidCaller) public { - cheats.assume(invalidCaller != defaultStaker); + + function test_Revert_WhenWithdrawerNotCaller(Randomness r) rand(r) public { + address invalidCaller = r.Address(); _registerOperatorWithBaseDetails(defaultOperator); ( @@ -4906,13 +5364,11 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage } /** - * @notice should revert if minWithdrawalDelayBlocks has not passed, and if - * delegationManager.getCompletableTimestamp returns a value greater than minWithdrawalDelayBlocks + * @notice should revert if MIN_WITHDRAWAL_DELAY_BLOCKS has not passed, and if + * delegationManager.getCompletableTimestamp returns a value greater than MIN_WITHDRAWAL_DELAY_BLOCKS * then it should revert if the validBlockNumber has not passed either. */ - function test_Revert_WhenWithdrawalDelayNotPassed( - Randomness r - ) public { + function test_Revert_WhenWithdrawalDelayNotPassed(Randomness r) rand(r) public { uint32 numStrategies = r.Uint32(1, 32); bool receiveAsTokens = r.Boolean(); ( @@ -4935,7 +5391,7 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage }); // prank as withdrawer address - cheats.roll(withdrawal.startBlock + minWithdrawalDelayBlocks - 1); + cheats.roll(withdrawal.startBlock + MIN_WITHDRAWAL_DELAY_BLOCKS - 1); cheats.expectRevert(WithdrawalDelayNotElapsed.selector); cheats.prank(defaultStaker); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, receiveAsTokens); @@ -4954,14 +5410,10 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage /** * Test completing multiple queued withdrawals for a single strategy by passing in the withdrawals */ - function test_completeQueuedWithdrawals_MultipleWithdrawals( - address staker, - uint64 depositAmount, - uint numWithdrawals - ) public filterFuzzedAddressInputs(staker) { - cheats.assume(staker != defaultOperator); - cheats.assume(depositAmount > 0); - cheats.assume(numWithdrawals > 1 && numWithdrawals < 20); + function test_completeQueuedWithdrawals_MultipleWithdrawals(Randomness r) public rand(r) { + address staker = r.Address(); + uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); + uint256 numWithdrawals = r.Uint256(2, 20); ( IDelegationManagerTypes.Withdrawal[] memory withdrawals, @@ -4984,13 +5436,18 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage bool[] memory receiveAsTokens = new bool[](withdrawals.length); for (uint i = 0; i < withdrawals.length; i++) { - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit SlashingWithdrawalCompleted(withdrawalRoots[i]); receiveAsTokens[i] = true; } // completeQueuedWithdrawal cheats.roll(withdrawals[0].startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); + _completeQueuedWithdrawals_expectEmit( + CompleteQueuedWithdrawalsEmitStruct({ + withdrawals: withdrawals, + tokens: tokens, + receiveAsTokens: receiveAsTokens + }) + ); cheats.prank(staker); delegationManager.completeQueuedWithdrawals(withdrawals, tokens, receiveAsTokens); @@ -5005,16 +5462,11 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage /** * Test completing multiple queued withdrawals for a single strategy without passing in the withdrawals */ - function test_completeQueuedWithdrawals_NumToComplete( - address staker, - uint64 depositAmount, - uint numWithdrawals, - uint numToComplete - ) public filterFuzzedAddressInputs(staker) { - cheats.assume(staker != defaultOperator); - cheats.assume(depositAmount > 0); - numWithdrawals = bound(numWithdrawals, 2, 20); - numToComplete = bound(numToComplete, 1, numWithdrawals); + function test_completeQueuedWithdrawals_NumToComplete(Randomness r) public rand(r) { + address staker = r.Address(); + uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); + uint256 numWithdrawals = r.Uint256(2, 20); + uint256 numToComplete = r.Uint256(2, numWithdrawals); ( IDelegationManagerTypes.Withdrawal[] memory withdrawals, @@ -5036,14 +5488,19 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage } bool[] memory receiveAsTokens = new bool[](withdrawals.length); - for (uint i = 0; i < numToComplete; i++) { - // cheats.expectEmit(true, true, true, true, address(delegationManager)); - // emit SlashingWithdrawalCompleted(withdrawalRoots[i]); + for (uint i = 0; i < withdrawals.length; i++) { receiveAsTokens[i] = true; } // completeQueuedWithdrawal cheats.roll(withdrawals[0].startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); + _completeQueuedWithdrawals_expectEmit( + CompleteQueuedWithdrawalsEmitStruct({ + withdrawals: withdrawals, + tokens: tokens, + receiveAsTokens: receiveAsTokens + }) + ); cheats.prank(staker); delegationManager.completeQueuedWithdrawals(tokens, receiveAsTokens, numToComplete); @@ -5066,7 +5523,9 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage * - Asserts operatorShares is unchanged after `completeQueuedWithdrawal` * - Checks that event `WithdrawalCompleted` is emitted with withdrawalRoot */ - function test_completeQueuedWithdrawal_SingleStratWithdrawAsTokens(Randomness r) public { + function test_completeQueuedWithdrawal_SingleStratWithdrawAsTokens( + Randomness r + ) public rand(r) { address staker = r.Address(); uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); uint256 withdrawalAmount = r.Uint256(1, depositAmount); @@ -5089,9 +5548,14 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage // completeQueuedWithdrawal cheats.roll(withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); + _completeQueuedWithdrawal_expectEmit( + CompleteQueuedWithdrawalEmitStruct({ + withdrawal: withdrawal, + tokens: tokens, + receiveAsTokens: true + }) + ); cheats.prank(staker); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit SlashingWithdrawalCompleted(withdrawalRoot); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, true); uint256 operatorSharesAfter = delegationManager.operatorShares(defaultOperator, withdrawal.strategies[0]); @@ -5108,15 +5572,12 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage * - Asserts that the shares the staker completed withdrawal for are less than what is expected since its operator is slashed */ function test_completeQueuedWithdrawal_SingleStratWithdrawAsTokens_slashOperatorDuringQueue( - uint256 depositAmount, - uint256 withdrawalAmount, - uint64 prevMaxMagnitude, - uint64 newMaxMagnitude - ) public { - depositAmount = bound(depositAmount, 1, MAX_STRATEGY_SHARES); - withdrawalAmount = bound(withdrawalAmount, 1, depositAmount); - prevMaxMagnitude = uint64(bound(prevMaxMagnitude, 2, WAD)); - newMaxMagnitude = uint64(bound(newMaxMagnitude, 1, prevMaxMagnitude - 1)); + Randomness r + ) public rand(r) { + uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); + uint256 withdrawalAmount = r.Uint256(1, depositAmount); + uint64 prevMaxMagnitude = r.Uint64(2, WAD); + uint64 newMaxMagnitude = r.Uint64(1, prevMaxMagnitude - 1); // Deposit Staker strategyManagerMock.addDeposit(defaultStaker, strategyMock, depositAmount); @@ -5161,9 +5622,14 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage IERC20[] memory tokens = new IERC20[](1); tokens[0] = IERC20(strategyMock.underlyingToken()); cheats.roll(withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); + _completeQueuedWithdrawal_expectEmit( + CompleteQueuedWithdrawalEmitStruct({ + withdrawal: withdrawal, + tokens: tokens, + receiveAsTokens: true + }) + ); cheats.prank(defaultStaker); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit SlashingWithdrawalCompleted(withdrawalRoot); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, true); } @@ -5172,15 +5638,13 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage assertEq(operatorSharesAfterWithdrawalComplete, operatorSharesAfterSlash, "operator shares should be unchanged from slash to withdrawal completion"); assertFalse(delegationManager.pendingWithdrawals(withdrawalRoot), "withdrawalRoot should be completed and marked false now"); - // Checks: staker shares: + // Checks: staker shares: uint256 stakerSharesWithdrawn = strategyManagerMock.strategySharesWithdrawn(defaultStaker, strategyMock); - { - dsf = DepositScalingFactor({ - _scalingFactor: delegationManager.depositScalingFactor(defaultStaker, strategyMock) - }); - } - - uint256 actualSharesWithdrawn = dsf.calcWithdrawable(withdrawalAmount, newMaxMagnitude); + uint256 actualSharesWithdrawn = _calcWithdrawableShares( + withdrawalAmount, + delegationManager.depositScalingFactor(defaultStaker, strategyMock), + newMaxMagnitude + ); assertEq(stakerSharesWithdrawn, actualSharesWithdrawn, "staker shares withdrawn not calculated correctly"); } @@ -5246,9 +5710,14 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage // Complete queue withdrawal IERC20[] memory tokens = new IERC20[](1); cheats.roll(withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); + _completeQueuedWithdrawal_expectEmit( + CompleteQueuedWithdrawalEmitStruct({ + withdrawal: withdrawal, + tokens: tokens, + receiveAsTokens: true + }) + ); cheats.prank(defaultStaker); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit SlashingWithdrawalCompleted(withdrawalRoot); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, true); // Checks: operator shares @@ -5334,9 +5803,14 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage // Complete queue withdrawal IERC20[] memory tokens = new IERC20[](1); cheats.roll(withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); + _completeQueuedWithdrawal_expectEmit( + CompleteQueuedWithdrawalEmitStruct({ + withdrawal: withdrawal, + tokens: tokens, + receiveAsTokens: true + }) + ); cheats.prank(defaultStaker); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit SlashingWithdrawalCompleted(withdrawalRoot); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, true); // Checks: operator shares @@ -5387,9 +5861,14 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage // completeQueuedWithdrawal cheats.roll(withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS()); + _completeQueuedWithdrawal_expectEmit( + CompleteQueuedWithdrawalEmitStruct({ + withdrawal: withdrawal, + tokens: tokens, + receiveAsTokens: false + }) + ); cheats.prank(staker); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit SlashingWithdrawalCompleted(withdrawalRoot); delegationManager.completeQueuedWithdrawal(withdrawal, tokens, false); uint256 operatorSharesAfter = delegationManager.operatorShares(defaultOperator, withdrawal.strategies[0]); @@ -5399,15 +5878,140 @@ contract DelegationManagerUnitTests_completeQueuedWithdrawal is DelegationManage } } -contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests { - using SingleItemArrayLib for *; +contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests { + using SingleItemArrayLib for *; + using SlashingLib for *; + using Math for *; + + /// @notice Verifies that `DelegationManager.burnOperatorShares` reverts if not called by the AllocationManager + function testFuzz_Revert_burnOperatorShares_invalidCaller(Randomness r) public rand(r) { + address invalidCaller = r.Address(); + + cheats.startPrank(invalidCaller); + cheats.expectRevert(IDelegationManagerErrors.OnlyAllocationManager.selector); + delegationManager.burnOperatorShares(defaultOperator, strategyMock, 0, 0); + } + + /// @notice Verifies that there is no change in shares if the staker is not delegatedd + function testFuzz_Revert_burnOperatorShares_noop() public { + _registerOperatorWithBaseDetails(defaultOperator); + + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares(defaultOperator, strategyMock, WAD, WAD/2); + assertEq(delegationManager.operatorShares(defaultOperator, strategyMock), 0, "shares should not have changed"); + } + + /** + * @notice Verifies that `DelegationManager.burnOperatorShares` properly decreases the delegated `shares` that the operator + * who the `defaultStaker` is delegated to has in the strategies + */ + function testFuzz_burnOperatorShares_slashedOperator(Randomness r) public rand(r) { + // sanity-filtering on fuzzed input length & staker + IStrategy[] memory strategies = r.StrategyArray(16); + uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); + uint64 prevMaxMagnitude = r.Uint64(2, WAD); + uint64 newMaxMagnitude = r.Uint64(1, prevMaxMagnitude); + bool hasBeaconChainStrategy = r.Boolean(); + if (hasBeaconChainStrategy) { + // Set last strategy in array as beacon chain strategy + strategies[strategies.length - 1] = beaconChainETHStrategy; + } + + // Register operator + _registerOperatorWithBaseDetails(defaultOperator); + + // Set the staker deposits in the strategies + uint256[] memory sharesToSet = new uint256[](strategies.length); + uint256[] memory depositScalingFactors = new uint256[](strategies.length); + for(uint256 i = 0; i < strategies.length; i++) { + strategies[i] = IStrategy(random().Address()); + sharesToSet[i] = shares; + depositScalingFactors[i] = uint256(WAD).divWad(uint256(prevMaxMagnitude)); + _setOperatorMagnitude(defaultOperator, strategies[i], prevMaxMagnitude); + } + + // Okay to set beacon chain shares in SM mock, wont' be called by DM + strategyManagerMock.setDeposits(defaultStaker, strategies, sharesToSet); + if (hasBeaconChainStrategy) { + eigenPodManagerMock.setPodOwnerShares(defaultStaker, int256(uint256(shares))); + } + + // events expected emitted for each strategy + _delegateTo_expectEmit( + DelegateToEmitStruct({ + staker: defaultStaker, + operator: defaultOperator, + strategies: strategies, + depositShares: sharesToSet, + depositScalingFactors: depositScalingFactors + }) + ); + // delegate from the `staker` to the operator + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + address delegatedTo = delegationManager.delegatedTo(defaultStaker); + + // check shares before call to `burnOperatorShares` + for (uint256 i = 0; i < strategies.length; ++i) { + // store delegated shares in a mapping + delegatedSharesBefore[strategies[i]] = delegationManager.operatorShares(delegatedTo, strategies[i]); + // also construct an array which we'll use in another loop + totalSharesForStrategyInArray[address(strategies[i])] += shares; + } + + // for each strategy in `strategies`, decrease delegated shares by `shares` + { + cheats.startPrank(address(allocationManagerMock)); + for (uint256 i = 0; i < strategies.length; ++i) { + uint256 currentShares = delegationManager.operatorShares(defaultOperator, strategies[i]); + uint256 sharesToDecrease = SlashingLib.calcSlashedAmount({ + operatorShares: currentShares, + prevMaxMagnitude: prevMaxMagnitude, + newMaxMagnitude: newMaxMagnitude + }); + + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit OperatorSharesDecreased( + defaultOperator, + address(0), + strategies[i], + sharesToDecrease + ); + delegationManager.burnOperatorShares(defaultOperator, strategies[i], prevMaxMagnitude, newMaxMagnitude); + + // Also update maxMagnitude in ALM mock + _setOperatorMagnitude(defaultOperator, strategies[i], newMaxMagnitude); + + totalSharesDecreasedForStrategy[strategies[i]] += sharesToDecrease; + } + cheats.stopPrank(); + } + + // check shares after call to `burnOperatorShares` + (uint256[] memory withdrawableShares, ) = delegationManager.getWithdrawableShares(defaultStaker, strategies); + for (uint256 i = 0; i < strategies.length; ++i) { + uint256 delegatedSharesAfter = delegationManager.operatorShares(delegatedTo, strategies[i]); + assertEq( + delegatedSharesAfter, + delegatedSharesBefore[strategies[i]] - totalSharesDecreasedForStrategy[strategies[i]], + "delegated shares did not decrement correctly" + ); + + _assertWithdrawableAndOperatorShares( + withdrawableShares[i], + delegatedSharesAfter, + "withdrawable and operator shares not decremented correctly" + ); + } + } /** * @notice Test burning shares for an operator with no queued withdrawals * - Asserts slashable shares before and after in queue is 0 * - Asserts operator shares are decreased by half */ - function testFuzz_burnOperatorShares_NoQueuedWithdrawals(Randomness r) public { + function testFuzz_burnOperatorShares_NoQueuedWithdrawals( + Randomness r + ) public rand(r) { address operator = r.Address(); address staker = r.Address(); uint64 initMagnitude = WAD; @@ -5430,11 +6034,15 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests uint256 sharesToBurn = shares/2; // Burn shares + _burnOperatorShares_expectEmit( + BurnOperatorSharesEmitStruct({ + operator: operator, + strategy: strategyMock, + sharesToDecrease: sharesToBurn, + sharesToBurn: sharesToBurn + }) + ); cheats.prank(address(allocationManagerMock)); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased(operator, address(0), strategyMock, sharesToBurn); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesBurned(operator, strategyMock, sharesToBurn); delegationManager.burnOperatorShares({ operator: operator, strategy: strategyMock, @@ -5457,7 +6065,9 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests * - Asserts operator shares are decreased by half after burning * - Asserts that the slashable shares in queue before/after burning are 0 */ - function testFuzz_burnOperatorShares_NoQueuedWithdrawalsInWindow(Randomness r) public { + function testFuzz_burnOperatorShares_NoQueuedWithdrawalsInWindow( + Randomness r + ) public rand(r) { // 1. Randomize operator and staker info // Operator info address operator = r.Address(); @@ -5472,15 +6082,8 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 2. Register the operator, set the staker deposits, and delegate the 2 stakers to them _registerOperatorWithBaseDetails(operator); - { - // Set the first staker deposits in the strategies - IStrategy[] memory strategyArray = strategyMock.toArray(); - uint256[] memory sharesArray = shares.toArrayU256(); - uint256[] memory depositArray = depositAmount.toArrayU256(); - strategyManagerMock.setDeposits(staker1, strategyArray, sharesArray); - // Set the second staker's deposits in the strategies - strategyManagerMock.setDeposits(staker2, strategyArray, depositArray); - } + strategyManagerMock.addDeposit(staker1, strategyMock, shares); + strategyManagerMock.addDeposit(staker2, strategyMock, depositAmount); _delegateToOperatorWhoAcceptsAllStakers(staker1, operator); _delegateToOperatorWhoAcceptsAllStakers(staker2, operator); @@ -5516,11 +6119,15 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 4. Burn shares _setOperatorMagnitude(operator, strategyMock, newMagnitude); + _burnOperatorShares_expectEmit( + BurnOperatorSharesEmitStruct({ + operator: operator, + strategy: strategyMock, + sharesToDecrease: sharesToBurn, + sharesToBurn: sharesToBurn + }) + ); cheats.prank(address(allocationManagerMock)); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased(operator, address(0), strategyMock, sharesToBurn); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesBurned(operator, strategyMock, sharesToBurn); delegationManager.burnOperatorShares({ operator: operator, strategy: strategyMock, @@ -5540,7 +6147,7 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests * @notice Test burning shares for an operator with slashable queued withdrawals in past MIN_WITHDRAWAL_DELAY_BLOCKS window. * There exists a single withdrawal that is slashable. */ - function testFuzz_burnOperatorShares_SingleSlashableWithdrawal(Randomness r) public { + function testFuzz_burnOperatorShares_SingleSlashableWithdrawal(Randomness r) public rand(r) { // 1. Randomize operator and staker info // Operator info address operator = r.Address(); @@ -5555,15 +6162,8 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 2. Register the operator, set the staker deposits, and delegate the 2 stakers to them _registerOperatorWithBaseDetails(operator); - { - // Set the first staker deposits in the strategies - IStrategy[] memory strategyArray = strategyMock.toArray(); - uint256[] memory sharesArray = shares.toArrayU256(); - uint256[] memory depositArray = depositAmount.toArrayU256(); - strategyManagerMock.setDeposits(staker1, strategyArray, sharesArray); - // Set the second staker's deposits in the strategies - strategyManagerMock.setDeposits(staker2, strategyArray, depositArray); - } + strategyManagerMock.addDeposit(staker1, strategyMock, shares); + strategyManagerMock.addDeposit(staker2, strategyMock, depositAmount); _delegateToOperatorWhoAcceptsAllStakers(staker1, operator); _delegateToOperatorWhoAcceptsAllStakers(staker2, operator); @@ -5598,11 +6198,15 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 4. Burn shares _setOperatorMagnitude(operator, strategyMock, newMagnitude); + _burnOperatorShares_expectEmit( + BurnOperatorSharesEmitStruct({ + operator: operator, + strategy: strategyMock, + sharesToDecrease: sharesToDecrease, + sharesToBurn: sharesToBurn + }) + ); cheats.prank(address(allocationManagerMock)); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased(operator, address(0), strategyMock, sharesToDecrease); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesBurned(operator, strategyMock, sharesToBurn); delegationManager.burnOperatorShares({ operator: operator, strategy: strategyMock, @@ -5622,7 +6226,9 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests * @notice Test burning shares for an operator with slashable queued withdrawals in past MIN_WITHDRAWAL_DELAY_BLOCKS window. * There exists multiple withdrawals that are slashable. */ - function testFuzz_burnOperatorShares_MultipleSlashableWithdrawals(Randomness r) public { + function testFuzz_burnOperatorShares_MultipleSlashableWithdrawals( + Randomness r + ) public rand(r) { // 1. Randomize operator and staker info // Operator info address operator = r.Address(); @@ -5635,12 +6241,7 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 2. Register the operator, set the staker deposits, and delegate the 2 stakers to them _registerOperatorWithBaseDetails(operator); - { - // Set the first staker deposits in the strategies - IStrategy[] memory strategyArray = strategyMock.toArray(); - uint256[] memory sharesArray = depositAmount.toArrayU256(); - strategyManagerMock.setDeposits(staker, strategyArray, sharesArray); - } + strategyManagerMock.addDeposit(staker, strategyMock, depositAmount); _delegateToOperatorWhoAcceptsAllStakers(staker, operator); // 3. Queue withdrawal for staker and roll blocks forward so that the withdrawal is not slashable @@ -5690,11 +6291,15 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 4. Burn shares _setOperatorMagnitude(operator, strategyMock, newMagnitude); + _burnOperatorShares_expectEmit( + BurnOperatorSharesEmitStruct({ + operator: operator, + strategy: strategyMock, + sharesToDecrease: sharesToDecrease, + sharesToBurn: sharesToBurn + }) + ); cheats.prank(address(allocationManagerMock)); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased(operator, address(0), strategyMock, sharesToDecrease); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesBurned(operator, strategyMock, sharesToBurn); delegationManager.burnOperatorShares({ operator: operator, strategy: strategyMock, @@ -5723,7 +6328,9 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests * slashed amount for staker 1 should be 75% and staker 2 should be 50% where the total * slashed amount is the sum of both */ - function testFuzz_burnOperatorShares_MultipleWithdrawalsMultipleSlashings(Randomness r) public { + function testFuzz_burnOperatorShares_MultipleWithdrawalsMultipleSlashings( + Randomness r + ) public rand(r) { address operator = r.Address(); address staker = r.Address(); uint256 depositAmount = r.Uint256(3, MAX_STRATEGY_SHARES); @@ -5734,12 +6341,7 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 2. Register the operator, set the staker deposits, and delegate the 2 stakers to them _registerOperatorWithBaseDetails(operator); - { - // Set the first staker deposits in the strategies - IStrategy[] memory strategyArray = strategyMock.toArray(); - uint256[] memory depositArray = depositAmount.toArrayU256(); - strategyManagerMock.setDeposits(staker, strategyArray, depositArray); - } + strategyManagerMock.addDeposit(staker, strategyMock, depositAmount); _delegateToOperatorWhoAcceptsAllStakers(staker, operator); // 3. Queue withdrawal for staker and slash operator for 50% @@ -5764,11 +6366,15 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 3.2 Burn shares _setOperatorMagnitude(operator, strategyMock, newMagnitude); + _burnOperatorShares_expectEmit( + BurnOperatorSharesEmitStruct({ + operator: operator, + strategy: strategyMock, + sharesToDecrease: sharesToDecrease, + sharesToBurn: sharesToBurn + }) + ); cheats.prank(address(allocationManagerMock)); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased(operator, address(0), strategyMock, sharesToDecrease); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesBurned(operator, strategyMock, sharesToBurn); delegationManager.burnOperatorShares({ operator: operator, strategy: strategyMock, @@ -5820,11 +6426,15 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests // 4.2 Burn shares _setOperatorMagnitude(operator, strategyMock, newMagnitude); + _burnOperatorShares_expectEmit( + BurnOperatorSharesEmitStruct({ + operator: operator, + strategy: strategyMock, + sharesToDecrease: sharesToDecrease, + sharesToBurn: sharesToBurn + }) + ); cheats.prank(address(allocationManagerMock)); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased(operator, address(0), strategyMock, sharesToDecrease); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesBurned(operator, strategyMock, sharesToBurn); delegationManager.burnOperatorShares({ operator: operator, strategy: strategyMock, @@ -5849,106 +6459,422 @@ contract DelegationManagerUnitTests_burningShares is DelegationManagerUnitTests "operator shares should be decreased by sharesToBurn" ); } - } /** - * @notice Ensure that when a withdrawal is completable then there are no slashable shares in the queue. - * However if the withdrawal is not completable and the withdrawal delay hasn't elapsed, then the withdrawal - * should be counted as slashable. + * @notice Ensure that when a withdrawal is completable then there are no slashable shares in the queue. + * However if the withdrawal is not completable and the withdrawal delay hasn't elapsed, then the withdrawal + * should be counted as slashable. + */ + function testFuzz_burnOperatorShares_Timings(Randomness r) public rand(r) { + // 1. Randomize operator and staker info + // Operator info + address operator = r.Address(); + uint64 newMagnitude = 25e16; + // staker + address staker = r.Address(); + uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); + + // 2. Register the operator, set the staker deposits, and delegate the staker to them + _registerOperatorWithBaseDetails(operator); + strategyManagerMock.addDeposit(staker, strategyMock, depositAmount); + _delegateToOperatorWhoAcceptsAllStakers(staker, operator); + + // 3. Queue withdrawal for staker and roll blocks forward so that the withdrawal is completable + uint256 completableBlock; + { + ( + QueuedWithdrawalParams[] memory queuedWithdrawalParams, + Withdrawal memory withdrawal, + ) = _setUpQueueWithdrawalsSingleStrat({ + staker: staker, + withdrawer: staker, + strategy: strategyMock, + depositSharesToWithdraw: depositAmount + }); + cheats.startPrank(staker); + delegationManager.queueWithdrawals(queuedWithdrawalParams); + // 3.1 after queuing the withdrawal, check that there are slashable shares in queue + assertEq( + delegationManager.getSlashableSharesInQueue(operator, strategyMock), + depositAmount, + "there should be depositAmount slashable shares in queue" + ); + // Check slashable shares in queue before and when the withdrawal is completable + completableBlock = withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS(); + IERC20[] memory tokenArray = strategyMock.underlyingToken().toArray(); + + // 3.2 roll to right before withdrawal is completable, check that slashable shares are still there + // attempting to complete a withdrawal should revert + cheats.roll(completableBlock - 1); + cheats.expectRevert(WithdrawalDelayNotElapsed.selector); + delegationManager.completeQueuedWithdrawal(withdrawal, tokenArray, true); + assertEq( + delegationManager.getSlashableSharesInQueue(operator, strategyMock), + depositAmount, + "there should still be depositAmount slashable shares in queue" + ); + + // 3.3 roll to blocknumber that the withdrawal is completable, there should be no slashable shares in queue + cheats.roll(completableBlock); + delegationManager.completeQueuedWithdrawal(withdrawal, tokenArray, true); + assertEq( + delegationManager.getSlashableSharesInQueue(operator, strategyMock), + 0, + "there should be no slashable shares in queue when the withdrawal is completable" + ); + + cheats.stopPrank(); + + } + + uint256 operatorSharesBefore = delegationManager.operatorShares(operator, strategyMock); + + // 4. Burn 0 shares when new magnitude is set + _setOperatorMagnitude(operator, strategyMock, newMagnitude); + _burnOperatorShares_expectEmit( + BurnOperatorSharesEmitStruct({ + operator: operator, + strategy: strategyMock, + sharesToDecrease: 0, + sharesToBurn: 0 + }) + ); + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares({ + operator: operator, + strategy: strategyMock, + prevMaxMagnitude: WAD, + newMaxMagnitude: newMagnitude + }); + + // 5. Assert expected values + uint256 operatorSharesAfter = delegationManager.operatorShares(operator, strategyMock); + assertEq( + delegationManager.getSlashableSharesInQueue(operator, strategyMock), + 0, + "there should still be no slashable shares in queue after burning 0 shares" + ); + assertEq(operatorSharesAfter, operatorSharesBefore, "operator shares should be unchanged and equal to 0"); + assertEq(operatorSharesBefore, 0, "operator shares should be unchanged and equal to 0"); + } + + /** + * @notice Ensure that no burning takes place for the beaconChainETHStrategy when the operator is slashed + * and there are no slashable shares in the queue. Note: this will be implemented in a future release with + * consideration of the Pectra upgrade. + */ + function testFuzz_burnOperatorShares_BeaconChainStrategy(Randomness r) public rand(r) { + // 1. Randomize operator and staker info + // Operator info + address operator = r.Address(); + uint64 newMagnitude = 25e16; + // First staker + address staker1 = r.Address(); + uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); + // Second Staker, will queue withdraw shares + address staker2 = r.Address(); + uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); + uint256 withdrawAmount = r.Uint256(1, depositAmount); + + // 2. Register the operator, set the staker deposits, and delegate the 2 stakers to them + _registerOperatorWithBaseDetails(operator); + eigenPodManagerMock.setPodOwnerShares(staker1, int256(shares)); + eigenPodManagerMock.setPodOwnerShares(staker2, int256(depositAmount)); + _delegateToOperatorWhoAcceptsAllStakers(staker1, operator); + _delegateToOperatorWhoAcceptsAllStakers(staker2, operator); + + // 3. Queue withdrawal for staker2 so that the withdrawal is slashable + { + ( + QueuedWithdrawalParams[] memory queuedWithdrawalParams,, + ) = _setUpQueueWithdrawalsSingleStrat({ + staker: staker2, + withdrawer: staker2, + strategy: beaconChainETHStrategy, + depositSharesToWithdraw: withdrawAmount + }); + cheats.prank(staker2); + delegationManager.queueWithdrawals(queuedWithdrawalParams); + assertEq( + delegationManager.getSlashableSharesInQueue(operator, beaconChainETHStrategy), + 0, + "there should be 0 withdrawAmount slashable shares in queue since this is beaconChainETHStrategy" + ); + } + + uint256 operatorSharesBefore = delegationManager.operatorShares(operator, beaconChainETHStrategy); + uint256 queuedSlashableSharesBefore = delegationManager.getSlashableSharesInQueue(operator, beaconChainETHStrategy); + + // calculate burned shares, should be 3/4 of the original shares + // staker2 queue withdraws shares + // Therefore amount of shares to burn should be what the staker still has remaining + staker1 shares and then + // divided by 2 since the operator was slashed 50% + uint256 sharesToDecrease = (shares + depositAmount - withdrawAmount) * 3 / 4; + + // 4. Burn shares + _setOperatorMagnitude(operator, beaconChainETHStrategy, newMagnitude); + cheats.expectEmit(true, true, true, true, address(delegationManager)); + emit OperatorSharesDecreased(operator, address(0), beaconChainETHStrategy, sharesToDecrease); + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares({ + operator: operator, + strategy: beaconChainETHStrategy, + prevMaxMagnitude: WAD, + newMaxMagnitude: newMagnitude + }); + + // 5. Assert expected values + uint256 queuedSlashableSharesAfter = delegationManager.getSlashableSharesInQueue(operator, beaconChainETHStrategy); + uint256 operatorSharesAfter = delegationManager.operatorShares(operator, beaconChainETHStrategy); + assertEq(queuedSlashableSharesBefore, 0, "Slashable shares in queue should be 0 for beaconChainStrategy"); + assertEq(queuedSlashableSharesAfter, 0, "Slashable shares in queue should be 0 for beaconChainStrategy"); + assertEq(operatorSharesAfter, operatorSharesBefore - sharesToDecrease, "operator shares should be decreased by sharesToDecrease"); + } + + /** + * @notice This test demonstrates that the rate that withdrawable shares decrease from slashing is at LEAST + * greater than or equal to the rate that the operator shares decrease from slashing. + * We want this property otherwise undelegating/queue withdrawing all shares as a staker could lead to a underflow revert. + * Note: If the SlashingLib.calcSlashedAmount function were to round down (overslash) then this test would fail. + */ + function test_burnOperatorShares_slashedRepeatedly() public { + uint64 initialMagnitude = 90009; + uint256 shares = 40000000004182209037560531097078597505; + + // register *this contract* as an operator + _registerOperatorWithBaseDetails(defaultOperator); + _setOperatorMagnitude(defaultOperator, strategyMock, initialMagnitude); + + // Set the staker deposits in the strategies + IStrategy[] memory strategies = new IStrategy[](1); + strategies[0] = strategyMock; + strategyManagerMock.addDeposit(defaultStaker, strategyMock, shares); + + // delegate from the `defaultStaker` to the operator + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + + // Set operator magnitude + uint64 newOperatorMagnitude = initialMagnitude; + + for (uint256 i = 0; i < 100; ++i) { + uint64 slashMagnitude = 100; + newOperatorMagnitude -= slashMagnitude; + _setOperatorMagnitude(defaultOperator, strategyMock, newOperatorMagnitude); + + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares( + defaultOperator, + strategyMock, + newOperatorMagnitude + slashMagnitude, + newOperatorMagnitude + ); + + uint256 operatorSharesAfterSlash = delegationManager.operatorShares(defaultOperator, strategyMock); + ( + uint256[] memory withdrawableShares, + uint256[] memory depositShares + ) = delegationManager.getWithdrawableShares(defaultStaker, strategies); + assertEq(depositShares[0], shares, "staker deposit shares not reset correctly"); + assertLe( + withdrawableShares[0], + operatorSharesAfterSlash, + "withdrawable should always be <= operatorShares even after rounding" + ); + } + } + + /** + * @notice This unit test will slash a staker's beaconChainETH strategy shares from both on EigenLayer + * and also on the beaconChain. This test ensures that the order of slashing does not matter and nets + * the same withdrawableShares for the staker whether slashing occurred on the beaconChain, or on EigenLayer first. */ - function testFuzz_burnOperatorShares_Timings(Randomness r) public { - // 1. Randomize operator and staker info - // Operator info - address operator = r.Address(); - uint64 newMagnitude = 25e16; - // staker - address staker = r.Address(); - uint256 depositAmount = r.Uint256(1, MAX_STRATEGY_SHARES); + function testFuzz_beaconSlashAndAVSSlash(Randomness r) public rand(r) { + uint64 initialMagnitude = r.Uint64(2, WAD); + uint64 newMaxMagnitude = r.Uint64(1, initialMagnitude); + // note: beaconShares only goes negative when performing withdrawal -- and this will change post-migration + // so it's ok to make this assumption of positive shares + int256 beaconShares = int256(r.Uint256(2, MAX_ETH_SUPPLY)); + uint256 sharesDecrease = r.Uint256(1, uint256(beaconShares) - 1); + + //////////////////////////// + // 0. setup operator and staker with Beacon Chain stake + //////////////////////////// + _registerOperatorWithBaseDetails(defaultOperator); + _setOperatorMagnitude(defaultOperator, beaconChainETHStrategy, initialMagnitude); + eigenPodManagerMock.setPodOwnerShares(defaultStaker, beaconShares); + // delegate staker to operator with expected events emitted + _delegateTo_expectEmit( + DelegateToEmitStruct({ + staker: defaultStaker, + operator: defaultOperator, + strategies: beaconChainETHStrategy.toArray(), + depositShares: uint256(beaconShares).toArrayU256(), + depositScalingFactors: uint256(WAD).divWad(initialMagnitude).toArrayU256() + }) + ); + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) + }); - // 2. Register the operator, set the staker deposits, and delegate the staker to them - _registerOperatorWithBaseDetails(operator); - { - // Set the first staker deposits in the strategies - IStrategy[] memory strategyArray = strategyMock.toArray(); - uint256[] memory depositArray = depositAmount.toArrayU256(); - strategyManagerMock.setDeposits(staker, strategyArray, depositArray); - } - _delegateToOperatorWhoAcceptsAllStakers(staker, operator); + uint256[] memory withdrawableShares; + uint64 newBeaconSlashingFactor; + // withdrawable shares after both slashing, this will be checked with the other scenario when + // slashing in reverse order + uint256 sharesAfterAllSlashing; - // 3. Queue withdrawal for staker and roll blocks forward so that the withdrawal is completable - uint256 completableBlock; + //////////////////////////// + // 1. do beacon chain slash then AVS slash + //////////////////////////// { - ( - QueuedWithdrawalParams[] memory queuedWithdrawalParams, - Withdrawal memory withdrawal, - ) = _setUpQueueWithdrawalsSingleStrat({ - staker: staker, - withdrawer: staker, - strategy: strategyMock, - depositSharesToWithdraw: depositAmount - }); - cheats.startPrank(staker); - delegationManager.queueWithdrawals(queuedWithdrawalParams); - // 3.1 after queuing the withdrawal, check that there are slashable shares in queue - assertEq( - delegationManager.getSlashableSharesInQueue(operator, strategyMock), - depositAmount, - "there should be depositAmount slashable shares in queue" - ); - // Check slashable shares in queue before and when the withdrawal is completable - completableBlock = withdrawal.startBlock + delegationManager.MIN_WITHDRAWAL_DELAY_BLOCKS(); - IERC20[] memory tokenArray = strategyMock.underlyingToken().toArray(); + // Slash beaconChain first + { + (withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); + uint256 beaconSharesBeforeSlash = withdrawableShares[0]; + + uint64 prevBeaconChainSlashingFactor; + (prevBeaconChainSlashingFactor,,newBeaconSlashingFactor) = _decreaseBeaconChainShares( + defaultStaker, + beaconShares, + sharesDecrease + ); - // 3.2 roll to right before withdrawal is completable, check that slashable shares are still there - // attempting to complete a withdrawal should revert - cheats.roll(completableBlock - 1); - cheats.expectRevert(WithdrawalDelayNotElapsed.selector); - delegationManager.completeQueuedWithdrawal(withdrawal, tokenArray, true); - assertEq( - delegationManager.getSlashableSharesInQueue(operator, strategyMock), - depositAmount, - "there should still be depositAmount slashable shares in queue" - ); + uint256 expectedWithdrawableShares = _calcWithdrawableShares({ + depositShares: uint256(beaconShares), + depositScalingFactor: uint256(WAD).divWad(initialMagnitude), + slashingFactor: initialMagnitude.mulWad(newBeaconSlashingFactor) + }); + _assertSharesAfterBeaconSlash({ + staker: defaultStaker, + withdrawableSharesBefore: beaconSharesBeforeSlash, + expectedWithdrawableShares: expectedWithdrawableShares, + prevBeaconChainScalingFactor: prevBeaconChainSlashingFactor + }); + } + // Slash on EigenLayer second + { + (withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); + uint256 beaconSharesBeforeSlash = withdrawableShares[0]; - // 3.3 roll to blocknumber that the withdrawal is completable, there should be no slashable shares in queue - cheats.roll(completableBlock); - delegationManager.completeQueuedWithdrawal(withdrawal, tokenArray, true); - assertEq( - delegationManager.getSlashableSharesInQueue(operator, strategyMock), - 0, - "there should be no slashable shares in queue when the withdrawal is completable" - ); + // do a slash via an AVS + _setOperatorMagnitude(defaultOperator, beaconChainETHStrategy, newMaxMagnitude); + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares(defaultOperator, beaconChainETHStrategy, initialMagnitude, newMaxMagnitude); + + // save the outcome + (withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); + uint256 beaconSharesAfterSecondSlash = withdrawableShares[0]; + uint256 expectedWithdrawable = _calcWithdrawableShares( + uint256(beaconShares), + uint256(WAD).divWad(initialMagnitude), + _getSlashingFactor(defaultStaker, beaconChainETHStrategy, newMaxMagnitude) + ); - cheats.stopPrank(); + _assertSharesAfterSlash({ + staker: defaultStaker, + strategy: beaconChainETHStrategy, + withdrawableSharesBefore: beaconSharesBeforeSlash, + expectedWithdrawableShares: expectedWithdrawable, + prevMaxMagnitude: initialMagnitude, + currMaxMagnitude: newMaxMagnitude + }); + sharesAfterAllSlashing = beaconSharesAfterSecondSlash; + } } - uint256 operatorSharesBefore = delegationManager.operatorShares(operator, strategyMock); + //////////////////////////// + // 2. do AVS slash then beacon chain slash + //////////////////////////// - // 4. Burn 0 shares when new magnitude is set - _setOperatorMagnitude(operator, strategyMock, newMagnitude); - cheats.prank(address(allocationManagerMock)); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesDecreased(operator, address(0), strategyMock, 0); - cheats.expectEmit(true, true, true, true, address(delegationManager)); - emit OperatorSharesBurned(operator, strategyMock, 0); - delegationManager.burnOperatorShares({ - operator: operator, - strategy: strategyMock, - prevMaxMagnitude: WAD, - newMaxMagnitude: newMagnitude + // restore the staker and operator to their original state + // Reset operator's magnitude, beaconChainScalingFactor + delegationManager.undelegate(defaultStaker); + _registerOperatorWithBaseDetails(defaultOperator2); + _setOperatorMagnitude(defaultOperator2, beaconChainETHStrategy, initialMagnitude); + eigenPodManagerMock.setPodOwnerShares(defaultStaker, beaconShares); + eigenPodManagerMock.setBeaconChainSlashingFactor(defaultStaker, WAD); + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator2); + _assertDeposit({ + staker: defaultStaker, + operator: defaultOperator2, + strategy: beaconChainETHStrategy, + operatorSharesBefore: 0, + withdrawableSharesBefore: 0, + depositSharesBefore: 0, + prevDsf: WAD, + depositAmount: uint256(beaconShares) }); - // 5. Assert expected values - uint256 operatorSharesAfter = delegationManager.operatorShares(operator, strategyMock); + { + // Slash on EigenLayer first + { + (withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); + uint256 beaconSharesBeforeSlash = withdrawableShares[0]; + + _setOperatorMagnitude(defaultOperator2, beaconChainETHStrategy, newMaxMagnitude); + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares(defaultOperator2, beaconChainETHStrategy, initialMagnitude, newMaxMagnitude); + + uint256 expectedWithdrawable = _calcWithdrawableShares( + uint256(beaconShares), + uint256(WAD).divWad(initialMagnitude), + _getSlashingFactor(defaultStaker, beaconChainETHStrategy, newMaxMagnitude) + ); + + _assertSharesAfterSlash({ + staker: defaultStaker, + strategy: beaconChainETHStrategy, + withdrawableSharesBefore: beaconSharesBeforeSlash, + expectedWithdrawableShares: expectedWithdrawable, + prevMaxMagnitude: initialMagnitude, + currMaxMagnitude: newMaxMagnitude + }); + } + + // Slash beaconChain second + { + (withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); + uint256 beaconSharesBeforeSlash = withdrawableShares[0]; + + uint64 prevBeaconChainSlashingFactor; + (prevBeaconChainSlashingFactor,,newBeaconSlashingFactor) = _decreaseBeaconChainShares( + defaultStaker, + beaconShares, + sharesDecrease + ); + + uint256 expectedWithdrawableShares = _calcWithdrawableShares({ + depositShares: uint256(beaconShares), + depositScalingFactor: uint256(WAD).divWad(initialMagnitude), + slashingFactor: newMaxMagnitude.mulWad(newBeaconSlashingFactor) + }); + _assertSharesAfterBeaconSlash({ + staker: defaultStaker, + withdrawableSharesBefore: beaconSharesBeforeSlash, + expectedWithdrawableShares: expectedWithdrawableShares, + prevBeaconChainScalingFactor: prevBeaconChainSlashingFactor + }); + } + } + + //////////////////////////// + // 3. Confirm withdrawable shares are the same regardless of order of operations in Test 1 or Test 2 + //////////////////////////// + (withdrawableShares,) = delegationManager.getWithdrawableShares(defaultStaker, beaconChainETHStrategy.toArray()); assertEq( - delegationManager.getSlashableSharesInQueue(operator, strategyMock), - 0, - "there should still be no slashable shares in queue after burning 0 shares" + withdrawableShares[0], + sharesAfterAllSlashing, + "shares after all slashing should be the same" ); - assertEq(operatorSharesAfter, operatorSharesBefore, "operator shares should be unchanged and equal to 0"); - assertEq(operatorSharesBefore, 0, "operator shares should be unchanged and equal to 0"); } } @@ -5964,11 +6890,10 @@ contract DelegationManagerUnitTests_SharesUnderflowChecks is DelegationManagerUn */ /// forge-config: default.fuzz.runs = 50 function testFuzz_slashDepositRepeatedly( - uint64 initMagnitude, - uint256 shares - ) public { - initMagnitude = uint64(bound(initMagnitude, 10000, WAD)); - shares = bound(shares, 1, MAX_STRATEGY_SHARES); + Randomness r + ) public rand(r) { + uint64 initMagnitude = r.Uint64(10000, WAD); + uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); cheats.assume(initMagnitude % 2 != 0); cheats.assume(shares % 2 != 0); @@ -6049,13 +6974,11 @@ contract DelegationManagerUnitTests_SharesUnderflowChecks is DelegationManagerUn */ /// forge-config: default.fuzz.runs = 50 function testFuzz_slashDepositRepeatedly_randDeposits( - uint64 initMagnitude, - uint256 shares, - uint256 depositAmount - ) public { - initMagnitude = uint64(bound(initMagnitude, 10000, WAD)); - depositAmount = bound(depositAmount, 1, 1e34); - shares = bound(shares, 1, MAX_STRATEGY_SHARES / 1e4); + Randomness r + ) public rand(r) { + uint64 initMagnitude = r.Uint64(10000, WAD); + uint256 depositAmount = r.Uint256(1, 1e34); + uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES / 1e4); cheats.assume(initMagnitude % 2 != 0); cheats.assume(shares % 2 != 0); @@ -6137,11 +7060,10 @@ contract DelegationManagerUnitTests_SharesUnderflowChecks is DelegationManagerUn */ /// forge-config: default.fuzz.runs = 50 function testFuzz_depositMultipleStakers_slash_repeatedly( - uint64 initMagnitude, - uint256 shares - ) public { - initMagnitude = uint64(bound(initMagnitude, 50000, WAD)); - shares = bound(shares, MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); + Randomness r + ) public rand(r) { + uint64 initMagnitude = r.Uint64(50000, WAD); + uint256 shares = r.Uint256(MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); cheats.assume(initMagnitude % 2 != 0); cheats.assume(shares % 2 != 0); @@ -6215,11 +7137,10 @@ contract DelegationManagerUnitTests_SharesUnderflowChecks is DelegationManagerUn */ /// forge-config: default.fuzz.runs = 50 function testFuzz_depositMultipleStakers_slashLargeMagnitudes( - uint64 initMagnitude, - uint256 shares - ) public { - initMagnitude = uint64(bound(initMagnitude, 50000, WAD)); - shares = bound(shares, MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); + Randomness r + ) public rand(r) { + uint64 initMagnitude = r.Uint64(50000, WAD); + uint256 shares = r.Uint256(MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); cheats.assume(initMagnitude % 2 != 0); cheats.assume(shares % 2 != 0); @@ -6292,11 +7213,10 @@ contract DelegationManagerUnitTests_SharesUnderflowChecks is DelegationManagerUn */ /// forge-config: default.fuzz.runs = 50 function testFuzz_depositMultipleStakers_slashSmallMagnitudes( - uint64 initMagnitude, - uint256 shares - ) public { - initMagnitude = uint64(bound(initMagnitude, 1000, WAD)); - shares = bound(shares, MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); + Randomness r + ) public rand(r) { + uint64 initMagnitude = r.Uint64(1000, WAD); + uint256 shares = r.Uint256(MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); cheats.assume(initMagnitude % 2 != 0); cheats.assume(shares % 2 != 0); @@ -6369,11 +7289,10 @@ contract DelegationManagerUnitTests_SharesUnderflowChecks is DelegationManagerUn */ /// forge-config: default.fuzz.runs = 50 function testFuzz_depositMultipleStakersOnce_slashSmallMagnitudes( - uint64 initMagnitude, - uint256 shares - ) public { - initMagnitude = uint64(bound(initMagnitude, 1000, WAD)); - shares = bound(shares, MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); + Randomness r + ) public rand(r) { + uint64 initMagnitude = r.Uint64(1000, WAD); + uint256 shares = r.Uint256(MAX_STRATEGY_SHARES / 1e7, MAX_STRATEGY_SHARES / 1e4); cheats.assume(initMagnitude % 2 != 0); cheats.assume(shares % 2 != 0); @@ -6514,4 +7433,123 @@ contract DelegationManagerUnitTests_Lifecycle is DelegationManagerUnitTests { assertEq(delegationManager.cumulativeWithdrawalsQueued(staker), 1, "staker nonce should have incremented"); assertEq(delegationManager.operatorShares(operator, strategies[0]), 100 ether, "operator shares should be 0 after withdrawal"); } -} \ No newline at end of file + + /** + * @notice While delegated to an operator who becomes 100% slashed. When the staker undelegates and queues a withdrawal + * for all their shares which are now 0, the withdrawal should be completed with 0 shares even if they delegate to a new operator + * who has not been slashed. + * Note: This specifically tests that the completeQueuedWithdrawal is looking up the correct maxMagnitude for the operator + */ + function testFuzz_undelegate_slashOperator100Percent_delegate_complete( + Randomness r + ) public rand(r) { + uint256 shares = r.Uint256(1, MAX_STRATEGY_SHARES); + address newOperator = r.Address(); + IStrategy[] memory strategyArray = r.StrategyArray(1); + IStrategy strategy = strategyArray[0]; + + // register *this contract* as an operator + _registerOperatorWithBaseDetails(defaultOperator); + + // Set the staker deposits in the strategies + strategyManagerMock.addDeposit(defaultStaker, strategy, shares); + + // delegate from the `defaultStaker` to the operator + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, defaultOperator); + + // Set operator magnitude + uint64 operatorMagnitude = 0; + uint256 operatorSharesAfterSlash; + { + _setOperatorMagnitude(defaultOperator, strategy, operatorMagnitude); + cheats.prank(address(allocationManagerMock)); + delegationManager.burnOperatorShares(defaultOperator, strategy, WAD, 0); + operatorSharesAfterSlash = delegationManager.operatorShares(defaultOperator, strategy); + assertEq(operatorSharesAfterSlash, 0, "operator shares not fully slashed"); + } + + ( + , + IDelegationManagerTypes.Withdrawal memory withdrawal, + bytes32 withdrawalRoot + ) = _setUpQueueWithdrawalsSingleStrat({ + staker: defaultStaker, + withdrawer: defaultStaker, + strategy: strategy, + depositSharesToWithdraw: shares + }); + + uint256 depositScalingFactor = delegationManager.depositScalingFactor(defaultStaker, strategy); + assertEq(depositScalingFactor, WAD, "bad test setup"); + // Get withdrawable and deposit shares + { + ( + uint256[] memory withdrawableSharesBefore, + uint256[] memory depositSharesBefore + ) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); + assertEq( + withdrawableSharesBefore[0], + 0, + "withdrawable shares should be 0 after being slashed fully" + ); + assertEq( + depositSharesBefore[0], + shares, + "deposit shares should be unchanged after being slashed fully" + ); + } + + // Undelegate the staker + _undelegate_expectEmit_singleStrat( + UndelegateEmitStruct({ + staker: defaultStaker, + operator: defaultOperator, + strategy: strategy, + depositSharesQueued: shares, + operatorSharesDecreased: 0, + withdrawal: withdrawal, + withdrawalRoot: withdrawalRoot, + depositScalingFactor: WAD, + forceUndelegated: false + }) + ); + cheats.prank(defaultStaker); + delegationManager.undelegate(defaultStaker); + + // Checks - delegation status + assertEq( + delegationManager.delegatedTo(defaultStaker), + address(0), + "undelegated staker should be delegated to zero address" + ); + assertFalse(delegationManager.isDelegated(defaultStaker), "staker not undelegated"); + + // Checks - operator & staker shares + assertEq(delegationManager.operatorShares(defaultOperator, strategy), 0, "operator shares not decreased correctly"); + ( + uint256[] memory stakerWithdrawableShares, + uint256[] memory depositShares + ) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); + assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); + assertEq(depositShares[0], 0, "staker deposit shares not reset correctly"); + + // delegate to the `newOperator` who has never been slashed + // Ensure that completing withdrawal now still results in 0 shares + _registerOperatorWithBaseDetails(newOperator); + _delegateToOperatorWhoAcceptsAllStakers(defaultStaker, newOperator); + + (stakerWithdrawableShares, depositShares) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); + assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); + assertEq(depositShares[0], 0, "staker deposit shares not reset correctly"); + + bool[] memory receiveAsTokens = new bool[](1); + receiveAsTokens[0] = false; + IERC20[][] memory tokens = new IERC20[][](1); + delegationManager.completeQueuedWithdrawals(tokens, receiveAsTokens, 1); + + (stakerWithdrawableShares, depositShares) = delegationManager.getWithdrawableShares(defaultStaker, strategyArray); + assertEq(stakerWithdrawableShares[0], 0, "staker withdrawable shares not calculated correctly"); + assertEq(depositShares[0], 0, "staker deposit shares not reset correctly"); + assertEq(delegationManager.operatorShares(newOperator, strategy), 0, "new operator shares should be unchanged"); + } +}