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

feat: first implementation of the service staking #116

Merged
merged 17 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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
1 change: 1 addition & 0 deletions .gitleaksignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ d780294b8dccf047391a71b75dade50a6b89003b:scripts/deployment/l2/globals_gnosis_ch
7d2fbdb1556dc3ed289edf7e116b378b6f6a9d83:scripts/deployment/l2/bridges/gnosis/test_service_registry_token_utility_change_drainer.js:generic-api-key:44
7bb76e80382eff3c538af340289ed8241f4e4551:scripts/deployment/l2/globals_gnosis_mainnet.json:generic-api-key:1
7bb76e80382eff3c538af340289ed8241f4e4551:scripts/deployment/l2/globals_gnosis_mainnet.json:generic-api-key:2
233a57ccda8d5d84a86133422daa99807801d9fc:scripts/deployment/l2/globals_gnosis_mainnet.json:generic-api-key:2
4 changes: 4 additions & 0 deletions contracts/interfaces/IMultisig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@ interface IMultisig {
uint256 threshold,
bytes memory data
) external returns (address multisig);

/// @dev Gets the multisig nonce.
/// @return Multisig nonce.
function nonce() external returns (uint256);
}
27 changes: 26 additions & 1 deletion contracts/interfaces/IService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.15;

/// @dev Required interface for the service manipulation.
interface IService{
interface IService {
struct AgentParams {
// Number of agent instances
uint32 slots;
Expand Down Expand Up @@ -87,4 +87,29 @@ interface IService{
/// @return success True, if function executed successfully.
/// @return refund The amount of refund returned to the operator.
function unbond(address operator, uint256 serviceId) external returns (bool success, uint256 refund);

/// @dev Transfers the service that was previously approved to this contract address.
/// @param from Account address to transfer from.
/// @param to Account address to transfer to.
/// @param id Service Id.
function transferFrom(address from, address to, uint256 id) external;

/// @dev Gets the service instance from the map of services.
/// @param serviceId Service Id.
/// @return securityDeposit Registration activation deposit.
/// @return multisig Service multisig address.
/// @return configHash IPFS hashes pointing to the config metadata.
/// @return threshold Agent instance signers threshold.
/// @return maxNumAgentInstances Total number of agent instances.
/// @return numAgentInstances Actual number of agent instances.
/// @return state Service state.
function mapServices(uint256 serviceId) external view returns (
uint96 securityDeposit,
address multisig,
bytes32 configHash,
uint32 threshold,
uint32 maxNumAgentInstances,
uint32 numAgentInstances,
uint8 state
);
}
6 changes: 6 additions & 0 deletions contracts/interfaces/IServiceTokenUtility.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,10 @@ interface IServiceTokenUtility {
/// @param serviceId Service Id.
/// @return True if the service Id is token secured.
function isTokenSecuredService(uint256 serviceId) external view returns (bool);

/// @dev Gets the service security token info.
/// @param serviceId Service Id.
/// @return Token address.
/// @return Token security deposit.
function mapServiceIdTokenDeposit(uint256 serviceId) external view returns (address, uint96);
}
44 changes: 44 additions & 0 deletions contracts/interfaces/IToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

/// @dev Generic token interface for IERC20 and IERC721 tokens.
interface IToken {
/// @dev Gets the amount of tokens owned by a specified account.
/// @param account Account address.
/// @return Amount of tokens owned.
function balanceOf(address account) external view returns (uint256);

/// @dev Gets the owner of the token Id.
/// @param tokenId Token Id.
/// @return Token Id owner address.
function ownerOf(uint256 tokenId) external view returns (address);

/// @dev Gets the total amount of tokens stored by the contract.
/// @return Amount of tokens.
function totalSupply() external view returns (uint256);

/// @dev Transfers the token amount.
/// @param to Address to transfer to.
/// @param amount The amount to transfer.
/// @return True if the function execution is successful.
function transfer(address to, uint256 amount) external returns (bool);

/// @dev Gets remaining number of tokens that the `spender` can transfer on behalf of `owner`.
/// @param owner Token owner.
/// @param spender Account address that is able to transfer tokens on behalf of the owner.
/// @return Token amount allowed to be transferred.
function allowance(address owner, address spender) external view returns (uint256);

/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
/// @param spender Account address that will be able to transfer tokens on behalf of the caller.
/// @param amount Token amount.
/// @return True if the function execution is successful.
function approve(address spender, uint256 amount) external returns (bool);

/// @dev Transfers the token amount that was previously approved up until the maximum allowance.
/// @param from Account address to transfer from.
/// @param to Account address to transfer to.
/// @param amount Amount to transfer to.
/// @return True if the function execution is successful.
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
67 changes: 67 additions & 0 deletions contracts/staking/ServiceStaking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {ServiceStakingBase} from "./ServiceStakingBase.sol";
import "../interfaces/IService.sol";

/// @title ServiceStakingToken - Smart contract for staking a service by its owner when the service has an ETH as the deposit
/// @author Aleksandr Kuperman - <[email protected]>
/// @author Andrey Lebedev - <[email protected]>
/// @author Mariapia Moscatiello - <[email protected]>
contract ServiceStaking is ServiceStakingBase {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call it ServiceStakingNativeToken?

/// @dev ServiceStaking constructor.
/// @param _apy Staking APY (in single digits).
/// @param _minStakingDeposit Minimum security deposit for a service to be eligible to stake.
/// @param _stakingRatio Staking ratio: number of seconds per nonce (in 18 digits).
DavidMinarsch marked this conversation as resolved.
Show resolved Hide resolved
/// @param _serviceRegistry ServiceRegistry contract address.
constructor(uint256 _apy, uint256 _minStakingDeposit, uint256 _stakingRatio, address _serviceRegistry)
ServiceStakingBase(_apy, _minStakingDeposit, _stakingRatio, _serviceRegistry)
{
// TODO: calculate minBalance
}

/// @dev Checks token security deposit.
/// @param serviceId Service Id.
function _checkTokenSecurityDeposit(uint256 serviceId) internal view override {
// Get the service security token and deposit
(uint96 stakingDeposit, , , , , , ) = IService(serviceRegistry).mapServices(serviceId);

// The security deposit must be greater or equal to the minimum defined one
if (stakingDeposit < minStakingDeposit) {
revert();
}
}

/// @dev Withdraws the reward amount to a service owner.
/// @param to Address to.
/// @param amount Amount to withdraw.
function _withdraw(address to, uint256 amount) internal override {
// Update the contract balance
balance -= amount;

// Transfer the amount
(bool result, ) = to.call{value: amount}("");
if (!result) {
revert TransferFailed(address(0), address(this), to, amount);
}
}

receive() external payable {
// Distribute current staking rewards
_checkpoint(0);

// Add to the contract and available rewards balances
uint256 newBalance = balance + msg.value;
uint256 newAvailableRewards = availableRewards + msg.value;

// Update rewards per second
uint256 newRewardsPerSecond = (newAvailableRewards * apy) / (100 * 365 days);
rewardsPerSecond = newRewardsPerSecond;

// Record the new actual balance and available rewards
balance = newBalance;
availableRewards = newAvailableRewards;

emit Deposit(msg.sender, msg.value, newBalance, newAvailableRewards, newRewardsPerSecond);
}
}
Loading