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

gRING #12

Merged
merged 1 commit into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 9 additions & 4 deletions src/collator/CollatorStakingHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "./interfaces/INominationPool.sol";
import "./interfaces/IGRING.sol";
import "../governance/interfaces/IGRING.sol";
import "../deposit/interfaces/IDeposit.sol";
import "./interfaces/INominationPool.sol";
import "./NominationPool.sol";
import "./CollatorSet.sol";

Expand Down Expand Up @@ -69,15 +69,13 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
address pool = poolOf[collator];
require(pool != address(0), "!collator");
INominationPool(pool).stake(account, assets);
IGRING(gRING).mint(account, assets);
emit Staked(pool, collator, account, assets);
}

function _unstake(address collator, address account, uint256 assets) internal {
require(stakingLocks[collator][account] < block.timestamp, "!locked");
address pool = poolOf[collator];
require(pool != address(0), "!collator");
IGRING(gRING).burn(account, assets);
INominationPool(pool).withdraw(account, assets);
emit Unstaked(pool, collator, account, assets);
}
Expand All @@ -92,13 +90,15 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
_stake(collator, msg.sender, msg.value);
_increaseVotes(collator, _assetsToVotes(commissionOf[collator], msg.value), oldPrev, newPrev);
stakedRINGOf[msg.sender] += msg.value;
IGRING(gRING).depositFor{value: msg.value}(msg.sender);
}

function unstakeRING(address collator, uint256 assets, address oldPrev, address newPrev) public nonReentrant {
_unstake(collator, msg.sender, assets);
payable(msg.sender).sendValue(assets);
_reduceVotes(collator, _assetsToVotes(commissionOf[collator], assets), oldPrev, newPrev);
stakedRINGOf[msg.sender] -= assets;
IGRING(gRING).withdrawTo(msg.sender, assets);
}

function stakeDeposit(address collator, uint256 depositId, address oldPrev, address newPrev) public nonReentrant {
Expand All @@ -110,6 +110,8 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
_stake(collator, account, assets);
_increaseVotes(collator, _assetsToVotes(commissionOf[collator], assets), oldPrev, newPrev);
require(_stakedDeposits[account].add(depositId), "!add");
IDeposit(DEPOSIT).lock(depositId);
IGRING(gRING).transferFrom(address(this), account, assets);
}

function unstakeDeposit(uint256 depositId, address oldPrev, address newPrev) public nonReentrant {
Expand All @@ -122,6 +124,9 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet {
_unstake(info.collator, info.account, info.assets);
_reduceVotes(info.collator, _assetsToVotes(commissionOf[info.collator], info.assets), oldPrev, newPrev);
require(_stakedDeposits[account].remove(depositId), "!remove");
IGRING(gRING).transferFrom(account, address(this), info.assets);
IDeposit(DEPOSIT).unlock(depositId);
payable(account).sendValue(info.assets);
}

function collect(uint256 commission, address oldPrev, address newPrev) public nonReentrant {
Expand Down
30 changes: 29 additions & 1 deletion src/deposit/Deposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721Enumer
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../governance/interfaces/IGRING.sol";
import "./interfaces/IKTON.sol";

contract Deposit is
Expand All @@ -22,6 +23,8 @@ contract Deposit is
// precision = 10_000
uint256[37] public INTERESTS;
uint256 private _nextTokenId;
// Governance RING
IGRING public gRING;

struct DepositInfo {
uint64 months;
Expand All @@ -30,6 +33,7 @@ contract Deposit is
}

mapping(uint256 => DepositInfo) public depositOf;
mapping(uint256 => address) public stakedOf;

uint256 public constant MONTH = 30 days;
// Deposit Pallet Account
Expand All @@ -44,13 +48,16 @@ contract Deposit is
);
event DepositClaimed(uint256 indexed depositId, address indexed account, uint256 value);
event ClaimWithPenalty(uint256 indexed depositId, address indexed account, uint256 penalty);
event Lock(uint256 indexed depositId, address sender);
event UnLock(uint256 indexed depositId, address sender);

modifier onlySystem() {
require(msg.sender == DEPOSIT_PALLET);
_;
}

function initialize(string memory name, string memory symbol) public initializer {
function initialize(address gring, string memory name, string memory symbol) public initializer {
gRING = IGRING(gring);
__DepositInterest_init();
__ERC721_init(name, symbol);
__ERC721Enumerable_init();
Expand Down Expand Up @@ -105,6 +112,8 @@ contract Deposit is
_disableInitializers();
}

receive() external payable {}

/// @dev Migrate user's deposit from Deposit Pallet to Deposit smart contract.
/// The amount of the deposit value must be passed in via msg.value.
/// @notice Only Deposit Pallet Account could call this function.
Expand Down Expand Up @@ -146,6 +155,25 @@ contract Deposit is
emit ClaimWithPenalty(depositId, msg.sender, penalty);
}

function lock(uint256 depositId) public nonReentrant {
address sender = msg.sender;
transferFrom(sender, address(this), depositId);
stakedOf[depositId] = sender;
uint256 assets = assetsOf(depositId);
gRING.depositFor{value: assets}(sender);
emit Lock(sender, depositId);
}

function unlock(uint256 depositId) public nonReentrant {
address sender = msg.sender;
require(stakedOf[depositId] == sender, "!auth");
transferFrom(address(this), sender, depositId);
delete stakedOf[depositId];
uint256 assets = assetsOf(depositId);
gRING.withdrawTo(sender, assets);
emit UnLock(sender, depositId);
}

function assetsOf(uint256 id) public view returns (uint256) {
_requireOwned(id);
return depositOf[id].value;
Expand Down
2 changes: 2 additions & 0 deletions src/deposit/interfaces/IDeposit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import "@openzeppelin/contracts/token/ERC721/IERC721.sol";

interface IDeposit is IERC721 {
function assetsOf(uint256 id) external view returns (uint256);
function lock(uint256 depositId) external;
function unlock(uint256 depositId) external;
}
123 changes: 13 additions & 110 deletions src/governance/GovernanceRing.sol
Original file line number Diff line number Diff line change
@@ -1,136 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "../deposit/interfaces/IDeposit.sol";

contract GovernanceRing is
Initializable,
ERC20Upgradeable,
AccessControlEnumerableUpgradeable,
ERC20PermitUpgradeable,
ERC20VotesUpgradeable,
ReentrancyGuardUpgradeable
{
using Address for address payable;
using EnumerableSet for EnumerableSet.UintSet;
contract GovernanceRing is Initializable, ERC20Upgradeable, ERC20PermitUpgradeable, ERC20VotesUpgradeable {
event Deposit(address indexed dst, uint256 wad);
event Withdrawal(address indexed src, uint256 wad);

// depositId => user
mapping(uint256 => address) public depositorOf;
// user => token => assets
mapping(address => mapping(address => uint256)) public wrapAssets;
// user => wrap depositIds
mapping(address => EnumerableSet.UintSet) private _wrapDeposits;
IDeposit public DEPOSIT;

address public constant RING = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

event Wrap(address indexed account, address indexed token, uint256 assets);
event Unwrap(address indexed account, address indexed token, uint256 assets);
event WrapDeposit(address indexed account, address indexed token, uint256 depositId);
event UnwrapDeposit(address indexed account, address indexed token, uint256 depositId);

function initialize(address admin, address dps, string memory name, string memory symbol) public initializer {
DEPOSIT = IDeposit(dps);
function initialize(string memory name, string memory symbol) public initializer {
__ERC20_init(name, symbol);
__AccessControlEnumerable_init();
__ERC20Permit_init(symbol);
__ERC20Votes_init();
__ReentrancyGuard_init();

_grantRole(DEFAULT_ADMIN_ROLE, admin);
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}

function mint(address to, uint256 assets) public onlyRole(MINTER_ROLE) {
_mint(to, assets);
}

function burn(address from, uint256 assets) public onlyRole(BURNER_ROLE) {
_burn(from, assets);
}

function _wrap(address account, address token, uint256 assets) internal {
_mint(account, assets);
wrapAssets[account][token] += assets;
emit Wrap(account, token, assets);
}

function _unwrap(address account, address token, uint256 assets) internal {
_burn(account, assets);
wrapAssets[account][token] -= assets;
emit Unwrap(account, token, assets);
}

function wrapRING() public payable nonReentrant {
_wrap(msg.sender, RING, msg.value);
}

function unwrapRING(uint256 assets) public nonReentrant {
_unwrap(msg.sender, RING, assets);
payable(msg.sender).sendValue(assets);
}

function wrapDeposit(uint256 depositId) public nonReentrant {
address account = msg.sender;
DEPOSIT.transferFrom(account, address(this), depositId);
uint256 assets = DEPOSIT.assetsOf(depositId);
depositorOf[depositId] = account;
require(_wrapDeposits[account].add(depositId), "!add");
_wrap(account, address(DEPOSIT), assets);
emit WrapDeposit(account, address(DEPOSIT), assets);
}

function unwrapDeposit(uint256 depositId) public nonReentrant {
address account = msg.sender;
require(depositorOf[depositId] == account, "!account");
uint256 assets = DEPOSIT.assetsOf(depositId);
_unwrap(account, address(DEPOSIT), assets);
require(_wrapDeposits[account].remove(depositId), "!remove");
DEPOSIT.transferFrom(address(this), account, depositId);
emit UnwrapDeposit(account, address(DEPOSIT), depositId);
}

function wrapDepositsOf(address account) public view returns (uint256[] memory) {
return _wrapDeposits[account].values();
}

function wrapDepositsLength(address account) public view returns (uint256) {
return _wrapDeposits[account].length();
}

function wrapDepositsAt(address account, uint256 index) public view returns (uint256) {
return _wrapDeposits[account].at(index);
}

function wrapDepositsContains(address account, uint256 depositId) public view returns (bool) {
return _wrapDeposits[account].contains(depositId);
}

function transfer(address, uint256) public override returns (bool) {
revert();
receive() external payable {
depositFor(msg.sender);
}

function transferFrom(address, address, uint256) public override returns (bool) {
revert();
function depositFor(address account) public payable {
_mint(account, msg.value);
emit Deposit(account, msg.value);
}

function approve(address, uint256) public override returns (bool) {
revert();
function withdrawTo(address account, uint256 wad) public {
_burn(msg.sender, wad);
payable(msg.sender).transfer(wad);
emit Withdrawal(account, wad);
}

function clock() public view override returns (uint48) {
Expand Down
9 changes: 9 additions & 0 deletions src/governance/interfaces/IGRING.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface IGRING is IERC20 {
function depositFor(address account) external payable;
function withdrawTo(address account, uint256 wad) external;
}
Loading