Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support withdraw of excess RPL #34

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions web/src/components/ClaimAndStakeForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@ export function ClaimButtonTooltip({
currency="eth"
placeholder="0"
/>
<CurrencyValue
value={rplTotal.sub(stakeAmountRpl)}
currency="rpl"
placeholder="0"
/>
<CurrencyValue value={rplTotal} currency="rpl" placeholder="0" />
</Stack>
<FormHelperText sx={{ m: 0 }}>
approximate receipts (after gas)
Expand Down Expand Up @@ -183,8 +179,7 @@ export default function ClaimAndStakeForm({
<ClaimButtonTooltip
gasAmount={gasAmount}
ethTotal={ethTotal}
rplTotal={rplTotal}
stakeAmountRpl={stakeAmountRpl}
rplTotal={rplTotal.sub(stakeAmountRpl)}
/>
}
>
Expand Down
6 changes: 3 additions & 3 deletions web/src/components/NodeRewardsSummaryCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ function SummaryCardHeader({ asLink, nodeAddress }) {
let { rplStake } = details || {
rplStake: ethers.constants.Zero,
};
const rplStatus = useNodeRplStatus({ nodeAddress });
const { rplStatus } = useNodeRplStatus({ nodeAddress });
const depositEth = bnSum(
(minipools || [])
.filter((mp) => !mp.isFinalized)
Expand Down Expand Up @@ -653,7 +653,7 @@ function RplStakeEthRangeAxis({ sx, nodeAddress }) {
const theme = useTheme();
const minipools = useMinipoolDetails(nodeAddress);
const { data: details } = useNodeDetails({ nodeAddress });
let rplStatus = useNodeRplStatus({ nodeAddress });
let { rplStatus } = useNodeRplStatus({ nodeAddress });
let { ethMatched } = details || {
ethMatched: ethers.constants.Zero,
minipoolCount: ethers.constants.Zero,
Expand Down Expand Up @@ -785,7 +785,7 @@ function RplStakeChart({ sx, nodeAddress }) {
const theme = useTheme();
const { data: details } = useNodeDetails({ nodeAddress });
const rplEthPrice = useRplEthPrice();
let rplStatus = useNodeRplStatus({ nodeAddress });
let { rplStatus } = useNodeRplStatus({ nodeAddress });
let { rplStake, minimumRPLStake, maximumRPLStake } = details || {
rplStake: ethers.constants.Zero,
minimumRPLStake: ethers.constants.Zero,
Expand Down
172 changes: 162 additions & 10 deletions web/src/components/SafeSweepCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
estimateDistributeConsensusBatchGas,
estimateFinalizeGas,
estimateTipsMevGas,
estimateWithdrawRplGas,
MinipoolStatus,
safeAppUrl,
} from "../utils";
Expand All @@ -58,6 +59,7 @@ import useNodeFinalizedRewardSnapshots from "../hooks/useNodeFinalizedRewardSnap
import contracts from "../contracts";
import useNodeDetails from "../hooks/useNodeDetails";
import { ClaimButtonTooltip } from "./ClaimAndStakeForm";
import useNodeRplStatus from "../hooks/useNodeRplStatus";

function ConsensusConfigurationCard({
sx,
Expand Down Expand Up @@ -361,6 +363,8 @@ function SafeAlert({ sx, nodeAddress }) {

function useSweeper({ nodeAddress }) {
let { data: details } = useNodeDetails({ nodeAddress });
let { rplOver } = useNodeRplStatus({ nodeAddress });

let { feeDistributorAddress } = details || {};

// Intervals configuration
Expand Down Expand Up @@ -447,6 +451,14 @@ function useSweeper({ nodeAddress }) {
(finalizableMinipools || []).map(({ nodeBalance }) => nodeBalance)
);

let [isWithdrawingRpl, setIsWithdrawingRpl] = useState(rplOver.gt(0));
// If we get an updated `rplOver` later, we want to use it as the default.
useEffect(
() => setIsWithdrawingRpl(rplOver.gt(0)),
// eslint-disable-next-line react-hooks/exhaustive-deps
[rplOver?.toString()]
);

let beforeGas = {
intervalsEth: totalEth,
tipsMevEth: executionNodeTotal,
Expand All @@ -463,6 +475,7 @@ function useSweeper({ nodeAddress }) {
tipsMev: estimateTipsMevGas(),
consensus: estimateDistributeConsensusBatchGas((currentBatch || []).length),
finalize: estimateFinalizeGas(finalizableMinipools.length),
withdrawRpl: estimateWithdrawRplGas(),
};
let overall = {
eth: bnSum([
Expand All @@ -471,14 +484,16 @@ function useSweeper({ nodeAddress }) {
isDistributingConsensus ? beforeGas.consensusEth : ethers.constants.Zero,
isFinalizing ? beforeGas.finalizeEth : ethers.constants.Zero,
]),
rpl: isClaimingInterval
? totalRpl.sub(stakeAmountRpl)
: ethers.constants.Zero,
rpl: bnSum([
isClaimingInterval ? totalRpl.sub(stakeAmountRpl) : ethers.constants.Zero,
isWithdrawingRpl ? rplOver : ethers.constants.Zero,
]),
gas: bnSum([
isClaimingInterval ? gas.intervals : ethers.constants.Zero,
isDistributingTipsMev ? gas.tipsMev : ethers.constants.Zero,
isDistributingConsensus ? gas.consensus : ethers.constants.Zero,
isFinalizing ? gas.finalize : ethers.constants.Zero,
isWithdrawingRpl ? gas.withdrawRpl : ethers.constants.Zero,
]),
};
if (!overall.gas.isZero()) {
Expand Down Expand Up @@ -537,6 +552,18 @@ function useSweeper({ nodeAddress }) {
}))
);
}
if (isWithdrawingRpl) {
txs = txs.concat([
{
operation: "0x00",
to: contracts.RocketNodeStaking.address,
value: "0",
data: new ethers.utils.Interface(
contracts.RocketNodeStaking.abi
).encodeFunctionData("withdrawRPL", [rplOver]),
},
]);
}
return sdk.txs.send({
txs,
});
Expand Down Expand Up @@ -578,6 +605,11 @@ function useSweeper({ nodeAddress }) {
finalizableMinipools,
finalizeAmount,

// Excess RPL config
isWithdrawingRpl,
setIsWithdrawingRpl,
rplOver,

// Analysis
beforeGas,
gas,
Expand Down Expand Up @@ -612,6 +644,10 @@ function SweepCardContent({ sx, nodeAddress, sweeper }) {
setFinalizing,
finalizableMinipools,

isWithdrawingRpl,
setIsWithdrawingRpl,
rplOver,

beforeGas,
gas,
overall,
Expand Down Expand Up @@ -952,6 +988,82 @@ function SweepCardContent({ sx, nodeAddress, sweeper }) {
</Stack>
}
/>
<IconRow Icon={Add} />
<TransactionRow
lhs={
<Grid
sx={{ pt: 0, pr: 3 }}
container
rowSpacing={2}
columnSpacing={2}
alignItems="center"
>
<Grid item xs={5} sx={{ textAlign: "right" }}>
<Tooltip
arrow
sx={{ cursor: "help" }}
title="Withdraw your staked RPL beyond the maximum effective stake."
>
<Stack
direction={"row"}
spacing={1}
justifyContent="end"
alignItems={"center"}
>
<HelpOutline fontSize="inherit" color="disabled" />
<Typography color={"text.primary"} variant={"subtitle2"}>
Excess RPL
</Typography>
</Stack>
</Tooltip>
</Grid>
<Grid item xs={7}>
<FormControlLabel
control={
<Checkbox
checked={isWithdrawingRpl}
onChange={(e) => setIsWithdrawingRpl(e.target.checked)}
/>
}
color="text.secondary"
slotProps={{
typography: {
variant: "caption",
color: "text.secondary",
},
}}
disableTypography
label={
<Stack spacing={0} direction="column">
<CurrencyValue
size="xsmall"
value={rplOver}
currency="rpl"
/>
<FormHelperText sx={{ m: 0 }}>
{isWithdrawingRpl ? "Withdrawing" : "Not Withdrawing"}
</FormHelperText>
</Stack>
}
/>
</Grid>
</Grid>
}
rhs={
<Stack direction="column" sx={{ pl: 2, pr: 2 }} spacing={2}>
{!isWithdrawingRpl ? null : (
<ReceiptsInfo amountRpl={rplOver} amountGas={gas.withdrawRpl} />
)}
<TransactionDescription
title={
!isWithdrawingRpl
? "Don't withdraw excess RPL"
: "Withdraw excess RPL"
}
/>
</Stack>
}
/>
<IconRow
Icon={Merge}
iconProps={{
Expand Down Expand Up @@ -1027,6 +1139,8 @@ export default function SafeSweepCard({ sx, nodeAddress }) {
isDistributingTipsMev,
isDistributingConsensus,
isFinalizing,
isWithdrawingRpl,
rplOver,
totalRpl,
totalEth,
rewardIndexes,
Expand Down Expand Up @@ -1077,7 +1191,7 @@ export default function SafeSweepCard({ sx, nodeAddress }) {
<ClaimButtonTooltip
gasAmount={overall.gas}
ethTotal={overall.eth}
rplTotal={isClaimingInterval ? totalRpl : ethers.constants.Zero}
rplTotal={overall.rpl}
stakeAmountRpl={
isClaimingInterval ? stakeAmountRpl : ethers.constants.Zero
}
Expand Down Expand Up @@ -1227,6 +1341,35 @@ export default function SafeSweepCard({ sx, nodeAddress }) {
</Grid>
</>
)}
{!isWithdrawingRpl ? null : (
<>
<Grid item xs={4.5}>
<Stack direction="row" justifyContent="flex-end">
<Typography
component="span"
variant="caption"
color="text.secondary"
>
excess RPL
</Typography>
</Stack>
</Grid>
<Grid item xs={7.5}>
<Stack
direction="row"
spacing={1}
justifyContent="flex-start"
>
<CurrencyValue
size="xsmall"
value={rplOver}
currency="rpl"
placeholder="0"
/>
</Stack>
</Grid>
</>
)}
</Grid>
</Stack>
</ClaimButtonTooltip>
Expand Down Expand Up @@ -1267,12 +1410,21 @@ export default function SafeSweepCard({ sx, nodeAddress }) {
color={color}
disabled={!canWithdraw}
endIcon={
<CurrencyValue
value={overall.eth}
size="xsmall"
currency="eth"
placeholder="0"
/>
<>
<CurrencyValue
value={overall.eth}
size="xsmall"
currency="eth"
placeholder="0"
/>
<CurrencyValue
sx={{ ml: 1 }}
value={overall.rpl}
size="xsmall"
currency="rpl"
placeholder="0"
/>
</>
}
>
Sweep
Expand Down
5 changes: 5 additions & 0 deletions web/src/contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import RocketMinipoolManager from "./generated/contracts/RocketMinipoolManager.j
import RocketNetworkPrices from "./generated/contracts/RocketNetworkPrices.json";
import RocketNodeDistributorInterface from "./generated/contracts/RocketNodeDistributorInterface.json";
import RocketNodeManager from "./generated/contracts/RocketNodeManager.json";
import RocketNodeStaking from "./generated/contracts/RocketNodeStaking.json";
import RocketRewardsPool from "./generated/contracts/RocketRewardsPool.json";
import RocketStorageK from "./generated/contracts/RocketStorage.json";

Expand Down Expand Up @@ -39,6 +40,10 @@ const contracts = {
address: "0x89F478E6Cc24f052103628f36598D4C14Da3D287",
abi: RocketNodeManager.abi,
},
RocketNodeStaking: {
address: "0x0d8D8f8541B12A0e1194B7CC4b6D954b90AB82ec",
abi: RocketNodeStaking.abi,
},
RocketRewardsPool: {
address: [
"0xA805d68b61956BC92d556F2bE6d18747adAeEe82",
Expand Down
Loading
Loading