Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9e0416b
feat: new liquidity mining that rewards in multiple tokens
swamp-thing-sovryn Aug 11, 2021
37d79d0
Merge pull request #13 from swamp-thing-sovryn/multiple-LM-tokens-rew…
swamp-thing-sovryn Sep 18, 2021
5018e83
fix:solhint fixes
py-ro Sep 20, 2021
31e4a44
Merge pull request #15 from swamp-thing-sovryn/fix/solhint-fixes-on-LM
swamp-thing-sovryn Sep 22, 2021
c1c46e8
feat:allow lending with LM V2 during migration period
py-ro Sep 29, 2021
1aebbc9
fix: increase github CI memory
py-ro Sep 30, 2021
b0dd685
fix: fix PR reviews
py-ro Sep 30, 2021
4e4ca40
fix: increase github CI memory
py-ro Sep 30, 2021
5bcaf08
Merge pull request #16 from swamp-thing-sovryn/feat/redirect-onTokenD…
swamp-thing-sovryn Oct 12, 2021
bb41967
ci: increased nodejs memory so it can run the tests properly on node …
swamp-thing-sovryn Oct 12, 2021
574dbe7
Merge pull request #17 from swamp-thing-sovryn/fix/oom-when-running-t…
swamp-thing-sovryn Oct 12, 2021
915dbb8
fix: LM contracts initialize only once
py-ro Oct 13, 2021
44a2699
Merge pull request #18 from swamp-thing-sovryn/fix/PR-reviews
swamp-thing-sovryn Oct 13, 2021
dfee8c0
Merge branch 'development' of https://github.com/DistributedCollectiv…
smbsp May 4, 2022
f754f64
Ran Prettier
smbsp May 4, 2022
3727c5a
Fixed tests - need to optimise
smbsp May 4, 2022
68a20ee
Ran prettier
smbsp May 4, 2022
4ba69d8
Merge branch 'development' of https://github.com/DistributedCollectiv…
smbsp May 18, 2022
bfe8a83
Minor changes
smbsp May 19, 2022
bc4b08c
Ran prettier
smbsp May 19, 2022
5acc363
Merge branch 'development' of https://github.com/DistributedCollectiv…
smbsp May 19, 2022
99c5b52
Added a new function and fixed coverage issues
smbsp May 23, 2022
cd27d29
Ran prettier
smbsp May 23, 2022
8f1d918
Merge branch 'development' of https://github.com/DistributedCollectiv…
smbsp May 23, 2022
97f2098
Modified lock file
smbsp May 23, 2022
5add9b2
Modified all versions for coverage
smbsp May 23, 2022
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
40 changes: 40 additions & 0 deletions contracts/farm/ERC20TransferLogic.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
pragma solidity 0.5.17;

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) external 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);
}
}
9 changes: 9 additions & 0 deletions contracts/farm/ERC20TransferLogicStorage.sol
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;
}
43 changes: 43 additions & 0 deletions contracts/farm/ILiquidityMiningV1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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);
}
47 changes: 47 additions & 0 deletions contracts/farm/ILiquidityMiningV2.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
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;
}
22 changes: 22 additions & 0 deletions contracts/farm/IRewardTransferLogic.sol
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, the LP tokens are withdrawn and rewards are deposited to LockedSOV(not vested)
function transferReward(
address _to,
uint256 _value,
bool _isWithdrawal
) external;
}
182 changes: 182 additions & 0 deletions contracts/farm/LMV1toLMV2Migrator.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
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
) external 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");
require(address(SOV) == address(0), "Already initialized");
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");

_finishPoolsMigration();
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);
}

/**
* @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]) {
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 {
_finishFundsMigration();
liquidityMiningV1.migrateFunds();
liquidityMiningV2.finishMigration();
}
}
11 changes: 11 additions & 0 deletions contracts/farm/LiquidityMiningProxyV2.sol
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 {

}
18 changes: 18 additions & 0 deletions contracts/farm/LiquidityMiningStorageV1.sol
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;
}
Loading