// File: openzeppelin-solidity/contracts/utils/ReentrancyGuard.sol
pragma solidity ^0.5.2;
/**
* @title Helps contracts guard against reentrancy attacks.
* @author Remco Bloemen <remco@2π.com>, Eenae <alexey@mixbytes.io>
* @dev If you mark a function `nonReentrant`, you should also
* mark it `external`.
*/
contract ReentrancyGuard {
/// @dev counter to allow mutex lock with only one SSTORE operation
uint256 private _guardCounter;
constructor () internal {
// The counter starts at one to prevent changing it from zero to a non-zero
// value, which is a more expensive operation.
_guardCounter = 1;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_guardCounter += 1;
uint256 localCounter = _guardCounter;
_;
require(localCounter == _guardCounter);
}
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
pragma solidity ^0.5.2;
/**
* @title SafeMath
* @dev Unsigned math operations with safety checks that revert on error
*/
library SafeMath {
/**
* @dev Multiplies two unsigned integers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two unsigned integers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
* reverts when dividing by zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0);
return a % b;
}
}
// File: contracts/core/lib/RebalancingLibraryV2.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title RebalancingLibrary
* @author Set Protocol
*
* The RebalancingLibrary contains functions for facilitating the rebalancing process for
* Rebalancing Set Tokens. Removes the old calculation functions
*
*/
library RebalancingLibraryV2 {
using SafeMath for uint256;
/* ============ Enums ============ */
enum State { Default, Proposal, Rebalance, Drawdown }
/* ============ Structs ============ */
struct AuctionPriceParameters {
uint256 auctionStartTime;
uint256 auctionTimeToPivot;
uint256 auctionStartPrice;
uint256 auctionPivotPrice;
}
struct BiddingParameters {
uint256 minimumBid;
uint256 remainingCurrentSets;
uint256[] combinedCurrentUnits;
uint256[] combinedNextSetUnits;
address[] combinedTokenArray;
}
}
// File: contracts/core/interfaces/IRebalancingSetTokenV2.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title IRebalancingSetTokenV2
* @author Set Protocol
*
* The IRebalancingSetToken interface provides a light-weight, structured way to interact with the
* RebalancingSetToken contract from another contract.
*/
interface IRebalancingSetTokenV2 {
/*
* Get totalSupply of Rebalancing Set
*
* @return totalSupply
*/
function totalSupply()
external
view
returns (uint256);
/*
* Get lastRebalanceTimestamp of Rebalancing Set
*
* @return lastRebalanceTimestamp
*/
function lastRebalanceTimestamp()
external
view
returns (uint256);
/*
* Get rebalanceInterval of Rebalancing Set
*
* @return rebalanceInterval
*/
function rebalanceInterval()
external
view
returns (uint256);
/*
* Get rebalanceState of Rebalancing Set
*
* @return rebalanceState
*/
function rebalanceState()
external
view
returns (RebalancingLibraryV2.State);
/**
* Gets the balance of the specified address.
*
* @param owner The address to query the balance of.
* @return A uint256 representing the amount owned by the passed address.
*/
function balanceOf(
address owner
)
external
view
returns (uint256);
/**
* Function used to set the terms of the next rebalance and start the proposal period
*
* @param _nextSet The Set to rebalance into
* @param _auctionLibrary The library used to calculate the Dutch Auction price
* @param _auctionTimeToPivot The amount of time for the auction to go ffrom start to pivot price
* @param _auctionStartPrice The price to start the auction at
* @param _auctionPivotPrice The price at which the price curve switches from linear to exponential
*/
function propose(
address _nextSet,
address _auctionLibrary,
uint256 _auctionTimeToPivot,
uint256 _auctionStartPrice,
uint256 _auctionPivotPrice
)
external;
/*
* Get natural unit of Set
*
* @return uint256 Natural unit of Set
*/
function naturalUnit()
external
view
returns (uint256);
/**
* Returns the address of the current Base Set
*
* @return A address representing the base Set Token
*/
function currentSet()
external
view
returns (address);
/*
* Get the unit shares of the rebalancing Set
*
* @return unitShares Unit Shares of the base Set
*/
function unitShares()
external
view
returns (uint256);
/*
* Burn set token for given address.
* Can only be called by authorized contracts.
*
* @param _from The address of the redeeming account
* @param _quantity The number of sets to burn from redeemer
*/
function burn(
address _from,
uint256 _quantity
)
external;
/*
* Place bid during rebalance auction. Can only be called by Core.
*
* @param _quantity The amount of currentSet to be rebalanced
* @return combinedTokenArray Array of token addresses invovled in rebalancing
* @return inflowUnitArray Array of amount of tokens inserted into system in bid
* @return outflowUnitArray Array of amount of tokens taken out of system in bid
*/
function placeBid(
uint256 _quantity
)
external
returns (address[] memory, uint256[] memory, uint256[] memory);
/*
* Get combinedTokenArray of Rebalancing Set
*
* @return combinedTokenArray
*/
function getCombinedTokenArrayLength()
external
view
returns (uint256);
/*
* Get combinedTokenArray of Rebalancing Set
*
* @return combinedTokenArray
*/
function getCombinedTokenArray()
external
view
returns (address[] memory);
/*
* Get failedAuctionWithdrawComponents of Rebalancing Set
*
* @return failedAuctionWithdrawComponents
*/
function getFailedAuctionWithdrawComponents()
external
view
returns (address[] memory);
/*
* Get biddingParameters for current auction
*
* @return biddingParameters
*/
function getBiddingParameters()
external
view
returns (uint256[] memory);
}
// File: contracts/core/interfaces/ISetToken.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title ISetToken
* @author Set Protocol
*
* The ISetToken interface provides a light-weight, structured way to interact with the
* SetToken contract from another contract.
*/
interface ISetToken {
/* ============ External Functions ============ */
/*
* Get natural unit of Set
*
* @return uint256 Natural unit of Set
*/
function naturalUnit()
external
view
returns (uint256);
/*
* Get addresses of all components in the Set
*
* @return componentAddresses Array of component tokens
*/
function getComponents()
external
view
returns (address[] memory);
/*
* Get units of all tokens in Set
*
* @return units Array of component units
*/
function getUnits()
external
view
returns (uint256[] memory);
/*
* Checks to make sure token is component of Set
*
* @param _tokenAddress Address of token being checked
* @return bool True if token is component of Set
*/
function tokenIsComponent(
address _tokenAddress
)
external
view
returns (bool);
/*
* Mint set token for given address.
* Can only be called by authorized contracts.
*
* @param _issuer The address of the issuing account
* @param _quantity The number of sets to attribute to issuer
*/
function mint(
address _issuer,
uint256 _quantity
)
external;
/*
* Burn set token for given address
* Can only be called by authorized contracts
*
* @param _from The address of the redeeming account
* @param _quantity The number of sets to burn from redeemer
*/
function burn(
address _from,
uint256 _quantity
)
external;
/**
* Transfer token for a specified address
*
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function transfer(
address to,
uint256 value
)
external;
}
// File: contracts/lib/IWETH.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title IWETH
* @author Set Protocol
*
* Interface for Wrapped Ether. This interface allows for interaction for wrapped ether's deposit and withdrawal
* functionality.
*/
interface IWETH {
function deposit()
external
payable;
function withdraw(
uint256 wad
)
external;
}
// File: contracts/lib/CommonMath.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
library CommonMath {
using SafeMath for uint256;
/**
* Calculates and returns the maximum value for a uint256
*
* @return The maximum value for uint256
*/
function maxUInt256()
internal
pure
returns (uint256)
{
return 2 ** 256 - 1;
}
/**
* @dev Performs the power on a specified value, reverts on overflow.
*/
function safePower(
uint256 a,
uint256 pow
)
internal
pure
returns (uint256)
{
require(a > 0);
uint256 result = 1;
for (uint256 i = 0; i < pow; i++){
uint256 previousResult = result;
// Using safemath multiplication prevents overflows
result = previousResult.mul(a);
}
return result;
}
/**
* Checks for rounding errors and returns value of potential partial amounts of a principal
*
* @param _principal Number fractional amount is derived from
* @param _numerator Numerator of fraction
* @param _denominator Denominator of fraction
* @return uint256 Fractional amount of principal calculated
*/
function getPartialAmount(
uint256 _principal,
uint256 _numerator,
uint256 _denominator
)
internal
pure
returns (uint256)
{
// Get remainder of partial amount (if 0 not a partial amount)
uint256 remainder = mulmod(_principal, _numerator, _denominator);
// Return if not a partial amount
if (remainder == 0) {
return _principal.mul(_numerator).div(_denominator);
}
// Calculate error percentage
uint256 errPercentageTimes1000000 = remainder.mul(1000000).div(_numerator.mul(_principal));
// Require error percentage is less than 0.1%.
require(
errPercentageTimes1000000 < 1000,
"CommonMath.getPartialAmount: Rounding error exceeds bounds"
);
return _principal.mul(_numerator).div(_denominator);
}
}
// File: contracts/lib/IERC20.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title IERC20
* @author Set Protocol
*
* Interface for using ERC20 Tokens. This interface is needed to interact with tokens that are not
* fully ERC20 compliant and return something other than true on successful transfers.
*/
interface IERC20 {
function balanceOf(
address _owner
)
external
view
returns (uint256);
function allowance(
address _owner,
address _spender
)
external
view
returns (uint256);
function transfer(
address _to,
uint256 _quantity
)
external;
function transferFrom(
address _from,
address _to,
uint256 _quantity
)
external;
function approve(
address _spender,
uint256 _quantity
)
external
returns (bool);
}
// File: contracts/lib/ERC20Wrapper.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title ERC20Wrapper
* @author Set Protocol
*
* This library contains functions for interacting wtih ERC20 tokens, even those not fully compliant.
* For all functions we will only accept tokens that return a null or true value, any other values will
* cause the operation to revert.
*/
library ERC20Wrapper {
// ============ Internal Functions ============
/**
* Check balance owner's balance of ERC20 token
*
* @param _token The address of the ERC20 token
* @param _owner The owner who's balance is being checked
* @return uint256 The _owner's amount of tokens
*/
function balanceOf(
address _token,
address _owner
)
external
view
returns (uint256)
{
return IERC20(_token).balanceOf(_owner);
}
/**
* Checks spender's allowance to use token's on owner's behalf.
*
* @param _token The address of the ERC20 token
* @param _owner The token owner address
* @param _spender The address the allowance is being checked on
* @return uint256 The spender's allowance on behalf of owner
*/
function allowance(
address _token,
address _owner,
address _spender
)
internal
view
returns (uint256)
{
return IERC20(_token).allowance(_owner, _spender);
}
/**
* Transfers tokens from an address. Handle's tokens that return true or null.
* If other value returned, reverts.
*
* @param _token The address of the ERC20 token
* @param _to The address to transfer to
* @param _quantity The amount of tokens to transfer
*/
function transfer(
address _token,
address _to,
uint256 _quantity
)
external
{
IERC20(_token).transfer(_to, _quantity);
// Check that transfer returns true or null
require(
checkSuccess(),
"ERC20Wrapper.transfer: Bad return value"
);
}
/**
* Transfers tokens from an address (that has set allowance on the proxy).
* Handle's tokens that return true or null. If other value returned, reverts.
*
* @param _token The address of the ERC20 token
* @param _from The address to transfer from
* @param _to The address to transfer to
* @param _quantity The number of tokens to transfer
*/
function transferFrom(
address _token,
address _from,
address _to,
uint256 _quantity
)
external
{
IERC20(_token).transferFrom(_from, _to, _quantity);
// Check that transferFrom returns true or null
require(
checkSuccess(),
"ERC20Wrapper.transferFrom: Bad return value"
);
}
/**
* Grants spender ability to spend on owner's behalf.
* Handle's tokens that return true or null. If other value returned, reverts.
*
* @param _token The address of the ERC20 token
* @param _spender The address to approve for transfer
* @param _quantity The amount of tokens to approve spender for
*/
function approve(
address _token,
address _spender,
uint256 _quantity
)
internal
{
IERC20(_token).approve(_spender, _quantity);
// Check that approve returns true or null
require(
checkSuccess(),
"ERC20Wrapper.approve: Bad return value"
);
}
/**
* Ensure's the owner has granted enough allowance for system to
* transfer tokens.
*
* @param _token The address of the ERC20 token
* @param _owner The address of the token owner
* @param _spender The address to grant/check allowance for
* @param _quantity The amount to see if allowed for
*/
function ensureAllowance(
address _token,
address _owner,
address _spender,
uint256 _quantity
)
internal
{
uint256 currentAllowance = allowance(_token, _owner, _spender);
if (currentAllowance < _quantity) {
approve(
_token,
_spender,
CommonMath.maxUInt256()
);
}
}
// ============ Private Functions ============
/**
* Checks the return value of the previous function up to 32 bytes. Returns true if the previous
* function returned 0 bytes or 1.
*/
function checkSuccess(
)
private
pure
returns (bool)
{
// default to failure
uint256 returnValue = 0;
assembly {
// check number of bytes returned from last function call
switch returndatasize
// no bytes returned: assume success
case 0x0 {
returnValue := 1
}
// 32 bytes returned
case 0x20 {
// copy 32 bytes into scratch space
returndatacopy(0x0, 0x0, 0x20)
// load those bytes into returnValue
returnValue := mload(0x0)
}
// not sure what was returned: dont mark as success
default { }
}
// check if returned value is one or nothing
return returnValue == 1;
}
}
// File: contracts/core/interfaces/ICore.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title ICore
* @author Set Protocol
*
* The ICore Contract defines all the functions exposed in the Core through its
* various extensions and is a light weight way to interact with the contract.
*/
interface ICore {
/**
* Return transferProxy address.
*
* @return address transferProxy address
*/
function transferProxy()
external
view
returns (address);
/**
* Return vault address.
*
* @return address vault address
*/
function vault()
external
view
returns (address);
/**
* Return address belonging to given exchangeId.
*
* @param _exchangeId ExchangeId number
* @return address Address belonging to given exchangeId
*/
function exchangeIds(
uint8 _exchangeId
)
external
view
returns (address);
/*
* Returns if valid set
*
* @return bool Returns true if Set created through Core and isn't disabled
*/
function validSets(address)
external
view
returns (bool);
/*
* Returns if valid module
*
* @return bool Returns true if valid module
*/
function validModules(address)
external
view
returns (bool);
/**
* Return boolean indicating if address is a valid Rebalancing Price Library.
*
* @param _priceLibrary Price library address
* @return bool Boolean indicating if valid Price Library
*/
function validPriceLibraries(
address _priceLibrary
)
external
view
returns (bool);
/**
* Exchanges components for Set Tokens
*
* @param _set Address of set to issue
* @param _quantity Quantity of set to issue
*/
function issue(
address _set,
uint256 _quantity
)
external;
/**
* Issues a specified Set for a specified quantity to the recipient
* using the caller's components from the wallet and vault.
*
* @param _recipient Address to issue to
* @param _set Address of the Set to issue
* @param _quantity Number of tokens to issue
*/
function issueTo(
address _recipient,
address _set,
uint256 _quantity
)
external;
/**
* Converts user's components into Set Tokens held directly in Vault instead of user's account
*
* @param _set Address of the Set
* @param _quantity Number of tokens to redeem
*/
function issueInVault(
address _set,
uint256 _quantity
)
external;
/**
* Function to convert Set Tokens into underlying components
*
* @param _set The address of the Set token
* @param _quantity The number of tokens to redeem. Should be multiple of natural unit.
*/
function redeem(
address _set,
uint256 _quantity
)
external;
/**
* Redeem Set token and return components to specified recipient. The components
* are left in the vault
*
* @param _recipient Recipient of Set being issued
* @param _set Address of the Set
* @param _quantity Number of tokens to redeem
*/
function redeemTo(
address _recipient,
address _set,
uint256 _quantity
)
external;
/**
* Function to convert Set Tokens held in vault into underlying components
*
* @param _set The address of the Set token
* @param _quantity The number of tokens to redeem. Should be multiple of natural unit.
*/
function redeemInVault(
address _set,
uint256 _quantity
)
external;
/**
* Composite method to redeem and withdraw with a single transaction
*
* Normally, you should expect to be able to withdraw all of the tokens.
* However, some have central abilities to freeze transfers (e.g. EOS). _toExclude
* allows you to optionally specify which component tokens to exclude when
* redeeming. They will remain in the vault under the users' addresses.
*
* @param _set Address of the Set
* @param _to Address to withdraw or attribute tokens to
* @param _quantity Number of tokens to redeem
* @param _toExclude Mask of indexes of tokens to exclude from withdrawing
*/
function redeemAndWithdrawTo(
address _set,
address _to,
uint256 _quantity,
uint256 _toExclude
)
external;
/**
* Deposit multiple tokens to the vault. Quantities should be in the
* order of the addresses of the tokens being deposited.
*
* @param _tokens Array of the addresses of the ERC20 tokens
* @param _quantities Array of the number of tokens to deposit
*/
function batchDeposit(
address[] calldata _tokens,
uint256[] calldata _quantities
)
external;
/**
* Withdraw multiple tokens from the vault. Quantities should be in the
* order of the addresses of the tokens being withdrawn.
*
* @param _tokens Array of the addresses of the ERC20 tokens
* @param _quantities Array of the number of tokens to withdraw
*/
function batchWithdraw(
address[] calldata _tokens,
uint256[] calldata _quantities
)
external;
/**
* Deposit any quantity of tokens into the vault.
*
* @param _token The address of the ERC20 token
* @param _quantity The number of tokens to deposit
*/
function deposit(
address _token,
uint256 _quantity
)
external;
/**
* Withdraw a quantity of tokens from the vault.
*
* @param _token The address of the ERC20 token
* @param _quantity The number of tokens to withdraw
*/
function withdraw(
address _token,
uint256 _quantity
)
external;
/**
* Transfer tokens associated with the sender's account in vault to another user's
* account in vault.
*
* @param _token Address of token being transferred
* @param _to Address of user receiving tokens
* @param _quantity Amount of tokens being transferred
*/
function internalTransfer(
address _token,
address _to,
uint256 _quantity
)
external;
/**
* Deploys a new Set Token and adds it to the valid list of SetTokens
*
* @param _factory The address of the Factory to create from
* @param _components The address of component tokens
* @param _units The units of each component token
* @param _naturalUnit The minimum unit to be issued or redeemed
* @param _name The bytes32 encoded name of the new Set
* @param _symbol The bytes32 encoded symbol of the new Set
* @param _callData Byte string containing additional call parameters
* @return setTokenAddress The address of the new Set
*/
function createSet(
address _factory,
address[] calldata _components,
uint256[] calldata _units,
uint256 _naturalUnit,
bytes32 _name,
bytes32 _symbol,
bytes calldata _callData
)
external
returns (address);
/**
* Exposes internal function that deposits a quantity of tokens to the vault and attributes
* the tokens respectively, to system modules.
*
* @param _from Address to transfer tokens from
* @param _to Address to credit for deposit
* @param _token Address of token being deposited
* @param _quantity Amount of tokens to deposit
*/
function depositModule(
address _from,
address _to,
address _token,
uint256 _quantity
)
external;
/**
* Exposes internal function that withdraws a quantity of tokens from the vault and
* deattributes the tokens respectively, to system modules.
*
* @param _from Address to decredit for withdraw
* @param _to Address to transfer tokens to
* @param _token Address of token being withdrawn
* @param _quantity Amount of tokens to withdraw
*/
function withdrawModule(
address _from,
address _to,
address _token,
uint256 _quantity
)
external;
/**
* Exposes internal function that deposits multiple tokens to the vault, to system
* modules. Quantities should be in the order of the addresses of the tokens being
* deposited.
*
* @param _from Address to transfer tokens from
* @param _to Address to credit for deposits
* @param _tokens Array of the addresses of the tokens being deposited
* @param _quantities Array of the amounts of tokens to deposit
*/
function batchDepositModule(
address _from,
address _to,
address[] calldata _tokens,
uint256[] calldata _quantities
)
external;
/**
* Exposes internal function that withdraws multiple tokens from the vault, to system
* modules. Quantities should be in the order of the addresses of the tokens being withdrawn.
*
* @param _from Address to decredit for withdrawals
* @param _to Address to transfer tokens to
* @param _tokens Array of the addresses of the tokens being withdrawn
* @param _quantities Array of the amounts of tokens to withdraw
*/
function batchWithdrawModule(
address _from,
address _to,
address[] calldata _tokens,
uint256[] calldata _quantities
)
external;
/**
* Expose internal function that exchanges components for Set tokens,
* accepting any owner, to system modules
*
* @param _owner Address to use tokens from
* @param _recipient Address to issue Set to
* @param _set Address of the Set to issue
* @param _quantity Number of tokens to issue
*/
function issueModule(
address _owner,
address _recipient,
address _set,
uint256 _quantity
)
external;
/**
* Expose internal function that exchanges Set tokens for components,
* accepting any owner, to system modules
*
* @param _burnAddress Address to burn token from
* @param _incrementAddress Address to increment component tokens to
* @param _set Address of the Set to redeem
* @param _quantity Number of tokens to redeem
*/
function redeemModule(
address _burnAddress,
address _incrementAddress,
address _set,
uint256 _quantity
)
external;
/**
* Expose vault function that increments user's balance in the vault.
* Available to system modules
*
* @param _tokens The addresses of the ERC20 tokens
* @param _owner The address of the token owner
* @param _quantities The numbers of tokens to attribute to owner
*/
function batchIncrementTokenOwnerModule(
address[] calldata _tokens,
address _owner,
uint256[] calldata _quantities
)
external;
/**
* Expose vault function that decrement user's balance in the vault
* Only available to system modules.
*
* @param _tokens The addresses of the ERC20 tokens
* @param _owner The address of the token owner
* @param _quantities The numbers of tokens to attribute to owner
*/
function batchDecrementTokenOwnerModule(
address[] calldata _tokens,
address _owner,
uint256[] calldata _quantities
)
external;
/**
* Expose vault function that transfer vault balances between users
* Only available to system modules.
*
* @param _tokens Addresses of tokens being transferred
* @param _from Address tokens being transferred from
* @param _to Address tokens being transferred to
* @param _quantities Amounts of tokens being transferred
*/
function batchTransferBalanceModule(
address[] calldata _tokens,
address _from,
address _to,
uint256[] calldata _quantities
)
external;
/**
* Transfers token from one address to another using the transfer proxy.
* Only available to system modules.
*
* @param _token The address of the ERC20 token
* @param _quantity The number of tokens to transfer
* @param _from The address to transfer from
* @param _to The address to transfer to
*/
function transferModule(
address _token,
uint256 _quantity,
address _from,
address _to
)
external;
/**
* Expose transfer proxy function to transfer tokens from one address to another
* Only available to system modules.
*
* @param _tokens The addresses of the ERC20 token
* @param _quantities The numbers of tokens to transfer
* @param _from The address to transfer from
* @param _to The address to transfer to
*/
function batchTransferModule(
address[] calldata _tokens,
uint256[] calldata _quantities,
address _from,
address _to
)
external;
}
// File: contracts/core/interfaces/ITransferProxy.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title ITransferProxy
* @author Set Protocol
*
* The ITransferProxy interface provides a light-weight, structured way to interact with the
* TransferProxy contract from another contract.
*/
interface ITransferProxy {
/* ============ External Functions ============ */
/**
* Transfers tokens from an address (that has set allowance on the proxy).
* Can only be called by authorized core contracts.
*
* @param _token The address of the ERC20 token
* @param _quantity The number of tokens to transfer
* @param _from The address to transfer from
* @param _to The address to transfer to
*/
function transfer(
address _token,
uint256 _quantity,
address _from,
address _to
)
external;
/**
* Transfers tokens from an address (that has set allowance on the proxy).
* Can only be called by authorized core contracts.
*
* @param _tokens The addresses of the ERC20 token
* @param _quantities The numbers of tokens to transfer
* @param _from The address to transfer from
* @param _to The address to transfer to
*/
function batchTransfer(
address[] calldata _tokens,
uint256[] calldata _quantities,
address _from,
address _to
)
external;
}
// File: contracts/core/interfaces/IVault.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title IVault
* @author Set Protocol
*
* The IVault interface provides a light-weight, structured way to interact with the Vault
* contract from another contract.
*/
interface IVault {
/*
* Withdraws user's unassociated tokens to user account. Can only be
* called by authorized core contracts.
*
* @param _token The address of the ERC20 token
* @param _to The address to transfer token to
* @param _quantity The number of tokens to transfer
*/
function withdrawTo(
address _token,
address _to,
uint256 _quantity
)
external;
/*
* Increment quantity owned of a token for a given address. Can
* only be called by authorized core contracts.
*
* @param _token The address of the ERC20 token
* @param _owner The address of the token owner
* @param _quantity The number of tokens to attribute to owner
*/
function incrementTokenOwner(
address _token,
address _owner,
uint256 _quantity
)
external;
/*
* Decrement quantity owned of a token for a given address. Can only
* be called by authorized core contracts.
*
* @param _token The address of the ERC20 token
* @param _owner The address of the token owner
* @param _quantity The number of tokens to deattribute to owner
*/
function decrementTokenOwner(
address _token,
address _owner,
uint256 _quantity
)
external;
/**
* Transfers tokens associated with one account to another account in the vault
*
* @param _token Address of token being transferred
* @param _from Address token being transferred from
* @param _to Address token being transferred to
* @param _quantity Amount of tokens being transferred
*/
function transferBalance(
address _token,
address _from,
address _to,
uint256 _quantity
)
external;
/*
* Withdraws user's unassociated tokens to user account. Can only be
* called by authorized core contracts.
*
* @param _tokens The addresses of the ERC20 tokens
* @param _owner The address of the token owner
* @param _quantities The numbers of tokens to attribute to owner
*/
function batchWithdrawTo(
address[] calldata _tokens,
address _to,
uint256[] calldata _quantities
)
external;
/*
* Increment quantites owned of a collection of tokens for a given address. Can
* only be called by authorized core contracts.
*
* @param _tokens The addresses of the ERC20 tokens
* @param _owner The address of the token owner
* @param _quantities The numbers of tokens to attribute to owner
*/
function batchIncrementTokenOwner(
address[] calldata _tokens,
address _owner,
uint256[] calldata _quantities
)
external;
/*
* Decrements quantites owned of a collection of tokens for a given address. Can
* only be called by authorized core contracts.
*
* @param _tokens The addresses of the ERC20 tokens
* @param _owner The address of the token owner
* @param _quantities The numbers of tokens to attribute to owner
*/
function batchDecrementTokenOwner(
address[] calldata _tokens,
address _owner,
uint256[] calldata _quantities
)
external;
/**
* Transfers tokens associated with one account to another account in the vault
*
* @param _tokens Addresses of tokens being transferred
* @param _from Address tokens being transferred from
* @param _to Address tokens being transferred to
* @param _quantities Amounts of tokens being transferred
*/
function batchTransferBalance(
address[] calldata _tokens,
address _from,
address _to,
uint256[] calldata _quantities
)
external;
/*
* Get balance of particular contract for owner.
*
* @param _token The address of the ERC20 token
* @param _owner The address of the token owner
*/
function getOwnerBalance(
address _token,
address _owner
)
external
view
returns (uint256);
}
// File: contracts/core/modules/lib/ModuleCoreState.sol
/*
Copyright 2018 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title ModuleCoreState
* @author Set Protocol
*
* The ModuleCoreState library maintains Core-related state for modules
*/
contract ModuleCoreState {
/* ============ State Variables ============ */
// Address of core contract
address public core;
// Address of vault contract
address public vault;
// Instance of core contract
ICore public coreInstance;
// Instance of vault contract
IVault public vaultInstance;
/* ============ Public Getters ============ */
/**
* Constructor function for ModuleCoreState
*
* @param _core The address of Core
* @param _vault The address of Vault
*/
constructor(
address _core,
address _vault
)
public
{
// Commit passed address to core state variable
core = _core;
// Commit passed address to coreInstance state variable
coreInstance = ICore(_core);
// Commit passed address to vault state variable
vault = _vault;
// Commit passed address to vaultInstance state variable
vaultInstance = IVault(_vault);
}
}
// File: contracts/lib/AddressArrayUtils.sol
// Pulled in from Cryptofin Solidity package in order to control Solidity compiler version
// https://github.com/cryptofinlabs/cryptofin-solidity/blob/master/contracts/array-utils/AddressArrayUtils.sol
pragma solidity 0.5.7;
library AddressArrayUtils {
/**
* Finds the index of the first occurrence of the given element.
* @param A The input array to search
* @param a The value to find
* @return Returns (index and isIn) for the first occurrence starting from index 0
*/
function indexOf(address[] memory A, address a) internal pure returns (uint256, bool) {
uint256 length = A.length;
for (uint256 i = 0; i < length; i++) {
if (A[i] == a) {
return (i, true);
}
}
return (0, false);
}
/**
* Returns true if the value is present in the list. Uses indexOf internally.
* @param A The input array to search
* @param a The value to find
* @return Returns isIn for the first occurrence starting from index 0
*/
function contains(address[] memory A, address a) internal pure returns (bool) {
bool isIn;
(, isIn) = indexOf(A, a);
return isIn;
}
/// @return Returns index and isIn for the first occurrence starting from
/// end
function indexOfFromEnd(address[] memory A, address a) internal pure returns (uint256, bool) {
uint256 length = A.length;
for (uint256 i = length; i > 0; i--) {
if (A[i - 1] == a) {
return (i, true);
}
}
return (0, false);
}
/**
* Returns the combination of the two arrays
* @param A The first array
* @param B The second array
* @return Returns A extended by B
*/
function extend(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
uint256 aLength = A.length;
uint256 bLength = B.length;
address[] memory newAddresses = new address[](aLength + bLength);
for (uint256 i = 0; i < aLength; i++) {
newAddresses[i] = A[i];
}
for (uint256 j = 0; j < bLength; j++) {
newAddresses[aLength + j] = B[j];
}
return newAddresses;
}
/**
* Returns the array with a appended to A.
* @param A The first array
* @param a The value to append
* @return Returns A appended by a
*/
function append(address[] memory A, address a) internal pure returns (address[] memory) {
address[] memory newAddresses = new address[](A.length + 1);
for (uint256 i = 0; i < A.length; i++) {
newAddresses[i] = A[i];
}
newAddresses[A.length] = a;
return newAddresses;
}
/**
* Returns the combination of two storage arrays.
* @param A The first array
* @param B The second array
* @return Returns A appended by a
*/
function sExtend(address[] storage A, address[] storage B) internal {
uint256 length = B.length;
for (uint256 i = 0; i < length; i++) {
A.push(B[i]);
}
}
/**
* Returns the intersection of two arrays. Arrays are treated as collections, so duplicates are kept.
* @param A The first array
* @param B The second array
* @return The intersection of the two arrays
*/
function intersect(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
uint256 length = A.length;
bool[] memory includeMap = new bool[](length);
uint256 newLength = 0;
for (uint256 i = 0; i < length; i++) {
if (contains(B, A[i])) {
includeMap[i] = true;
newLength++;
}
}
address[] memory newAddresses = new address[](newLength);
uint256 j = 0;
for (uint256 k = 0; k < length; k++) {
if (includeMap[k]) {
newAddresses[j] = A[k];
j++;
}
}
return newAddresses;
}
/**
* Returns the union of the two arrays. Order is not guaranteed.
* @param A The first array
* @param B The second array
* @return The union of the two arrays
*/
function union(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
address[] memory leftDifference = difference(A, B);
address[] memory rightDifference = difference(B, A);
address[] memory intersection = intersect(A, B);
return extend(leftDifference, extend(intersection, rightDifference));
}
/**
* Alternate implementation
* Assumes there are no duplicates
*/
function unionB(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
bool[] memory includeMap = new bool[](A.length + B.length);
uint256 count = 0;
for (uint256 i = 0; i < A.length; i++) {
includeMap[i] = true;
count++;
}
for (uint256 j = 0; j < B.length; j++) {
if (!contains(A, B[j])) {
includeMap[A.length + j] = true;
count++;
}
}
address[] memory newAddresses = new address[](count);
uint256 k = 0;
for (uint256 m = 0; m < A.length; m++) {
if (includeMap[m]) {
newAddresses[k] = A[m];
k++;
}
}
for (uint256 n = 0; n < B.length; n++) {
if (includeMap[A.length + n]) {
newAddresses[k] = B[n];
k++;
}
}
return newAddresses;
}
/**
* Computes the difference of two arrays. Assumes there are no duplicates.
* @param A The first array
* @param B The second array
* @return The difference of the two arrays
*/
function difference(address[] memory A, address[] memory B) internal pure returns (address[] memory) {
uint256 length = A.length;
bool[] memory includeMap = new bool[](length);
uint256 count = 0;
// First count the new length because can't push for in-memory arrays
for (uint256 i = 0; i < length; i++) {
address e = A[i];
if (!contains(B, e)) {
includeMap[i] = true;
count++;
}
}
address[] memory newAddresses = new address[](count);
uint256 j = 0;
for (uint256 k = 0; k < length; k++) {
if (includeMap[k]) {
newAddresses[j] = A[k];
j++;
}
}
return newAddresses;
}
/**
* @dev Reverses storage array in place
*/
function sReverse(address[] storage A) internal {
address t;
uint256 length = A.length;
for (uint256 i = 0; i < length / 2; i++) {
t = A[i];
A[i] = A[A.length - i - 1];
A[A.length - i - 1] = t;
}
}
/**
* Removes specified index from array
* Resulting ordering is not guaranteed
* @return Returns the new array and the removed entry
*/
function pop(address[] memory A, uint256 index)
internal
pure
returns (address[] memory, address)
{
uint256 length = A.length;
address[] memory newAddresses = new address[](length - 1);
for (uint256 i = 0; i < index; i++) {
newAddresses[i] = A[i];
}
for (uint256 j = index + 1; j < length; j++) {
newAddresses[j - 1] = A[j];
}
return (newAddresses, A[index]);
}
/**
* @return Returns the new array
*/
function remove(address[] memory A, address a)
internal
pure
returns (address[] memory)
{
(uint256 index, bool isIn) = indexOf(A, a);
if (!isIn) {
revert();
} else {
(address[] memory _A,) = pop(A, index);
return _A;
}
}
function sPop(address[] storage A, uint256 index) internal returns (address) {
uint256 length = A.length;
if (index >= length) {
revert("Error: index out of bounds");
}
address entry = A[index];
for (uint256 i = index; i < length - 1; i++) {
A[i] = A[i + 1];
}
A.length--;
return entry;
}
/**
* Deletes address at index and fills the spot with the last address.
* Order is not preserved.
* @return Returns the removed entry
*/
function sPopCheap(address[] storage A, uint256 index) internal returns (address) {
uint256 length = A.length;
if (index >= length) {
revert("Error: index out of bounds");
}
address entry = A[index];
if (index != length - 1) {
A[index] = A[length - 1];
delete A[length - 1];
}
A.length--;
return entry;
}
/**
* Deletes address at index. Works by swapping it with the last address, then deleting.
* Order is not preserved
* @param A Storage array to remove from
*/
function sRemoveCheap(address[] storage A, address a) internal {
(uint256 index, bool isIn) = indexOf(A, a);
if (!isIn) {
revert("Error: entry not found");
} else {
sPopCheap(A, index);
return;
}
}
/**
* Returns whether or not there's a duplicate. Runs in O(n^2).
* @param A Array to search
* @return Returns true if duplicate, false otherwise
*/
function hasDuplicate(address[] memory A) internal pure returns (bool) {
if (A.length == 0) {
return false;
}
for (uint256 i = 0; i < A.length - 1; i++) {
for (uint256 j = i + 1; j < A.length; j++) {
if (A[i] == A[j]) {
return true;
}
}
}
return false;
}
/**
* Returns whether the two arrays are equal.
* @param A The first array
* @param B The second array
* @return True is the arrays are equal, false if not.
*/
function isEqual(address[] memory A, address[] memory B) internal pure returns (bool) {
if (A.length != B.length) {
return false;
}
for (uint256 i = 0; i < A.length; i++) {
if (A[i] != B[i]) {
return false;
}
}
return true;
}
/**
* Returns the elements indexed at indexArray.
* @param A The array to index
* @param indexArray The array to use to index
* @return Returns array containing elements indexed at indexArray
*/
function argGet(address[] memory A, uint256[] memory indexArray)
internal
pure
returns (address[] memory)
{
address[] memory array = new address[](indexArray.length);
for (uint256 i = 0; i < indexArray.length; i++) {
array[i] = A[indexArray[i]];
}
return array;
}
}
// File: contracts/core/modules/lib/RebalancingSetIssuance.sol
/*
Copyright 2019 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title RebalancingSetIssuance
* @author Set Protocol
*
* The RebalancingSetIssuance contains utility functions used in rebalancing SetToken
* issuance
*/
contract RebalancingSetIssuance is
ModuleCoreState
{
using SafeMath for uint256;
using AddressArrayUtils for address[];
// ============ Internal ============
/**
* Validates that wrapped Ether is a component of the SetToken
*
* @param _setAddress Address of the SetToken
* @param _wrappedEtherAddress Address of wrapped Ether
*/
function validateWETHIsAComponentOfSet(
address _setAddress,
address _wrappedEtherAddress
)
internal
view
{
require(
ISetToken(_setAddress).tokenIsComponent(_wrappedEtherAddress),
"RebalancingSetIssuance.validateWETHIsAComponentOfSet: Components must contain weth"
);
}
/**
* Validates that the passed in address is tracked by Core and that the quantity
* is a multiple of the natural unit
*
* @param _rebalancingSetAddress Address of the rebalancing SetToken to issue/redeem
* @param _rebalancingSetQuantity The issuance quantity of rebalancing SetToken
*/
function validateRebalancingSetIssuance(
address _rebalancingSetAddress,
uint256 _rebalancingSetQuantity
)
internal
view
{
// Expect rebalancing SetToken to be valid and enabled SetToken
require(
coreInstance.validSets(_rebalancingSetAddress),
"RebalancingSetIssuance.validateRebalancingIssuance: Invalid or disabled SetToken address"
);
// Make sure Issuance quantity is multiple of the rebalancing SetToken natural unit
require(
_rebalancingSetQuantity.mod(ISetToken(_rebalancingSetAddress).naturalUnit()) == 0,
"RebalancingSetIssuance.validateRebalancingIssuance: Quantity must be multiple of natural unit"
);
}
/**
* Given a rebalancing SetToken and a desired issue quantity, calculates the
* minimum issuable quantity of the base SetToken. If the calculated quantity is initially
* not a multiple of the base SetToken's natural unit, the quantity is rounded up
* to the next base set natural unit.
*
* @param _rebalancingSetAddress Address of the rebalancing SetToken to issue
* @param _rebalancingSetQuantity The issuance quantity of rebalancing SetToken
* @return requiredBaseSetQuantity The quantity of base SetToken to issue
*/
function getBaseSetIssuanceRequiredQuantity(
address _rebalancingSetAddress,
uint256 _rebalancingSetQuantity
)
internal
view
returns (uint256)
{
IRebalancingSetTokenV2 rebalancingSet = IRebalancingSetTokenV2(_rebalancingSetAddress);
uint256 unitShares = rebalancingSet.unitShares();
uint256 naturalUnit = rebalancingSet.naturalUnit();
uint256 requiredBaseSetQuantity = _rebalancingSetQuantity.div(naturalUnit).mul(unitShares);
address baseSet = rebalancingSet.currentSet();
uint256 baseSetNaturalUnit = ISetToken(baseSet).naturalUnit();
// If there is a mismatch between the required quantity and the base SetToken natural unit,
// round up to the next base SetToken natural unit if required.
uint256 roundDownQuantity = requiredBaseSetQuantity.mod(baseSetNaturalUnit);
if (roundDownQuantity > 0) {
requiredBaseSetQuantity = requiredBaseSetQuantity.sub(roundDownQuantity).add(baseSetNaturalUnit);
}
return requiredBaseSetQuantity;
}
/**
* Given a rebalancing SetToken address, retrieve the base SetToken quantity redeem quantity based on the quantity
* held in the Vault. Rounds down to the nearest base SetToken natural unit
*
* @param _baseSetAddress The address of the base SetToken
* @return baseSetRedeemQuantity The quantity of base SetToken to redeem
*/
function getBaseSetRedeemQuantity(
address _baseSetAddress
)
internal
view
returns (uint256)
{
// Get base SetToken Details from the rebalancing SetToken
uint256 baseSetNaturalUnit = ISetToken(_baseSetAddress).naturalUnit();
uint256 baseSetBalance = vaultInstance.getOwnerBalance(
_baseSetAddress,
address(this)
);
// Round the balance down to the base SetToken natural unit and return
return baseSetBalance.sub(baseSetBalance.mod(baseSetNaturalUnit));
}
/**
* Checks the base SetToken balances in the Vault and on the contract.
* Sends any positive quantity to the user directly or into the Vault
* depending on the keepChangeInVault flag.
*
* @param _baseSetAddress The address of the base SetToken
* @param _transferProxyAddress The address of the TransferProxy
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transferred to the user
* or left in the vault
*/
function returnExcessBaseSet(
address _baseSetAddress,
address _transferProxyAddress,
bool _keepChangeInVault
)
internal
{
returnExcessBaseSetFromContract(_baseSetAddress, _transferProxyAddress, _keepChangeInVault);
returnExcessBaseSetInVault(_baseSetAddress, _keepChangeInVault);
}
/**
* Checks the base SetToken balances on the contract and sends
* any positive quantity to the user directly or into the Vault
* depending on the keepChangeInVault flag.
*
* @param _baseSetAddress The address of the base SetToken
* @param _transferProxyAddress The address of the TransferProxy
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transfered to the user
* or left in the vault
*/
function returnExcessBaseSetFromContract(
address _baseSetAddress,
address _transferProxyAddress,
bool _keepChangeInVault
)
internal
{
uint256 baseSetQuantity = ERC20Wrapper.balanceOf(_baseSetAddress, address(this));
if (baseSetQuantity == 0) {
return;
} else if (_keepChangeInVault) {
// Ensure base SetToken allowance
ERC20Wrapper.ensureAllowance(
_baseSetAddress,
address(this),
_transferProxyAddress,
baseSetQuantity
);
// Deposit base SetToken to the user
coreInstance.depositModule(
address(this),
msg.sender,
_baseSetAddress,
baseSetQuantity
);
} else {
// Transfer directly to the user
ERC20Wrapper.transfer(
_baseSetAddress,
msg.sender,
baseSetQuantity
);
}
}
/**
* Checks the base SetToken balances in the Vault and sends
* any positive quantity to the user directly or into the Vault
* depending on the keepChangeInVault flag.
*
* @param _baseSetAddress The address of the base SetToken
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transfered to the user
* or left in the vault
*/
function returnExcessBaseSetInVault(
address _baseSetAddress,
bool _keepChangeInVault
)
internal
{
// Return base SetToken if any that are in the Vault
uint256 baseSetQuantityInVault = vaultInstance.getOwnerBalance(
_baseSetAddress,
address(this)
);
if (baseSetQuantityInVault == 0) {
return;
} else if (_keepChangeInVault) {
// Transfer ownership within the vault to the user
coreInstance.internalTransfer(
_baseSetAddress,
msg.sender,
baseSetQuantityInVault
);
} else {
// Transfer ownership directly to the user
coreInstance.withdrawModule(
address(this),
msg.sender,
_baseSetAddress,
baseSetQuantityInVault
);
}
}
}
// File: contracts/core/modules/RebalancingSetIssuanceModule.sol
/*
Copyright 2019 Set Labs Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity 0.5.7;
/**
* @title RebalancingSetIssuanceModule
* @author Set Protocol
*
* A module that includes functions for issuing / redeeming rebalancing SetToken to/from its base components and ether.
*/
contract RebalancingSetIssuanceModule is
ModuleCoreState,
RebalancingSetIssuance,
ReentrancyGuard
{
using SafeMath for uint256;
// Address and instance of TransferProxy contract
address public transferProxy;
// Address and instance of Wrapped Ether contract
IWETH public weth;
/* ============ Events ============ */
event LogRebalancingSetIssue(
address indexed rebalancingSetAddress,
address indexed callerAddress,
uint256 rebalancingSetQuantity
);
event LogRebalancingSetRedeem(
address indexed rebalancingSetAddress,
address indexed callerAddress,
uint256 rebalancingSetQuantity
);
/* ============ Constructor ============ */
/**
* Constructor function for RebalancingSetIssuanceModule
*
* @param _core The address of Core
* @param _vault The address of Vault
* @param _transferProxy The address of TransferProxy
* @param _weth The address of Wrapped Ether
*/
constructor(
address _core,
address _vault,
address _transferProxy,
IWETH _weth
)
public
ModuleCoreState(
_core,
_vault
)
{
// Commit the WETH instance
weth = _weth;
// Commit the transferProxy instance
transferProxy = _transferProxy;
}
/**
* Fallback function. Disallows ether to be sent to this contract without data except when
* unwrapping WETH.
*/
function ()
external
payable
{
require(
msg.sender == address(weth),
"RebalancingSetIssuanceModule.fallback: Cannot receive ETH directly unless unwrapping WETH"
);
}
/* ============ External Functions ============ */
/**
* Issue a rebalancing SetToken using the base components of the base SetToken.
* The base SetToken is then issued into the rebalancing SetToken. The base SetToken quantity issued is calculated
* by taking the rebalancing SetToken's quantity, unit shares, and natural unit. If the calculated quantity is not
* a multiple of the natural unit of the base SetToken, the quantity is rounded up to the base SetToken natural unit.
* NOTE: Potential to receive more baseSet than expected if someone transfers some to this module.
* Be careful with balance checks.
*
* @param _rebalancingSetAddress Address of the rebalancing SetToken to issue
* @param _rebalancingSetQuantity The issuance quantity of rebalancing SetToken
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transfered to the user
* or left in the vault
*/
function issueRebalancingSet(
address _rebalancingSetAddress,
uint256 _rebalancingSetQuantity,
bool _keepChangeInVault
)
external
nonReentrant
{
// Get baseSet address and quantity required for issuance of Rebalancing Set
(
address baseSetAddress,
uint256 requiredBaseSetQuantity
) = getBaseSetAddressAndQuantity(_rebalancingSetAddress, _rebalancingSetQuantity);
// Issue base SetToken to this contract, held in this contract
coreInstance.issueModule(
msg.sender,
address(this),
baseSetAddress,
requiredBaseSetQuantity
);
// Ensure base SetToken allowance to the transferProxy
ERC20Wrapper.ensureAllowance(
baseSetAddress,
address(this),
transferProxy,
requiredBaseSetQuantity
);
// Issue rebalancing SetToken to the sender and return any excess base to sender
issueRebalancingSetAndReturnExcessBase(
_rebalancingSetAddress,
baseSetAddress,
_rebalancingSetQuantity,
_keepChangeInVault
);
}
/**
* Issue a rebalancing SetToken using the base components and ether of the base SetToken. The ether is wrapped
* into wrapped Ether and utilized in issuance.
* The base SetToken is then issued and reissued into the rebalancing SetToken. Read more about base SetToken quantity
* in the issueRebalancingSet function.
* NOTE: Potential to receive more baseSet and ether dust than expected if someone transfers some to this module.
* Be careful with balance checks.
*
* @param _rebalancingSetAddress Address of the rebalancing SetToken to issue
* @param _rebalancingSetQuantity The issuance quantity of rebalancing SetToken
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transfered to the user
* or left in the vault
*/
function issueRebalancingSetWrappingEther(
address _rebalancingSetAddress,
uint256 _rebalancingSetQuantity,
bool _keepChangeInVault
)
external
payable
nonReentrant
{
// Get baseSet address and quantity required for issuance of Rebalancing Set
(
address baseSetAddress,
uint256 requiredBaseSetQuantity
) = getBaseSetAddressAndQuantity(_rebalancingSetAddress, _rebalancingSetQuantity);
// Validate that WETH is a component of baseSet
validateWETHIsAComponentOfSet(baseSetAddress, address(weth));
// Deposit all the required non-weth components to the vault under the name of this contract
// The required ether is wrapped and approved to the transferProxy
depositComponentsAndHandleEth(
baseSetAddress,
requiredBaseSetQuantity
);
// Issue base SetToken to this contract, with the base SetToken held in the Vault
coreInstance.issueInVault(
baseSetAddress,
requiredBaseSetQuantity
);
// Note: Don't need to set allowance of the base SetToken as the base SetToken is already in the vault
// Issue rebalancing SetToken to the sender and return any excess base to sender
issueRebalancingSetAndReturnExcessBase(
_rebalancingSetAddress,
baseSetAddress,
_rebalancingSetQuantity,
_keepChangeInVault
);
// Any eth that is not wrapped is sent back to the user
// Only the amount required for the base SetToken issuance is wrapped.
uint256 leftoverEth = address(this).balance;
if (leftoverEth > 0) {
msg.sender.transfer(leftoverEth);
}
}
/**
* Redeems a rebalancing SetToken into the base components of the base SetToken.
* NOTE: Potential to receive more baseSet than expected if someone transfers some to this module.
* Be careful with balance checks.
*
* @param _rebalancingSetAddress Address of the rebalancing SetToken to redeem
* @param _rebalancingSetQuantity The Quantity of the rebalancing SetToken to redeem
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transfered to the user
* or left in the vault
*/
function redeemRebalancingSet(
address _rebalancingSetAddress,
uint256 _rebalancingSetQuantity,
bool _keepChangeInVault
)
external
nonReentrant
{
// Validate the rebalancing SetToken is valid and the quantity is a multiple of the natural unit
validateRebalancingSetIssuance(_rebalancingSetAddress, _rebalancingSetQuantity);
// Redeem RB Set to the vault attributed to this contract
coreInstance.redeemModule(
msg.sender,
address(this),
_rebalancingSetAddress,
_rebalancingSetQuantity
);
// Calculate the base SetToken Redeem quantity
address baseSetAddress = IRebalancingSetTokenV2(_rebalancingSetAddress).currentSet();
uint256 baseSetRedeemQuantity = getBaseSetRedeemQuantity(baseSetAddress);
// Withdraw base SetToken to this contract
coreInstance.withdraw(
baseSetAddress,
baseSetRedeemQuantity
);
// Redeem base SetToken and send components to the the user
// Set exclude to 0, as tokens in rebalancing SetToken are already whitelisted
coreInstance.redeemAndWithdrawTo(
baseSetAddress,
msg.sender,
baseSetRedeemQuantity,
0
);
// Transfer any change of the base SetToken to the end user
returnExcessBaseSet(baseSetAddress, transferProxy, _keepChangeInVault);
// Log RebalancingSetRedeem
emit LogRebalancingSetRedeem(
_rebalancingSetAddress,
msg.sender,
_rebalancingSetQuantity
);
}
/**
* Redeems a rebalancing SetToken into the base components of the base SetToken. Unwraps
* wrapped ether and sends eth to the user. If no wrapped ether in Set then will REVERT.
* NOTE: Potential to receive more baseSet and ether dust than expected if someone transfers some to this module.
* Be careful with balance checks.
*
* @param _rebalancingSetAddress Address of the rebalancing SetToken to redeem
* @param _rebalancingSetQuantity The Quantity of the rebalancing SetToken to redeem
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transferred to the user
* or left in the vault
*/
function redeemRebalancingSetUnwrappingEther(
address _rebalancingSetAddress,
uint256 _rebalancingSetQuantity,
bool _keepChangeInVault
)
external
nonReentrant
{
// Validate the rebalancing SetToken is valid and the quantity is a multiple of the natural unit
validateRebalancingSetIssuance(_rebalancingSetAddress, _rebalancingSetQuantity);
address baseSetAddress = IRebalancingSetTokenV2(_rebalancingSetAddress).currentSet();
validateWETHIsAComponentOfSet(baseSetAddress, address(weth));
// Redeem rebalancing SetToken to the vault attributed to this contract
coreInstance.redeemModule(
msg.sender,
address(this),
_rebalancingSetAddress,
_rebalancingSetQuantity
);
// Calculate the base SetToken Redeem quantity
uint256 baseSetRedeemQuantity = getBaseSetRedeemQuantity(baseSetAddress);
// Withdraw base SetToken to this contract
coreInstance.withdraw(
baseSetAddress,
baseSetRedeemQuantity
);
// Redeem the base SetToken. The components stay in the vault
coreInstance.redeem(
baseSetAddress,
baseSetRedeemQuantity
);
// Loop through the base SetToken components and transfer to sender.
withdrawComponentsToSenderWithEther(baseSetAddress);
// Transfer any change of the base SetToken to the end user
returnExcessBaseSet(baseSetAddress, transferProxy, _keepChangeInVault);
// Log RebalancingSetRedeem
emit LogRebalancingSetRedeem(
_rebalancingSetAddress,
msg.sender,
_rebalancingSetQuantity
);
}
/* ============ Private Functions ============ */
/**
* Gets base set address from rebalancing set token and calculates amount of base set needed
* for issuance.
*
* @param _rebalancingSetAddress Address of the RebalancingSetToken to issue
* @param _rebalancingSetQuantity The Quantity of the rebalancing SetToken to issue
* @return baseSetAddress The address of RebalancingSet's base SetToken
* @return requiredBaseSetQuantity The quantity of base SetToken to issue
*/
function getBaseSetAddressAndQuantity(
address _rebalancingSetAddress,
uint256 _rebalancingSetQuantity
)
internal
view
returns (address, uint256)
{
// Validate the rebalancing SetToken is valid and the quantity is a multiple of the natural unit
validateRebalancingSetIssuance(_rebalancingSetAddress, _rebalancingSetQuantity);
address baseSetAddress = IRebalancingSetTokenV2(_rebalancingSetAddress).currentSet();
// Calculate required base SetToken quantity
uint256 requiredBaseSetQuantity = getBaseSetIssuanceRequiredQuantity(
_rebalancingSetAddress,
_rebalancingSetQuantity
);
return (baseSetAddress, requiredBaseSetQuantity);
}
/**
* Issues the rebalancing set token to sender and returns any excess baseSet.
*
* @param _rebalancingSetAddress Address of the rebalancing SetToken to issue
* @param _baseSetAddress Address of the rebalancing SetToken's base set
* @param _rebalancingSetQuantity The Quantity of the rebalancing SetToken to redeem
* @param _keepChangeInVault Boolean signifying whether excess base SetToken is transferred to the user
* or left in the vault
*/
function issueRebalancingSetAndReturnExcessBase(
address _rebalancingSetAddress,
address _baseSetAddress,
uint256 _rebalancingSetQuantity,
bool _keepChangeInVault
)
internal
{
// Issue rebalancing SetToken to the sender
coreInstance.issueTo(
msg.sender,
_rebalancingSetAddress,
_rebalancingSetQuantity
);
// Return any excess base SetToken token to the sender
returnExcessBaseSet(_baseSetAddress, transferProxy, _keepChangeInVault);
// Log RebalancingSetIssue
emit LogRebalancingSetIssue(
_rebalancingSetAddress,
msg.sender,
_rebalancingSetQuantity
);
}
/**
* During issuance, deposit the required quantity of base SetToken, wrap Ether, and deposit components
* (excluding Ether, which is deposited during issuance) to the Vault in the name of the module.
*
* @param _baseSetAddress Address of the base SetToken token
* @param _baseSetQuantity The Quantity of the base SetToken token to issue
*/
function depositComponentsAndHandleEth(
address _baseSetAddress,
uint256 _baseSetQuantity
)
private
{
ISetToken baseSet = ISetToken(_baseSetAddress);
address[] memory baseSetComponents = baseSet.getComponents();
uint256[] memory baseSetUnits = baseSet.getUnits();
uint256 baseSetNaturalUnit = baseSet.naturalUnit();
// Loop through the base SetToken components and deposit components
for (uint256 i = 0; i < baseSetComponents.length; i++) {
address currentComponent = baseSetComponents[i];
uint256 currentUnit = baseSetUnits[i];
// Calculate required component quantity
uint256 currentComponentQuantity = _baseSetQuantity.div(baseSetNaturalUnit).mul(currentUnit);
// If address is weth, deposit weth and transfer eth
if (currentComponent == address(weth)) {
// Expect the ether included exceeds the required Weth quantity
require(
msg.value >= currentComponentQuantity,
"RebalancingSetIssuanceModule.depositComponentsAndHandleEth: Not enough ether included for base SetToken"
);
// wrap the required ether quantity
weth.deposit.value(currentComponentQuantity)();
// Ensure weth allowance
ERC20Wrapper.ensureAllowance(
address(weth),
address(this),
transferProxy,
currentComponentQuantity
);
} else {
// Deposit components to the vault in the name of the contract
coreInstance.depositModule(
msg.sender,
address(this),
currentComponent,
currentComponentQuantity
);
}
}
}
/**
* During redemption, withdraw the required quantity of base SetToken, unwrapping Ether, and withdraw
* components to the sender
*
* @param _baseSetAddress Address of the base SetToken
*/
function withdrawComponentsToSenderWithEther(
address _baseSetAddress
)
private
{
address[] memory baseSetComponents = ISetToken(_baseSetAddress).getComponents();
// Loop through the base SetToken components.
for (uint256 i = 0; i < baseSetComponents.length; i++) {
address currentComponent = baseSetComponents[i];
uint256 currentComponentQuantity = vaultInstance.getOwnerBalance(
currentComponent,
address(this)
);
// If address is weth, withdraw weth and transfer eth to sender
if (currentComponent == address(weth)) {
// Transfer the wrapped ether to this address from the Vault
coreInstance.withdrawModule(
address(this),
address(this),
address(weth),
currentComponentQuantity
);
// Unwrap wrapped ether
weth.withdraw(currentComponentQuantity);
// Transfer to recipient
msg.sender.transfer(currentComponentQuantity);
} else {
// Withdraw component from the Vault and send to the user
coreInstance.withdrawModule(
address(this),
msg.sender,
currentComponent,
currentComponentQuantity
);
}
}
}
}
{
"compilationTarget": {
"RebalancingSetIssuanceModule.sol": "RebalancingSetIssuanceModule"
},
"evmVersion": "byzantium",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"constant":false,"inputs":[{"name":"_rebalancingSetAddress","type":"address"},{"name":"_rebalancingSetQuantity","type":"uint256"},{"name":"_keepChangeInVault","type":"bool"}],"name":"issueRebalancingSet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_rebalancingSetAddress","type":"address"},{"name":"_rebalancingSetQuantity","type":"uint256"},{"name":"_keepChangeInVault","type":"bool"}],"name":"issueRebalancingSetWrappingEther","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"weth","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"vaultInstance","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"transferProxy","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"coreInstance","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_rebalancingSetAddress","type":"address"},{"name":"_rebalancingSetQuantity","type":"uint256"},{"name":"_keepChangeInVault","type":"bool"}],"name":"redeemRebalancingSet","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"core","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_rebalancingSetAddress","type":"address"},{"name":"_rebalancingSetQuantity","type":"uint256"},{"name":"_keepChangeInVault","type":"bool"}],"name":"redeemRebalancingSetUnwrappingEther","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"vault","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_core","type":"address"},{"name":"_vault","type":"address"},{"name":"_transferProxy","type":"address"},{"name":"_weth","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"rebalancingSetAddress","type":"address"},{"indexed":true,"name":"callerAddress","type":"address"},{"indexed":false,"name":"rebalancingSetQuantity","type":"uint256"}],"name":"LogRebalancingSetIssue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"rebalancingSetAddress","type":"address"},{"indexed":true,"name":"callerAddress","type":"address"},{"indexed":false,"name":"rebalancingSetQuantity","type":"uint256"}],"name":"LogRebalancingSetRedeem","type":"event"}]