diff --git a/bindings/dao/protocol/proposals.go b/bindings/dao/protocol/proposals.go index f205e8e49..2acd57bd7 100644 --- a/bindings/dao/protocol/proposals.go +++ b/bindings/dao/protocol/proposals.go @@ -125,6 +125,32 @@ func ProposeSetAddress(rp *rocketpool.RocketPool, message, contractName, setting return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) } +// Estimate the gas of proposalSettingAddressList +func EstimateProposeSetAddressListGas(rp *rocketpool.RocketPool, message, contractName, settingPath string, value []common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingAddressList", contractName, settingPath, value) + if err != nil { + return rocketpool.GasInfo{}, fmt.Errorf("error encoding set address list setting proposal payload: %w", err) + } + return estimateProposalGas(rp, message, payload, blockNumber, treeNodes, opts) +} + +// Submit a proposal to update an address list Protocol DAO setting +func ProposeSetAddressList(rp *rocketpool.RocketPool, message, contractName, settingPath string, value []common.Address, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (uint64, common.Hash, error) { + rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) + if err != nil { + return 0, common.Hash{}, err + } + payload, err := rocketDAOProtocolProposals.ABI.Pack("proposalSettingAddressList", contractName, settingPath, value) + if err != nil { + return 0, common.Hash{}, fmt.Errorf("error encoding set address list setting proposal payload: %w", err) + } + return submitProposal(rp, message, payload, blockNumber, treeNodes, opts) +} + // Estimate the gas of ProposeSetRewardsPercentage func EstimateProposeSetRewardsPercentageGas(rp *rocketpool.RocketPool, message string, odaoPercentage *big.Int, pdaoPercentage *big.Int, nodePercentage *big.Int, blockNumber uint32, treeNodes []types.VotingTreeNode, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { rocketDAOProtocolProposals, err := getRocketDAOProtocolProposals(rp, nil) diff --git a/bindings/legacy/v1.3.1/node/staking.go b/bindings/legacy/v1.3.1/node/staking.go new file mode 100644 index 000000000..4f64ce98b --- /dev/null +++ b/bindings/legacy/v1.3.1/node/staking.go @@ -0,0 +1,295 @@ +package node + +import ( + "fmt" + "math/big" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/rocket-pool/smartnode/bindings/rocketpool" +) + +// Get the version of the Node Staking contract +func GetNodeStakingVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint8, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return 0, err + } + return rocketpool.GetContractVersion(rp, *rocketNodeStaking.Address, opts) +} + +// Get the total RPL staked in the network +func GetTotalRPLStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + totalRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalRplStake, "getTotalRPLStake"); err != nil { + return nil, fmt.Errorf("error getting total network RPL stake: %w", err) + } + return *totalRplStake, nil +} + +// Get a node's RPL stake +func GetNodeRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStake, "getNodeRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting total node RPL stake: %w", err) + } + return *nodeRplStake, nil +} + +// Get a node's effective RPL stake +func GetNodeEffectiveRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeEffectiveRplStakeWrapper := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEffectiveRplStakeWrapper, "getNodeEffectiveRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting effective node RPL stake: %w", err) + } + + minimumStake, err := GetNodeMinimumRPLStake(rp, nodeAddress, opts) + if err != nil { + return nil, fmt.Errorf("error getting minimum node RPL stake to verify effective stake: %w", err) + } + + nodeEffectiveRplStake := *nodeEffectiveRplStakeWrapper + if nodeEffectiveRplStake.Cmp(minimumStake) == -1 { + // Effective stake should be zero if it's less than the minimum RPL stake + return big.NewInt(0), nil + } + + return nodeEffectiveRplStake, nil +} + +// Get a node's minimum RPL stake to collateralize their minipools +func GetNodeMinimumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeMinimumRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMinimumRplStake, "getNodeMinimumRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting minimum node RPL stake: %w", err) + } + return *nodeMinimumRplStake, nil +} + +// Get a node's maximum RPL stake to collateralize their minipools +func GetNodeMaximumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeMaximumRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMaximumRplStake, "getNodeMaximumRPLStake", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting maximum node RPL stake: %w", err) + } + return *nodeMaximumRplStake, nil +} + +// Get the time a node last staked RPL +func GetNodeRPLStakedTime(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return 0, err + } + nodeRplStakedTime := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStakedTime, "getNodeRPLStakedTime", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node RPL staked time: %w", err) + } + return (*nodeRplStakedTime).Uint64(), nil +} + +// Get the amount of ETH the node has borrowed from the deposit pool to create its minipools +func GetNodeEthMatched(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeEthMatched := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEthMatched, "getNodeETHMatched", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched: %w", err) + } + return *nodeEthMatched, nil +} + +// Get the amount of ETH the node can borrow from the deposit pool to create its minipools +func GetNodeEthMatchedLimit(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeEthMatchedLimit := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEthMatchedLimit, "getNodeETHMatchedLimit", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched limit: %w", err) + } + return *nodeEthMatchedLimit, nil +} + +// Estimate the gas of Stake +func EstimateStakeGas(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "stakeRPL", rplAmount) +} + +// Stake RPL +func StakeRPL(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "stakeRPL", rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error staking RPL: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of Burn RPL +func EstimateBurnRpl(rp *rocketpool.RocketPool, from common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "burnRPL", from, rplAmount) +} + +// Burn RPL +func BurnRPL(rp *rocketpool.RocketPool, from common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "burnRPL", from, rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error burning RPL: %w", err) + } + return tx.Hash(), nil + +} + +// Estimate the gas of set RPL locking allowed +func EstimateSetRPLLockingAllowedGas(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "setRPLLockingAllowed", caller, allowed) +} + +// Set RPL locking allowed +func SetRPLLockingAllowed(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "setRPLLockingAllowed", caller, allowed) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting RPL locking allowed: %w", err) + } + return tx.Hash(), nil +} + +// Get RPL locking allowed state for a node +func GetRPLLockedAllowed(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (bool, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return false, err + } + value := new(bool) + if err := rocketNodeStaking.Call(opts, value, "getRPLLockingAllowed", nodeAddress); err != nil { + return false, fmt.Errorf("error getting node RPL locked: %w", err) + } + return *value, nil +} + +// Estimate the gas of set stake RPL for allowed +func EstimateSetStakeRPLForAllowedGas(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "setStakeRPLForAllowed", caller, allowed) +} + +// Set stake RPL for allowed +func SetStakeRPLForAllowed(rp *rocketpool.RocketPool, caller common.Address, allowed bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "setStakeRPLForAllowed", caller, allowed) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting stake RPL for allowed: %w", err) + } + return tx.Hash(), nil +} + +// Estimate the gas of WithdrawRPL +func EstimateWithdrawRPLGas(rp *rocketpool.RocketPool, nodeAddress common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "withdrawRPL", nodeAddress, rplAmount) +} + +// Withdraw staked RPL +func WithdrawRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "withdrawRPL", nodeAddress, rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error withdrawing staked RPL: %w", err) + } + return tx.Hash(), nil +} + +// Calculate total effective RPL stake +func CalculateTotalEffectiveRPLStake(rp *rocketpool.RocketPool, offset, limit, rplPrice *big.Int, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + totalEffectiveRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalEffectiveRplStake, "calculateTotalEffectiveRPLStake", offset, limit, rplPrice); err != nil { + return nil, fmt.Errorf("error getting total effective RPL stake: %w", err) + } + return *totalEffectiveRplStake, nil +} + +// Get the amount of RPL locked as part of active PDAO proposals or challenges +func GetNodeRPLLocked(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := rocketNodeStaking.Call(opts, value, "getNodeRPLLocked", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node RPL locked: %w", err) + } + return *value, nil +} + +// Get contracts +var rocketNodeStakingLock sync.Mutex + +func getRocketNodeStaking(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*rocketpool.Contract, error) { + rocketNodeStakingLock.Lock() + defer rocketNodeStakingLock.Unlock() + return rp.GetContract("rocketNodeStaking", opts) +} diff --git a/bindings/node/staking.go b/bindings/node/staking.go index 4c0f21f21..9b73e7e42 100644 --- a/bindings/node/staking.go +++ b/bindings/node/staking.go @@ -21,78 +21,105 @@ func GetNodeStakingVersion(rp *rocketpool.RocketPool, opts *bind.CallOpts) (uint } // Get the total RPL staked in the network -func GetTotalRPLStake(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { +func GetTotalStakedRPL(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } totalRplStake := new(*big.Int) - if err := rocketNodeStaking.Call(opts, totalRplStake, "getTotalRPLStake"); err != nil { + if err := rocketNodeStaking.Call(opts, totalRplStake, "getTotalStakedRPL"); err != nil { return nil, fmt.Errorf("error getting total network RPL stake: %w", err) } return *totalRplStake, nil } -// Get a node's RPL stake -func GetNodeRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { +// Get the total RPL staked in the network on megapools +func GetTotalMegapoolStakedRPL(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + totalRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalRplStake, "getTotalMegapoolStakedRPL"); err != nil { + return nil, fmt.Errorf("error getting total network megapool RPL stake: %w", err) + } + return *totalRplStake, nil +} + +// Get the total RPL staked in the network on megapools +func GetTotalLegacyStakedRPL(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + totalRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, totalRplStake, "getTotalLegacyStakedRPL"); err != nil { + return nil, fmt.Errorf("error getting total network legacy RPL stake: %w", err) + } + return *totalRplStake, nil +} + +// Get a node's total RPL staked +func GetNodeStakedRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } nodeRplStake := new(*big.Int) - if err := rocketNodeStaking.Call(opts, nodeRplStake, "getNodeRPLStake", nodeAddress); err != nil { + if err := rocketNodeStaking.Call(opts, nodeRplStake, "getNodeStakedRPL", nodeAddress); err != nil { return nil, fmt.Errorf("error getting total node RPL stake: %w", err) } return *nodeRplStake, nil } -// Get a node's effective RPL stake -func GetNodeEffectiveRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { +// Get a node's megapool RPL staked +func GetNodeMegapoolStakedRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } - nodeEffectiveRplStakeWrapper := new(*big.Int) - if err := rocketNodeStaking.Call(opts, nodeEffectiveRplStakeWrapper, "getNodeEffectiveRPLStake", nodeAddress); err != nil { - return nil, fmt.Errorf("error getting effective node RPL stake: %w", err) + nodeRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStake, "getNodeMegapoolStakedRPL", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting megapool node RPL stake: %w", err) } + return *nodeRplStake, nil +} - minimumStake, err := GetNodeMinimumRPLStake(rp, nodeAddress, opts) +// Get a node's legacy RPL staked +func GetNodeLegacyStakedRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { - return nil, fmt.Errorf("error getting minimum node RPL stake to verify effective stake: %w", err) + return nil, err } - - nodeEffectiveRplStake := *nodeEffectiveRplStakeWrapper - if nodeEffectiveRplStake.Cmp(minimumStake) == -1 { - // Effective stake should be zero if it's less than the minimum RPL stake - return big.NewInt(0), nil + nodeRplStake := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStake, "getNodeLegacyStakedRPL", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting megapool node RPL stake: %w", err) } - - return nodeEffectiveRplStake, nil + return *nodeRplStake, nil } -// Get a node's minimum RPL stake to collateralize their minipools -func GetNodeMinimumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { +// Get the amount of unstaking RPL for a node +func GetNodeUnstakingRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } - nodeMinimumRplStake := new(*big.Int) - if err := rocketNodeStaking.Call(opts, nodeMinimumRplStake, "getNodeMinimumRPLStake", nodeAddress); err != nil { - return nil, fmt.Errorf("error getting minimum node RPL stake: %w", err) + unstakingRpl := new(*big.Int) + if err := rocketNodeStaking.Call(opts, unstakingRpl, "getNodeUnstakingRPL", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node unstaking RPL: %w", err) } - return *nodeMinimumRplStake, nil + return *unstakingRpl, nil } // Get a node's maximum RPL stake to collateralize their minipools -func GetNodeMaximumRPLStake(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { +func GetNodeMaximumRPLStakeForMinipools(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } nodeMaximumRplStake := new(*big.Int) - if err := rocketNodeStaking.Call(opts, nodeMaximumRplStake, "getNodeMaximumRPLStake", nodeAddress); err != nil { - return nil, fmt.Errorf("error getting maximum node RPL stake: %w", err) + if err := rocketNodeStaking.Call(opts, nodeMaximumRplStake, "getNodeMaximumRPLStakeForMinipools", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting maximum node RPL stake for minipools: %w", err) } return *nodeMaximumRplStake, nil } @@ -110,30 +137,108 @@ func GetNodeRPLStakedTime(rp *rocketpool.RocketPool, nodeAddress common.Address, return (*nodeRplStakedTime).Uint64(), nil } -// Get the amount of ETH the node has borrowed from the deposit pool to create its minipools -func GetNodeEthMatched(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { +// Get the time a node last unstaked RPL +func GetNodeLastUnstakeTime(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (uint64, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return 0, err + } + nodeRplStakedTime := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeRplStakedTime, "getNodeLastUnstakeTime", nodeAddress); err != nil { + return 0, fmt.Errorf("error getting node last unstaked RPL time: %w", err) + } + return (*nodeRplStakedTime).Uint64(), nil +} + +// Get the ratio between capital taken from users and provided by a node operator for minipools +func GetNodeETHCollateralisationRatio(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeEthCol := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeEthCol, "getNodeETHCollateralisationRatio", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting NodeETHCollateralisationRatio: %w", err) + } + return *nodeEthCol, nil +} + +// Get the amount of ETH the node has borrowed from the deposit pool +func GetNodeETHBorrowed(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeETHBorrowed := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeETHBorrowed, "getNodeETHBorrowed", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched: %w", err) + } + return *nodeETHBorrowed, nil +} + +// Get the amount of ETH the node has borrowed from the deposit pool for its megapool +func GetNodeMegapoolETHBorrowed(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } - nodeEthMatched := new(*big.Int) - if err := rocketNodeStaking.Call(opts, nodeEthMatched, "getNodeETHMatched", nodeAddress); err != nil { + nodeMegapoolETHBorrowed := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMegapoolETHBorrowed, "getNodeMegapoolETHBorrowed", nodeAddress); err != nil { return nil, fmt.Errorf("error getting node ETH matched: %w", err) } - return *nodeEthMatched, nil + return *nodeMegapoolETHBorrowed, nil } -// Get the amount of ETH the node can borrow from the deposit pool to create its minipools -func GetNodeEthMatchedLimit(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { +// Get the amount of ETH the node has borrowed from the deposit pool for its minipools +func GetNodeMinipoolETHBorrowed(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } - nodeEthMatchedLimit := new(*big.Int) - if err := rocketNodeStaking.Call(opts, nodeEthMatchedLimit, "getNodeETHMatchedLimit", nodeAddress); err != nil { - return nil, fmt.Errorf("error getting node ETH matched limit: %w", err) + nodeMinipoolETHBorrowed := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMinipoolETHBorrowed, "getNodeMinipoolETHBorrowed", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched: %w", err) } - return *nodeEthMatchedLimit, nil + return *nodeMinipoolETHBorrowed, nil +} + +// Get the amount of ETH the node has bonded +func GetNodeEthBonded(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeETHBonded := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeETHBonded, "getNodeETHBonded", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched: %w", err) + } + return *nodeETHBonded, nil +} + +// Get the amount of ETH the node has bonded for its megapool +func GetNodeMegapoolETHBonded(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeMegapoolETHBonded := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMegapoolETHBonded, "getNodeMegapoolETHBonded", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched: %w", err) + } + return *nodeMegapoolETHBonded, nil +} + +// Get the amount of ETH the node has bonded for its minipools +func GetNodeMinipoolETHBonded(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, opts) + if err != nil { + return nil, err + } + nodeMinipoolETHBonded := new(*big.Int) + if err := rocketNodeStaking.Call(opts, nodeMinipoolETHBonded, "getNodeMinipoolETHBonded", nodeAddress); err != nil { + return nil, fmt.Errorf("error getting node ETH matched: %w", err) + } + return *nodeMinipoolETHBonded, nil } // Estimate the gas of Stake @@ -158,27 +263,26 @@ func StakeRPL(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.Transact return tx.Hash(), nil } -// Estimate the gas of Burn RPL -func EstimateBurnRpl(rp *rocketpool.RocketPool, from common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { +// Estimate the gas of UnstakeRPL +func EstimateUnstakeGas(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, nil) if err != nil { return rocketpool.GasInfo{}, err } - return rocketNodeStaking.GetTransactionGasInfo(opts, "burnRPL", from, rplAmount) + return rocketNodeStaking.GetTransactionGasInfo(opts, "unstakeRPL", rplAmount) } -// Burn RPL -func BurnRPL(rp *rocketpool.RocketPool, from common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { +// Unstake RPL +func UnstakeRPL(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, nil) if err != nil { return common.Hash{}, err } - tx, err := rocketNodeStaking.Transact(opts, "burnRPL", from, rplAmount) + tx, err := rocketNodeStaking.Transact(opts, "unstakeRPL", rplAmount) if err != nil { - return common.Hash{}, fmt.Errorf("error burning RPL: %w", err) + return common.Hash{}, fmt.Errorf("error unstaking RPL: %w", err) } return tx.Hash(), nil - } // Estimate the gas of set RPL locking allowed @@ -238,49 +342,71 @@ func SetStakeRPLForAllowed(rp *rocketpool.RocketPool, caller common.Address, all return tx.Hash(), nil } +// Set stake RPL for allowed for a certain node +func SetNodeStakeRPLForAllowed(rp *rocketpool.RocketPool, nodeAddress common.Address, caller common.Address, allowed bool, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err + } + tx, err := rocketNodeStaking.Transact(opts, "setStakeRPLForAllowed", nodeAddress, caller, allowed) + if err != nil { + return common.Hash{}, fmt.Errorf("error setting node stake RPL for allowed: %w", err) + } + return tx.Hash(), nil +} + // Estimate the gas of WithdrawRPL -func EstimateWithdrawRPLGas(rp *rocketpool.RocketPool, nodeAddress common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { +func EstimateWithdrawRPLGas(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, nil) if err != nil { return rocketpool.GasInfo{}, err } - return rocketNodeStaking.GetTransactionGasInfo(opts, "withdrawRPL", nodeAddress, rplAmount) + return rocketNodeStaking.GetTransactionGasInfo(opts, "withdrawRPL") } // Withdraw staked RPL -func WithdrawRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { +func WithdrawRPL(rp *rocketpool.RocketPool, opts *bind.TransactOpts) (common.Hash, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, nil) if err != nil { return common.Hash{}, err } - tx, err := rocketNodeStaking.Transact(opts, "withdrawRPL", nodeAddress, rplAmount) + tx, err := rocketNodeStaking.Transact(opts, "withdrawRPL") if err != nil { return common.Hash{}, fmt.Errorf("error withdrawing staked RPL: %w", err) } return tx.Hash(), nil } -// Calculate total effective RPL stake -func CalculateTotalEffectiveRPLStake(rp *rocketpool.RocketPool, offset, limit, rplPrice *big.Int, opts *bind.CallOpts) (*big.Int, error) { - rocketNodeStaking, err := getRocketNodeStaking(rp, opts) +// Estimate the gas of WithdrawLegacyRPL +func EstimateWithdrawLegacyRPLGas(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (rocketpool.GasInfo, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) if err != nil { - return nil, err + return rocketpool.GasInfo{}, err + } + return rocketNodeStaking.GetTransactionGasInfo(opts, "withdrawLegacyRPL", rplAmount) +} + +// Withdraw legacy RPL +func WithdrawLegacyRPL(rp *rocketpool.RocketPool, rplAmount *big.Int, opts *bind.TransactOpts) (common.Hash, error) { + rocketNodeStaking, err := getRocketNodeStaking(rp, nil) + if err != nil { + return common.Hash{}, err } - totalEffectiveRplStake := new(*big.Int) - if err := rocketNodeStaking.Call(opts, totalEffectiveRplStake, "calculateTotalEffectiveRPLStake", offset, limit, rplPrice); err != nil { - return nil, fmt.Errorf("error getting total effective RPL stake: %w", err) + tx, err := rocketNodeStaking.Transact(opts, "withdrawLegacyRPL", rplAmount) + if err != nil { + return common.Hash{}, fmt.Errorf("error withdrawing legacy RPL: %w", err) } - return *totalEffectiveRplStake, nil + return tx.Hash(), nil } // Get the amount of RPL locked as part of active PDAO proposals or challenges -func GetNodeRPLLocked(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { +func GetNodeLockedRPL(rp *rocketpool.RocketPool, nodeAddress common.Address, opts *bind.CallOpts) (*big.Int, error) { rocketNodeStaking, err := getRocketNodeStaking(rp, opts) if err != nil { return nil, err } value := new(*big.Int) - if err := rocketNodeStaking.Call(opts, value, "getNodeRPLLocked", nodeAddress); err != nil { + if err := rocketNodeStaking.Call(opts, value, "getNodeLockedRPL", nodeAddress); err != nil { return nil, fmt.Errorf("error getting node RPL locked: %w", err) } return *value, nil diff --git a/bindings/settings/protocol/node.go b/bindings/settings/protocol/node.go index fc964aedc..4c37b547f 100644 --- a/bindings/settings/protocol/node.go +++ b/bindings/settings/protocol/node.go @@ -165,6 +165,19 @@ func GetMaximumPerMinipoolStakeRaw(rp *rocketpool.RocketPool, opts *bind.CallOpt return *value, nil } +// The the period of time a node must wait before withdrawing RPL +func GetNodeUnstakingPeriod(rp *rocketpool.RocketPool, opts *bind.CallOpts) (*big.Int, error) { + nodeSettingsContract, err := getNodeSettingsContract(rp, opts) + if err != nil { + return nil, err + } + value := new(*big.Int) + if err := nodeSettingsContract.Call(opts, value, "getUnstakingPeriod"); err != nil { + return nil, fmt.Errorf("error getting the unstaking period: %w", err) + } + return *value, nil +} + // Get contracts var nodeSettingsContractLock sync.Mutex diff --git a/bindings/utils/state/node.go b/bindings/utils/state/node.go index 2d5e016d0..392783709 100644 --- a/bindings/utils/state/node.go +++ b/bindings/utils/state/node.go @@ -32,8 +32,17 @@ type NativeNodeDetails struct { EffectiveRPLStake *big.Int `json:"effective_rpl_stake"` MinimumRPLStake *big.Int `json:"minimum_rpl_stake"` MaximumRPLStake *big.Int `json:"maximum_rpl_stake"` - EthMatched *big.Int `json:"eth_matched"` - EthMatchedLimit *big.Int `json:"eth_matched_limit"` + EthBorrowed *big.Int `json:"eth_borrowed"` + EthBorrowedLimit *big.Int `json:"eth_borrowed_limit"` + MegapoolETHBorrowed *big.Int `json:"megapool_eth_borrowed"` + MinipoolETHBorrowed *big.Int `json:"minipool_eth_borrowed"` + EthBonded *big.Int `json:"eth_bonded"` + MegapoolEthBonded *big.Int `json:"megapool_eth_bonded"` + MinipoolETHBonded *big.Int `json:"minipool_eth_bonded"` + MegapoolStakedRPL *big.Int `json:"megapool_staked_rpl"` + LegacyStakedRPL *big.Int `json:"legacy_staked_rpl"` + UnstakingRPL *big.Int `json:"unstaking_rpl"` + LockedRPL *big.Int `json:"locked_rpl"` MinipoolCount *big.Int `json:"minipool_count"` BalanceETH *big.Int `json:"balance_eth"` BalanceRETH *big.Int `json:"balance_reth"` @@ -334,12 +343,16 @@ func addNodeDetailsCalls(contracts *NetworkContracts, mc *multicall.MultiCaller, mc.AddCall(contracts.RocketNodeManager, &details.FeeDistributorInitialised, "getFeeDistributorInitialised", address) mc.AddCall(contracts.RocketNodeDistributorFactory, &details.FeeDistributorAddress, "getProxyAddress", address) mc.AddCall(contracts.RocketNodeManager, &details.RewardNetwork, "getRewardNetwork", address) - mc.AddCall(contracts.RocketNodeStaking, &details.RplStake, "getNodeRPLStake", address) - mc.AddCall(contracts.RocketNodeStaking, &details.EffectiveRPLStake, "getNodeEffectiveRPLStake", address) - mc.AddCall(contracts.RocketNodeStaking, &details.MinimumRPLStake, "getNodeMinimumRPLStake", address) - mc.AddCall(contracts.RocketNodeStaking, &details.MaximumRPLStake, "getNodeMaximumRPLStake", address) - mc.AddCall(contracts.RocketNodeStaking, &details.EthMatched, "getNodeETHMatched", address) - mc.AddCall(contracts.RocketNodeStaking, &details.EthMatchedLimit, "getNodeETHMatchedLimit", address) + if !contracts.isSaturnDeployed() { + mc.AddCall(contracts.RocketNodeStaking, &details.RplStake, "getNodeRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &details.EffectiveRPLStake, "getNodeEffectiveRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &details.MinimumRPLStake, "getNodeMinimumRPLStake", address) + mc.AddCall(contracts.RocketNodeStaking, &details.MaximumRPLStake, "getNodeMaximumRPLStake", address) + // Matched is renamed to borrowed in Saturn v1.4 + // getNodeETHMatched and getNodeETHMatchedLimit is the naming for these calls pre-saturn + mc.AddCall(contracts.RocketNodeStaking, &details.EthBorrowed, "getNodeETHMatched", address) + mc.AddCall(contracts.RocketNodeStaking, &details.EthBorrowedLimit, "getNodeETHMatchedLimit", address) + } mc.AddCall(contracts.RocketMinipoolManager, &details.MinipoolCount, "getNodeMinipoolCount", address) mc.AddCall(contracts.RocketTokenRETH, &details.BalanceRETH, "balanceOf", address) mc.AddCall(contracts.RocketTokenRPL, &details.BalanceRPL, "balanceOf", address) @@ -355,6 +368,28 @@ func addNodeDetailsCalls(contracts *NetworkContracts, mc *multicall.MultiCaller, // Saturn if contracts.isSaturnDeployed() { + // a node's total borrowed ETH amount (minipool + megapool) + mc.AddCall(contracts.RocketNodeStaking, &details.EthBorrowed, "getNodeETHBorrowed", address) + // a node's borrowed megapool ETH amount + mc.AddCall(contracts.RocketNodeStaking, &details.MegapoolETHBorrowed, "getNodeMegapoolETHBorrowed", address) + // a node's borrowed minipool ETH amount + mc.AddCall(contracts.RocketNodeStaking, &details.MinipoolETHBorrowed, "getNodeMinipoolETHBorrowed", address) + // a node's total amount of a node operator's bonded ETH (minipool + megapool) + mc.AddCall(contracts.RocketNodeStaking, &details.EthBonded, "getNodeETHBonded", address) + // the amount of a node operator's megapool bonded ETH + mc.AddCall(contracts.RocketNodeStaking, &details.MegapoolEthBonded, "getNodeMegapoolETHBonded", address) + // the amount of a node operator's minipool bonded ETH + mc.AddCall(contracts.RocketNodeStaking, &details.MinipoolETHBonded, "getNodeMinipoolETHBonded", address) + // the total amount of RPL staked by a node operator (both legacy and megapool staked RPL) + mc.AddCall(contracts.RocketNodeStaking, &details.RplStake, "getNodeStakedRPL", address) + // the amount of megapool staked RPL for a node operator + mc.AddCall(contracts.RocketNodeStaking, &details.MegapoolStakedRPL, "getNodeMegapoolStakedRPL", address) + // the amount of legacy staked RPL for a node operator + mc.AddCall(contracts.RocketNodeStaking, &details.LegacyStakedRPL, "getNodeLegacyStakedRPL", address) + // the timestamp at which a node last unstaked megapool staked RPL + mc.AddCall(contracts.RocketNodeStaking, &details.UnstakingRPL, "getNodeUnstakingRPL", address) + // the amount of RPL that is locked for a given node + mc.AddCall(contracts.RocketNodeStaking, &details.LockedRPL, "getNodeLockedRPL", address) mc.AddCall(contracts.RocketMegapoolFactory, &details.MegapoolAddress, "getExpectedAddress", address) mc.AddCall(contracts.RocketMegapoolFactory, &details.MegapoolDeployed, "getMegapoolDeployed", address) }