Contract Name:
TransferProxy
Contract Source Code:
File 1 of 1 : TransferProxy
// 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: openzeppelin-solidity/contracts/ownership/Ownable.sol
pragma solidity ^0.5.2;
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor () internal {
_owner = msg.sender;
emit OwnershipTransferred(address(0), _owner);
}
/**
* @return the address of the owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner());
_;
}
/**
* @return true if `msg.sender` is the owner of the contract.
*/
function isOwner() public view returns (bool) {
return msg.sender == _owner;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
* @notice Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0));
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// 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/lib/TimeLockUpgrade.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 TimeLockUpgrade
* @author Set Protocol
*
* The TimeLockUpgrade contract contains a modifier for handling minimum time period updates
*/
contract TimeLockUpgrade is
Ownable
{
using SafeMath for uint256;
/* ============ State Variables ============ */
// Timelock Upgrade Period in seconds
uint256 public timeLockPeriod;
// Mapping of upgradable units and initialized timelock
mapping(bytes32 => uint256) public timeLockedUpgrades;
/* ============ Events ============ */
event UpgradeRegistered(
bytes32 _upgradeHash,
uint256 _timestamp
);
/* ============ Modifiers ============ */
modifier timeLockUpgrade() {
// If the time lock period is 0, then allow non-timebound upgrades.
// This is useful for initialization of the protocol and for testing.
if (timeLockPeriod == 0) {
_;
return;
}
// The upgrade hash is defined by the hash of the transaction call data,
// which uniquely identifies the function as well as the passed in arguments.
bytes32 upgradeHash = keccak256(
abi.encodePacked(
msg.data
)
);
uint256 registrationTime = timeLockedUpgrades[upgradeHash];
// If the upgrade hasn't been registered, register with the current time.
if (registrationTime == 0) {
timeLockedUpgrades[upgradeHash] = block.timestamp;
emit UpgradeRegistered(
upgradeHash,
block.timestamp
);
return;
}
require(
block.timestamp >= registrationTime.add(timeLockPeriod),
"TimeLockUpgrade: Time lock period must have elapsed."
);
// Reset the timestamp to 0
timeLockedUpgrades[upgradeHash] = 0;
// Run the rest of the upgrades
_;
}
/* ============ Function ============ */
/**
* Change timeLockPeriod period. Generally called after initially settings have been set up.
*
* @param _timeLockPeriod Time in seconds that upgrades need to be evaluated before execution
*/
function setTimeLockPeriod(
uint256 _timeLockPeriod
)
external
onlyOwner
{
// Only allow setting of the timeLockPeriod if the period is greater than the existing
require(
_timeLockPeriod > timeLockPeriod,
"TimeLockUpgrade: New period must be greater than existing"
);
timeLockPeriod = _timeLockPeriod;
}
}
// File: contracts/lib/Authorizable.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 Authorizable
* @author Set Protocol
*
* The Authorizable contract is an inherited contract that sets permissions on certain function calls
* through the onlyAuthorized modifier. Permissions can be managed only by the Owner of the contract.
*/
contract Authorizable is
Ownable,
TimeLockUpgrade
{
using SafeMath for uint256;
using AddressArrayUtils for address[];
/* ============ State Variables ============ */
// Mapping of addresses to bool indicator of authorization
mapping (address => bool) public authorized;
// Array of authorized addresses
address[] public authorities;
/* ============ Modifiers ============ */
// Only authorized addresses can invoke functions with this modifier.
modifier onlyAuthorized {
require(
authorized[msg.sender],
"Authorizable.onlyAuthorized: Sender not included in authorities"
);
_;
}
/* ============ Events ============ */
// Event emitted when new address is authorized.
event AddressAuthorized (
address indexed authAddress,
address authorizedBy
);
// Event emitted when address is deauthorized.
event AuthorizedAddressRemoved (
address indexed addressRemoved,
address authorizedBy
);
/* ============ Setters ============ */
/**
* Add authorized address to contract. Can only be set by owner.
*
* @param _authTarget The address of the new authorized contract
*/
function addAuthorizedAddress(address _authTarget)
external
onlyOwner
timeLockUpgrade
{
// Require that address is not already authorized
require(
!authorized[_authTarget],
"Authorizable.addAuthorizedAddress: Address already registered"
);
// Set address authority to true
authorized[_authTarget] = true;
// Add address to authorities array
authorities.push(_authTarget);
// Emit authorized address event
emit AddressAuthorized(
_authTarget,
msg.sender
);
}
/**
* Remove authorized address from contract. Can only be set by owner.
*
* @param _authTarget The address to be de-permissioned
*/
function removeAuthorizedAddress(address _authTarget)
external
onlyOwner
{
// Require address is authorized
require(
authorized[_authTarget],
"Authorizable.removeAuthorizedAddress: Address not authorized"
);
// Delete address from authorized mapping
authorized[_authTarget] = false;
authorities = authorities.remove(_authTarget);
// Emit AuthorizedAddressRemoved event.
emit AuthorizedAddressRemoved(
_authTarget,
msg.sender
);
}
/* ============ Getters ============ */
/**
* Get array of authorized addresses.
*
* @return address[] Array of authorized addresses
*/
function getAuthorizedAddresses()
external
view
returns (address[] memory)
{
// Return array of authorized addresses
return authorities;
}
}
// 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/TransferProxy.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 TransferProxy
* @author Set Protocol
*
* The transferProxy contract is responsible for moving tokens through the system to
* assist with issuance and usage from modules.
*/
contract TransferProxy is
Authorizable
{
using SafeMath for uint256;
/* ============ External Functions ============ */
/**
* Transfers tokens from an address (that has set allowance on the proxy).
* Can only be called by Core.
*
* @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
)
public
onlyAuthorized
{
// Call specified ERC20 contract to transfer tokens (via proxy).
if (_quantity > 0) {
// Retrieve current balance of token for the receiver
uint256 existingBalance = ERC20Wrapper.balanceOf(
_token,
_to
);
ERC20Wrapper.transferFrom(
_token,
_from,
_to,
_quantity
);
// Get new balance of transferred token for receiver
uint256 newBalance = ERC20Wrapper.balanceOf(
_token,
_to
);
// Verify transfer quantity is reflected in balance
require(
newBalance == existingBalance.add(_quantity),
"TransferProxy.transfer: Invalid post transfer balance"
);
}
}
/**
* Transfers tokens from an address (that has set allowance on the proxy).
* Can only be called by Core.
*
* @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
onlyAuthorized
{
// Storing token count to local variable to save on invocation
uint256 tokenCount = _tokens.length;
// Confirm and empty _tokens array is not passed
require(
tokenCount > 0,
"TransferProxy.batchTransfer: Tokens must not be empty"
);
// Confirm there is one quantity for every token address
require(
tokenCount == _quantities.length,
"TransferProxy.batchTransfer: Tokens and quantities lengths mismatch"
);
for (uint256 i = 0; i < tokenCount; i++) {
if (_quantities[i] > 0) {
transfer(
_tokens[i],
_quantities[i],
_from,
_to
);
}
}
}
}