Skip to content

Commit 6a6d164

Browse files
[Task] Withdrawal buffer for OS Vault (#2649)
* Enhance undelegateValidator: log vault assets and available balance after buffer calculation * Added buffer option to sonicUndelegate Set vault buffer to 0.5% for undelegating S from the validator --------- Co-authored-by: Nicholas Addison <nick@addisonbrown.com.au>
1 parent d5fd2cd commit 6a6d164

3 files changed

Lines changed: 67 additions & 14 deletions

File tree

contracts/scripts/defender-actions/sonicRequestWithdrawal.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ const handler = async (event) => {
1515
const provider = new DefenderRelayProvider(event);
1616
const signer = new DefenderRelaySigner(event, provider, { speed: "fastest" });
1717

18-
await undelegateValidator({ signer });
18+
// The vault buffer in basis points, so 100 = 1%
19+
const bufferPct = 50;
20+
21+
await undelegateValidator({ signer, bufferPct });
1922
};
2023

2124
module.exports = { handler };

contracts/tasks/tasks.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1786,10 +1786,20 @@ subtask("sonicUndelegate", "Remove liquidity from a Sonic validator")
17861786
undefined,
17871787
types.float
17881788
)
1789+
.addOptionalParam(
1790+
"buffer",
1791+
"Percentage of total assets to keep as buffer in basis points. 100 = 1%",
1792+
50,
1793+
types.float
1794+
)
17891795
.setAction(async (taskArgs) => {
17901796
const signer = await getSigner();
17911797

1792-
await undelegateValidator({ ...taskArgs, signer });
1798+
await undelegateValidator({
1799+
...taskArgs,
1800+
bufferPct: taskArgs.buffer,
1801+
signer,
1802+
});
17931803
});
17941804
task("sonicUndelegate").setAction(async (_, __, runSuper) => {
17951805
return runSuper();

contracts/utils/sonicActions.js

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ const { logTxDetails } = require("../utils/txLogger");
99

1010
const log = require("../utils/logger")("task:sonic");
1111

12-
async function undelegateValidator({ id, amount, signer }) {
12+
/**
13+
* @param {*} id Validator ID to undelegate from. If not provided, will use the default validator ID.
14+
* @param {*} amount Amount of wS to undelegate. If not provided, it will be calculated based on available wS, pending withdrawals and the vault buffer.
15+
* @param {*} bufferPct Percentage of total assets to keep as a buffer for the vault in basis points. 100 = 1%
16+
* @returns
17+
*/
18+
async function undelegateValidator({ id, amount, signer, bufferPct = 0 }) {
1319
const sonicStakingStrategy = new ethers.Contract(
1420
addresses.sonic.SonicStakingStrategy,
1521
sonicStakingStrategyAbi,
@@ -25,29 +31,63 @@ async function undelegateValidator({ id, amount, signer }) {
2531
let amountBN;
2632
if (amount == undefined) {
2733
const wsBalance = await ws.balanceOf(addresses.sonic.OSonicVaultProxy);
28-
const queue = await vault.withdrawalQueueMetadata();
29-
const pendingWithdrawals = await sonicStakingStrategy.pendingWithdrawals();
34+
log(`wS balance in vault : ${formatUnits(wsBalance, 18)} wS`);
3035

31-
const available = wsBalance
32-
.add(queue.claimed)
33-
.sub(queue.queued)
34-
.add(pendingWithdrawals);
36+
const queue = await vault.withdrawalQueueMetadata();
37+
const unclaimedWithdrawals = queue.queued.sub(queue.claimed);
38+
log(
39+
`Unclaimed vault withdrawals : ${formatUnits(
40+
unclaimedWithdrawals,
41+
18
42+
)} wS`
43+
);
44+
const wsAvailable = wsBalance.sub(unclaimedWithdrawals);
45+
log(`Available wS in vault : ${formatUnits(wsAvailable, 18)} wS`);
3546

36-
log(`Available balance: ${formatUnits(available, 18)} wS`);
47+
const pendingWithdrawals = await sonicStakingStrategy.pendingWithdrawals();
48+
log(
49+
`Pending validator withdrawals : ${formatUnits(
50+
pendingWithdrawals,
51+
18
52+
)} wS`
53+
);
54+
55+
const wsWithValidatorWithdrawals = wsAvailable.add(pendingWithdrawals);
56+
log(
57+
`wS with validator withdrawals : ${formatUnits(
58+
wsWithValidatorWithdrawals,
59+
18
60+
)} wS`
61+
);
62+
63+
const vaultTotalAsset = await vault.totalValue();
64+
log(
65+
`Total vault assets : ${formatUnits(vaultTotalAsset, 18)} wS`
66+
);
67+
const buffer = vaultTotalAsset.mul(bufferPct).div(10000);
68+
log(
69+
`Buffer (${bufferPct / 100}% of total assets) : ${formatUnits(
70+
buffer,
71+
18
72+
)} wS`
73+
);
74+
75+
const wsWithBuffer = wsWithValidatorWithdrawals.sub(buffer);
76+
log(`wS target with buffer : ${formatUnits(wsWithBuffer, 18)} wS`);
3777

3878
// Threshold is negative 1000 wS
3979
const threshold = parseUnits("1000", 18).mul(-1);
40-
if (available.gt(threshold)) {
80+
if (wsWithBuffer.gt(threshold)) {
4181
log(
42-
`No need to undelgate as available balance ${formatUnits(
43-
available,
82+
`No need to undelegate as target with buffer of ${formatUnits(
83+
wsWithBuffer,
4484
18
4585
)} wS is above threshold.`
4686
);
4787
return;
4888
}
4989
// Convert back to a positive amount
50-
amountBN = available.mul(-1);
90+
amountBN = wsWithBuffer.mul(-1);
5191
} else {
5292
// Use amount passed in from Hardhat task
5393
amountBN = parseUnits(amount.toString(), 18);

0 commit comments

Comments
 (0)