From 14bb680e72c387e98c48ec13b3dcda46acc090a8 Mon Sep 17 00:00:00 2001 From: Mike-CZ Date: Thu, 14 Nov 2024 15:53:52 +0100 Subject: [PATCH 1/5] Remove unlock penalty and withdraw period --- contracts/sfc/SFCLib.sol | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/contracts/sfc/SFCLib.sol b/contracts/sfc/SFCLib.sol index 1a9a631..79d0e3a 100644 --- a/contracts/sfc/SFCLib.sol +++ b/contracts/sfc/SFCLib.sol @@ -227,15 +227,15 @@ contract SFCLib is SFCBase { require(request.epoch != 0, "request doesn't exist"); require(_checkAllowedToWithdraw(delegator, toValidatorID), "outstanding sFTM balance"); - uint256 requestTime = request.time; - uint256 requestEpoch = request.epoch; - if (getValidator[toValidatorID].deactivatedTime != 0 && getValidator[toValidatorID].deactivatedTime < requestTime) { - requestTime = getValidator[toValidatorID].deactivatedTime; - requestEpoch = getValidator[toValidatorID].deactivatedEpoch; - } - - require(_now() >= requestTime + c.withdrawalPeriodTime(), "not enough time passed"); - require(currentEpoch() >= requestEpoch + c.withdrawalPeriodEpochs(), "not enough epochs passed"); +// uint256 requestTime = request.time; +// uint256 requestEpoch = request.epoch; +// if (getValidator[toValidatorID].deactivatedTime != 0 && getValidator[toValidatorID].deactivatedTime < requestTime) { +// requestTime = getValidator[toValidatorID].deactivatedTime; +// requestEpoch = getValidator[toValidatorID].deactivatedEpoch; +// } +// +// require(_now() >= requestTime + c.withdrawalPeriodTime(), "not enough time passed"); +// require(currentEpoch() >= requestEpoch + c.withdrawalPeriodEpochs(), "not enough epochs passed"); uint256 amount = getWithdrawalRequest[delegator][toValidatorID][wrID].amount; bool isCheater = isSlashed(toValidatorID); @@ -525,15 +525,16 @@ contract SFCLib is SFCBase { _stashRewards(delegator, toValidatorID); - uint256 penalty = _popWholeUnlockPenalty(delegator, toValidatorID, amount, ld.lockedStake); - if (penalty > amount) { - penalty = amount; - } + uint256 penalty = 0; +// uint256 penalty = _popWholeUnlockPenalty(delegator, toValidatorID, amount, ld.lockedStake); +// if (penalty > amount) { +// penalty = amount; +// } ld.lockedStake -= amount; - if (penalty != 0) { - _rawUndelegate(delegator, toValidatorID, penalty, true, false, false); - treasuryAddress.call.value(penalty)(""); - } +// if (penalty != 0) { +// _rawUndelegate(delegator, toValidatorID, penalty, true, false, false); +// treasuryAddress.call.value(penalty)(""); +// } emit UnlockedStake(delegator, toValidatorID, amount, penalty); return penalty; From 550aa5db6b7460f977128e4e6b78bc46930d6fb5 Mon Sep 17 00:00:00 2001 From: Mike-CZ Date: Fri, 15 Nov 2024 09:27:30 +0100 Subject: [PATCH 2/5] Let redirected validators unlock --- contracts/sfc/SFCLib.sol | 69 ++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/contracts/sfc/SFCLib.sol b/contracts/sfc/SFCLib.sol index 79d0e3a..d247016 100644 --- a/contracts/sfc/SFCLib.sol +++ b/contracts/sfc/SFCLib.sol @@ -227,15 +227,15 @@ contract SFCLib is SFCBase { require(request.epoch != 0, "request doesn't exist"); require(_checkAllowedToWithdraw(delegator, toValidatorID), "outstanding sFTM balance"); -// uint256 requestTime = request.time; -// uint256 requestEpoch = request.epoch; -// if (getValidator[toValidatorID].deactivatedTime != 0 && getValidator[toValidatorID].deactivatedTime < requestTime) { -// requestTime = getValidator[toValidatorID].deactivatedTime; -// requestEpoch = getValidator[toValidatorID].deactivatedEpoch; -// } -// -// require(_now() >= requestTime + c.withdrawalPeriodTime(), "not enough time passed"); -// require(currentEpoch() >= requestEpoch + c.withdrawalPeriodEpochs(), "not enough epochs passed"); + uint256 requestTime = request.time; + uint256 requestEpoch = request.epoch; + if (getValidator[toValidatorID].deactivatedTime != 0 && getValidator[toValidatorID].deactivatedTime < requestTime) { + requestTime = getValidator[toValidatorID].deactivatedTime; + requestEpoch = getValidator[toValidatorID].deactivatedEpoch; + } + + require(_now() >= requestTime + c.withdrawalPeriodTime(), "not enough time passed"); + require(currentEpoch() >= requestEpoch + c.withdrawalPeriodEpochs(), "not enough epochs passed"); uint256 amount = getWithdrawalRequest[delegator][toValidatorID][wrID].amount; bool isCheater = isSlashed(toValidatorID); @@ -486,25 +486,27 @@ contract SFCLib is SFCBase { } function _popNonStashedUnlockPenalty(address delegator, uint256 toValidatorID, uint256 unlockAmount, uint256 totalAmount) internal returns (uint256) { - Rewards storage r = getStashedLockupRewards[delegator][toValidatorID]; - uint256 lockupExtraRewardShare = r.lockupExtraReward.mul(unlockAmount).div(totalAmount); - uint256 lockupBaseRewardShare = r.lockupBaseReward.mul(unlockAmount).div(totalAmount); - uint256 penalty = lockupExtraRewardShare + lockupBaseRewardShare / 2; - r.lockupExtraReward = r.lockupExtraReward.sub(lockupExtraRewardShare); - r.lockupBaseReward = r.lockupBaseReward.sub(lockupBaseRewardShare); - return penalty; +// Rewards storage r = getStashedLockupRewards[delegator][toValidatorID]; +// uint256 lockupExtraRewardShare = r.lockupExtraReward.mul(unlockAmount).div(totalAmount); +// uint256 lockupBaseRewardShare = r.lockupBaseReward.mul(unlockAmount).div(totalAmount); +// uint256 penalty = lockupExtraRewardShare + lockupBaseRewardShare / 2; +// r.lockupExtraReward = r.lockupExtraReward.sub(lockupExtraRewardShare); +// r.lockupBaseReward = r.lockupBaseReward.sub(lockupBaseRewardShare); +// return penalty; + return 0; } function _popStashedUnlockPenalty(address delegator, uint256 toValidatorID, uint256 unlockAmount, uint256 totalAmount) internal returns (uint256) { _delStalePenalties(delegator, toValidatorID); - Penalty[] storage penalties = getStashedPenalties[delegator][toValidatorID]; - uint256 total = 0; - for (uint256 i = 0; i < penalties.length; i++) { - uint256 penalty = penalties[i].amount.mul(unlockAmount).div(totalAmount); - penalties[i].amount = penalties[i].amount.sub(penalty); - total = total.add(penalty); - } - return total; +// Penalty[] storage penalties = getStashedPenalties[delegator][toValidatorID]; +// uint256 total = 0; +// for (uint256 i = 0; i < penalties.length; i++) { +// uint256 penalty = penalties[i].amount.mul(unlockAmount).div(totalAmount); +// penalties[i].amount = penalties[i].amount.sub(penalty); +// total = total.add(penalty); +// } +// return total; + return 0; } function _popWholeUnlockPenalty(address delegator, uint256 toValidatorID, uint256 unlockAmount, uint256 totalAmount) internal returns (uint256) { @@ -521,20 +523,19 @@ contract SFCLib is SFCBase { require(isLockedUp(delegator, toValidatorID), "not locked up"); require(amount <= ld.lockedStake, "not enough locked stake"); require(_checkAllowedToWithdraw(delegator, toValidatorID), "outstanding sFTM balance"); - require(!_redirected(delegator), "redirected"); +// require(!_redirected(delegator), "redirected"); _stashRewards(delegator, toValidatorID); - uint256 penalty = 0; -// uint256 penalty = _popWholeUnlockPenalty(delegator, toValidatorID, amount, ld.lockedStake); -// if (penalty > amount) { -// penalty = amount; -// } + uint256 penalty = _popWholeUnlockPenalty(delegator, toValidatorID, amount, ld.lockedStake); + if (penalty > amount) { + penalty = amount; + } ld.lockedStake -= amount; -// if (penalty != 0) { -// _rawUndelegate(delegator, toValidatorID, penalty, true, false, false); -// treasuryAddress.call.value(penalty)(""); -// } + if (penalty != 0) { + _rawUndelegate(delegator, toValidatorID, penalty, true, false, false); + treasuryAddress.call.value(penalty)(""); + } emit UnlockedStake(delegator, toValidatorID, amount, penalty); return penalty; From 520d7d0a87c0bbc9360b21a2f585d231e57f6177 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 16 Dec 2024 12:22:52 +0100 Subject: [PATCH 3/5] Disable delegations and allow whitelisted address to undelegate fantom validators --- contracts/sfc/SFCLib.sol | 42 ++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/contracts/sfc/SFCLib.sol b/contracts/sfc/SFCLib.sol index d247016..41c57ad 100644 --- a/contracts/sfc/SFCLib.sol +++ b/contracts/sfc/SFCLib.sol @@ -93,7 +93,7 @@ contract SFCLib is SFCBase { } function delegate(uint256 toValidatorID) external payable { - _delegate(msg.sender, toValidatorID, msg.value); + _delegate(_addrToValidator(msg.sender, toValidatorID), toValidatorID, msg.value); } function _delegate(address delegator, uint256 toValidatorID, uint256 amount) internal { @@ -104,6 +104,9 @@ contract SFCLib is SFCBase { } function _rawDelegate(address delegator, uint256 toValidatorID, uint256 amount, bool strict) internal { + // Delegations are disabled to protect current chain while migrating to the new sonic chain + revert("delegation disabled"); + require(amount > 0, "zero amount"); _stashRewards(delegator, toValidatorID); @@ -154,7 +157,7 @@ contract SFCLib is SFCBase { } function undelegate(uint256 toValidatorID, uint256 wrID, uint256 amount) public { - address delegator = msg.sender; + address delegator = _addrToValidator(msg.sender, toValidatorID); _stashRewards(delegator, toValidatorID); @@ -253,7 +256,8 @@ contract SFCLib is SFCBase { } function withdraw(uint256 toValidatorID, uint256 wrID) public { - _withdraw(msg.sender, toValidatorID, wrID, _receiverOf(msg.sender)); + address payable sender = address(uint160(_addrToValidator(msg.sender, toValidatorID))); + _withdraw(sender, toValidatorID, wrID, _receiverOf(msg.sender)); } function deactivateValidator(uint256 validatorID, uint256 status) external onlyDriver { @@ -380,7 +384,7 @@ contract SFCLib is SFCBase { } function claimRewards(uint256 toValidatorID) public { - address payable delegator = msg.sender; + address delegator = _addrToValidator(msg.sender, toValidatorID); Rewards memory rewards = _claimRewards(delegator, toValidatorID); // It's important that we transfer after erasing (protection against Re-Entrancy) (bool sent,) = _receiverOf(delegator).call.value(rewards.lockupExtraReward.add(rewards.lockupBaseReward).add(rewards.unlockedReward))(""); @@ -390,7 +394,7 @@ contract SFCLib is SFCBase { } function restakeRewards(uint256 toValidatorID) public { - address delegator = msg.sender; + address delegator = _addrToValidator(msg.sender, toValidatorID); Rewards memory rewards = _claimRewards(delegator, toValidatorID); uint256 lockupReward = rewards.lockupExtraReward.add(rewards.lockupBaseReward); @@ -473,14 +477,14 @@ contract SFCLib is SFCBase { } function lockStake(uint256 toValidatorID, uint256 lockupDuration, uint256 amount) public { - address delegator = msg.sender; + address delegator = _addrToValidator(msg.sender, toValidatorID); require(amount > 0, "zero amount"); require(!isLockedUp(delegator, toValidatorID), "already locked up"); _lockStake(delegator, toValidatorID, lockupDuration, amount, false); } function relockStake(uint256 toValidatorID, uint256 lockupDuration, uint256 amount) public { - address delegator = msg.sender; + address delegator = _addrToValidator(msg.sender, toValidatorID); require(isLockedUp(delegator, toValidatorID), "not locked up"); _lockStake(delegator, toValidatorID, lockupDuration, amount, true); } @@ -516,7 +520,7 @@ contract SFCLib is SFCBase { } function unlockStake(uint256 toValidatorID, uint256 amount) external returns (uint256) { - address delegator = msg.sender; + address delegator = _addrToValidator(msg.sender, toValidatorID); LockedDelegation storage ld = getLockupInfo[delegator][toValidatorID]; require(amount > 0, "zero amount"); @@ -653,6 +657,28 @@ contract SFCLib is SFCBase { return address(uint160(to)); } + /// Convert an address to validator's address if the address is white-listed address. + /// Converts only to validators managed by Fantom Foundation. It is necessary to enable + /// smooth transition to the new sonic chain. + function _addrToValidator(address addr, uint256 validatorID) internal view returns(address) { + // Fantom's wallet to make requests from + if (addr != 0x0b2E90c831626A65a26f75153Be54aeaAeeb8363) { + return addr; + } + + // Fantom's validators <1,11>, 64 + if ((validatorID < 1 || validatorID > 11) && validatorID != 64) { + return addr; + } + + address validatorAddr = getValidator[validatorID].auth; + if (validatorAddr == address(0)) { + return addr; + } + + return validatorAddr; + } + // code below can be erased after 1 year since deployment of multipenalties function _getAvgEpochStep(uint256 duration) internal view returns(uint256) { From d5b40d2f972b2fe2a6cd9a8c1a31ec00d5065a2d Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 16 Dec 2024 15:32:56 +0100 Subject: [PATCH 4/5] Disable locks, reduce total supply on burn --- contracts/sfc/SFCLib.sol | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/contracts/sfc/SFCLib.sol b/contracts/sfc/SFCLib.sol index 41c57ad..ce782a2 100644 --- a/contracts/sfc/SFCLib.sol +++ b/contracts/sfc/SFCLib.sol @@ -257,7 +257,7 @@ contract SFCLib is SFCBase { function withdraw(uint256 toValidatorID, uint256 wrID) public { address payable sender = address(uint160(_addrToValidator(msg.sender, toValidatorID))); - _withdraw(sender, toValidatorID, wrID, _receiverOf(msg.sender)); + _withdraw(sender, toValidatorID, wrID, _receiverOf(sender)); } function deactivateValidator(uint256 validatorID, uint256 status) external onlyDriver { @@ -408,8 +408,14 @@ contract SFCLib is SFCBase { _burnFTM(amount); } + function burnNativeTokens() external payable { + require(msg.value > 0, "No amount sent"); + _burnFTM(msg.value); + } + function _burnFTM(uint256 amount) internal { - if (amount != 0) { + if (amount != 0 && totalSupply >= amount) { + totalSupply -= amount; address(0).transfer(amount); emit BurntFTM(amount); } @@ -438,6 +444,9 @@ contract SFCLib is SFCBase { } function _lockStake(address delegator, uint256 toValidatorID, uint256 lockupDuration, uint256 amount, bool relock) internal { + // Locks are disabled due to chain migrating to the new sonic chain + revert("stake lock disabled"); + require(!_redirected(delegator), "redirected"); require(amount <= getUnlockedStake(delegator, toValidatorID), "not enough stake"); require(getValidator[toValidatorID].status == OK_STATUS, "validator isn't active"); From 0cde51e815d3518ce17c6566c7b4c671da92cc9c Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 16 Dec 2024 16:58:48 +0100 Subject: [PATCH 5/5] Revert when amount too big --- contracts/sfc/SFCLib.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/sfc/SFCLib.sol b/contracts/sfc/SFCLib.sol index ce782a2..09b1d4b 100644 --- a/contracts/sfc/SFCLib.sol +++ b/contracts/sfc/SFCLib.sol @@ -414,7 +414,8 @@ contract SFCLib is SFCBase { } function _burnFTM(uint256 amount) internal { - if (amount != 0 && totalSupply >= amount) { + if (amount != 0) { + require(totalSupply >= amount, "Amount to burn too large"); totalSupply -= amount; address(0).transfer(amount); emit BurntFTM(amount);