Skip to content

Commit 3a7f98d

Browse files
Merge pull request #6 from Layer-Edge/unstake-window
added unstaking logic
2 parents 94286c4 + df44f88 commit 3a7f98d

5 files changed

Lines changed: 435 additions & 139 deletions

File tree

src/interfaces/IWETH.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ interface IWETH {
99
function approve(address spender, uint256 amount) external returns (bool);
1010

1111
function transferFrom(address src, address dst, uint256 wad) external returns (bool);
12+
13+
function balanceOf(address account) external view returns (uint256);
1214
}

src/stake/LayerEdgeStaking.sol

Lines changed: 47 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ contract LayerEdgeStaking is
5757
event TierChanged(address indexed user, Tier to);
5858
event APYUpdated(Tier indexed tier, uint256 rate, uint256 timestamp);
5959
event RewardsDeposited(address indexed sender, uint256 amount);
60+
event UnstakedQueued(address indexed user, uint256 index, uint256 amount);
6061

6162
// User information
6263
struct UserInfo {
6364
uint256 balance; // Current staked balance
64-
uint256 depositTime; // When user first deposited
6565
uint256 lastClaimTime; // Last time user claimed or updated interest
6666
uint256 interestEarned; // Unclaimed interest earned
6767
uint256 totalClaimed; // Total interest claimed
@@ -85,11 +85,19 @@ contract LayerEdgeStaking is
8585
uint256 timestamp;
8686
}
8787

88+
// Add a struct for unstake requests
89+
struct UnstakeRequest {
90+
uint256 amount;
91+
uint256 timestamp;
92+
bool completed;
93+
}
94+
8895
// Storage
8996
mapping(Tier => APYPeriod[]) public tierAPYHistory; // tier => APY periods
9097
mapping(address => UserInfo) public users;
9198
mapping(uint256 => address) public stakerAddress;
9299
mapping(address => TierEvent[]) public stakerTierHistory;
100+
mapping(address => UnstakeRequest[]) public unstakeRequests;
93101
uint256 public stakerCountInTree;
94102
uint256 public stakerCountOutOfTree;
95103
uint256 public totalStaked;
@@ -166,15 +174,23 @@ contract LayerEdgeStaking is
166174
* @param amount Amount to unstake
167175
*/
168176
function unstake(uint256 amount) external nonReentrant whenNotPaused {
169-
_unstake(amount, msg.sender, false);
177+
_unstake(amount, msg.sender);
170178
}
171179

172180
/**
173-
* @notice Unstake native tokens
174-
* @param amount Amount to unstake
181+
* @notice Complete a specific unstake request
182+
* @param index Index of the unstake request to complete
183+
*/
184+
function completeUnstake(uint256 index) external nonReentrant whenNotPaused {
185+
_completeUnstake(msg.sender, index, false);
186+
}
187+
188+
/**
189+
* @notice Complete a specific unstake request with native token
190+
* @param index Index of the unstake request to complete
175191
*/
176-
function unstakeNative(uint256 amount) external nonReentrant whenNotPaused {
177-
_unstake(amount, msg.sender, true);
192+
function completeUnstakeNative(uint256 index) external nonReentrant whenNotPaused {
193+
_completeUnstake(msg.sender, index, true);
178194
}
179195

180196
/**
@@ -498,23 +514,16 @@ contract LayerEdgeStaking is
498514
* @return balance Current staked balance
499515
* @return tier Current tier
500516
* @return apy Current APY rate
501-
* @return depositTime Initial deposit time
502517
* @return pendingRewards Unclaimed rewards
503518
*/
504519
function getUserInfo(address userAddr)
505520
external
506521
view
507-
returns (uint256 balance, Tier tier, uint256 apy, uint256 depositTime, uint256 pendingRewards)
522+
returns (uint256 balance, Tier tier, uint256 apy, uint256 pendingRewards)
508523
{
509524
UserInfo memory user = users[userAddr];
510525

511-
return (
512-
user.balance,
513-
getCurrentTier(userAddr),
514-
getUserAPY(userAddr),
515-
user.depositTime,
516-
calculateUnclaimedInterest(userAddr)
517-
);
526+
return (user.balance, getCurrentTier(userAddr), getUserAPY(userAddr), calculateUnclaimedInterest(userAddr));
518527
}
519528

520529
/**
@@ -621,19 +630,11 @@ contract LayerEdgeStaking is
621630
function getAllInfoOfUser(address userAddr)
622631
external
623632
view
624-
returns (
625-
UserInfo memory user,
626-
Tier tier,
627-
uint256 apy,
628-
uint256 depositTime,
629-
uint256 pendingRewards,
630-
TierEvent[] memory tierHistory
631-
)
633+
returns (UserInfo memory user, Tier tier, uint256 apy, uint256 pendingRewards, TierEvent[] memory tierHistory)
632634
{
633635
user = users[userAddr];
634636
tier = getCurrentTier(userAddr);
635637
apy = getUserAPY(userAddr);
636-
depositTime = user.depositTime;
637638
pendingRewards = calculateUnclaimedInterest(userAddr);
638639
tierHistory = stakerTierHistory[userAddr];
639640
}
@@ -713,7 +714,6 @@ contract LayerEdgeStaking is
713714

714715
// Update user balances
715716
user.balance += amount;
716-
user.depositTime = block.timestamp;
717717
user.lastClaimTime = block.timestamp;
718718

719719
// Update total staked
@@ -722,13 +722,11 @@ contract LayerEdgeStaking is
722722
emit Staked(userAddr, amount, tier);
723723
}
724724

725-
function _unstake(uint256 amount, address userAddr, bool isNative) internal {
725+
function _unstake(uint256 amount, address userAddr) internal {
726726
UserInfo storage user = users[userAddr];
727727

728728
// require(user.isActive, "No active stake");
729729
require(user.balance >= amount, "Insufficient balance");
730-
require(block.timestamp >= user.depositTime + UNSTAKE_WINDOW, "Unstaking window not reached");
731-
732730
// Update interest before changing balance
733731
_updateInterest(userAddr);
734732

@@ -750,16 +748,32 @@ contract LayerEdgeStaking is
750748
_checkBoundariesAndRecord(true);
751749
}
752750

753-
// Transfer tokens from contract to user
751+
// Add unstake request instead of immediate transfer
752+
unstakeRequests[userAddr].push(UnstakeRequest({amount: amount, timestamp: block.timestamp, completed: false}));
753+
754+
emit UnstakedQueued(userAddr, unstakeRequests[userAddr].length - 1, amount);
755+
}
756+
757+
function _completeUnstake(address userAddr, uint256 index, bool isNative) internal {
758+
UnstakeRequest[] storage requests = unstakeRequests[userAddr];
759+
require(index < requests.length, "Invalid unstake request index");
760+
761+
UnstakeRequest storage request = requests[index];
762+
require(!request.completed, "Unstake request already completed");
763+
require(block.timestamp >= request.timestamp + UNSTAKE_WINDOW, "Unstaking window not reached");
764+
765+
request.completed = true;
766+
767+
// Transfer tokens back to user
754768
if (!isNative) {
755-
require(stakingToken.transfer(userAddr, amount), "Token transfer failed");
769+
require(stakingToken.transfer(userAddr, request.amount), "Token transfer failed");
756770
} else {
757-
IWETH(address(stakingToken)).withdraw(amount);
758-
(bool success,) = payable(userAddr).call{value: amount}("");
771+
IWETH(address(stakingToken)).withdraw(request.amount);
772+
(bool success,) = payable(userAddr).call{value: request.amount}("");
759773
require(success, "Unstake native transfer failed");
760774
}
761775

762-
emit Unstaked(userAddr, amount);
776+
emit Unstaked(userAddr, request.amount);
763777
}
764778

765779
function _claimInterest(address userAddr, bool isNative) internal {

0 commit comments

Comments
 (0)