-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #13 from swamp-thing-sovryn/multiple-LM-tokens-rew…
…ards feat: new liquidity mining that rewards in multiple tokens
- Loading branch information
Showing
38 changed files
with
8,594 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
pragma solidity 0.5.17; | ||
|
||
import "./IRewardTransferLogic.sol"; | ||
import "./ERC20TransferLogicStorage.sol"; | ||
import "../interfaces/IERC20.sol"; | ||
import "../openzeppelin/SafeERC20.sol"; | ||
|
||
contract ERC20TransferLogic is IRewardTransferLogic, ERC20TransferLogicStorage { | ||
using SafeERC20 for IERC20; | ||
|
||
event TokenAddressUpdated(address _newTokenAddress); | ||
|
||
/** | ||
* @param _token Reward token to be distributed | ||
*/ | ||
function initialize(address _token) public onlyAuthorized { | ||
setTokenAddress(_token); | ||
} | ||
|
||
function setTokenAddress(address _token) public onlyAuthorized { | ||
require(_token != address(0), "Invalid token address"); | ||
token = IERC20(_token); | ||
emit TokenAddressUpdated(_token); | ||
} | ||
|
||
function getRewardTokenAddress() external view returns (address) { | ||
return address(token); | ||
} | ||
|
||
function senderToAuthorize() external view returns (address) { | ||
return address(this); | ||
} | ||
|
||
function transferReward( | ||
address _to, | ||
uint256 _value, | ||
bool // it doesn't matter if it's a withdrawal or not | ||
) external { | ||
token.safeTransferFrom(msg.sender, _to, _value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
pragma solidity 0.5.17; | ||
|
||
import "./IRewardTransferLogic.sol"; | ||
import "../utils/AdminRole.sol"; | ||
import "../interfaces/IERC20.sol"; | ||
|
||
contract ERC20TransferLogicStorage is IRewardTransferLogic, AdminRole { | ||
IERC20 public token; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
pragma solidity 0.5.17; | ||
|
||
interface ILiquidityMiningV1 { | ||
function withdraw( | ||
address _poolToken, | ||
uint256 _amount, | ||
address _user | ||
) external; | ||
|
||
function onTokensDeposited(address _user, uint256 _amount) external; | ||
|
||
function getUserPoolTokenBalance(address _poolToken, address _user) external view returns (uint256); | ||
|
||
function getPoolInfoListArray() | ||
external | ||
view | ||
returns ( | ||
address[] memory, | ||
uint96[] memory, | ||
uint256[] memory, | ||
uint256[] memory | ||
); | ||
|
||
function getUserInfoListArray(address _user) | ||
external | ||
view | ||
returns ( | ||
uint256[] memory, | ||
uint256[] memory, | ||
uint256[] memory | ||
); | ||
|
||
function migrateFunds() external; | ||
|
||
function finishMigrationGracePeriod() external; | ||
|
||
function getTotalUsersBalance() external view returns (uint256); | ||
|
||
function getStartBlock() external view returns (uint256); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
pragma solidity 0.5.17; | ||
|
||
interface ILiquidityMiningV2 { | ||
function withdraw( | ||
address _poolToken, | ||
uint256 _amount, | ||
address _user | ||
) external; | ||
|
||
function onTokensDeposited(address _user, uint256 _amount) external; | ||
|
||
function getUserPoolTokenBalance(address _poolToken, address _user) external view returns (uint256); | ||
|
||
function setPoolInfoRewardToken( | ||
address _poolToken, | ||
address _rewardToken, | ||
uint256 _lastRewardBlock, | ||
uint256 _accumulatedRewardPerShare | ||
) external; | ||
|
||
function setRewardToken( | ||
address _rewardToken, | ||
uint256 _startBlock, | ||
uint256 _totalUsersBalance | ||
) external; | ||
|
||
function setUserInfo( | ||
uint256 _poolId, | ||
address _user, | ||
address _rewardToken, | ||
uint256 _amount, | ||
uint256 _rewardDebt, | ||
uint256 _accumulatedReward | ||
) external; | ||
|
||
function add( | ||
address _poolToken, | ||
address[] calldata _rewardTokens, | ||
uint96[] calldata _allocationPoints, | ||
bool _withUpdate | ||
) external; | ||
|
||
function finishMigration() external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
pragma solidity 0.5.17; | ||
|
||
/// @title This interface helps decoupling the Liquidity Mining reward | ||
/// @dev Implement this interface in order to transfer the rewards with different logic. For example: | ||
/// SOV tokens | ||
interface IRewardTransferLogic { | ||
/// @dev Returns the reward token address this contract will transfer | ||
function getRewardTokenAddress() external view returns (address); | ||
|
||
/// @notice Transfers will be executed from this address so it must be approved before invoking | ||
function senderToAuthorize() external view returns (address); | ||
|
||
/// @notice Transfers the reward amount to the specified address | ||
/// @param _to The address to transfer the reward to | ||
/// @param _value The amount of the reward to transfer | ||
/// @param _isWithdrawal If true, means that the reward and the LP deposited tokens are being compeltely withdrawn | ||
function transferReward( | ||
address _to, | ||
uint256 _value, | ||
bool _isWithdrawal | ||
) external; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
pragma solidity 0.5.17; | ||
pragma experimental ABIEncoderV2; | ||
|
||
import "../openzeppelin/ERC20.sol"; | ||
import "../openzeppelin/SafeERC20.sol"; | ||
import "../openzeppelin/SafeMath.sol"; | ||
import "../utils/AdminRole.sol"; | ||
import "./ILiquidityMiningV1.sol"; | ||
import "./ILiquidityMiningV2.sol"; | ||
|
||
contract LMV1toLMV2Migrator is AdminRole { | ||
using SafeMath for uint256; | ||
using SafeERC20 for IERC20; | ||
enum MigrationStates { MigratingPools, MigratingUsers, MigratingFunds, MigrationFinished } | ||
|
||
//represents de migration state from LiquidityMiningV1 to LiquidityMiningV2 | ||
MigrationStates public migrationState; | ||
|
||
//LiquidityMiningV1 contract address | ||
ILiquidityMiningV1 public liquidityMiningV1; | ||
|
||
//LiquidityMiningV2 contract address | ||
ILiquidityMiningV2 public liquidityMiningV2; | ||
|
||
/// @dev it is true if the user has been already migrated | ||
mapping(address => bool) public userMigrated; | ||
|
||
/// @dev The SOV token | ||
IERC20 public SOV; | ||
|
||
event UserMigrated(address indexed user); | ||
|
||
/* Modifiers */ | ||
modifier onlyPoolsMigrationState() { | ||
require(migrationState == MigrationStates.MigratingPools, "Wrong state: should be MigratingPools"); | ||
_; | ||
} | ||
|
||
modifier onlyUsersMigrationState() { | ||
require(migrationState == MigrationStates.MigratingUsers, "Wrong state: should be MigratingUsers"); | ||
_; | ||
} | ||
|
||
modifier onlyFundsMigrationState() { | ||
require(migrationState == MigrationStates.MigratingFunds, "Wrong state: should be MigratingFunds"); | ||
_; | ||
} | ||
|
||
/** | ||
* @notice Initialize migrator | ||
* | ||
* @param _SOV The SOV token address | ||
* @param _liquidityMiningV1 The LiquidityMiningV1 contract address | ||
* @param _liquidityMiningV2 The LiquidityMiningV2 contract address | ||
*/ | ||
function initialize( | ||
IERC20 _SOV, | ||
ILiquidityMiningV1 _liquidityMiningV1, | ||
ILiquidityMiningV2 _liquidityMiningV2 | ||
) public onlyAuthorized { | ||
require(address(_SOV) != address(0), "invalid token address"); | ||
require(address(_liquidityMiningV1) != address(0), "invalid contract address"); | ||
require(address(_liquidityMiningV2) != address(0), "invalid contract address"); | ||
liquidityMiningV1 = _liquidityMiningV1; | ||
liquidityMiningV2 = _liquidityMiningV2; | ||
SOV = _SOV; | ||
migrationState = MigrationStates.MigratingPools; | ||
} | ||
|
||
function _finishPoolsMigration() internal onlyPoolsMigrationState { | ||
migrationState = MigrationStates.MigratingUsers; | ||
} | ||
|
||
function finishUsersMigration() external onlyAuthorized onlyUsersMigrationState { | ||
migrationState = MigrationStates.MigratingFunds; | ||
} | ||
|
||
function _finishFundsMigration() internal onlyFundsMigrationState { | ||
migrationState = MigrationStates.MigrationFinished; | ||
} | ||
|
||
/** | ||
* @notice read all pools from liquidity mining V1 contract and add them | ||
*/ | ||
function migratePools() external onlyAuthorized onlyPoolsMigrationState { | ||
( | ||
address[] memory _poolToken, | ||
uint96[] memory _allocationPoints, | ||
uint256[] memory _lastRewardBlock, | ||
uint256[] memory _accumulatedRewardPerShare | ||
) = liquidityMiningV1.getPoolInfoListArray(); | ||
|
||
require(_poolToken.length == _allocationPoints.length, "Arrays mismatch"); | ||
require(_poolToken.length == _lastRewardBlock.length, "Arrays mismatch"); | ||
|
||
liquidityMiningV1.finishMigrationGracePeriod(); | ||
for (uint256 i = 0; i < _poolToken.length; i++) { | ||
address poolToken = _poolToken[i]; | ||
uint96[] memory allocationPoints = new uint96[](1); | ||
allocationPoints[0] = _allocationPoints[i]; | ||
uint256 lastRewardBlock = _lastRewardBlock[i]; | ||
uint256 accumulatedRewardPerShare = _accumulatedRewardPerShare[i]; | ||
address[] memory SOVAddress = new address[](1); | ||
SOVAddress[0] = address(SOV); | ||
//add will revert if poolToken is invalid or if it was already added | ||
liquidityMiningV2.add(poolToken, SOVAddress, allocationPoints, false); | ||
//add pool function put lastRewardBlock with current block number value, so we need to retrieve the original | ||
liquidityMiningV2.setPoolInfoRewardToken(poolToken, address(SOV), lastRewardBlock, accumulatedRewardPerShare); | ||
} | ||
uint256 _startblock = liquidityMiningV1.getStartBlock(); | ||
uint256 _totalUsersBalance = liquidityMiningV1.getTotalUsersBalance(); | ||
liquidityMiningV2.setRewardToken(address(SOV), _startblock, _totalUsersBalance); | ||
_finishPoolsMigration(); | ||
} | ||
|
||
/** | ||
* @notice read all users of all the pools from liquidity mining V1 contract and copy their info | ||
* @param _users a list of users to be copied | ||
*/ | ||
|
||
function migrateUsers(address[] calldata _users) external onlyAuthorized onlyUsersMigrationState { | ||
for (uint256 i = 0; i < _users.length; i++) { | ||
(uint256[] memory _amount, uint256[] memory _rewardDebt, uint256[] memory _accumulatedReward) = | ||
liquidityMiningV1.getUserInfoListArray(_users[i]); | ||
|
||
require(_amount.length == _rewardDebt.length, "Arrays mismatch"); | ||
require(_amount.length == _accumulatedReward.length, "Arrays mismatch"); | ||
|
||
address user = _users[i]; | ||
|
||
if (userMigrated[user] == false) { | ||
userMigrated[user] = true; | ||
for (uint256 j = 0; j < _amount.length; j++) { | ||
uint256 poolId = j; | ||
uint256 _userAmount = _amount[j]; | ||
uint256 _userRewardDebt = _rewardDebt[j]; | ||
uint256 _userAccumulatedReward = _accumulatedReward[j]; | ||
liquidityMiningV2.setUserInfo(poolId, user, address(SOV), _userAmount, _userRewardDebt, _userAccumulatedReward); | ||
} | ||
emit UserMigrated(user); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* @notice transfer all funds from liquidity mining V1 | ||
*/ | ||
function migrateFunds() external onlyAuthorized onlyFundsMigrationState { | ||
liquidityMiningV1.migrateFunds(); | ||
liquidityMiningV2.finishMigration(); | ||
_finishFundsMigration(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
pragma solidity ^0.5.17; | ||
|
||
import "./LiquidityMiningStorageV2.sol"; | ||
import "../proxy/UpgradableProxy.sol"; | ||
|
||
/** | ||
* @dev LiquidityMining contract should be upgradable, use UpgradableProxy | ||
*/ | ||
contract LiquidityMiningProxyV2 is LiquidityMiningStorageV2, UpgradableProxy { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
pragma solidity 0.5.17; | ||
|
||
import "./LiquidityMiningStorage.sol"; | ||
|
||
contract LiquidityMiningStorageV1 is LiquidityMiningStorage { | ||
/// @dev Careful when adding new states as there is a < comparison being used in the modifiers | ||
enum MigrationGracePeriodStates { | ||
None, | ||
Started, // users can withdraw funds and rewards but not deposit | ||
Finished // users can't operate with the contract | ||
} | ||
|
||
/// @dev Represents migration grace period state | ||
MigrationGracePeriodStates public migrationGracePeriodState; | ||
|
||
/// @dev liquidity mining V2 contract address | ||
address public liquidityMiningV2; | ||
} |
Oops, something went wrong.