From a0943e7f18c18815afeabe148c7c4e3f3a571938 Mon Sep 17 00:00:00 2001 From: echo Date: Mon, 17 Jun 2024 23:32:46 +0800 Subject: [PATCH] gring --- src/collator/CollatorStakingHub.sol | 13 ++- src/deposit/Deposit.sol | 30 ++++++- src/deposit/interfaces/IDeposit.sol | 2 + src/governance/GovernanceRing.sol | 123 +++------------------------ src/governance/interfaces/IGRING.sol | 9 ++ 5 files changed, 62 insertions(+), 115 deletions(-) create mode 100644 src/governance/interfaces/IGRING.sol diff --git a/src/collator/CollatorStakingHub.sol b/src/collator/CollatorStakingHub.sol index 516feb7..625d052 100644 --- a/src/collator/CollatorStakingHub.sol +++ b/src/collator/CollatorStakingHub.sol @@ -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"; @@ -69,7 +69,6 @@ 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); } @@ -77,7 +76,6 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet { 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); } @@ -92,6 +90,7 @@ 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 { @@ -99,6 +98,7 @@ contract CollatorStakingHub is ReentrancyGuardUpgradeable, CollatorSet { 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 { @@ -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 { @@ -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 { diff --git a/src/deposit/Deposit.sol b/src/deposit/Deposit.sol index f31b3f8..bd50283 100644 --- a/src/deposit/Deposit.sol +++ b/src/deposit/Deposit.sol @@ -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 @@ -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; @@ -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 @@ -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(); @@ -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. @@ -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; diff --git a/src/deposit/interfaces/IDeposit.sol b/src/deposit/interfaces/IDeposit.sol index 1be39f2..5f44ac3 100644 --- a/src/deposit/interfaces/IDeposit.sol +++ b/src/deposit/interfaces/IDeposit.sol @@ -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; } diff --git a/src/governance/GovernanceRing.sol b/src/governance/GovernanceRing.sol index ab4786a..628484a 100644 --- a/src/governance/GovernanceRing.sol +++ b/src/governance/GovernanceRing.sol @@ -1,54 +1,19 @@ // 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 @@ -56,81 +21,19 @@ contract GovernanceRing is _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) { diff --git a/src/governance/interfaces/IGRING.sol b/src/governance/interfaces/IGRING.sol new file mode 100644 index 0000000..de973b7 --- /dev/null +++ b/src/governance/interfaces/IGRING.sol @@ -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; +}