// File: solidity/contracts/utility/interfaces/IOwned.sol
// SPDX-License-Identifier: SEE LICENSE IN LICENSE
pragma solidity 0.6.12;
/*
Owned contract interface
*/
interface IOwned {
// this function isn't since the compiler emits automatically generated getter functions as external
function owner() external view returns (address);
function transferOwnership(address _newOwner) external;
function acceptOwnership() external;
}
// File: solidity/contracts/converter/interfaces/IConverterAnchor.sol
pragma solidity 0.6.12;
/*
Converter Anchor interface
*/
interface IConverterAnchor is IOwned {
}
// File: solidity/contracts/token/interfaces/IERC20Token.sol
pragma solidity 0.6.12;
/*
ERC20 Standard Token interface
*/
interface IERC20Token {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function balanceOf(address _owner) external view returns (uint256);
function allowance(address _owner, address _spender) external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
function approve(address _spender, uint256 _value) external returns (bool);
}
// File: solidity/contracts/token/interfaces/IDSToken.sol
pragma solidity 0.6.12;
/*
DSToken interface
*/
interface IDSToken is IConverterAnchor, IERC20Token {
function issue(address _to, uint256 _amount) external;
function destroy(address _from, uint256 _amount) external;
}
// File: solidity/contracts/liquidity-protection/interfaces/ILiquidityProtectionStore.sol
pragma solidity 0.6.12;
/*
Liquidity Protection Store interface
*/
interface ILiquidityProtectionStore is IOwned {
function addPoolToWhitelist(IConverterAnchor _anchor) external;
function removePoolFromWhitelist(IConverterAnchor _anchor) external;
function isPoolWhitelisted(IConverterAnchor _anchor) external view returns (bool);
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) external;
function protectedLiquidity(uint256 _id)
external
view
returns (address, IDSToken, IERC20Token, uint256, uint256, uint256, uint256, uint256);
function addProtectedLiquidity(
address _provider,
IDSToken _poolToken,
IERC20Token _reserveToken,
uint256 _poolAmount,
uint256 _reserveAmount,
uint256 _reserveRateN,
uint256 _reserveRateD,
uint256 _timestamp
) external returns (uint256);
function updateProtectedLiquidityAmounts(uint256 _id, uint256 _poolNewAmount, uint256 _reserveNewAmount) external;
function removeProtectedLiquidity(uint256 _id) external;
function lockedBalance(address _provider, uint256 _index) external view returns (uint256, uint256);
function lockedBalanceRange(address _provider, uint256 _startIndex, uint256 _endIndex) external view returns (uint256[] memory, uint256[] memory);
function addLockedBalance(address _provider, uint256 _reserveAmount, uint256 _expirationTime) external returns (uint256);
function removeLockedBalance(address _provider, uint256 _index) external;
function systemBalance(IERC20Token _poolToken) external view returns (uint256);
function incSystemBalance(IERC20Token _poolToken, uint256 _poolAmount) external;
function decSystemBalance(IERC20Token _poolToken, uint256 _poolAmount ) external;
}
// File: solidity/contracts/utility/Owned.sol
pragma solidity 0.6.12;
/**
* @dev Provides support and utilities for contract ownership
*/
contract Owned is IOwned {
address public override owner;
address public newOwner;
/**
* @dev triggered when the owner is updated
*
* @param _prevOwner previous owner
* @param _newOwner new owner
*/
event OwnerUpdate(address indexed _prevOwner, address indexed _newOwner);
/**
* @dev initializes a new Owned instance
*/
constructor() public {
owner = msg.sender;
}
// allows execution by the owner only
modifier ownerOnly {
_ownerOnly();
_;
}
// error message binary size optimization
function _ownerOnly() internal view {
require(msg.sender == owner, "ERR_ACCESS_DENIED");
}
/**
* @dev allows transferring the contract ownership
* the new owner still needs to accept the transfer
* can only be called by the contract owner
*
* @param _newOwner new contract owner
*/
function transferOwnership(address _newOwner) public override ownerOnly {
require(_newOwner != owner, "ERR_SAME_OWNER");
newOwner = _newOwner;
}
/**
* @dev used by a new owner to accept an ownership transfer
*/
function acceptOwnership() override public {
require(msg.sender == newOwner, "ERR_ACCESS_DENIED");
emit OwnerUpdate(owner, newOwner);
owner = newOwner;
newOwner = address(0);
}
}
// File: solidity/contracts/utility/SafeMath.sol
pragma solidity 0.6.12;
/**
* @dev Library for basic math operations with overflow/underflow protection
*/
library SafeMath {
/**
* @dev returns the sum of _x and _y, reverts if the calculation overflows
*
* @param _x value 1
* @param _y value 2
*
* @return sum
*/
function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
uint256 z = _x + _y;
require(z >= _x, "ERR_OVERFLOW");
return z;
}
/**
* @dev returns the difference of _x minus _y, reverts if the calculation underflows
*
* @param _x minuend
* @param _y subtrahend
*
* @return difference
*/
function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_x >= _y, "ERR_UNDERFLOW");
return _x - _y;
}
/**
* @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
*
* @param _x factor 1
* @param _y factor 2
*
* @return product
*/
function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
// gas optimization
if (_x == 0)
return 0;
uint256 z = _x * _y;
require(z / _x == _y, "ERR_OVERFLOW");
return z;
}
/**
* @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
*
* @param _x dividend
* @param _y divisor
*
* @return quotient
*/
function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
require(_y > 0, "ERR_DIVIDE_BY_ZERO");
uint256 c = _x / _y;
return c;
}
}
// File: solidity/contracts/utility/TokenHandler.sol
pragma solidity 0.6.12;
contract TokenHandler {
bytes4 private constant APPROVE_FUNC_SELECTOR = bytes4(keccak256("approve(address,uint256)"));
bytes4 private constant TRANSFER_FUNC_SELECTOR = bytes4(keccak256("transfer(address,uint256)"));
bytes4 private constant TRANSFER_FROM_FUNC_SELECTOR = bytes4(keccak256("transferFrom(address,address,uint256)"));
/**
* @dev executes the ERC20 token's `approve` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _spender approved address
* @param _value allowance amount
*/
function safeApprove(IERC20Token _token, address _spender, uint256 _value) internal {
(bool success, bytes memory data) = address(_token).call(abi.encodeWithSelector(APPROVE_FUNC_SELECTOR, _spender, _value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ERR_APPROVE_FAILED');
}
/**
* @dev executes the ERC20 token's `transfer` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _to target address
* @param _value transfer amount
*/
function safeTransfer(IERC20Token _token, address _to, uint256 _value) internal {
(bool success, bytes memory data) = address(_token).call(abi.encodeWithSelector(TRANSFER_FUNC_SELECTOR, _to, _value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ERR_TRANSFER_FAILED');
}
/**
* @dev executes the ERC20 token's `transferFrom` function and reverts upon failure
* the main purpose of this function is to prevent a non standard ERC20 token
* from failing silently
*
* @param _token ERC20 token address
* @param _from source address
* @param _to target address
* @param _value transfer amount
*/
function safeTransferFrom(IERC20Token _token, address _from, address _to, uint256 _value) internal {
(bool success, bytes memory data) = address(_token).call(abi.encodeWithSelector(TRANSFER_FROM_FUNC_SELECTOR, _from, _to, _value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'ERR_TRANSFER_FROM_FAILED');
}
}
// File: solidity/contracts/utility/Utils.sol
pragma solidity 0.6.12;
/**
* @dev Utilities & Common Modifiers
*/
contract Utils {
// verifies that a value is greater than zero
modifier greaterThanZero(uint256 _value) {
_greaterThanZero(_value);
_;
}
// error message binary size optimization
function _greaterThanZero(uint256 _value) internal pure {
require(_value > 0, "ERR_ZERO_VALUE");
}
// validates an address - currently only checks that it isn't null
modifier validAddress(address _address) {
_validAddress(_address);
_;
}
// error message binary size optimization
function _validAddress(address _address) internal pure {
require(_address != address(0), "ERR_INVALID_ADDRESS");
}
// verifies that the address is different than this contract address
modifier notThis(address _address) {
_notThis(_address);
_;
}
// error message binary size optimization
function _notThis(address _address) internal view {
require(_address != address(this), "ERR_ADDRESS_IS_SELF");
}
}
// File: solidity/contracts/liquidity-protection/LiquidityProtectionStore.sol
pragma solidity 0.6.12;
/**
* @dev The Liquidity Protection Store contract serves as the storage of the liquidity protection
* mechanism. It holds the data and tokens and is non upgradable.
*
*/
contract LiquidityProtectionStore is ILiquidityProtectionStore, Owned, TokenHandler, Utils {
using SafeMath for uint256;
struct PoolIndex {
bool isValid;
uint256 value;
}
struct ProtectedLiquidity {
address provider; // liquidity provider
uint256 index; // index in the provider liquidity ids array
IDSToken poolToken; // pool token address
IERC20Token reserveToken; // reserve token address
uint256 poolAmount; // pool token amount
uint256 reserveAmount; // reserve token amount
uint256 reserveRateN; // rate of 1 protected reserve token in units of the other reserve token (numerator)
uint256 reserveRateD; // rate of 1 protected reserve token in units of the other reserve token (denominator)
uint256 timestamp; // timestamp
}
struct LockedBalance {
uint256 amount; // amount of network tokens
uint256 expirationTime; // lock expiration time
}
// list of whitelisted pools and mapping of pool anchor address -> index in the pool whitelist for quick access
IConverterAnchor[] private poolWhitelist;
mapping(IConverterAnchor => PoolIndex) private poolWhitelistIndices;
// protected liquidity by provider
uint256 private nextProtectedLiquidityId;
mapping (address => uint256[]) private protectedLiquidityIdsByProvider;
mapping (uint256 => ProtectedLiquidity) private protectedLiquidities;
// user locked network token balances
mapping (address => LockedBalance[]) private lockedBalances;
// system balances
mapping (IERC20Token => uint256) private systemBalances;
// total protected pool supplies / reserve amounts
mapping (IDSToken => uint256) private totalProtectedPoolAmounts;
mapping (IDSToken => mapping (IERC20Token => uint256)) private totalProtectedReserveAmounts;
/**
* @dev triggered when the pool whitelist is updated
*
* @param _poolAnchor pool anchor
* @param _added true if the pool was added to the whitelist, false if it was removed
*/
event PoolWhitelistUpdated(
IConverterAnchor indexed _poolAnchor,
bool _added
);
/**
* @dev triggered when liquidity protection is added
*
* @param _provider liquidity provider
* @param _poolToken pool token address
* @param _reserveToken reserve token address
* @param _poolAmount amount of pool tokens
* @param _reserveAmount amount of reserve tokens
*/
event ProtectionAdded(
address indexed _provider,
IDSToken indexed _poolToken,
IERC20Token indexed _reserveToken,
uint256 _poolAmount,
uint256 _reserveAmount
);
/**
* @dev triggered when liquidity protection is updated
*
* @param _provider liquidity provider
* @param _prevPoolAmount previous amount of pool tokens
* @param _prevReserveAmount previous amount of reserve tokens
* @param _newPoolAmount new amount of pool tokens
* @param _newReserveAmount new amount of reserve tokens
*/
event ProtectionUpdated(
address indexed _provider,
uint256 _prevPoolAmount,
uint256 _prevReserveAmount,
uint256 _newPoolAmount,
uint256 _newReserveAmount
);
/**
* @dev triggered when liquidity protection is removed
*
* @param _provider liquidity provider
* @param _poolToken pool token address
* @param _reserveToken reserve token address
* @param _poolAmount amount of pool tokens
* @param _reserveAmount amount of reserve tokens
*/
event ProtectionRemoved(
address indexed _provider,
IDSToken indexed _poolToken,
IERC20Token indexed _reserveToken,
uint256 _poolAmount,
uint256 _reserveAmount
);
/**
* @dev triggered when network tokens are locked
*
* @param _provider provider of the network tokens
* @param _amount amount of network tokens
* @param _expirationTime lock expiration time
*/
event BalanceLocked(
address indexed _provider,
uint256 _amount,
uint256 _expirationTime
);
/**
* @dev triggered when network tokens are unlocked
*
* @param _provider provider of the network tokens
* @param _amount amount of network tokens
*/
event BalanceUnlocked(
address indexed _provider,
uint256 _amount
);
/**
* @dev triggered when the system balance for a given token is updated
*
* @param _token token address
* @param _prevAmount previous amount
* @param _newAmount new amount
*/
event SystemBalanceUpdated(
IERC20Token _token,
uint256 _prevAmount,
uint256 _newAmount
);
/**
* @dev adds a pool to the whitelist
* can only be called by the contract owner
*
* @param _poolAnchor pool anchor
*/
function addPoolToWhitelist(IConverterAnchor _poolAnchor)
external
override
ownerOnly
validAddress(address(_poolAnchor))
notThis(address(_poolAnchor))
{
// validate input
PoolIndex storage poolIndex = poolWhitelistIndices[_poolAnchor];
require(!poolIndex.isValid, "ERR_POOL_ALREADY_WHITELISTED");
poolIndex.value = poolWhitelist.length;
poolWhitelist.push(_poolAnchor);
poolIndex.isValid = true;
emit PoolWhitelistUpdated(_poolAnchor, true);
}
/**
* @dev removes a pool from the whitelist
* can only be called by the contract owner
*
* @param _poolAnchor pool anchor
*/
function removePoolFromWhitelist(IConverterAnchor _poolAnchor)
external
override
ownerOnly
validAddress(address(_poolAnchor))
notThis(address(_poolAnchor))
{
// validate input
PoolIndex storage poolIndex = poolWhitelistIndices[_poolAnchor];
require(poolIndex.isValid, "ERR_POOL_NOT_WHITELISTED");
uint256 index = poolIndex.value;
uint256 length = poolWhitelist.length;
assert(length > 0);
uint256 lastIndex = length - 1;
if (index < lastIndex) {
IConverterAnchor lastAnchor = poolWhitelist[lastIndex];
poolWhitelistIndices[lastAnchor].value = index;
poolWhitelist[index] = lastAnchor;
}
poolWhitelist.pop();
delete poolWhitelistIndices[_poolAnchor];
emit PoolWhitelistUpdated(_poolAnchor, false);
}
/**
* @dev returns the number of whitelisted pools
*
* @return number of whitelisted pools
*/
function whitelistedPoolCount() external view returns (uint256) {
return poolWhitelist.length;
}
/**
* @dev returns the list of whitelisted pools
*
* @return list of whitelisted pools
*/
function whitelistedPools() external view returns (IConverterAnchor[] memory) {
return poolWhitelist;
}
/**
* @dev returns the whitelisted pool at a given index
*
* @param _index index
* @return whitelisted pool anchor
*/
function whitelistedPool(uint256 _index) external view returns (IConverterAnchor) {
return poolWhitelist[_index];
}
/**
* @dev checks whether a given pool is whitelisted
*
* @param _poolAnchor pool anchor
* @return true if the given pool is whitelisted, false otherwise
*/
function isPoolWhitelisted(IConverterAnchor _poolAnchor) external view override returns (bool) {
return poolWhitelistIndices[_poolAnchor].isValid;
}
/**
* @dev withdraws tokens held by the contract
* can only be called by the contract owner
*
* @param _token token address
* @param _to recipient address
* @param _amount amount to withdraw
*/
function withdrawTokens(IERC20Token _token, address _to, uint256 _amount)
external
override
ownerOnly
validAddress(_to)
notThis(_to)
{
safeTransfer(_token, _to, _amount);
}
/**
* @dev returns the number of protected liquidities for the given provider
*
* @param _provider liquidity provider
* @return number of protected liquidities
*/
function protectedLiquidityCount(address _provider) external view returns (uint256) {
return protectedLiquidityIdsByProvider[_provider].length;
}
/**
* @dev returns the list of protected liquidity ids for the given provider
*
* @param _provider liquidity provider
* @return protected liquidity ids
*/
function protectedLiquidityIds(address _provider) external view returns (uint256[] memory) {
return protectedLiquidityIdsByProvider[_provider];
}
/**
* @dev returns the id of a protected liquidity for the given provider at a specific index
*
* @param _provider liquidity provider
* @param _index protected liquidity index
* @return protected liquidity id
*/
function protectedLiquidityId(address _provider, uint256 _index) external view returns (uint256) {
return protectedLiquidityIdsByProvider[_provider][_index];
}
/**
* @dev returns an existing protected liquidity details
*
* @param _id protected liquidity id
*
* @return liquidity provider
* @return pool token address
* @return reserve token address
* @return pool token amount
* @return reserve token amount
* @return rate of 1 protected reserve token in units of the other reserve token (numerator)
* @return rate of 1 protected reserve token in units of the other reserve token (denominator)
* @return timestamp
*/
function protectedLiquidity(uint256 _id)
external
view
override
returns (address, IDSToken, IERC20Token, uint256, uint256, uint256, uint256, uint256)
{
ProtectedLiquidity storage liquidity = protectedLiquidities[_id];
return (
liquidity.provider,
liquidity.poolToken,
liquidity.reserveToken,
liquidity.poolAmount,
liquidity.reserveAmount,
liquidity.reserveRateN,
liquidity.reserveRateD,
liquidity.timestamp
);
}
/**
* @dev adds protected liquidity
* can only be called by the contract owner
*
* @param _provider liquidity provider
* @param _poolToken pool token address
* @param _reserveToken reserve token address
* @param _poolAmount pool token amount
* @param _reserveAmount reserve token amount
* @param _reserveRateN rate of 1 protected reserve token in units of the other reserve token (numerator)
* @param _reserveRateD rate of 1 protected reserve token in units of the other reserve token (denominator)
* @param _timestamp timestamp
* @return new protected liquidity id
*/
function addProtectedLiquidity(
address _provider,
IDSToken _poolToken,
IERC20Token _reserveToken,
uint256 _poolAmount,
uint256 _reserveAmount,
uint256 _reserveRateN,
uint256 _reserveRateD,
uint256 _timestamp
) external override ownerOnly returns (uint256) {
// validate input
require(
_provider != address(0) &&
_provider != address(this) &&
address(_poolToken) != address(0) &&
address(_poolToken) != address(this) &&
address(_reserveToken) != address(0) &&
address(_reserveToken) != address(this),
"ERR_INVALID_ADDRESS"
);
require(
_poolAmount > 0 &&
_reserveAmount > 0 &&
_reserveRateN > 0 &&
_reserveRateD > 0 &&
_timestamp > 0,
"ERR_ZERO_VALUE"
);
// add the protected liquidity
uint256[] storage ids = protectedLiquidityIdsByProvider[_provider];
uint256 id = nextProtectedLiquidityId;
nextProtectedLiquidityId += 1;
protectedLiquidities[id] = ProtectedLiquidity({
provider: _provider,
index: ids.length,
poolToken: _poolToken,
reserveToken: _reserveToken,
poolAmount: _poolAmount,
reserveAmount: _reserveAmount,
reserveRateN: _reserveRateN,
reserveRateD: _reserveRateD,
timestamp: _timestamp
});
ids.push(id);
// update the total amounts
totalProtectedPoolAmounts[_poolToken] = totalProtectedPoolAmounts[_poolToken].add(_poolAmount);
totalProtectedReserveAmounts[_poolToken][_reserveToken] = totalProtectedReserveAmounts[_poolToken][_reserveToken].add(_reserveAmount);
emit ProtectionAdded(_provider, _poolToken, _reserveToken, _poolAmount, _reserveAmount);
return id;
}
/**
* @dev updates an existing protected liquidity pool/reserve amounts
* can only be called by the contract owner
*
* @param _id protected liquidity id
* @param _newPoolAmount new pool tokens amount
* @param _newReserveAmount new reserve tokens amount
*/
function updateProtectedLiquidityAmounts(uint256 _id, uint256 _newPoolAmount, uint256 _newReserveAmount)
external
override
ownerOnly
greaterThanZero(_newPoolAmount)
greaterThanZero(_newReserveAmount)
{
// update the protected liquidity
ProtectedLiquidity storage liquidity = protectedLiquidities[_id];
// validate input
address provider = liquidity.provider;
require(provider != address(0), "ERR_INVALID_ID");
IDSToken poolToken = liquidity.poolToken;
IERC20Token reserveToken = liquidity.reserveToken;
uint256 prevPoolAmount = liquidity.poolAmount;
uint256 prevReserveAmount = liquidity.reserveAmount;
liquidity.poolAmount = _newPoolAmount;
liquidity.reserveAmount = _newReserveAmount;
// update the total amounts
totalProtectedPoolAmounts[poolToken] = totalProtectedPoolAmounts[poolToken].add(_newPoolAmount).sub(prevPoolAmount);
totalProtectedReserveAmounts[poolToken][reserveToken] = totalProtectedReserveAmounts[poolToken][reserveToken].add(_newReserveAmount).sub(prevReserveAmount);
emit ProtectionUpdated(provider, prevPoolAmount, prevReserveAmount, _newPoolAmount, _newReserveAmount);
}
/**
* @dev removes protected liquidity
* can only be called by the contract owner
*
* @param _id protected liquidity id
*/
function removeProtectedLiquidity(uint256 _id) external override ownerOnly {
// remove the protected liquidity
ProtectedLiquidity storage liquidity = protectedLiquidities[_id];
// validate input
address provider = liquidity.provider;
require(provider != address(0), "ERR_INVALID_ID");
uint256 index = liquidity.index;
IDSToken poolToken = liquidity.poolToken;
IERC20Token reserveToken = liquidity.reserveToken;
uint256 poolAmount = liquidity.poolAmount;
uint256 reserveAmount = liquidity.reserveAmount;
delete protectedLiquidities[_id];
uint256[] storage ids = protectedLiquidityIdsByProvider[provider];
uint256 length = ids.length;
assert(length > 0);
uint256 lastIndex = length - 1;
if (index < lastIndex) {
uint256 lastId = ids[lastIndex];
ids[index] = lastId;
protectedLiquidities[lastId].index = index;
}
ids.pop();
// update the total amounts
totalProtectedPoolAmounts[poolToken] = totalProtectedPoolAmounts[poolToken].sub(poolAmount);
totalProtectedReserveAmounts[poolToken][reserveToken] = totalProtectedReserveAmounts[poolToken][reserveToken].sub(reserveAmount);
emit ProtectionRemoved(provider, poolToken, reserveToken, poolAmount, reserveAmount);
}
/**
* @dev returns the number of network token locked balances for a given provider
*
* @param _provider locked balances provider
* @return the number of network token locked balances
*/
function lockedBalanceCount(address _provider) external view returns (uint256) {
return lockedBalances[_provider].length;
}
/**
* @dev returns an existing locked network token balance details
*
* @param _provider locked balances provider
* @param _index start index
* @return amount of network tokens
* @return lock expiration time
*/
function lockedBalance(address _provider, uint256 _index) external view override returns (uint256, uint256) {
LockedBalance storage balance = lockedBalances[_provider][_index];
return (
balance.amount,
balance.expirationTime
);
}
/**
* @dev returns a range of locked network token balances for a given provider
*
* @param _provider locked balances provider
* @param _startIndex start index
* @param _endIndex end index (exclusive)
* @return locked amounts
* @return expiration times
*/
function lockedBalanceRange(address _provider, uint256 _startIndex, uint256 _endIndex)
external
view
override
returns (uint256[] memory, uint256[] memory)
{
// limit the end index by the number of locked balances
if (_endIndex > lockedBalances[_provider].length) {
_endIndex = lockedBalances[_provider].length;
}
// ensure that the end index is higher than the start index
require(_endIndex > _startIndex, "ERR_INVALID_INDICES");
// get the locked balances for the given range and return them
uint256 length = _endIndex - _startIndex;
uint256[] memory amounts = new uint256[](length);
uint256[] memory expirationTimes = new uint256[](length);
for (uint256 i = 0; i < length; i++) {
LockedBalance storage balance = lockedBalances[_provider][_startIndex + i];
amounts[i] = balance.amount;
expirationTimes[i] = balance.expirationTime;
}
return (amounts, expirationTimes);
}
/**
* @dev adds new locked network token balance
* can only be called by the contract owner
*
* @param _provider liquidity provider
* @param _amount token amount
* @param _expirationTime lock expiration time
* @return new locked balance index
*/
function addLockedBalance(address _provider, uint256 _amount, uint256 _expirationTime)
external
override
ownerOnly
validAddress(_provider)
notThis(_provider)
greaterThanZero(_amount)
greaterThanZero(_expirationTime)
returns (uint256)
{
lockedBalances[_provider].push(LockedBalance({
amount: _amount,
expirationTime: _expirationTime
}));
emit BalanceLocked(_provider, _amount, _expirationTime);
return lockedBalances[_provider].length - 1;
}
/**
* @dev removes a locked network token balance
* can only be called by the contract owner
*
* @param _provider liquidity provider
* @param _index index of the locked balance
*/
function removeLockedBalance(address _provider, uint256 _index)
external
override
ownerOnly
validAddress(_provider)
{
LockedBalance[] storage balances = lockedBalances[_provider];
uint256 length = balances.length;
// validate input
require(_index < length, "ERR_INVALID_INDEX");
uint256 amount = balances[_index].amount;
uint256 lastIndex = length - 1;
if (_index < lastIndex) {
balances[_index] = balances[lastIndex];
}
balances.pop();
emit BalanceUnlocked(_provider, amount);
}
/**
* @dev returns the system balance for a given token
*
* @param _token token address
* @return system balance
*/
function systemBalance(IERC20Token _token) external view override returns (uint256) {
return systemBalances[_token];
}
/**
* @dev increases the system balance for a given token
* can only be called by the contract owner
*
* @param _token token address
* @param _amount token amount
*/
function incSystemBalance(IERC20Token _token, uint256 _amount)
external
override
ownerOnly
validAddress(address(_token))
{
uint256 prevAmount = systemBalances[_token];
uint256 newAmount = prevAmount.add(_amount);
systemBalances[_token] = newAmount;
emit SystemBalanceUpdated(_token, prevAmount, newAmount);
}
/**
* @dev decreases the system balance for a given token
* can only be called by the contract owner
*
* @param _token token address
* @param _amount token amount
*/
function decSystemBalance(IERC20Token _token, uint256 _amount)
external
override
ownerOnly
validAddress(address(_token))
{
uint256 prevAmount = systemBalances[_token];
uint256 newAmount = prevAmount.sub(_amount);
systemBalances[_token] = newAmount;
emit SystemBalanceUpdated(_token, prevAmount, newAmount);
}
/**
* @dev returns the total protected pool token amount for a given pool
*
* @param _poolToken pool token address
* @return total protected amount
*/
function totalProtectedPoolAmount(IDSToken _poolToken) external view returns (uint256) {
return totalProtectedPoolAmounts[_poolToken];
}
/**
* @dev returns the total protected reserve amount for a given pool
*
* @param _poolToken pool token address
* @param _reserveToken reserve token address
* @return total protected amount
*/
function totalProtectedReserveAmount(IDSToken _poolToken, IERC20Token _reserveToken) external view returns (uint256) {
return totalProtectedReserveAmounts[_poolToken][_reserveToken];
}
}
{
"compilationTarget": {
"LiquidityProtectionStore.sol": "LiquidityProtectionStore"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"BalanceLocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"BalanceUnlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_prevOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnerUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IConverterAnchor","name":"_poolAnchor","type":"address"},{"indexed":false,"internalType":"bool","name":"_added","type":"bool"}],"name":"PoolWhitelistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_provider","type":"address"},{"indexed":true,"internalType":"contract IDSToken","name":"_poolToken","type":"address"},{"indexed":true,"internalType":"contract IERC20Token","name":"_reserveToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_poolAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_reserveAmount","type":"uint256"}],"name":"ProtectionAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_provider","type":"address"},{"indexed":true,"internalType":"contract IDSToken","name":"_poolToken","type":"address"},{"indexed":true,"internalType":"contract IERC20Token","name":"_reserveToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"_poolAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_reserveAmount","type":"uint256"}],"name":"ProtectionRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_provider","type":"address"},{"indexed":false,"internalType":"uint256","name":"_prevPoolAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_prevReserveAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newPoolAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newReserveAmount","type":"uint256"}],"name":"ProtectionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20Token","name":"_token","type":"address"},{"indexed":false,"internalType":"uint256","name":"_prevAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newAmount","type":"uint256"}],"name":"SystemBalanceUpdated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_expirationTime","type":"uint256"}],"name":"addLockedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"_poolAnchor","type":"address"}],"name":"addPoolToWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"},{"internalType":"contract IDSToken","name":"_poolToken","type":"address"},{"internalType":"contract IERC20Token","name":"_reserveToken","type":"address"},{"internalType":"uint256","name":"_poolAmount","type":"uint256"},{"internalType":"uint256","name":"_reserveAmount","type":"uint256"},{"internalType":"uint256","name":"_reserveRateN","type":"uint256"},{"internalType":"uint256","name":"_reserveRateD","type":"uint256"},{"internalType":"uint256","name":"_timestamp","type":"uint256"}],"name":"addProtectedLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Token","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"decSystemBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Token","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"incSystemBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"_poolAnchor","type":"address"}],"name":"isPoolWhitelisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"lockedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"}],"name":"lockedBalanceCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"},{"internalType":"uint256","name":"_startIndex","type":"uint256"},{"internalType":"uint256","name":"_endIndex","type":"uint256"}],"name":"lockedBalanceRange","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"protectedLiquidity","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"contract IDSToken","name":"","type":"address"},{"internalType":"contract IERC20Token","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"}],"name":"protectedLiquidityCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"protectedLiquidityId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"}],"name":"protectedLiquidityIds","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_provider","type":"address"},{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"removeLockedBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IConverterAnchor","name":"_poolAnchor","type":"address"}],"name":"removePoolFromWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"removeProtectedLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20Token","name":"_token","type":"address"}],"name":"systemBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDSToken","name":"_poolToken","type":"address"}],"name":"totalProtectedPoolAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDSToken","name":"_poolToken","type":"address"},{"internalType":"contract IERC20Token","name":"_reserveToken","type":"address"}],"name":"totalProtectedReserveAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_newPoolAmount","type":"uint256"},{"internalType":"uint256","name":"_newReserveAmount","type":"uint256"}],"name":"updateProtectedLiquidityAmounts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_index","type":"uint256"}],"name":"whitelistedPool","outputs":[{"internalType":"contract IConverterAnchor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistedPoolCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistedPools","outputs":[{"internalType":"contract IConverterAnchor[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Token","name":"_token","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawTokens","outputs":[],"stateMutability":"nonpayable","type":"function"}]