Skip to content

Commit

Permalink
add the subnet deregistration end timestamp
Browse files Browse the repository at this point in the history
  • Loading branch information
FedokDL committed Dec 18, 2024
1 parent 2685c4a commit 288f49c
Show file tree
Hide file tree
Showing 10 changed files with 412 additions and 325 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,31 @@ import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/
import {UpgradeableBeacon} from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";

import {IProvidersDelegator} from "../interfaces/delegate/IProvidersDelegator.sol";
import {IDelegatorFactory} from "../interfaces/delegate/IDelegatorFactory.sol";
import {IProvidersDelegate} from "../interfaces/delegate/IProvidersDelegate.sol";
import {IDelegateFactory} from "../interfaces/delegate/IDelegateFactory.sol";
import {IOwnable} from "../interfaces/utils/IOwnable.sol";

contract DelegatorFactory is IDelegatorFactory, OwnableUpgradeable, PausableUpgradeable, UUPSUpgradeable {
contract DelegateFactory is IDelegateFactory, OwnableUpgradeable, PausableUpgradeable, UUPSUpgradeable {
address public lumerinDiamond;
address public beacon;

mapping(address => address[]) public proxies;
uint128 public minDeregistrationTimeout;

constructor() {
_disableInitializers();
}

function DelegatorFactory_init(address lumerinDiamond_, address implementation_) external initializer {
function DelegateFactory_init(
address lumerinDiamond_,
address implementation_,
uint128 minDeregistrationTimeout_
) external initializer {
__Pausable_init();
__Ownable_init();
__UUPSUpgradeable_init();

setMinDeregistrationTimeout(minDeregistrationTimeout_);
lumerinDiamond = lumerinDiamond_;

beacon = address(new UpgradeableBeacon(implementation_));
Expand All @@ -39,27 +46,35 @@ contract DelegatorFactory is IDelegatorFactory, OwnableUpgradeable, PausableUpgr
_unpause();
}

function setMinDeregistrationTimeout(uint128 minDeregistrationTimeout_) public onlyOwner {
minDeregistrationTimeout = minDeregistrationTimeout_;

emit MinDeregistrationTimeoutUpdated(minDeregistrationTimeout_);
}

function deployProxy(
address feeTreasury_,
uint256 fee_,
string memory name_,
string memory endpoint_,
uint128 deregistrationTimeout_,
uint128 deregistrationNonFeePeriod_
uint128 deregistrationOpenAt
) external whenNotPaused returns (address) {
if (deregistrationOpenAt <= block.timestamp + minDeregistrationTimeout) {
revert InvalidDeregistrationOpenAt(deregistrationOpenAt, uint128(block.timestamp + minDeregistrationTimeout));
}

bytes32 salt_ = _calculatePoolSalt(_msgSender());
address proxy_ = address(new BeaconProxy{salt: salt_}(beacon, bytes("")));

proxies[_msgSender()].push(proxy_);

IProvidersDelegator(proxy_).ProvidersDelegator_init(
IProvidersDelegate(proxy_).ProvidersDelegate_init(
lumerinDiamond,
feeTreasury_,
fee_,
name_,
endpoint_,
deregistrationTimeout_,
deregistrationNonFeePeriod_
deregistrationOpenAt
);
IOwnable(proxy_).transferOwnership(_msgSender());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,52 +7,51 @@ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

import {PRECISION} from "@solarity/solidity-lib/utils/Globals.sol";

import {IProvidersDelegator} from "../interfaces/delegate/IProvidersDelegator.sol";
import {IProvidersDelegate} from "../interfaces/delegate/IProvidersDelegate.sol";
import {IBidStorage} from "../interfaces/storage/IBidStorage.sol";
import {IProviderRegistry} from "../interfaces/facets/IProviderRegistry.sol";
import {IMarketplace} from "../interfaces/facets/IMarketplace.sol";

contract ProvidersDelegator is IProvidersDelegator, OwnableUpgradeable {
contract ProvidersDelegate is IProvidersDelegate, OwnableUpgradeable {
using SafeERC20 for IERC20;
using Math for uint256;

// Deps
// The contract deps
address public lumerinDiamond;
address public token;

// Fee
// The owner fee
address public feeTreasury;
uint256 public fee;

// Metadata
// The contract metadata
string public name;
string public endpoint;

// Main calculation storage
// The main calculation storage
uint256 public totalStaked;
uint256 public totalRate;
uint256 public lastContractBalance;

// The Staker data
bool public isStakeClosed;
mapping(address => Staker) public stakers;

// Deregistration limits
bool isDeregistered;
uint128 public deregistrationOpenAt;
uint128 public deregistrationTimeout;
uint128 public deregistrationNonFeeOpened;
uint128 public deregistrationNonFeePeriod;

constructor() {
_disableInitializers();
}

function ProvidersDelegator_init(
function ProvidersDelegate_init(
address lumerinDiamond_,
address feeTreasury_,
uint256 fee_,
string memory name_,
string memory endpoint_,
uint128 deregistrationTimeout_,
uint128 deregistrationNonFeePeriod_
uint128 deregistrationOpenAt_
) external initializer {
__Ownable_init();

Expand All @@ -66,13 +65,11 @@ contract ProvidersDelegator is IProvidersDelegator, OwnableUpgradeable {
if (fee_ > PRECISION) {
revert InvalidFee(fee_, PRECISION);
}

fee = fee_;
deregistrationOpenAt = deregistrationOpenAt_;

IERC20(token).approve(lumerinDiamond_, type(uint256).max);

deregistrationTimeout = deregistrationTimeout_;
deregistrationOpenAt = uint128(block.timestamp) + deregistrationTimeout_;
deregistrationNonFeePeriod = deregistrationNonFeePeriod_;
}

function setName(string memory name_) public onlyOwner {
Expand Down Expand Up @@ -118,21 +115,26 @@ contract ProvidersDelegator is IProvidersDelegator, OwnableUpgradeable {
}

function stake(uint256 amount_) external {
_stake(_msgSender(), amount_);
}

function _stake(address staker_, uint256 amount_) private {
if (isStakeClosed) {
revert StakeClosed();
}

if (isDeregistered) {
revert ProviderDeregistered();
}
if (amount_ == 0) {
revert InsufficientAmount();
}

address user_ = _msgSender();
Staker storage staker = stakers[user_];
Staker storage staker = stakers[staker_];

(uint256 currentRate_, uint256 contractBalance_) = getCurrentRate();
uint256 pendingRewards_ = _getCurrentStakerRewards(currentRate_, staker);

IERC20(token).safeTransferFrom(user_, address(this), amount_);
IERC20(token).safeTransferFrom(staker_, address(this), amount_);

totalRate = currentRate_;
totalStaked += amount_;
Expand All @@ -145,51 +147,22 @@ contract ProvidersDelegator is IProvidersDelegator, OwnableUpgradeable {

IProviderRegistry(lumerinDiamond).providerRegister(address(this), amount_, endpoint);

emit Staked(user_, staker.staked, staker.pendingRewards, staker.rate);
}
emit Staked(staker_, staker.staked, totalStaked, staker.rate);
}

function restake(address staker_, uint256 amount_) external {
if (_msgSender() != staker_ && _msgSender() != owner()) {
revert RestakeInvalidCaller(_msgSender(), staker_);
}

Staker storage staker = stakers[staker_];
if (staker.isRestakeDisabled) {
if (_msgSender() == owner() && stakers[staker_].isRestakeDisabled) {
revert RestakeDisabled(staker_);
}

(uint256 currentRate_, uint256 contractBalance_) = getCurrentRate();
uint256 pendingRewards_ = _getCurrentStakerRewards(currentRate_, staker);

amount_ = amount_.min(contractBalance_).min(pendingRewards_);
if (amount_ == 0) {
revert InsufficientAmount();
}

uint256 feeAmount_ = (amount_ * fee) / PRECISION;
uint256 amountWithFee_ = amount_ - feeAmount_;
if (feeAmount_ != 0) {
IERC20(token).safeTransfer(feeTreasury, feeAmount_);

emit FeeClaimed(feeTreasury, feeAmount_);
}

IProviderRegistry(lumerinDiamond).providerRegister(address(this), amountWithFee_, endpoint);

totalRate = currentRate_;
totalStaked += amountWithFee_;

lastContractBalance = contractBalance_ - amount_;

staker.rate = currentRate_;
staker.staked += amountWithFee_;
staker.claimed += amount_;
staker.pendingRewards = pendingRewards_ - amount_;

emit Restaked(staker_, staker.staked, staker.pendingRewards, staker.rate);
amount_ = claim(staker_, amount_);
_stake(staker_, amount_);
}

function claim(address staker_, uint256 amount_) external {
function claim(address staker_, uint256 amount_) public returns (uint256) {
Staker storage staker = stakers[staker_];

(uint256 currentRate_, uint256 contractBalance_) = getCurrentRate();
Expand All @@ -209,7 +182,7 @@ contract ProvidersDelegator is IProvidersDelegator, OwnableUpgradeable {
staker.claimed += amount_;

uint256 feeAmount_ = (amount_ * fee) / PRECISION;
if (feeAmount_ != 0 && block.timestamp > deregistrationNonFeeOpened + deregistrationNonFeePeriod) {
if (feeAmount_ != 0) {
IERC20(token).safeTransfer(feeTreasury, feeAmount_);

amount_ -= feeAmount_;
Expand All @@ -219,7 +192,9 @@ contract ProvidersDelegator is IProvidersDelegator, OwnableUpgradeable {

IERC20(token).safeTransfer(staker_, amount_);

emit Claimed(staker_, staker.staked, staker.pendingRewards, staker.rate);
emit Claimed(staker_, staker.claimed, staker.rate);

return amount_;
}

function getCurrentRate() public view returns (uint256, uint256) {
Expand All @@ -242,30 +217,40 @@ contract ProvidersDelegator is IProvidersDelegator, OwnableUpgradeable {
}

function providerDeregister(bytes32[] calldata bidIds_) external {
if (block.timestamp < deregistrationOpenAt) {
if (!isDeregisterAvailable()) {
_checkOwner();
} else {
deregistrationOpenAt = uint128(block.timestamp) + deregistrationTimeout;
}
if (isDeregistered) {
revert ProviderDeregistered();
}

_deleteModelBids(bidIds_);
IProviderRegistry(lumerinDiamond).providerDeregister(address(this));

deregistrationNonFeeOpened = uint128(block.timestamp);
isDeregistered = true;
fee = 0;
}

function postModelBid(bytes32 modelId_, uint256 pricePerSecond_) external onlyOwner returns (bytes32) {
if (isDeregisterAvailable()) {
revert BidCannotBeCreatedDuringThisPeriod();
}

return IMarketplace(lumerinDiamond).postModelBid(address(this), modelId_, pricePerSecond_);
}

function deleteModelBids(bytes32[] calldata bidIds_) external {
if (block.timestamp < deregistrationOpenAt) {
if (!isDeregisterAvailable()) {
_checkOwner();
}

_deleteModelBids(bidIds_);
}

function isDeregisterAvailable() public view returns (bool) {
return block.timestamp >= deregistrationOpenAt;
}

function _deleteModelBids(bytes32[] calldata bidIds_) private {
address lumerinDiamond_ = lumerinDiamond;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IDelegatorFactory {
interface IDelegateFactory {
error InvalidDeregistrationOpenAt(uint128 value, uint128 minimal);

/**
* The event that is emitted when the proxy deployed.
* @param proxyAddress The pool's id.
*/
event ProxyDeployed(address indexed proxyAddress);

/**
* The event that is emitted when the `minDeregistrationTimeout` changed.
* @param minDeregistrationTimeout_ The pool's id.
*/
event MinDeregistrationTimeoutUpdated(uint128 minDeregistrationTimeout_);

/**
* The function to initialize the contract.
* @param lumerinDiamond_ The Lumerin protocol address.
* @param implementation_ The implementation address.
* @param minDeregistrationTimeout_ The minimal timestamp before deregistration will start
*/
function DelegatorFactory_init(address lumerinDiamond_, address implementation_) external;
function DelegateFactory_init(address lumerinDiamond_, address implementation_, uint128 minDeregistrationTimeout_) external;

/**
* Triggers stopped state.
Expand All @@ -31,17 +40,15 @@ interface IDelegatorFactory {
* @param fee_ The fee percent where 100% = 10^25.
* @param name_ The Subnet name.
* @param endpoint_ The subnet endpoint.
* @param deregistrationTimeout_ Provider deregistration will be available after this timeout.
* @param deregistrationNonFeePeriod_ Period after deregistration when Stakers can claim rewards without fee.
* @param deregistrationOpenAt Provider deregistration will be available after this timestamp.
* @return Deployed proxy address
*/
function deployProxy(
address feeTreasury_,
uint256 fee_,
string memory name_,
string memory endpoint_,
uint128 deregistrationTimeout_,
uint128 deregistrationNonFeePeriod_
uint128 deregistrationOpenAt
) external returns (address);

/**
Expand Down
Loading

0 comments on commit 288f49c

Please sign in to comment.