Position <> PositionV2

Created Diff never expires
17 removals
Lines
Total
Removed
Words
Total
Removed
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
260 lines
19 additions
Lines
Total
Added
Words
Total
Added
To continue using this feature, upgrade to
Diffchecker logo
Diffchecker Pro
263 lines
/*
/*
Copyright 2020 Set Labs Inc.
Copyright 2022 Set Labs Inc.


Licensed under the Apache License, Version 2.0 (the "License");
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
You may obtain a copy of the License at


http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0


Unless required by applicable law or agreed to in writing, software
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
See the License for the specific language governing permissions and
limitations under the License.
limitations under the License.


SPDX-License-Identifier: Apache License, Version 2.0
SPDX-License-Identifier: Apache License, Version 2.0
*/
*/


pragma solidity 0.6.10;
pragma solidity 0.6.10;
pragma experimental "ABIEncoderV2";
pragma experimental "ABIEncoderV2";


import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
import { SignedSafeMath } from "@openzeppelin/contracts/math/SignedSafeMath.sol";
import { SignedSafeMath } from "@openzeppelin/contracts/math/SignedSafeMath.sol";


import { ISetToken } from "../../interfaces/ISetToken.sol";
import { ISetToken } from "../../interfaces/ISetToken.sol";
import { PreciseUnitMath } from "../../lib/PreciseUnitMath.sol";
import { PreciseUnitMath } from "../../lib/PreciseUnitMath.sol";




/**
/**
* @title Position
* @title PositionV2
* @author Set Protocol
* @author Set Protocol
*
*
* Collection of helper functions for handling and updating SetToken Positions
* Collection of helper functions for handling and updating SetToken Positions.
*
*
* CHANGELOG:
* CHANGELOG:
* - Updated editExternalPosition to work when no external position is associated with module
* - `Position` library has all internal functions which are inlined to the module contract during compilation.
* Inlining functions increases bytecode size of the module contract. This library contains the same functions
* as `Position` library but all the functions have public/external access modifier. Thus, making this version
* linkable which helps in reducing bytecode size of the module contract.
*/
*/
library Position {
library PositionV2 {
using SafeCast for uint256;
using SafeCast for uint256;
using SafeMath for uint256;
using SafeMath for uint256;
using SafeCast for int256;
using SafeCast for int256;
using SignedSafeMath for int256;
using SignedSafeMath for int256;
using PreciseUnitMath for uint256;
using PreciseUnitMath for uint256;


/* ============ Helper ============ */
/* ============ Helper ============ */


/**
/**
* Returns whether the SetToken has a default position for a given component (if the real unit is > 0)
* Returns whether the SetToken has a default position for a given component (if the real unit is > 0)
*/
*/
function hasDefaultPosition(ISetToken _setToken, address _component) internal view returns(bool) {
function hasDefaultPosition(ISetToken _setToken, address _component) public view returns(bool) {
return _setToken.getDefaultPositionRealUnit(_component) > 0;
return _setToken.getDefaultPositionRealUnit(_component) > 0;
}
}


/**
/**
* Returns whether the SetToken has an external position for a given component (if # of position modules is > 0)
* Returns whether the SetToken has an external position for a given component (if # of position modules is > 0)
*/
*/
function hasExternalPosition(ISetToken _setToken, address _component) internal view returns(bool) {
function hasExternalPosition(ISetToken _setToken, address _component) public view returns(bool) {
return _setToken.getExternalPositionModules(_component).length > 0;
return _setToken.getExternalPositionModules(_component).length > 0;
}
}
/**
/**
* Returns whether the SetToken component default position real unit is greater than or equal to units passed in.
* Returns whether the SetToken component default position real unit is greater than or equal to units passed in.
*/
*/
function hasSufficientDefaultUnits(ISetToken _setToken, address _component, uint256 _unit) internal view returns(bool) {
function hasSufficientDefaultUnits(ISetToken _setToken, address _component, uint256 _unit) external view returns(bool) {
return _setToken.getDefaultPositionRealUnit(_component) >= _unit.toInt256();
return _setToken.getDefaultPositionRealUnit(_component) >= _unit.toInt256();
}
}


/**
/**
* Returns whether the SetToken component external position is greater than or equal to the real units passed in.
* Returns whether the SetToken component external position is greater than or equal to the real units passed in.
*/
*/
function hasSufficientExternalUnits(
function hasSufficientExternalUnits(
ISetToken _setToken,
ISetToken _setToken,
address _component,
address _component,
address _positionModule,
address _positionModule,
uint256 _unit
uint256 _unit
)
)
internal
external
view
view
returns(bool)
returns(bool)
{
{
return _setToken.getExternalPositionRealUnit(_component, _positionModule) >= _unit.toInt256();
return _setToken.getExternalPositionRealUnit(_component, _positionModule) >= _unit.toInt256();
}
}


/**
/**
* If the position does not exist, create a new Position and add to the SetToken. If it already exists,
* If the position does not exist, create a new Position and add to the SetToken. If it already exists,
* then set the position units. If the new units is 0, remove the position. Handles adding/removing of
* then set the position units. If the new units is 0, remove the position. Handles adding/removing of
* components where needed (in light of potential external positions).
* components where needed (in light of potential external positions).
*
*
* @param _setToken Address of SetToken being modified
* @param _setToken Address of SetToken being modified
* @param _component Address of the component
* @param _component Address of the component
* @param _newUnit Quantity of Position units - must be >= 0
* @param _newUnit Quantity of Position units - must be >= 0
*/
*/
function editDefaultPosition(ISetToken _setToken, address _component, uint256 _newUnit) internal {
function editDefaultPosition(ISetToken _setToken, address _component, uint256 _newUnit) public {
bool isPositionFound = hasDefaultPosition(_setToken, _component);
bool isPositionFound = hasDefaultPosition(_setToken, _component);
if (!isPositionFound && _newUnit > 0) {
if (!isPositionFound && _newUnit > 0) {
// If there is no Default Position and no External Modules, then component does not exist
// If there is no Default Position and no External Modules, then component does not exist
if (!hasExternalPosition(_setToken, _component)) {
if (!hasExternalPosition(_setToken, _component)) {
_setToken.addComponent(_component);
_setToken.addComponent(_component);
}
}
} else if (isPositionFound && _newUnit == 0) {
} else if (isPositionFound && _newUnit == 0) {
// If there is a Default Position and no external positions, remove the component
// If there is a Default Position and no external positions, remove the component
if (!hasExternalPosition(_setToken, _component)) {
if (!hasExternalPosition(_setToken, _component)) {
_setToken.removeComponent(_component);
_setToken.removeComponent(_component);
}
}
}
}


_setToken.editDefaultPositionUnit(_component, _newUnit.toInt256());
_setToken.editDefaultPositionUnit(_component, _newUnit.toInt256());
}
}


/**
/**
* Update an external position and remove and external positions or components if necessary. The logic flows as follows:
* Update an external position and remove and external positions or components if necessary. The logic flows as follows:
* 1) If component is not already added then add component and external position.
* 1) If component is not already added then add component and external position.
* 2) If component is added but no existing external position using the passed module exists then add the external position.
* 2) If component is added but no existing external position using the passed module exists then add the external position.
* 3) If the existing position is being added to then just update the unit and data
* 3) If the existing position is being added to then just update the unit and data
* 4) If the position is being closed and no other external positions or default positions are associated with the component
* 4) If the position is being closed and no other external positions or default positions are associated with the component
* then untrack the component and remove external position.
* then untrack the component and remove external position.
* 5) If the position is being closed and other existing positions still exist for the component then just remove the
* 5) If the position is being closed and other existing positions still exist for the component then just remove the
* external position.
* external position.
*
*
* @param _setToken SetToken being updated
* @param _setToken SetToken being updated
* @param _component Component position being updated
* @param _component Component position being updated
* @param _module Module external position is associated with
* @param _module Module external position is associated with
* @param _newUnit Position units of new external position
* @param _newUnit Position units of new external position
* @param _data Arbitrary data associated with the position
* @param _data Arbitrary data associated with the position
*/
*/
function editExternalPosition(
function editExternalPosition(
ISetToken _setToken,
ISetToken _setToken,
address _component,
address _component,
address _module,
address _module,
int256 _newUnit,
int256 _newUnit,
bytes memory _data
bytes memory _data
)
)
internal
external
{
{
if (_newUnit != 0) {
if (_newUnit != 0) {
if (!_setToken.isComponent(_component)) {
if (!_setToken.isComponent(_component)) {
_setToken.addComponent(_component);
_setToken.addComponent(_component);
_setToken.addExternalPositionModule(_component, _module);
_setToken.addExternalPositionModule(_component, _module);
} else if (!_setToken.isExternalPositionModule(_component, _module)) {
} else if (!_setToken.isExternalPositionModule(_component, _module)) {
_setToken.addExternalPositionModule(_component, _module);
_setToken.addExternalPositionModule(_component, _module);
}
}
_setToken.editExternalPositionUnit(_component, _module, _newUnit);
_setToken.editExternalPositionUnit(_component, _module, _newUnit);
_setToken.editExternalPositionData(_component, _module, _data);
_setToken.editExternalPositionData(_component, _module, _data);
} else {
} else {
require(_data.length == 0, "Passed data must be null");
require(_data.length == 0, "Passed data must be null");
// If no default or external position remaining then remove component from components array
// If no default or external position remaining then remove component from components array
if (_setToken.getExternalPositionRealUnit(_component, _module) != 0) {
if (_setToken.getExternalPositionRealUnit(_component, _module) != 0) {
address[] memory positionModules = _setToken.getExternalPositionModules(_component);
address[] memory positionModules = _setToken.getExternalPositionModules(_component);
if (_setToken.getDefaultPositionRealUnit(_component) == 0 && positionModules.length == 1) {
if (_setToken.getDefaultPositionRealUnit(_component) == 0 && positionModules.length == 1) {
require(positionModules[0] == _module, "External positions must be 0 to remove component");
require(positionModules[0] == _module, "External positions must be 0 to remove component");
_setToken.removeComponent(_component);
_setToken.removeComponent(_component);
}
}
_setToken.removeExternalPositionModule(_component, _module);
_setToken.removeExternalPositionModule(_component, _module);
}
}
}
}
}
}


/**
/**
* Get total notional amount of Default position
* Get total notional amount of Default position
*
*
* @param _setTokenSupply Supply of SetToken in precise units (10^18)
* @param _setTokenSupply Supply of SetToken in precise units (10^18)
* @param _positionUnit Quantity of Position units
* @param _positionUnit Quantity of Position units
*
*
* @return Total notional amount of units
* @return Total notional amount of units
*/
*/
function getDefaultTotalNotional(uint256 _setTokenSupply, uint256 _positionUnit) internal pure returns (uint256) {
function getDefaultTotalNotional(uint256 _setTokenSupply, uint256 _positionUnit) external pure returns (uint256) {
return _setTokenSupply.preciseMul(_positionUnit);
return _setTokenSupply.preciseMul(_positionUnit);
}
}


/**
/**
* Get position unit from total notional amount
* Get position unit from total notional amount
*
*
* @param _setTokenSupply Supply of SetToken in precise units (10^18)
* @param _setTokenSupply Supply of SetToken in precise units (10^18)
* @param _totalNotional Total notional amount of component prior to
* @param _totalNotional Total notional amount of component prior to
* @return Default position unit
* @return Default position unit
*/
*/
function getDefaultPositionUnit(uint256 _setTokenSupply, uint256 _totalNotional) internal pure returns (uint256) {
function getDefaultPositionUnit(uint256 _setTokenSupply, uint256 _totalNotional) external pure returns (uint256) {
return _totalNotional.preciseDiv(_setTokenSupply);
return _totalNotional.preciseDiv(_setTokenSupply);
}
}


/**
/**
* Get the total tracked balance - total supply * position unit
* Get the total tracked balance - total supply * position unit
*
*
* @param _setToken Address of the SetToken
* @param _setToken Address of the SetToken
* @param _component Address of the component
* @param _component Address of the component
* @return Notional tracked balance
* @return Notional tracked balance
*/
*/
function getDefaultTrackedBalance(ISetToken _setToken, address _component) internal view returns(uint256) {
function getDefaultTrackedBalance(ISetToken _setToken, address _component) external view returns(uint256) {
int256 positionUnit = _setToken.getDefaultPositionRealUnit(_component);
int256 positionUnit = _setToken.getDefaultPositionRealUnit(_component);
return _setToken.totalSupply().preciseMul(positionUnit.toUint256());
return _setToken.totalSupply().preciseMul(positionUnit.toUint256());
}
}


/**
/**
* Calculates the new default position unit and performs the edit with the new unit
* Calculates the new default position unit and performs the edit with the new unit
*
*
* @param _setToken Address of the SetToken
* @param _setToken Address of the SetToken
* @param _component Address of the component
* @param _component Address of the component
* @param _setTotalSupply Current SetToken supply
* @param _setTotalSupply Current SetToken supply
* @param _componentPreviousBalance Pre-action component balance
* @param _componentPreviousBalance Pre-action component balance
* @return Current component balance
* @return Current component balance
* @return Previous position unit
* @return Previous position unit
* @return New position unit
* @return New position unit
*/
*/
function calculateAndEditDefaultPosition(
function calculateAndEditDefaultPosition(
ISetToken _setToken,
ISetToken _setToken,
address _component,
address _component,
uint256 _setTotalSupply,
uint256 _setTotalSupply,
uint256 _componentPreviousBalance
uint256 _componentPreviousBalance
)
)
internal
external
returns(uint256, uint256, uint256)
returns(uint256, uint256, uint256)
{
{
uint256 currentBalance = IERC20(_component).balanceOf(address(_setToken));
uint256 currentBalance = IERC20(_component).balanceOf(address(_setToken));
uint256 positionUnit = _setToken.getDefaultPositionRealUnit(_component).toUint256();
uint256 positionUnit = _setToken.getDefaultPositionRealUnit(_component).toUint256();


uint256 newTokenUnit;
uint256 newTokenUnit;
if (currentBalance > 0) {
if (currentBalance > 0) {
newTokenUnit = calculateDefaultEditPositionUnit(
newTokenUnit = calculateDefaultEditPositionUnit(
_setTotalSupply,
_setTotalSupply,
_componentPreviousBalance,
_componentPreviousBalance,
currentBalance,
currentBalance,
positionUnit
positionUnit
);
);
} else {
} else {
newTokenUnit = 0;
newTokenUnit = 0;
}
}


editDefaultPosition(_setToken, _component, newTokenUnit);
editDefaultPosition(_setToken, _component, newTokenUnit);


return (currentBalance, positionUnit, newTokenUnit);
return (currentBalance, positionUnit, newTokenUnit);
}
}


/**
/**
* Calculate the new position unit given total notional values pre and post executing an action that changes SetToken state
* Calculate the new position unit given total notional values pre and post executing an action that changes SetToken state
* The intention is to make updates to the units without accidentally picking up airdropped assets as well.
* The intention is to make updates to the units without accidentally picking up airdropped assets as well.
*
*
* @param _setTokenSupply Supply of SetToken in precise units (10^18)
* @param _setTokenSupply Supply of SetToken in precise units (10^18)
* @param _preTotalNotional Total notional amount of component prior to executing action
* @param _preTotalNotional Total notional amount of component prior to executing action
* @param _postTotalNotional Total notional amount of component after the executing action
* @param _postTotalNotional Total notional amount of component after the executing action
* @param _prePositionUnit Position unit of SetToken prior to executing action
* @param _prePositionUnit Position unit of SetToken prior to executing action
* @return New position unit
* @return New position unit
*/
*/
function calculateDefaultEditPositionUnit(
function calculateDefaultEditPositionUnit(
uint256 _setTokenSupply,
uint256 _setTokenSupply,
uint256 _preTotalNotional,
uint256 _preTotalNotional,
uint256 _postTotalNotional,
uint256 _postTotalNotional,
uint256 _prePositionUnit
uint256 _prePositionUnit
)
)
internal
public
pure
pure
returns (uint256)
returns (uint256)
{
{
// If pre action total notional amount is greater then subtract post action total notional and calculate new position units
// If pre action total notional amount is greater then subtract post action total notional and calculate new position units
uint256 airdroppedAmount = _preTotalNotional.sub(_prePositionUnit.preciseMul(_setTokenSupply));
uint256 airdroppedAmount = _preTotalNotional.sub(_prePositionUnit.preciseMul(_setTokenSupply));
return _postTotalNotional.sub(airdroppedAmount).preciseDiv(_setTokenSupply);
return _postTotalNotional.sub(airdroppedAmount).preciseDiv(_setTokenSupply);
}
}
}
}