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

weETH withdrawal: Instant withdrawal with Fee + Implicit withdrawal fee handling #207

Open
wants to merge 42 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
d356cc9
init. Instant Withdrawal via Buffer
seongyun-ko Dec 6, 2024
82fab2e
implemented instant fee mechanism, handling of the implicit fee
seongyun-ko Dec 9, 2024
0f13953
fix scripts
seongyun-ko Dec 12, 2024
0b68310
add role registry, consider eth amount locked for withdrawal in liqui…
seongyun-ko Dec 17, 2024
c9fd604
use setter for 'shareRemainderSplitToTreasuryInBps', add more fuzz te…
seongyun-ko Dec 19, 2024
c377e8e
handle issues in calculating the dust shares
seongyun-ko Dec 20, 2024
a3aeac9
improve comments
seongyun-ko Dec 20, 2024
57bd0fc
add sorted & unique constraints + type change to reduce gas
seongyun-ko Dec 24, 2024
b6100b6
update 'handleAccumulatedShareRemainder' to be callable by admin
seongyun-ko Dec 24, 2024
e8734d6
Certora audit: (1) add {aggregateSumEEthShareAmount}, (2) fix {_claim…
seongyun-ko Dec 26, 2024
71ffa8d
wip: to be amended
seongyun-ko Dec 30, 2024
9e0fb99
add simplified {invalidate, validate} request, fix unit tests
seongyun-ko Dec 30, 2024
98f483a
rename EtherFiWithdrawBuffer -> EtherFiRedemptionManager
seongyun-ko Dec 30, 2024
bcc1184
fix the logic to check the aggr calls
seongyun-ko Dec 30, 2024
1f85fb6
Update test/WithdrawRequestNFT.t.sol
jtfirek Dec 30, 2024
bdee463
reduce gas spending for 'call', update the upgrade init function, rem…
seongyun-ko Dec 30, 2024
d40a117
apply gas opt for BucketLimiter
seongyun-ko Dec 30, 2024
f4f2ffd
improve assetion tsets, apply design pattern, function rename
seongyun-ko Dec 30, 2024
5069c14
apply CEI pattern to 'handleRemainder'
seongyun-ko Dec 30, 2024
2e13202
apply CEI pattern to 'redeem'
seongyun-ko Dec 31, 2024
b18bd18
use 'totalRemainderEEthShares' instead of locked share
seongyun-ko Dec 31, 2024
1e12673
initializeOnUpgrade cant be called twice
seongyun-ko Dec 31, 2024
c14c348
initializeOnUpgrade onlyOnce
seongyun-ko Dec 31, 2024
35fba66
use uint256 instead of uint32
seongyun-ko Dec 31, 2024
e95cfc0
revert
seongyun-ko Dec 31, 2024
bbf2d83
improve the fuzz test
seongyun-ko Dec 31, 2024
2cbbc04
(1) pause the contract on upgrade, (2) prevent from calling 'aggregat…
seongyun-ko Jan 1, 2025
1482cb0
only owner of the funds can call {redeemEEth, redeemWeEth}
seongyun-ko Jan 2, 2025
8ced81e
disable unpause until the scan is completed
seongyun-ko Jan 2, 2025
86ac4a0
check the basis points params are below 1e4
seongyun-ko Jan 2, 2025
1e8cdea
disallow calling 'initializeOnUpgradeWithRedemptionManager' with inva…
seongyun-ko Jan 2, 2025
6fffba6
(1) withdrawRequestNFT cannot call LiquidityPool.addEthAmountLockedFo…
seongyun-ko Jan 2, 2025
89db9d5
prevent finalizing future requests
seongyun-ko Jan 2, 2025
c5a2dd1
add {redeemEEthWithPermit, redeemWeEthWithPermit}, use try-catch for
seongyun-ko Jan 2, 2025
c85e920
remove a redundant check
seongyun-ko Jan 2, 2025
cca361e
Prevent 'initializeOnUpgrade' of LiquidityPool from being called twic…
seongyun-ko Jan 2, 2025
268efe5
use 'isScanOfShareRemainderCompleted'
seongyun-ko Jan 2, 2025
a13919d
add the max value constraint on rate limit
seongyun-ko Jan 2, 2025
58fe3fb
remove equality condition
seongyun-ko Jan 2, 2025
898896a
fix the overflow issue in '_refill'
seongyun-ko Jan 2, 2025
25ac914
Update EtherFiRedemptionManager.sol
seongyun-ko Jan 2, 2025
fd52a52
prevent the total shares from going below 1 gwei after redemption
seongyun-ko Jan 2, 2025
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
21 changes: 15 additions & 6 deletions lib/BucketLimiter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ library BucketLimiter {
return limit.remaining >= amount;
}

function consumable(Limit memory limit) external view returns (uint64) {
_refill(limit);
return limit.remaining;
}

/*
* Consumes the given amount from the bucket, if there is sufficient capacity, and returns
* whether the bucket had enough remaining capacity to consume the amount.
Expand Down Expand Up @@ -104,18 +109,22 @@ library BucketLimiter {
}

function _refill(Limit memory limit) internal view {
// We allow for overflow here, as the delta is resilient against it.
uint64 now_ = uint64(block.timestamp);
uint64 delta;

if (now_ == limit.lastRefill) {
return;
}

uint256 delta;
unchecked {
delta = now_ - limit.lastRefill;
}
uint64 tokens = delta * limit.refillRate;
uint64 newRemaining = limit.remaining + tokens;
uint256 tokens = delta * uint256(limit.refillRate);
uint256 newRemaining = uint256(limit.remaining) + tokens;
if (newRemaining > limit.capacity) {
limit.remaining = limit.capacity;
} else {
limit.remaining = newRemaining;
limit.remaining = uint64(newRemaining);
}
limit.lastRefill = now_;
}
Expand Down Expand Up @@ -157,4 +166,4 @@ library BucketLimiter {
refill(limit);
limit.remaining = remaining;
}
}
}
42 changes: 42 additions & 0 deletions script/deploys/DeployEtherFiRestaker.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";

import "../../src/Liquifier.sol";
import "../../src/EtherFiRestaker.sol";
import "../../src/helpers/AddressProvider.sol";
import "../../src/UUPSProxy.sol";
import "@openzeppelin/contracts/utils/Strings.sol";

contract Deploy is Script {
using Strings for string;

UUPSProxy public liquifierProxy;

Liquifier public liquifierInstance;

AddressProvider public addressProvider;

address admin;

function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY");
addressProvider = AddressProvider(addressProviderAddress);

vm.startBroadcast(deployerPrivateKey);

EtherFiRestaker restaker = EtherFiRestaker(payable(new UUPSProxy(payable(new EtherFiRestaker()), "")));
restaker.initialize(
addressProvider.getContractAddress("LiquidityPool"),
addressProvider.getContractAddress("Liquifier")
);

new Liquifier();

// addressProvider.addContract(address(liquifierInstance), "Liquifier");

vm.stopBroadcast();
}
}
40 changes: 40 additions & 0 deletions script/deploys/DeployEtherFiWithdrawalBuffer.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";

import "@openzeppelin/contracts/utils/Strings.sol";

import "../../src/Liquifier.sol";
import "../../src/EtherFiRestaker.sol";
import "../../src/helpers/AddressProvider.sol";
import "../../src/UUPSProxy.sol";
import "../../src/EtherFiRedemptionManager.sol";


contract Deploy is Script {
using Strings for string;
AddressProvider public addressProvider;

function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address addressProviderAddress = vm.envAddress("CONTRACT_REGISTRY");
addressProvider = AddressProvider(addressProviderAddress);

vm.startBroadcast(deployerPrivateKey);

EtherFiRedemptionManager impl = new EtherFiRedemptionManager(
addressProvider.getContractAddress("LiquidityPool"),
addressProvider.getContractAddress("EETH"),
addressProvider.getContractAddress("WeETH"),
0x0c83EAe1FE72c390A02E426572854931EefF93BA, // protocol safe
0x1d3Af47C1607A2EF33033693A9989D1d1013BB50 // role registry
);
UUPSProxy proxy = new UUPSProxy(payable(impl), "");

EtherFiRedemptionManager instance = EtherFiRedemptionManager(payable(proxy));
instance.initialize(10_00, 1_00, 1_00, 5 ether, 0.001 ether);

vm.stopBroadcast();
}
}
2 changes: 1 addition & 1 deletion script/deploys/DeployPhaseTwo.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ contract DeployPhaseTwoScript is Script {
}
retrieve_contract_addresses();

withdrawRequestNftImplementation = new WithdrawRequestNFT();
withdrawRequestNftImplementation = new WithdrawRequestNFT(address(0));
withdrawRequestNftProxy = new UUPSProxy(address(withdrawRequestNftImplementation), "");
withdrawRequestNftInstance = WithdrawRequestNFT(payable(withdrawRequestNftProxy));

Expand Down
69 changes: 69 additions & 0 deletions script/specialized/weEth_withdrawal_v2.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "test/TestSetup.sol";

import "src/helpers/AddressProvider.sol";

contract Upgrade is Script {

AddressProvider public addressProvider;
address public addressProviderAddress = 0x8487c5F8550E3C3e7734Fe7DCF77DB2B72E4A848;
address public roleRegistry = 0x1d3Af47C1607A2EF33033693A9989D1d1013BB50;
address public treasury = 0x0c83EAe1FE72c390A02E426572854931EefF93BA;
address public pauser = 0x9AF1298993DC1f397973C62A5D47a284CF76844D;

WithdrawRequestNFT withdrawRequestNFTInstance;
LiquidityPool liquidityPoolInstance;

function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
AddressProvider addressProvider = AddressProvider(addressProviderAddress);

withdrawRequestNFTInstance = WithdrawRequestNFT(payable(addressProvider.getContractAddress("WithdrawRequestNFT")));
liquidityPoolInstance = LiquidityPool(payable(addressProvider.getContractAddress("LiquidityPool")));

vm.startBroadcast(deployerPrivateKey);

// deploy_upgrade();
// agg();
// handle_remainder();

vm.stopBroadcast();
}

function deploy_upgrade() internal {
UUPSProxy etherFiRedemptionManagerProxy = new UUPSProxy(address(new EtherFiRedemptionManager(
addressProvider.getContractAddress("LiquidityPool"),
addressProvider.getContractAddress("EETH"),
addressProvider.getContractAddress("WeETH"),
treasury,
roleRegistry)), "");
EtherFiRedemptionManager etherFiRedemptionManagerInstance = EtherFiRedemptionManager(payable(etherFiRedemptionManagerProxy));
etherFiRedemptionManagerInstance.initialize(10_00, 1_00, 1_00, 5 ether, 0.001 ether); // 10% fee split to treasury, 1% exit fee, 1% low watermark

withdrawRequestNFTInstance.upgradeTo(address(new WithdrawRequestNFT(treasury)));
withdrawRequestNFTInstance.initializeOnUpgrade(pauser, 50_00); // 50% fee split to treasury

liquidityPoolInstance.upgradeTo(address(new LiquidityPool()));
liquidityPoolInstance.initializeOnUpgradeWithRedemptionManager(address(etherFiRedemptionManagerInstance));
}

function agg() internal {
uint256 numToScanPerTx = 1024;
uint256 cnt = (withdrawRequestNFTInstance.nextRequestId() / numToScanPerTx) + 1;
console.log(cnt);
for (uint256 i = 0; i < cnt; i++) {
withdrawRequestNFTInstance.aggregateSumEEthShareAmount(numToScanPerTx);
}
}

function handle_remainder() internal {
withdrawRequestNFTInstance.updateAdmin(msg.sender, true);
withdrawRequestNFTInstance.unPauseContract();
uint256 remainder = withdrawRequestNFTInstance.getEEthRemainderAmount();
console.log(remainder);
withdrawRequestNFTInstance.handleRemainder(remainder);
}
}
2 changes: 1 addition & 1 deletion script/upgrades/WithdrawRequestNFTUpgradeScript.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ contract WithdrawRequestNFTUpgrade is Script {
vm.startBroadcast(deployerPrivateKey);

WithdrawRequestNFT oracleInstance = WithdrawRequestNFT(proxyAddress);
WithdrawRequestNFT v2Implementation = new WithdrawRequestNFT();
WithdrawRequestNFT v2Implementation = new WithdrawRequestNFT(address(0));

oracleInstance.upgradeTo(address(v2Implementation));

Expand Down
Loading
Loading