diff --git a/contracts/Abstract/InterfaceStorage.sol b/contracts/Abstract/InterfaceStorage.sol
deleted file mode 100644
index 54134e6..0000000
--- a/contracts/Abstract/InterfaceStorage.sol
+++ /dev/null
@@ -1,6 +0,0 @@
-pragma solidity ^0.5.0;
-
-
-interface InterfaceStorage {
- function whitelistedAddresses(address) external view returns (bool);
-}
diff --git a/contracts/README.md b/contracts/README.md
index c1274de..cee0038 100644
--- a/contracts/README.md
+++ b/contracts/README.md
@@ -4,7 +4,7 @@
- Creation and Redemption
- Daily Rebalance and Threshold Rebalance
-- Composition PCF And Funding Rate [WIP]
+- Composition PCF And Funding Rate
### List of Smart Contracts:
diff --git a/contracts/TestStorageExample/Storage.sol b/contracts/TestStorageExample/Storage.sol
deleted file mode 100644
index c97baf4..0000000
--- a/contracts/TestStorageExample/Storage.sol
+++ /dev/null
@@ -1,42 +0,0 @@
-pragma solidity ^0.5.0;
-
-import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
-
-
-contract Storage is Ownable {
- uint256 public instrumentCounter;
- mapping(string => uint256) instruments;
- mapping(address => uint256[]) public allowedInstruments;
- event AddInstrument(string addedInstrument);
-
- function getInstrumentIndex(string memory instrument)
- public
- view
- returns (uint256)
- {
- return instruments[instrument];
- }
-
- function insertInstrument(string memory instrument) public onlyOwner {
- require(instruments[instrument] == 0, "instrument already exists");
- instrumentCounter = instrumentCounter + 1;
- instruments[instrument] = instrumentCounter;
- emit AddInstrument(instrument);
- }
-
- function addAllowedInstruments(address user, string memory instrument)
- public
- onlyOwner
- {
- require(instruments[instrument] != 0, "instrument does not exist");
- allowedInstruments[user].push(instruments[instrument]);
- }
-
- function getUserAllowedInstruments(address user)
- public
- view
- returns (uint256[] memory)
- {
- return allowedInstruments[user];
- }
-}
diff --git a/contracts/TestStorageExample/StorageProxy.sol b/contracts/TestStorageExample/StorageProxy.sol
deleted file mode 100644
index d0163f6..0000000
--- a/contracts/TestStorageExample/StorageProxy.sol
+++ /dev/null
@@ -1,8 +0,0 @@
-pragma solidity ^0.5.0;
-// Taken from: https://blog.openzeppelin.com/smart-contract-upgradeability-using-eternal-storage/
-
-import "@openzeppelin/upgrades/contracts/upgradeability/UpgradeabilityProxy.sol";
-import "./Storage.sol";
-
-
-contract StorageProxy is UpgradeabilityProxy, Storage {}
diff --git a/contracts/TestStorageExample/StorageVersionUpgradeExample.sol b/contracts/TestStorageExample/StorageVersionUpgradeExample.sol
deleted file mode 100644
index b216783..0000000
--- a/contracts/TestStorageExample/StorageVersionUpgradeExample.sol
+++ /dev/null
@@ -1,17 +0,0 @@
-pragma solidity ^0.5.0;
-
-import "./UpgradeableStorage.sol";
-
-
-contract Storage_V0 is UpgradeableStorage {
- function test_storage_v0() public view returns (uint256) {
- return instrumentCounter;
- }
-}
-
-
-contract Storage_V1 is UpgradeableStorage {
- function test_storage_v1() public view returns (uint256) {
- return instrumentCounter + 1;
- }
-}
diff --git a/contracts/TestStorageExample/UpgradeableStorage.sol b/contracts/TestStorageExample/UpgradeableStorage.sol
deleted file mode 100644
index dd4b36d..0000000
--- a/contracts/TestStorageExample/UpgradeableStorage.sol
+++ /dev/null
@@ -1,8 +0,0 @@
-pragma solidity ^0.5.0;
-// Taken from: https://blog.openzeppelin.com/smart-contract-upgradeability-using-eternal-storage/
-
-import "@openzeppelin/upgrades/contracts/upgradeability/InitializableUpgradeabilityProxy.sol";
-import "./Storage.sol";
-
-
-contract UpgradeableStorage is InitializableUpgradeabilityProxy, Storage {}
diff --git a/contracts/leverage-tokens/.keep b/contracts/leverage-tokens/.keep
new file mode 100644
index 0000000..79c0014
--- /dev/null
+++ b/contracts/leverage-tokens/.keep
@@ -0,0 +1 @@
+Here will live leverage tokens smart contracts
\ No newline at end of file
diff --git a/contracts/leverage-tokens/Abstract/InterfaceCalculator.sol b/contracts/leverage-tokens/Abstract/InterfaceCalculator.sol
new file mode 100644
index 0000000..f200482
--- /dev/null
+++ b/contracts/leverage-tokens/Abstract/InterfaceCalculator.sol
@@ -0,0 +1,28 @@
+pragma solidity ^0.5.0;
+
+
+interface InterfaceCalculator {
+ function getTokensCreatedByCash(
+ uint256 mintingPrice,
+ uint256 cash,
+ uint256 gasFee
+ ) external view returns (uint256 tokensCreated);
+
+ function getCashCreatedByTokens(
+ uint256 burningPrice,
+ uint256 elapsedTime,
+ uint256 tokens,
+ uint256 gasFee
+ ) external view returns (uint256 stablecoinRedeemed);
+
+ function removeCurrentMintingFeeFromCash(uint256 _cash)
+ external
+ view
+ returns (uint256 cashAfterFee);
+
+ function removeMintingFeeFromCash(
+ uint256 _cash,
+ uint256 _mintingFee,
+ uint256 _minimumMintingFee
+ ) external pure returns (uint256 cashAfterFee);
+}
diff --git a/contracts/leverage-tokens/Abstract/InterfaceStorageLeverage.sol b/contracts/leverage-tokens/Abstract/InterfaceStorageLeverage.sol
new file mode 100644
index 0000000..9f955d4
--- /dev/null
+++ b/contracts/leverage-tokens/Abstract/InterfaceStorageLeverage.sol
@@ -0,0 +1,66 @@
+pragma solidity ^0.5.0;
+
+
+
+interface InterfaceStorageLeverage {
+ function whitelistedAddresses(address) external view returns (bool);
+
+ function isPaused() external view returns (bool);
+
+ function isShutdown() external view returns (bool);
+
+ function tokenSwapManager() external view returns (address);
+
+ function bridge() external view returns (address);
+
+ function managementFee() external view returns (uint256);
+
+ function getExecutionPrice() external view returns (uint256);
+
+ function getMarkPrice() external view returns (uint256);
+
+ function getNotional() external view returns (uint256);
+
+ function getTokenValue() external view returns (uint256);
+
+ function getFundingRate() external view returns (uint256);
+
+ function getMintingFee(uint256 cash) external view returns (uint256);
+
+ function minimumMintingFee() external view returns (uint256);
+
+ function minRebalanceAmount() external view returns (uint8);
+
+ function delayedRedemptionsByUser(address) external view returns (uint256);
+
+ function setDelayedRedemptionsByUser(
+ uint256 amountToRedeem,
+ address whitelistedAddress
+ ) external;
+
+ function setOrderByUser(
+ address whitelistedAddress,
+ string calldata orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ uint256 orderIndex,
+ bool overwrite
+ ) external;
+
+ function setAccounting(
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external;
+
+ function setAccountingForLastActivityDay(
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external;
+}
diff --git a/contracts/leverage-tokens/CalculatorLeverage.sol b/contracts/leverage-tokens/CalculatorLeverage.sol
new file mode 100644
index 0000000..dc0f764
--- /dev/null
+++ b/contracts/leverage-tokens/CalculatorLeverage.sol
@@ -0,0 +1,118 @@
+pragma solidity ^0.5.0;
+
+import "@openzeppelin/upgrades/contracts/Initializable.sol";
+import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
+
+import "../short-tokens/utils/Math.sol";
+import "../short-tokens/utils/DateTimeLibrary.sol";
+import "./Abstract/InterfaceStorageLeverage.sol";
+import "../short-tokens/Abstract/InterfaceInverseToken.sol";
+
+
+/**
+ * @dev uint256 are expected to use last 18 numbers as decimal points except when specifid differently in @params
+ */
+contract CalculatorLeverage is Initializable {
+ using SafeMath for uint256;
+
+ InterfaceStorageLeverage public persistentStorage;
+ InterfaceInverseToken public inverseToken;
+
+ function initialize(
+ address _persistentStorageAddress,
+ address _inverseTokenAddress
+ ) public initializer {
+ persistentStorage = InterfaceStorageLeverage(_persistentStorageAddress);
+ inverseToken = InterfaceInverseToken(_inverseTokenAddress);
+ }
+
+ // Start fill in the values
+ // Creation 3x Long
+
+ function getTokensCreatedByCash(
+ uint256 mintingPrice,
+ uint256 cash,
+ uint256 gasFee
+ ) public view returns (uint256 tokensCreated) {
+ // Cash Remove Gas Fee
+ // Cash Remove Minting Fee (Cash Proceeds)
+ // Cash Proceeds / Minting Price (# of Tokens to Mint)
+
+ uint256 cashAfterGas = DSMath.sub(cash, gasFee);
+ uint256 cashAfterFee = removeCurrentMintingFeeFromCash(cashAfterGas);
+
+ uint256 tokensMinted = DSMath.wdiv(cashAfterFee, mintingPrice);
+
+ return tokensMinted;
+ }
+
+ function getCashCreatedByTokens(
+ uint256 burningPrice,
+ uint256 elapsedTime,
+ uint256 tokens,
+ uint256 gasFee
+ ) public view returns (uint256 stablecoinAfterFees) {
+ uint256 stablecoin = DSMath.wmul(tokens, burningPrice);
+ uint256 stablecoinAfterGas = DSMath.sub(stablecoin, gasFee);
+ uint256 stablecoinRedeemed = removeCurrentMintingFeeFromCash(
+ stablecoinAfterGas
+ );
+ uint256 managementFeeDaily = DSMath.wdiv(
+ persistentStorage.managementFee(),
+ 365 ether
+ );
+ uint256 managementFeeHourly = DSMath.wdiv(
+ DSMath.wmul(managementFeeDaily, elapsedTime),
+ 24 ether
+ );
+ uint256 normalizedManagementFee = DSMath.sub(
+ 1 ether,
+ DSMath.wdiv(managementFeeHourly, 100 ether)
+ );
+
+ stablecoinAfterFees = DSMath.wmul(
+ stablecoinRedeemed,
+ normalizedManagementFee
+ );
+ }
+
+ function removeCurrentMintingFeeFromCash(uint256 _cash)
+ public
+ view
+ returns (uint256 cashAfterFee)
+ {
+ uint256 creationFee = persistentStorage.getMintingFee(_cash);
+ uint256 minimumMintingFee = persistentStorage.minimumMintingFee();
+ cashAfterFee = removeMintingFeeFromCash(
+ _cash,
+ creationFee,
+ minimumMintingFee
+ );
+ }
+
+ function removeMintingFeeFromCash(
+ uint256 _cash,
+ uint256 _mintingFee,
+ uint256 _minimumMintingFee
+ ) public pure returns (uint256 cashAfterFee) {
+ uint256 creationFeeInCash = DSMath.wmul(_cash, _mintingFee);
+ if (_minimumMintingFee > creationFeeInCash) {
+ creationFeeInCash = _minimumMintingFee;
+ }
+ cashAfterFee = DSMath.sub(_cash, creationFeeInCash);
+ }
+
+ int256 constant WAD = 10**18;
+
+ function addInt256(int256 x, int256 y) internal pure returns (int256 z) {
+ require((z = x + y) >= x, "ds-math-add-overflow");
+ }
+
+ function mulInt256(int256 x, int256 y) internal pure returns (int256 z) {
+ require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
+ }
+
+ function wmulInt256(int256 x, int256 y) internal pure returns (int256 z) {
+ z = addInt256(mulInt256(x, y), WAD / 2) / WAD;
+ }
+}
diff --git a/contracts/leverage-tokens/StorageLeverage.sol b/contracts/leverage-tokens/StorageLeverage.sol
new file mode 100644
index 0000000..c870be2
--- /dev/null
+++ b/contracts/leverage-tokens/StorageLeverage.sol
@@ -0,0 +1,427 @@
+pragma solidity ^0.5.0;
+
+import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
+import "../short-tokens/utils/DateTimeLibrary.sol";
+import "../short-tokens/utils/Math.sol";
+
+
+contract StorageLeverage is Ownable {
+ address public tokenSwapManager;
+ address public bridge;
+
+ bool public isPaused;
+ bool public isShutdown;
+
+ struct Accounting {
+ uint256 bestExecutionPrice;
+ uint256 markPrice;
+ uint256 notional;
+ uint256 tokenValue;
+ uint256 effectiveFundingRate;
+ }
+
+ struct Order {
+ string orderType;
+ uint256 tokensGiven;
+ uint256 tokensRecieved;
+ uint256 mintingPrice;
+ }
+
+ uint256 public lastActivityDay;
+ uint256 public minRebalanceAmount;
+ uint256 public managementFee;
+ uint256 public minimumMintingFee;
+ uint256 public minimumTrade;
+
+ uint8 public balancePrecision;
+
+ mapping(uint256 => Accounting[]) private accounting;
+
+ uint256[] public mintingFeeBracket;
+ mapping(uint256 => uint256) public mintingFee;
+
+ Order[] public allOrders;
+ mapping(address => Order[]) public orderByUser;
+ mapping(address => uint256) public delayedRedemptionsByUser;
+
+ event AccountingValuesSet(uint256 today);
+ event RebalanceValuesSet(uint256 newMinRebalanceAmount);
+ event ManagementFeeValuesSet(uint256 newManagementFee);
+
+ function initialize(
+ address ownerAddress,
+ uint256 _managementFee,
+ uint256 _minRebalanceAmount,
+ uint8 _balancePrecision,
+ uint256 _lastMintingFee,
+ uint256 _minimumMintingFee,
+ uint256 _minimumTrade
+ ) public initializer {
+ initialize(ownerAddress);
+ managementFee = _managementFee;
+ minRebalanceAmount = _minRebalanceAmount;
+ mintingFee[~uint256(0)] = _lastMintingFee;
+ balancePrecision = _balancePrecision;
+ minimumMintingFee = _minimumMintingFee;
+ minimumTrade = _minimumTrade;
+ }
+
+ function setTokenSwapManager(address _tokenSwapManager) public onlyOwner {
+ require(_tokenSwapManager != address(0), "adddress must not be empty");
+ tokenSwapManager = _tokenSwapManager;
+ }
+
+ function setBridge(address _bridge) public onlyOwner {
+ require(_bridge != address(0), "adddress must not be empty");
+ bridge = _bridge;
+ }
+
+ function setIsPaused(bool _isPaused) public onlyOwner {
+ isPaused = _isPaused;
+ }
+
+ function shutdown() public onlyOwner {
+ isShutdown = true;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwnerOrTokenSwap() {
+ require(
+ isOwner() || _msgSender() == tokenSwapManager,
+ "caller is not the owner or token swap manager"
+ );
+ _;
+ }
+
+ modifier onlyOwnerOrBridge() {
+ require(
+ isOwner() || _msgSender() == bridge,
+ "caller is not the owner or bridge"
+ );
+ _;
+ }
+
+ function setDelayedRedemptionsByUser(
+ uint256 amountToRedeem,
+ address whitelistedAddress
+ ) public onlyOwnerOrTokenSwap {
+ delayedRedemptionsByUser[whitelistedAddress] = amountToRedeem;
+ }
+
+ /*
+ * Saves order in mapping (address => Order[]) orderByUser
+ * overwrite == false, append to Order[]
+ * overwrite == true, overwrite element at orderIndex
+ */
+
+ function setOrderByUser(
+ address whitelistedAddress,
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ uint256 orderIndex,
+ bool overwrite
+ ) public onlyOwnerOrTokenSwap() {
+ Order memory newOrder = Order(
+ orderType,
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice
+ );
+
+ if (!overwrite) {
+ orderByUser[whitelistedAddress].push(newOrder);
+ setOrder(
+ orderType,
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice,
+ orderIndex,
+ overwrite
+ );
+ } else {
+ orderByUser[whitelistedAddress][orderIndex] = newOrder;
+ }
+ }
+
+ /*
+ * Gets Order[] For User Address
+ * Return order at Index in Order[]
+ */
+
+ function getOrderByUser(address whitelistedAddress, uint256 orderIndex)
+ public
+ view
+ returns (
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice
+ )
+ {
+
+ Order storage orderAtIndex
+ = orderByUser[whitelistedAddress][orderIndex];
+ return (
+ orderAtIndex.orderType,
+ orderAtIndex.tokensGiven,
+ orderAtIndex.tokensRecieved,
+ orderAtIndex.mintingPrice
+ );
+ }
+
+ /*
+ * Save order to allOrders array
+ * overwrite == false, append to allOrders array
+ * overwrite == true, overwrite element at orderIndex
+ */
+ function setOrder(
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ uint256 orderIndex,
+ bool overwrite
+ ) public onlyOwnerOrTokenSwap() {
+ Order memory newOrder = Order(
+ orderType,
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice
+ );
+
+ if (!overwrite) {
+ allOrders.push(newOrder);
+ } else {
+ allOrders[orderIndex] = newOrder;
+ }
+ }
+
+ /*
+ * Get Order
+ */
+ function getOrder(uint256 index)
+ public
+ view
+ returns (
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice
+ )
+ {
+ Order storage orderAtIndex = allOrders[index];
+ return (
+ orderAtIndex.orderType,
+ orderAtIndex.tokensGiven,
+ orderAtIndex.tokensRecieved,
+ orderAtIndex.mintingPrice
+ );
+ }
+
+ // @dev Get accounting values for a specific day
+ // @param date format as 20200123 for 23th of January 2020
+ function getAccounting(uint256 date)
+ public
+ view
+ returns (uint256, uint256, uint256, uint256, uint256)
+ {
+ Accounting[] storage accountingsForDate = accounting[date];
+ uint256 lastIndex = accountingsForDate.length - 1;
+ return (
+ accountingsForDate[lastIndex].bestExecutionPrice,
+ accountingsForDate[lastIndex].markPrice,
+ accountingsForDate[lastIndex].notional,
+ accountingsForDate[lastIndex].tokenValue,
+ accountingsForDate[lastIndex].effectiveFundingRate
+ );
+ }
+
+ // @dev Set accounting values for the day
+ function setAccounting(
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external onlyOwnerOrTokenSwap() {
+ (uint256 year, uint256 month, uint256 day) = DateTimeLibrary
+ .timestampToDate(block.timestamp);
+ uint256 today = year * 10000 + month * 100 + day;
+ accounting[today].push(
+ Accounting(
+ _bestExecutionPrice,
+ _markPrice,
+ _notional,
+ _tokenValue,
+ _effectiveFundingRate
+ )
+ );
+ lastActivityDay = today;
+ emit AccountingValuesSet(today);
+ }
+
+ // @dev Set accounting values for the day
+ function setAccountingForLastActivityDay(
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external onlyOwnerOrTokenSwap() {
+ accounting[lastActivityDay].push(
+ Accounting(
+ _bestExecutionPrice,
+ _markPrice,
+ _notional,
+ _tokenValue,
+ _effectiveFundingRate
+ )
+ );
+ emit AccountingValuesSet(lastActivityDay);
+ }
+
+ // @dev Set last rebalance information
+ function setMinRebalanceAmount(uint256 _minRebalanceAmount)
+ external
+ onlyOwner
+ {
+ minRebalanceAmount = _minRebalanceAmount;
+
+ emit RebalanceValuesSet(minRebalanceAmount);
+ }
+
+ // @dev Set last rebalance information
+ function setManagementFee(uint256 _managementFee) external onlyOwner {
+ managementFee = _managementFee;
+ emit ManagementFeeValuesSet(managementFee);
+ }
+
+ // @dev Returns execution price
+ function getExecutionPrice() public view returns (uint256 price) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .bestExecutionPrice;
+ }
+
+ // @dev Returns mark price
+ function getMarkPrice() public view returns (uint256 price) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .markPrice;
+ }
+
+ // @dev Returns notional amount
+ function getNotional() public view returns (uint256 amount) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .notional;
+ }
+
+ // @dev Returns token value
+ function getTokenValue() public view returns (uint256 tokenValue) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .tokenValue;
+ }
+
+ // @dev Returns effective funding rate
+ function getFundingRate() public view returns (uint256 fundingRate) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .effectiveFundingRate;
+ }
+
+ // @dev Sets last minting fee
+ function setLastMintingFee(uint256 _mintingFee) public onlyOwner {
+ mintingFee[~uint256(0)] = _mintingFee;
+ }
+
+ // @dev Adds minting fee
+ function addMintingFeeBracket(uint256 _mintingFeeLimit, uint256 _mintingFee)
+ public
+ onlyOwner
+ {
+ require(
+ mintingFeeBracket.length == 0 ||
+ _mintingFeeLimit >
+ mintingFeeBracket[mintingFeeBracket.length - 1],
+ "New minting fee bracket needs to be bigger then last one"
+ );
+ mintingFeeBracket.push(_mintingFeeLimit);
+ mintingFee[_mintingFeeLimit] = _mintingFee;
+ }
+
+ // @dev Deletes last minting fee
+ function deleteLastMintingFeeBracket() public onlyOwner {
+ delete mintingFee[mintingFeeBracket[mintingFeeBracket.length - 1]];
+ mintingFeeBracket.length--;
+ }
+
+ // @dev Changes minting fee
+ function changeMintingLimit(
+ uint256 _position,
+ uint256 _mintingFeeLimit,
+ uint256 _mintingFee
+ ) public onlyOwner {
+ require(
+ _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
+ "New minting fee bracket needs to be bigger then last one"
+ );
+ if (_position != 0) {
+ require(
+ _mintingFeeLimit > mintingFeeBracket[_position - 1],
+ "New minting fee bracket needs to be bigger then last one"
+ );
+ }
+ if (_position < mintingFeeBracket.length - 1) {
+ require(
+ _mintingFeeLimit < mintingFeeBracket[_position + 1],
+ "New minting fee bracket needs to be smaller then next one"
+ );
+ }
+ mintingFeeBracket[_position] = _mintingFeeLimit;
+ mintingFee[_mintingFeeLimit] = _mintingFee;
+ }
+
+ function getMintingFee(uint256 cash) public view returns (uint256) {
+ // Define Start + End Index
+ uint256 startIndex = 0;
+ if (mintingFeeBracket.length > 0) {
+ uint256 endIndex = mintingFeeBracket.length - 1;
+ uint256 middleIndex = endIndex / 2;
+
+ if (cash <= mintingFeeBracket[middleIndex]) {
+ endIndex = middleIndex;
+ } else {
+ startIndex = middleIndex + 1;
+ }
+
+ for (uint256 i = startIndex; i <= endIndex; i++) {
+ if (cash <= mintingFeeBracket[i]) {
+ return mintingFee[mintingFeeBracket[i]];
+ }
+ }
+ }
+
+ return mintingFee[~uint256(0)];
+ }
+
+ // @dev Sets last balance precision
+ function setLastPrecision(uint8 _balancePrecision) public onlyOwner {
+ balancePrecision = _balancePrecision;
+ }
+
+ // @dev Sets minimum minting fee
+ function setMinimumMintingFee(uint256 _minimumMintingFee) public onlyOwner {
+ minimumMintingFee = _minimumMintingFee;
+ }
+
+ // @dev Sets minimum trade value
+ function setMinimumTrade(uint256 _minimumTrade) public onlyOwner {
+ minimumTrade = _minimumTrade;
+ }
+}
diff --git a/contracts/leverage-tokens/TokenSwapLeverage.sol b/contracts/leverage-tokens/TokenSwapLeverage.sol
new file mode 100644
index 0000000..bde75d6
--- /dev/null
+++ b/contracts/leverage-tokens/TokenSwapLeverage.sol
@@ -0,0 +1,436 @@
+pragma solidity ^0.5.0;
+
+import "@openzeppelin/upgrades/contracts/Initializable.sol";
+import "solidity-util/lib/Strings.sol";
+import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
+import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
+
+import "../short-tokens/Abstract/InterfaceCashPool.sol";
+import "../short-tokens/Abstract/InterfaceKYCVerifier.sol";
+import "./Abstract/InterfaceCalculator.sol";
+import "../short-tokens/Abstract/InterfaceERC20.sol";
+
+import "../short-tokens/Abstract/InterfaceInverseToken.sol";
+import "./Abstract/InterfaceStorageLeverage.sol";
+import "../short-tokens/utils/Math.sol";
+
+
+contract TokenSwapLeverage is Initializable, Ownable {
+ using Strings for string;
+ using SafeMath for uint256;
+
+ address public inverseToken;
+
+ InterfaceERC20 public erc20;
+ InterfaceKYCVerifier public kycVerifier;
+ InterfaceCashPool public cashPool;
+ InterfaceStorageLeverage public persistentStorage;
+ InterfaceCalculator public compositionCalculator;
+
+ event SuccessfulOrder(
+ string orderType,
+ address whitelistedAddress,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ address stablecoin,
+ uint256 price
+ );
+
+ event RebalanceEvent(
+ uint256 bestExecutionPrice,
+ uint256 markPrice,
+ uint256 notional,
+ uint256 tokenValue,
+ uint256 effectiveFundingRate
+ );
+
+ function initialize(
+ address _owner,
+ address _inverseToken,
+ address _cashPool,
+ address _storage,
+ address _compositionCalculator
+ ) public initializer {
+ initialize(_owner);
+
+ require(
+ _owner != address(0) &&
+ _inverseToken != address(0) &&
+ _cashPool != address(0) &&
+ _storage != address(0) &&
+ _compositionCalculator != address(0),
+ "addresses cannot be zero"
+ );
+
+ inverseToken = _inverseToken;
+
+ cashPool = InterfaceCashPool(_cashPool);
+ persistentStorage = InterfaceStorageLeverage(_storage);
+ kycVerifier = InterfaceKYCVerifier(address(cashPool.kycVerifier()));
+ compositionCalculator = InterfaceCalculator(_compositionCalculator);
+ }
+
+ //////////////// Create + Redeem Order Request ////////////////
+ //////////////// Create: Recieve Inverse Token ////////////////
+ //////////////// Redeem: Recieve Stable Coin ////////////////
+
+ function createOrder(
+ bool success,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 gasFee
+ ) public onlyOwnerOrBridge() notPausedOrShutdown() returns (bool retVal) {
+ // Require is Whitelisted
+ require(
+ kycVerifier.isAddressWhitelisted(whitelistedAddress),
+ "only whitelisted address may place orders"
+ );
+
+ // Return Funds if Bridge Pass an Error
+ if (!success) {
+ transferTokenFromPool(
+ stablecoin,
+ whitelistedAddress,
+ normalizeStablecoin(tokensGiven, stablecoin)
+ );
+ return false;
+ }
+
+ // Check Tokens Recieved with Composition Calculator
+ uint256 _tokensRecieved = compositionCalculator.getTokensCreatedByCash(
+ mintingPrice,
+ tokensGiven,
+ gasFee
+ );
+ require(
+ _tokensRecieved == tokensRecieved,
+ "tokens created must equal tokens recieved"
+ );
+
+ // Save Order to Storage and Lock Funds for 1 Hour
+ persistentStorage.setOrderByUser(
+ whitelistedAddress,
+ "CREATE",
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice,
+ 0,
+ false
+ );
+
+ // Write Successful Order to Log
+ writeOrderResponse(
+ "CREATE",
+ whitelistedAddress,
+ tokensGiven,
+ tokensRecieved,
+ stablecoin,
+ mintingPrice
+ );
+
+ // Mint Tokens to Address
+ InterfaceInverseToken token = InterfaceInverseToken(inverseToken);
+ token.mintTokens(whitelistedAddress, tokensRecieved);
+
+ return true;
+ }
+
+ function redeemOrder(
+ bool success,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 burningPrice,
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 gasFee,
+ uint256 elapsedTime
+ ) public onlyOwnerOrBridge() notPausedOrShutdown() returns (bool retVal) {
+ // Require Whitelisted
+ require(
+ kycVerifier.isAddressWhitelisted(whitelistedAddress),
+ "only whitelisted address may place orders"
+ );
+
+ // Return Funds if Bridge Pass an Error
+ if (!success) {
+ transferTokenFromPool(
+ inverseToken,
+ whitelistedAddress,
+ tokensGiven
+ );
+ return false;
+ }
+
+ // Check Cash Recieved with Composition Calculator
+ uint256 _tokensRecieved = compositionCalculator.getCashCreatedByTokens(
+ burningPrice,
+ elapsedTime,
+ tokensGiven,
+ gasFee
+ );
+ require(
+ _tokensRecieved == tokensRecieved,
+ "cash redeemed must equal tokens recieved"
+ );
+
+ // Save To Storage
+ persistentStorage.setOrderByUser(
+ whitelistedAddress,
+ "REDEEM",
+ tokensGiven,
+ tokensRecieved,
+ burningPrice,
+ 0,
+ false
+ );
+
+ // Redeem Stablecoin or Perform Delayed Settlement
+ redeemFunds(
+ tokensGiven,
+ tokensRecieved,
+ whitelistedAddress,
+ stablecoin,
+ burningPrice
+ );
+
+ // Burn Tokens to Address
+ InterfaceInverseToken token = InterfaceInverseToken(inverseToken);
+ token.burnTokens(address(cashPool), tokensGiven);
+
+ return true;
+ }
+
+ function writeOrderResponse(
+ string memory orderType,
+ address whiteListedAddress,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ address stablecoin,
+ uint256 price
+ ) internal {
+ require(
+ tokensGiven != 0 && tokensRecieved != 0,
+ "amount must be greater than 0"
+ );
+
+ emit SuccessfulOrder(
+ orderType,
+ whiteListedAddress,
+ tokensGiven,
+ tokensRecieved,
+ stablecoin,
+ price
+ );
+ }
+
+ function settleDelayedFunds(
+ uint256 tokensToRedeem,
+ address whitelistedAddress,
+ address stablecoin
+ ) public onlyOwnerOrBridge notPausedOrShutdown {
+ require(
+ kycVerifier.isAddressWhitelisted(whitelistedAddress),
+ "only whitelisted may redeem funds"
+ );
+
+ bool isSufficientFunds = isHotWalletSufficient(
+ tokensToRedeem,
+ stablecoin
+ );
+ require(
+ isSufficientFunds == true,
+ "not enough funds in the hot wallet"
+ );
+
+ uint256 tokensOutstanding = persistentStorage.delayedRedemptionsByUser(
+ whitelistedAddress
+ );
+ uint256 tokensRemaining = DSMath.sub(tokensOutstanding, tokensToRedeem);
+
+ persistentStorage.setDelayedRedemptionsByUser(
+ tokensRemaining,
+ whitelistedAddress
+ );
+ transferTokenFromPool(
+ stablecoin,
+ whitelistedAddress,
+ normalizeStablecoin(tokensToRedeem, stablecoin)
+ );
+ }
+
+ function redeemFunds(
+ uint256 tokensGiven,
+ uint256 tokensToRedeem,
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 price
+ ) internal {
+ bool isSufficientFunds = isHotWalletSufficient(
+ tokensToRedeem,
+ stablecoin
+ );
+
+ if (isSufficientFunds) {
+ transferTokenFromPool(
+ stablecoin,
+ whitelistedAddress,
+ normalizeStablecoin(tokensToRedeem, stablecoin)
+ );
+ writeOrderResponse(
+ "REDEEM",
+ whitelistedAddress,
+ tokensGiven,
+ tokensToRedeem,
+ stablecoin,
+ price
+ );
+ } else {
+ uint256 tokensOutstanding = persistentStorage
+ .delayedRedemptionsByUser(whitelistedAddress);
+ tokensOutstanding = DSMath.add(tokensOutstanding, tokensToRedeem);
+ persistentStorage.setDelayedRedemptionsByUser(
+ tokensOutstanding,
+ whitelistedAddress
+ );
+ writeOrderResponse(
+ "REDEEM_NO_SETTLEMENT",
+ whitelistedAddress,
+ tokensGiven,
+ tokensToRedeem,
+ stablecoin,
+ price
+ );
+ }
+ }
+
+ function isHotWalletSufficient(uint256 tokensToRedeem, address stablecoin)
+ internal
+ returns (bool)
+ {
+ InterfaceInverseToken _stablecoin = InterfaceInverseToken(stablecoin);
+ uint256 stablecoinBalance = _stablecoin.balanceOf(address(cashPool));
+
+ if (normalizeStablecoin(tokensToRedeem, stablecoin) > stablecoinBalance)
+ return false;
+ return true;
+ }
+
+ function normalizeStablecoin(uint256 stablecoinValue, address stablecoin)
+ internal
+ returns (uint256)
+ {
+ erc20 = InterfaceERC20(stablecoin);
+ uint256 exponent = 18 - erc20.decimals();
+ return stablecoinValue / 10**exponent; // 6 decimal stable coin = 10**12
+ }
+
+ //////////////// Daily Rebalance ////////////////
+ //////////////// Threshold Rebalance ////////////////
+
+ /**
+ * @dev Saves results of rebalance calculations in persistent storages
+ * @param _bestExecutionPrice The best execution price for rebalancing
+ * @param _markPrice The Mark Price
+ * @param _notional The new notional amount after rebalance
+ * @param _tokenValue The targetLeverage
+ * @param _effectiveFundingRate The effectiveFundingRate
+ */
+
+ function rebalance(
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) public onlyOwnerOrBridge() notPausedOrShutdown() {
+ persistentStorage.setAccounting(
+ _bestExecutionPrice,
+ _markPrice,
+ _notional,
+ _tokenValue,
+ _effectiveFundingRate
+ );
+ emit RebalanceEvent(
+ _bestExecutionPrice,
+ _markPrice,
+ _notional,
+ _tokenValue,
+ _effectiveFundingRate
+ );
+ }
+
+ //////////////// Transfer Stablecoin Out of Pool ////////////////
+ //////////////// Transfer Stablecoin In of Pool ////////////////
+ //////////////// Transfer InverseToken Out of Pool ////////////////
+ //////////////// Transfer InverseToken In of Pool ////////////////
+
+ function transferTokenToPool(
+ address tokenContract,
+ address whiteListedAddress,
+ uint256 orderAmount
+ ) internal returns (bool) {
+ // Check orderAmount <= availableAmount
+ // Transfer USDC to Stablecoin Cash Pool
+ return
+ cashPool.moveTokenToPool(
+ tokenContract,
+ whiteListedAddress,
+ orderAmount
+ );
+ }
+
+ function transferTokenFromPool(
+ address tokenContract,
+ address destinationAddress,
+ uint256 orderAmount
+ ) internal returns (bool) {
+ // Check orderAmount <= availableAmount
+ // Transfer USDC to Destination Address
+ return
+ cashPool.moveTokenfromPool(
+ tokenContract,
+ destinationAddress,
+ orderAmount
+ );
+ }
+
+ function setCashPool(address _cashPool) public onlyOwner {
+ require(_cashPool != address(0), "adddress must not be empty");
+ cashPool = InterfaceCashPool(_cashPool);
+ }
+
+ function setStorage(address _storage) public onlyOwner {
+ require(_storage != address(0), "adddress must not be empty");
+ persistentStorage = InterfaceStorageLeverage(_storage);
+ }
+
+ function setKycVerfier(address _kycVerifier) public onlyOwner {
+ require(_kycVerifier != address(0), "adddress must not be empty");
+ kycVerifier = InterfaceKYCVerifier(_kycVerifier);
+ }
+
+ function setCalculator(address _calculator) public onlyOwner {
+ require(_calculator != address(0), "adddress must not be empty");
+ compositionCalculator = InterfaceCalculator(_calculator);
+ }
+
+ modifier onlyOwnerOrBridge() {
+ require(
+ isOwner() || _msgSender() == persistentStorage.bridge(),
+ "caller is not the owner or bridge"
+ );
+ _;
+ }
+
+ modifier notPausedOrShutdown() {
+ require(persistentStorage.isPaused() == false, "contract is paused");
+ require(
+ persistentStorage.isShutdown() == false,
+ "contract is shutdown"
+ );
+ _;
+ }
+}
diff --git a/contracts/AdminMultiSig/OwnerMultiSig.sol b/contracts/shared/AdminMultiSig/OwnerMultiSig.sol
similarity index 100%
rename from contracts/AdminMultiSig/OwnerMultiSig.sol
rename to contracts/shared/AdminMultiSig/OwnerMultiSig.sol
diff --git a/contracts/Token/ERC20.sol b/contracts/shared/Token/ERC20.sol
similarity index 100%
rename from contracts/Token/ERC20.sol
rename to contracts/shared/Token/ERC20.sol
diff --git a/contracts/Token/ERC20Detailed.sol b/contracts/shared/Token/ERC20Detailed.sol
similarity index 91%
rename from contracts/Token/ERC20Detailed.sol
rename to contracts/shared/Token/ERC20Detailed.sol
index a50934c..3d2439e 100644
--- a/contracts/Token/ERC20Detailed.sol
+++ b/contracts/shared/Token/ERC20Detailed.sol
@@ -3,7 +3,7 @@ pragma solidity ^0.5.0;
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/upgrades/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
-import "../PersistentStorage.sol";
+import "../../short-tokens/Abstract/InterfaceStorage.sol";
/**
@@ -13,7 +13,7 @@ contract ERC20Detailed is IERC20, Initializable, Ownable {
string private _name;
string private _symbol;
uint8 private _decimals;
- PersistentStorage public _persistenStorage;
+ InterfaceStorage public _persistenStorage;
/**
* @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
@@ -31,7 +31,7 @@ contract ERC20Detailed is IERC20, Initializable, Ownable {
_name = name;
_symbol = symbol;
_decimals = decimals;
- _persistenStorage = PersistentStorage(persistenStorage);
+ _persistenStorage = InterfaceStorage(persistenStorage);
}
/**
diff --git a/contracts/Token/Sender.sol b/contracts/shared/Token/Sender.sol
similarity index 100%
rename from contracts/Token/Sender.sol
rename to contracts/shared/Token/Sender.sol
diff --git a/contracts/Token/USDC.sol b/contracts/shared/Token/USDC.sol
similarity index 100%
rename from contracts/Token/USDC.sol
rename to contracts/shared/Token/USDC.sol
diff --git a/contracts/shared/Token/USDT.sol b/contracts/shared/Token/USDT.sol
new file mode 100644
index 0000000..c8e2800
--- /dev/null
+++ b/contracts/shared/Token/USDT.sol
@@ -0,0 +1,653 @@
+/**
+ *Submitted for verification at Etherscan.io on 2020-03-03
+ */
+
+/**
+ *Submitted for verification at Etherscan.io on 2018-08-03
+ */
+
+pragma solidity ^0.5.0;
+
+
+// File: contracts/OwnableUSDCT.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title OwnableUSDCT
+ * @dev The OwnableUSDCT contract from https://github.com/zeppelinos/labs/blob/master/upgradeability_ownership/contracts/ownership/Ownable.sol
+ * branch: master commit: 3887ab77b8adafba4a26ace002f3a684c1a3388b modified to:
+ * 1) Add emit prefix to OwnershipTransferred event (7/13/18)
+ * 2) Replace constructor with constructor syntax (7/13/18)
+ * 3) consolidate OwnableUSDCTStorage into this contract
+ */
+contract OwnableUSDCT {
+ // Owner of the contract
+ address private _owner;
+
+ /**
+ * @dev Event to show ownership has been transferred
+ * @param previousOwner representing the address of the previous owner
+ * @param newOwner representing the address of the new owner
+ */
+ event OwnershipTransferred(address previousOwner, address newOwner);
+
+ /**
+ * @dev The constructor sets the original owner of the contract to the sender account.
+ */
+ function initialize() public {
+ setOwner(msg.sender);
+ }
+
+ /**
+ * @dev Tells the address of the owner
+ * @return the address of the owner
+ */
+ function owner() public view returns (address) {
+ return _owner;
+ }
+
+ /**
+ * @dev Sets a new owner address
+ */
+ function setOwner(address newOwner) internal {
+ _owner = newOwner;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwner() {
+ require(msg.sender == owner());
+ _;
+ }
+
+ /**
+ * @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 {
+ require(newOwner != address(0));
+ emit OwnershipTransferred(owner(), newOwner);
+ setOwner(newOwner);
+ }
+}
+
+
+// File: contracts/Blacklistable.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title Blacklistable Token
+ * @dev Allows accounts to be blacklisted by a "blacklister" role
+ */
+contract Blacklistable is OwnableUSDCT {
+ address public blacklister;
+ mapping(address => bool) internal blacklisted;
+
+ event Blacklisted(address indexed _account);
+ event UnBlacklisted(address indexed _account);
+ event BlacklisterChanged(address indexed newBlacklister);
+
+ /**
+ * @dev Throws if called by any account other than the blacklister
+ */
+ modifier onlyBlacklister() {
+ require(msg.sender == blacklister);
+ _;
+ }
+
+ /**
+ * @dev Throws if argument account is blacklisted
+ * @param _account The address to check
+ */
+ modifier notBlacklisted(address _account) {
+ require(blacklisted[_account] == false);
+ _;
+ }
+
+ /**
+ * @dev Checks if account is blacklisted
+ * @param _account The address to check
+ */
+ function isBlacklisted(address _account) public view returns (bool) {
+ return blacklisted[_account];
+ }
+
+ /**
+ * @dev Adds account to blacklist
+ * @param _account The address to blacklist
+ */
+ function blacklist(address _account) public onlyBlacklister {
+ blacklisted[_account] = true;
+ emit Blacklisted(_account);
+ }
+
+ /**
+ * @dev Removes account from blacklist
+ * @param _account The address to remove from the blacklist
+ */
+ function unBlacklist(address _account) public onlyBlacklister {
+ blacklisted[_account] = false;
+ emit UnBlacklisted(_account);
+ }
+
+ function updateBlacklister(address _newBlacklister) public onlyOwner {
+ require(_newBlacklister != address(0));
+ blacklister = _newBlacklister;
+ emit BlacklisterChanged(blacklister);
+ }
+}
+
+
+// File: contracts/Pausable.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title Pausable
+ * @dev Base contract which allows children to implement an emergency stop mechanism.
+ * Based on openzeppelin tag v1.10.0 commit: feb665136c0dae9912e08397c1a21c4af3651ef3
+ * Modifications:
+ * 1) Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
+ * 2) Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
+ * 3) Removed whenPaused (6/14/2018)
+ * 4) Switches ownable library to use zeppelinos (7/12/18)
+ * 5) Remove constructor (7/13/18)
+ */
+contract Pausable is OwnableUSDCT {
+ event Pause();
+ event Unpause();
+ event PauserChanged(address indexed newAddress);
+
+ address public pauser;
+ bool public paused = false;
+
+ /**
+ * @dev Modifier to make a function callable only when the contract is not paused.
+ */
+ modifier whenNotPaused() {
+ require(!paused);
+ _;
+ }
+
+ /**
+ * @dev throws if called by any account other than the pauser
+ */
+ modifier onlyPauser() {
+ require(msg.sender == pauser);
+ _;
+ }
+
+ /**
+ * @dev called by the owner to pause, triggers stopped state
+ */
+ function pause() public onlyPauser {
+ paused = true;
+ emit Pause();
+ }
+
+ /**
+ * @dev called by the owner to unpause, returns to normal state
+ */
+ function unpause() public onlyPauser {
+ paused = false;
+ emit Unpause();
+ }
+
+ /**
+ * @dev update the pauser role
+ */
+ function updatePauser(address _newPauser) public onlyOwner {
+ require(_newPauser != address(0));
+ pauser = _newPauser;
+ emit PauserChanged(pauser);
+ }
+}
+
+
+// File: openzeppelin-solidity/contracts/math/SafeMath.sol
+
+/**
+ * @title SafeMath
+ * @dev Math operations with safety checks that throw on error
+ */
+library SafeMath {
+ /**
+ * @dev Multiplies two numbers, throws on overflow.
+ */
+ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
+ // Gas optimization: this is cheaper than asserting '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;
+ }
+
+ c = a * b;
+ assert(c / a == b);
+ return c;
+ }
+
+ /**
+ * @dev Integer division of two numbers, truncating the quotient.
+ */
+ function div(uint256 a, uint256 b) internal pure returns (uint256) {
+ // assert(b > 0); // Solidity automatically throws when dividing by 0
+ // uint256 c = a / b;
+ // assert(a == b * c + a % b); // There is no case in which this doesn't hold
+ return a / b;
+ }
+
+ /**
+ * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
+ */
+ function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+ assert(b <= a);
+ return a - b;
+ }
+
+ /**
+ * @dev Adds two numbers, throws on overflow.
+ */
+ function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
+ c = a + b;
+ assert(c >= a);
+ return c;
+ }
+}
+
+
+// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
+
+/**
+ * @title ERC20Basic
+ * @dev Simpler version of ERC20 interface
+ * See https://github.com/ethereum/EIPs/issues/179
+ */
+contract ERC20Basic {
+ function totalSupply() public view returns (uint256);
+
+ function balanceOf(address who) public view returns (uint256);
+
+ function transfer(address to, uint256 value) public returns (bool);
+
+ event Transfer(address indexed from, address indexed to, uint256 value);
+}
+
+
+// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
+
+/**
+ * @title ERC20 interface
+ * @dev see https://github.com/ethereum/EIPs/issues/20
+ */
+contract ERC20T is ERC20Basic {
+ function allowance(address owner, address spender)
+ public
+ view
+ returns (uint256);
+
+ function transferFrom(address from, address to, uint256 value)
+ public
+ returns (bool);
+
+ function approve(address spender, uint256 value) public returns (bool);
+
+ event Approval(
+ address indexed owner,
+ address indexed spender,
+ uint256 value
+ );
+}
+
+
+// File: contracts/FiatTokenV1.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title FiatToken
+ * @dev ERC20 Token backed by fiat reserves
+ */
+contract USDT is OwnableUSDCT, ERC20T, Pausable, Blacklistable {
+ using SafeMath for uint256;
+
+ string public name;
+ string public symbol;
+ uint8 public decimals;
+ string public currency;
+ address public masterMinter;
+ bool internal initialized;
+ uint256 internal totalSupply_;
+
+ mapping(address => uint256) internal balances;
+ mapping(address => mapping(address => uint256)) internal allowed;
+ mapping(address => bool) internal minters;
+ mapping(address => uint256) internal minterAllowed;
+
+ event Mint(address indexed minter, address indexed to, uint256 amount);
+ event Burn(address indexed burner, uint256 amount);
+ event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
+ event MinterRemoved(address indexed oldMinter);
+ event MasterMinterChanged(address indexed newMasterMinter);
+
+ function initialize(
+ string memory _name,
+ string memory _symbol,
+ string memory _currency,
+ uint8 _decimals,
+ address _masterMinter,
+ address _pauser,
+ address _blacklister,
+ address _owner
+ ) public {
+ require(!initialized);
+ require(_masterMinter != address(0));
+ require(_pauser != address(0));
+ require(_blacklister != address(0));
+ require(_owner != address(0));
+
+ totalSupply_ = 0;
+ name = _name;
+ symbol = _symbol;
+ currency = _currency;
+ decimals = _decimals;
+ masterMinter = _masterMinter;
+ pauser = _pauser;
+ blacklister = _blacklister;
+ setOwner(_owner);
+ initialized = true;
+ }
+
+ /**
+ * @dev Throws if called by any account other than a minter
+ */
+ modifier onlyMinters() {
+ require(minters[msg.sender] == true);
+ _;
+ }
+
+ /**
+ * @dev Function to mint tokens
+ * @param _to The address that will receive the minted tokens.
+ * @param _amount The amount of tokens to mint. Must be less than or equal to the minterAllowance of the caller.
+ * @return A boolean that indicates if the operation was successful.
+ */
+ function mint(address _to, uint256 _amount)
+ public
+ whenNotPaused
+ onlyMinters
+ notBlacklisted(msg.sender)
+ notBlacklisted(_to)
+ returns (bool)
+ {
+ require(_to != address(0));
+ require(_amount > 0);
+
+ uint256 mintingAllowedAmount = minterAllowed[msg.sender];
+ require(_amount <= mintingAllowedAmount);
+
+ totalSupply_ = totalSupply_.add(_amount);
+ balances[_to] = balances[_to].add(_amount);
+ minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
+ emit Mint(msg.sender, _to, _amount);
+ emit Transfer(address(0), _to, _amount);
+ return true;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the masterMinter
+ */
+ modifier onlyMasterMinter() {
+ require(msg.sender == masterMinter);
+ _;
+ }
+
+ /**
+ * @dev Get minter allowance for an account
+ * @param minter The address of the minter
+ */
+ function minterAllowance(address minter) public view returns (uint256) {
+ return minterAllowed[minter];
+ }
+
+ /**
+ * @dev Checks if account is a minter
+ * @param account The address to check
+ */
+ function isMinter(address account) public view returns (bool) {
+ return minters[account];
+ }
+
+ /**
+ * @dev Get allowed amount for an account
+ * @param owner address The account owner
+ * @param spender address The account spender
+ */
+ function allowance(address owner, address spender)
+ public
+ view
+ returns (uint256)
+ {
+ return allowed[owner][spender];
+ }
+
+ /**
+ * @dev Get totalSupply of token
+ */
+ function totalSupply() public view returns (uint256) {
+ return totalSupply_;
+ }
+
+ /**
+ * @dev Get token balance of an account
+ * @param account address The account
+ */
+ function balanceOf(address account) public view returns (uint256) {
+ return balances[account];
+ }
+
+ /**
+ * @dev Adds blacklisted check to approve
+ * @return True if the operation was successful.
+ */
+ function approve(address _spender, uint256 _value)
+ public
+ whenNotPaused
+ notBlacklisted(msg.sender)
+ notBlacklisted(_spender)
+ returns (bool)
+ {
+ allowed[msg.sender][_spender] = _value;
+ emit Approval(msg.sender, _spender, _value);
+ return true;
+ }
+
+ /**
+ * @dev Transfer tokens from one address to another.
+ * @param _from address The address which you want to send tokens from
+ * @param _to address The address which you want to transfer to
+ * @param _value uint256 the amount of tokens to be transferred
+ * @return bool success
+ */
+ function transferFrom(address _from, address _to, uint256 _value)
+ public
+ whenNotPaused
+ notBlacklisted(_to)
+ notBlacklisted(msg.sender)
+ notBlacklisted(_from)
+ returns (bool)
+ {
+ require(_to != address(0));
+ require(_value <= balances[_from]);
+ require(_value <= allowed[_from][msg.sender]);
+
+ balances[_from] = balances[_from].sub(_value);
+ balances[_to] = balances[_to].add(_value);
+ allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
+ emit Transfer(_from, _to, _value);
+ return true;
+ }
+
+ /**
+ * @dev transfer token for a specified address
+ * @param _to The address to transfer to.
+ * @param _value The amount to be transferred.
+ * @return bool success
+ */
+ function transfer(address _to, uint256 _value)
+ public
+ whenNotPaused
+ notBlacklisted(msg.sender)
+ notBlacklisted(_to)
+ returns (bool)
+ {
+ require(_to != address(0));
+ require(_value <= balances[msg.sender]);
+
+ balances[msg.sender] = balances[msg.sender].sub(_value);
+ balances[_to] = balances[_to].add(_value);
+ emit Transfer(msg.sender, _to, _value);
+ return true;
+ }
+
+ /**
+ * @dev Function to add/update a new minter
+ * @param minter The address of the minter
+ * @param minterAllowedAmount The minting amount allowed for the minter
+ * @return True if the operation was successful.
+ */
+ function configureMinter(address minter, uint256 minterAllowedAmount)
+ public
+ whenNotPaused
+ onlyMasterMinter
+ returns (bool)
+ {
+ minters[minter] = true;
+ minterAllowed[minter] = minterAllowedAmount;
+ emit MinterConfigured(minter, minterAllowedAmount);
+ return true;
+ }
+
+ /**
+ * @dev Function to remove a minter
+ * @param minter The address of the minter to remove
+ * @return True if the operation was successful.
+ */
+ function removeMinter(address minter)
+ public
+ onlyMasterMinter
+ returns (bool)
+ {
+ minters[minter] = false;
+ minterAllowed[minter] = 0;
+ emit MinterRemoved(minter);
+ return true;
+ }
+
+ /**
+ * @dev allows a minter to burn some of its own tokens
+ * Validates that caller is a minter and that sender is not blacklisted
+ * amount is less than or equal to the minter's account balance
+ * @param _amount uint256 the amount of tokens to be burned
+ */
+ function burn(uint256 _amount)
+ public
+ whenNotPaused
+ onlyMinters
+ notBlacklisted(msg.sender)
+ {
+ uint256 balance = balances[msg.sender];
+ require(_amount > 0);
+ require(balance >= _amount);
+
+ totalSupply_ = totalSupply_.sub(_amount);
+ balances[msg.sender] = balance.sub(_amount);
+ emit Burn(msg.sender, _amount);
+ emit Transfer(msg.sender, address(0), _amount);
+ }
+
+ function updateMasterMinter(address _newMasterMinter) public onlyOwner {
+ require(_newMasterMinter != address(0));
+ masterMinter = _newMasterMinter;
+ emit MasterMinterChanged(masterMinter);
+ }
+}
diff --git a/contracts/short-tokens/Abstract/InterfaceCashPool.sol b/contracts/short-tokens/Abstract/InterfaceCashPool.sol
new file mode 100644
index 0000000..1b4301c
--- /dev/null
+++ b/contracts/short-tokens/Abstract/InterfaceCashPool.sol
@@ -0,0 +1,18 @@
+pragma solidity ^0.5.0;
+
+
+interface InterfaceCashPool {
+ function kycVerifier() external view returns (address);
+
+ function moveTokenToPool(
+ address _token,
+ address whiteListedAddress,
+ uint256 orderAmount
+ ) external returns (bool);
+
+ function moveTokenfromPool(
+ address _token,
+ address destinationAddress,
+ uint256 orderAmount
+ ) external returns (bool);
+}
diff --git a/contracts/short-tokens/Abstract/InterfaceCompositionCalculator.sol b/contracts/short-tokens/Abstract/InterfaceCompositionCalculator.sol
new file mode 100644
index 0000000..67c66af
--- /dev/null
+++ b/contracts/short-tokens/Abstract/InterfaceCompositionCalculator.sol
@@ -0,0 +1,21 @@
+pragma solidity ^0.5.0;
+
+
+interface InterfaceCompositionCalculator {
+ function getCurrentCashAmountCreatedByToken(
+ uint256 _tokenAmount,
+ uint256 _spotPrice,
+ uint256 _gasFee
+ ) external view returns (uint256);
+
+ function getCurrentTokenAmountCreatedByCash(
+ uint256 _cash,
+ uint256 _spotPrice,
+ uint256 _gasFee
+ ) external view returns (uint256);
+
+ function calculateDailyPCF(uint256 _price, uint256 _lendingFee)
+ external
+ view
+ returns (uint256, uint256, uint256, uint256, uint256, bool);
+}
diff --git a/contracts/short-tokens/Abstract/InterfaceERC20.sol b/contracts/short-tokens/Abstract/InterfaceERC20.sol
new file mode 100644
index 0000000..4e1d92b
--- /dev/null
+++ b/contracts/short-tokens/Abstract/InterfaceERC20.sol
@@ -0,0 +1,6 @@
+pragma solidity ^0.5.0;
+
+
+interface InterfaceERC20 {
+ function decimals() external view returns (uint8);
+}
diff --git a/contracts/Abstract/InterfaceInverseToken.sol b/contracts/short-tokens/Abstract/InterfaceInverseToken.sol
similarity index 100%
rename from contracts/Abstract/InterfaceInverseToken.sol
rename to contracts/short-tokens/Abstract/InterfaceInverseToken.sol
diff --git a/contracts/short-tokens/Abstract/InterfaceKYCVerifier.sol b/contracts/short-tokens/Abstract/InterfaceKYCVerifier.sol
new file mode 100644
index 0000000..55d782a
--- /dev/null
+++ b/contracts/short-tokens/Abstract/InterfaceKYCVerifier.sol
@@ -0,0 +1,6 @@
+pragma solidity ^0.5.0;
+
+
+interface InterfaceKYCVerifier {
+ function isAddressWhitelisted(address) external view returns (bool);
+}
diff --git a/contracts/short-tokens/Abstract/InterfaceStorage.sol b/contracts/short-tokens/Abstract/InterfaceStorage.sol
new file mode 100644
index 0000000..500e9ae
--- /dev/null
+++ b/contracts/short-tokens/Abstract/InterfaceStorage.sol
@@ -0,0 +1,59 @@
+pragma solidity ^0.5.0;
+
+
+interface InterfaceStorage {
+ function whitelistedAddresses(address) external view returns (bool);
+
+ function isPaused() external view returns (bool);
+
+ function isShutdown() external view returns (bool);
+
+ function tokenSwapManager() external view returns (address);
+
+ function bridge() external view returns (address);
+
+ function getCashPositionPerTokenUnit() external view returns (uint256);
+
+ function getBalancePerTokenUnit() external view returns (uint256);
+
+ function getMintingFee(uint256 cash) external view returns (uint256);
+
+ function getPrice() external view returns (uint256);
+
+ function minimumMintingFee() external view returns (uint256);
+
+ function getLendingFee() external view returns (uint256);
+
+ function minRebalanceAmount() external view returns (uint8);
+
+ function delayedRedemptionsByUser(address) external view returns (uint256);
+
+ function setDelayedRedemptionsByUser(
+ uint256 amountToRedeem,
+ address whitelistedAddress
+ ) external;
+
+ function setOrderByUser(
+ address whitelistedAddress,
+ string calldata orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 avgBlendedFee,
+ uint256 orderIndex,
+ bool overwrite
+ ) external;
+
+ function setAccounting(
+ uint256 _price,
+ uint256 _cashPositionPerTokenUnit,
+ uint256 _balancePerTokenUnit,
+ uint256 _lendingFee
+ ) external;
+
+ function setAccountingForLastActivityDay(
+ uint256 _price,
+ uint256 _cashPositionPerTokenUnit,
+ uint256 _balancePerTokenUnit,
+ uint256 _lendingFee
+ ) external;
+}
diff --git a/contracts/CashPool.sol b/contracts/short-tokens/CashPool.sol
similarity index 76%
rename from contracts/CashPool.sol
rename to contracts/short-tokens/CashPool.sol
index 78996d7..e75514c 100644
--- a/contracts/CashPool.sol
+++ b/contracts/short-tokens/CashPool.sol
@@ -3,18 +3,18 @@ pragma solidity ^0.5.0;
import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
import "./Abstract/InterfaceInverseToken.sol";
-import "./PersistentStorage.sol";
-import "./KYCVerifier.sol";
+import "./Abstract//InterfaceKYCVerifier.sol";
contract CashPool is Ownable {
using SafeMath for uint256;
- KYCVerifier public kycVerifier;
- PersistentStorage public persistentStorage;
+ InterfaceKYCVerifier public kycVerifier;
uint256[2] public percentageOfFundsForColdStorage;
address public coldStorage;
+ mapping(address => bool) public approvedTokenManagers;
+
event SetPercentageOfFundsForColdStorageEvent(
uint256[2] newPercentageOfFundsForColdStorage
);
@@ -22,7 +22,6 @@ contract CashPool is Ownable {
function initialize(
address ownerAddress,
address _kycVerifier,
- address _persistentStorage,
address _coldStorage,
uint256[2] memory _percentageOfFundsForColdStorage
) public initializer {
@@ -39,8 +38,7 @@ contract CashPool is Ownable {
"cannot set more than 100% for coldstorage"
);
initialize(ownerAddress);
- kycVerifier = KYCVerifier(_kycVerifier);
- persistentStorage = PersistentStorage(_persistentStorage);
+ kycVerifier = InterfaceKYCVerifier(_kycVerifier);
coldStorage = _coldStorage;
percentageOfFundsForColdStorage = _percentageOfFundsForColdStorage;
}
@@ -51,56 +49,6 @@ contract CashPool is Ownable {
return tokenBalance;
}
- // @dev Move tokens to cash pool
- // @param _token ERC20 address
- // @param whiteListedAddress address allowed to transfer to pool
- // @param orderAmount amount to transfer to cash pool
- function moveTokenToPool(
- address _token,
- address whiteListedAddress,
- uint256 orderAmount
- ) public onlyOwnerOrTokenSwap() returns (bool) {
- InterfaceInverseToken token_ = InterfaceInverseToken(_token);
- require(
- kycVerifier.isAddressWhitelisted(whiteListedAddress),
- "only whitelisted address are allowed to move tokens to pool"
- );
-
- uint256 percentageNumerator = percentageOfFundsForColdStorage[0];
- uint256 percentageDenominator = percentageOfFundsForColdStorage[1];
- uint256 amountForColdStorage = orderAmount.mul(percentageNumerator).div(
- percentageDenominator
- );
-
- token_.transferFrom(whiteListedAddress, address(this), orderAmount);
- token_.transfer(coldStorage, amountForColdStorage);
-
- return true;
- }
-
- // @dev Move tokens out of cash pool
- // @param _token ERC20 address
- // @param destinationAddress address to send to
- // @param orderAmount amount to transfer from cash pool
- function moveTokenfromPool(
- address _token,
- address destinationAddress,
- uint256 orderAmount
- ) public onlyOwnerOrTokenSwap() returns (bool) {
- InterfaceInverseToken token_ = InterfaceInverseToken(_token);
-
- token_.transfer(destinationAddress, orderAmount);
- return true;
- }
-
- modifier onlyOwnerOrTokenSwap() {
- require(
- isOwner() || _msgSender() == persistentStorage.tokenSwapManager(),
- "caller is not the owner or token swap manager"
- );
- _;
- }
-
// @dev Sets new coldStorage
// @param _newColdStorage Address for new cold storage wallet
function setColdStorage(address _newColdStorage) public onlyOwner {
@@ -130,4 +78,77 @@ contract CashPool is Ownable {
_newPercentageOfFundsForColdStorage
);
}
+
+ // ############################################## Add/Remove ############################################## //
+ // ############################################## Token ################################################### //
+ // ############################################## Manager ################################################# //
+
+ function addTokenManager(address tokenManager) public onlyOwner {
+ require(tokenManager != address(0), "adddress must not be empty");
+ approvedTokenManagers[tokenManager] = true;
+ }
+
+ function removeTokenManager(address tokenManager) public onlyOwner {
+ require(tokenManager != address(0), "adddress must not be empty");
+ delete approvedTokenManagers[tokenManager];
+ }
+
+ function isTokenManager(address potentialAddress)
+ public
+ view
+ returns (bool)
+ {
+ require(potentialAddress != address(0), "adddress must not be empty");
+ return approvedTokenManagers[potentialAddress];
+ }
+
+ modifier onlyOwnerOrTokenSwap() {
+ require(
+ isOwner() || approvedTokenManagers[_msgSender()] == true,
+ "caller is not the owner or an approved token swap manager"
+ );
+ _;
+ }
+
+ // @dev Move tokens out of cash pool
+ // @param _token ERC20 address
+ // @param destinationAddress address to send to
+ // @param orderAmount amount to transfer from cash pool
+ function moveTokenfromPool(
+ address _token,
+ address destinationAddress,
+ uint256 orderAmount
+ ) public onlyOwnerOrTokenSwap() returns (bool) {
+ InterfaceInverseToken token_ = InterfaceInverseToken(_token);
+
+ token_.transfer(destinationAddress, orderAmount);
+ return true;
+ }
+
+ // @dev Move tokens to cash pool
+ // @param _token ERC20 address
+ // @param whiteListedAddress address allowed to transfer to pool
+ // @param orderAmount amount to transfer to cash pool
+ function moveTokenToPool(
+ address _token,
+ address whiteListedAddress,
+ uint256 orderAmount
+ ) public onlyOwnerOrTokenSwap() returns (bool) {
+ InterfaceInverseToken token_ = InterfaceInverseToken(_token);
+ require(
+ kycVerifier.isAddressWhitelisted(whiteListedAddress),
+ "only whitelisted address are allowed to move tokens to pool"
+ );
+
+ uint256 percentageNumerator = percentageOfFundsForColdStorage[0];
+ uint256 percentageDenominator = percentageOfFundsForColdStorage[1];
+ uint256 amountForColdStorage = orderAmount.mul(percentageNumerator).div(
+ percentageDenominator
+ );
+
+ token_.transferFrom(whiteListedAddress, address(this), orderAmount);
+ token_.transfer(coldStorage, amountForColdStorage);
+
+ return true;
+ }
}
diff --git a/contracts/CompositionCalculator.sol b/contracts/short-tokens/CompositionCalculator.sol
similarity index 100%
rename from contracts/CompositionCalculator.sol
rename to contracts/short-tokens/CompositionCalculator.sol
diff --git a/contracts/Token/InverseToken.sol b/contracts/short-tokens/InverseToken.sol
similarity index 96%
rename from contracts/Token/InverseToken.sol
rename to contracts/short-tokens/InverseToken.sol
index 4d3b124..21adc19 100644
--- a/contracts/Token/InverseToken.sol
+++ b/contracts/short-tokens/InverseToken.sol
@@ -1,6 +1,6 @@
pragma solidity ^0.5.0;
-import "./ERC20.sol";
+import "../shared/Token/ERC20.sol";
contract InverseToken is ERC20 {
diff --git a/contracts/KYCVerifier.sol b/contracts/short-tokens/KYCVerifier.sol
similarity index 76%
rename from contracts/KYCVerifier.sol
rename to contracts/short-tokens/KYCVerifier.sol
index 867d286..2bbe75e 100644
--- a/contracts/KYCVerifier.sol
+++ b/contracts/short-tokens/KYCVerifier.sol
@@ -5,9 +5,10 @@ import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol
contract KYCVerifier is Ownable {
address public bridge;
-
mapping(address => bool) public whitelistedAddresses;
+ event WhitelistedAddressAdded(address);
+
function initialize(address ownerAddress) public initializer {
require(ownerAddress != address(0), "owner adddress must not be empty");
Ownable.initialize(ownerAddress);
@@ -22,13 +23,21 @@ contract KYCVerifier is Ownable {
}
// @dev Set whitelisted addresses
- function setWhitelistedAddress(address adddressToAdd)
+ function setWhitelistedAddress(address addressToAdd)
public
onlyOwnerOrBridge
{
- require(adddressToAdd != address(0), "adddress must not be empty");
+ require(addressToAdd != address(0), "adddress must not be empty");
+
+ whitelistedAddresses[addressToAdd] = true;
+
+ emit WhitelistedAddressAdded(addressToAdd);
+ }
- whitelistedAddresses[adddressToAdd] = true;
+ function batchWhitelistedAddress(address[] calldata addresses) external {
+ for (uint8 index = 0; index < addresses.length; index++) {
+ setWhitelistedAddress(addresses[index]);
+ }
}
// @dev Remove whitelisted addresses
diff --git a/contracts/PersistentStorage.sol b/contracts/short-tokens/PersistentStorage.sol
similarity index 100%
rename from contracts/PersistentStorage.sol
rename to contracts/short-tokens/PersistentStorage.sol
diff --git a/contracts/TokenSwapManager.sol b/contracts/short-tokens/TokenSwapManager.sol
similarity index 94%
rename from contracts/TokenSwapManager.sol
rename to contracts/short-tokens/TokenSwapManager.sol
index 8f8a6ba..7d25e9e 100644
--- a/contracts/TokenSwapManager.sol
+++ b/contracts/short-tokens/TokenSwapManager.sol
@@ -5,13 +5,13 @@ import "solidity-util/lib/Strings.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
-import "./CashPool.sol";
-import "./KYCVerifier.sol";
-import "./CompositionCalculator.sol";
-import "./Token/InverseToken.sol";
+import "./Abstract/InterfaceCashPool.sol";
+import "./Abstract/InterfaceKYCVerifier.sol";
+import "./Abstract/InterfaceCompositionCalculator.sol";
+import "./Abstract/InterfaceERC20.sol";
import "./Abstract/InterfaceInverseToken.sol";
-import "./PersistentStorage.sol";
+import "./Abstract/InterfaceStorage.sol";
import "./utils/Math.sol";
@@ -21,11 +21,11 @@ contract TokenSwapManager is Initializable, Ownable {
address public inverseToken;
- InverseToken public erc20;
- KYCVerifier public kycVerifier;
- CashPool public cashPool;
- PersistentStorage public persistentStorage;
- CompositionCalculator public compositionCalculator;
+ InterfaceERC20 public erc20;
+ InterfaceKYCVerifier public kycVerifier;
+ InterfaceCashPool public cashPool;
+ InterfaceStorage public persistentStorage;
+ InterfaceCompositionCalculator public compositionCalculator;
event SuccessfulOrder(
string orderType,
@@ -46,6 +46,7 @@ contract TokenSwapManager is Initializable, Ownable {
address _owner,
address _inverseToken,
address _cashPool,
+ address _persistentStorage,
address _compositionCalculator
) public initializer {
initialize(_owner);
@@ -60,12 +61,12 @@ contract TokenSwapManager is Initializable, Ownable {
inverseToken = _inverseToken;
- cashPool = CashPool(_cashPool);
- persistentStorage = PersistentStorage(
- address(cashPool.persistentStorage())
+ cashPool = InterfaceCashPool(_cashPool);
+ persistentStorage = InterfaceStorage(_persistentStorage);
+ kycVerifier = InterfaceKYCVerifier(address(cashPool.kycVerifier()));
+ compositionCalculator = InterfaceCompositionCalculator(
+ _compositionCalculator
);
- kycVerifier = KYCVerifier(address(cashPool.kycVerifier()));
- compositionCalculator = CompositionCalculator(_compositionCalculator);
}
//////////////// Create + Redeem Order Request ////////////////
@@ -315,7 +316,7 @@ contract TokenSwapManager is Initializable, Ownable {
internal
returns (uint256)
{
- erc20 = InverseToken(stablecoin);
+ erc20 = InterfaceERC20(stablecoin);
uint256 exponent = 18 - erc20.decimals();
return stablecoinValue / 10**exponent; // 6 decimal stable coin = 10**12
}
diff --git a/contracts/short-tokens/upgrades/CashPoolv2.sol b/contracts/short-tokens/upgrades/CashPoolv2.sol
new file mode 100644
index 0000000..c5243e9
--- /dev/null
+++ b/contracts/short-tokens/upgrades/CashPoolv2.sol
@@ -0,0 +1,71 @@
+pragma solidity ^0.5.0;
+
+import "../CashPool.sol";
+
+
+// used for Reference only
+// with oz sdk one does not actually need to create another smart contract as this and inheret the parent. Add the code directly in the parent smart contract and run `npx upgrade` and choose that contract to upgrade.
+contract CashPoolv2 is CashPool {
+ mapping(address => bool) public approvedTokenManagers;
+
+ function addTokenManager(address tokenManager) public onlyOwner {
+ require(tokenManager != address(0), "adddress must not be empty");
+ approvedTokenManagers[tokenManager] = true;
+ }
+
+ function removeTokenManager(address tokenManager) public onlyOwner {
+ require(tokenManager != address(0), "adddress must not be empty");
+ delete approvedTokenManagers[tokenManager];
+ }
+
+ function isTokenManager(address potentialAddress)
+ public
+ view
+ returns (bool)
+ {
+ require(potentialAddress != address(0), "adddress must not be empty");
+ return approvedTokenManagers[potentialAddress];
+ }
+
+ modifier onlyOwnerOrTokenSwapv2() {
+ require(
+ isOwner() || approvedTokenManagers[_msgSender()] == true,
+ "caller is not the owner or an approved token swap manager"
+ );
+ _;
+ }
+
+ function moveTokenfromPoolv2(
+ address _token,
+ address destinationAddress,
+ uint256 orderAmount
+ ) public onlyOwnerOrTokenSwapv2() returns (bool) {
+ InterfaceInverseToken token_ = InterfaceInverseToken(_token);
+
+ token_.transfer(destinationAddress, orderAmount);
+ return true;
+ }
+
+ function moveTokenToPoolv2(
+ address _token,
+ address whiteListedAddress,
+ uint256 orderAmount
+ ) public onlyOwnerOrTokenSwapv2() returns (bool) {
+ InterfaceInverseToken token_ = InterfaceInverseToken(_token);
+ require(
+ kycVerifier.isAddressWhitelisted(whiteListedAddress),
+ "only whitelisted address are allowed to move tokens to pool"
+ );
+
+ uint256 percentageNumerator = percentageOfFundsForColdStorage[0];
+ uint256 percentageDenominator = percentageOfFundsForColdStorage[1];
+ uint256 amountForColdStorage = orderAmount.mul(percentageNumerator).div(
+ percentageDenominator
+ );
+
+ token_.transferFrom(whiteListedAddress, address(this), orderAmount);
+ token_.transfer(coldStorage, amountForColdStorage);
+
+ return true;
+ }
+}
diff --git a/contracts/utils/DateTimeLibrary.sol b/contracts/short-tokens/utils/DateTimeLibrary.sol
similarity index 100%
rename from contracts/utils/DateTimeLibrary.sol
rename to contracts/short-tokens/utils/DateTimeLibrary.sol
diff --git a/contracts/utils/Math.sol b/contracts/short-tokens/utils/Math.sol
similarity index 100%
rename from contracts/utils/Math.sol
rename to contracts/short-tokens/utils/Math.sol
diff --git a/flats/CalculatorLeverage.sol b/flats/CalculatorLeverage.sol
new file mode 100644
index 0000000..6e609a2
--- /dev/null
+++ b/flats/CalculatorLeverage.sol
@@ -0,0 +1,1134 @@
+
+// File: @openzeppelin/upgrades/contracts/Initializable.sol
+
+pragma solidity >=0.4.24 <0.6.0;
+
+
+/**
+ * @title Initializable
+ *
+ * @dev Helper contract to support initializer functions. To use it, replace
+ * the constructor with a function that has the `initializer` modifier.
+ * WARNING: Unlike constructors, initializer functions must be manually
+ * invoked. This applies both to deploying an Initializable contract, as well
+ * as extending an Initializable contract via inheritance.
+ * WARNING: When used with inheritance, manual care must be taken to not invoke
+ * a parent initializer twice, or ensure that all initializers are idempotent,
+ * because this is not dealt with automatically as with constructors.
+ */
+contract Initializable {
+
+ /**
+ * @dev Indicates that the contract has been initialized.
+ */
+ bool private initialized;
+
+ /**
+ * @dev Indicates that the contract is in the process of being initialized.
+ */
+ bool private initializing;
+
+ /**
+ * @dev Modifier to use in the initializer function of a contract.
+ */
+ modifier initializer() {
+ require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
+
+ bool isTopLevelCall = !initializing;
+ if (isTopLevelCall) {
+ initializing = true;
+ initialized = true;
+ }
+
+ _;
+
+ if (isTopLevelCall) {
+ initializing = false;
+ }
+ }
+
+ /// @dev Returns true if and only if the function is running in the constructor
+ function isConstructor() private view returns (bool) {
+ // extcodesize checks the size of the code stored in an address, and
+ // address returns the current address. Since the code is still not
+ // deployed when running a constructor, any checks on its code size will
+ // yield zero, making it an effective way to detect if a contract is
+ // under construction or not.
+ uint256 cs;
+ assembly { cs := extcodesize(address) }
+ return cs == 0;
+ }
+
+ // Reserved storage space to allow for layout changes in the future.
+ uint256[50] private ______gap;
+}
+
+// File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
+
+pragma solidity ^0.5.0;
+
+/**
+ * @dev Wrappers over Solidity's arithmetic operations with added overflow
+ * checks.
+ *
+ * Arithmetic operations in Solidity wrap on overflow. This can easily result
+ * in bugs, because programmers usually assume that an overflow raises an
+ * error, which is the standard behavior in high level programming languages.
+ * `SafeMath` restores this intuition by reverting the transaction when an
+ * operation overflows.
+ *
+ * Using this library instead of the unchecked operations eliminates an entire
+ * class of bugs, so it's recommended to use it always.
+ */
+library SafeMath {
+ /**
+ * @dev Returns the addition of two unsigned integers, reverting on
+ * overflow.
+ *
+ * Counterpart to Solidity's `+` operator.
+ *
+ * Requirements:
+ * - Addition cannot overflow.
+ */
+ function add(uint256 a, uint256 b) internal pure returns (uint256) {
+ uint256 c = a + b;
+ require(c >= a, "SafeMath: addition overflow");
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the subtraction of two unsigned integers, reverting on
+ * overflow (when the result is negative).
+ *
+ * Counterpart to Solidity's `-` operator.
+ *
+ * Requirements:
+ * - Subtraction cannot overflow.
+ */
+ function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+ return sub(a, b, "SafeMath: subtraction overflow");
+ }
+
+ /**
+ * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
+ * overflow (when the result is negative).
+ *
+ * Counterpart to Solidity's `-` operator.
+ *
+ * Requirements:
+ * - Subtraction cannot overflow.
+ *
+ * _Available since v2.4.0._
+ */
+ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+ require(b <= a, errorMessage);
+ uint256 c = a - b;
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the multiplication of two unsigned integers, reverting on
+ * overflow.
+ *
+ * Counterpart to Solidity's `*` operator.
+ *
+ * Requirements:
+ * - Multiplication cannot 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-contracts/pull/522
+ if (a == 0) {
+ return 0;
+ }
+
+ uint256 c = a * b;
+ require(c / a == b, "SafeMath: multiplication overflow");
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the integer division of two unsigned integers. Reverts on
+ * division by zero. The result is rounded towards zero.
+ *
+ * Counterpart to Solidity's `/` operator. Note: this function uses a
+ * `revert` opcode (which leaves remaining gas untouched) while Solidity
+ * uses an invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ */
+ function div(uint256 a, uint256 b) internal pure returns (uint256) {
+ return div(a, b, "SafeMath: division by zero");
+ }
+
+ /**
+ * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
+ * division by zero. The result is rounded towards zero.
+ *
+ * Counterpart to Solidity's `/` operator. Note: this function uses a
+ * `revert` opcode (which leaves remaining gas untouched) while Solidity
+ * uses an invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ *
+ * _Available since v2.4.0._
+ */
+ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+ // Solidity only automatically asserts when dividing by 0
+ require(b > 0, errorMessage);
+ uint256 c = a / b;
+ // assert(a == b * c + a % b); // There is no case in which this doesn't hold
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
+ * Reverts when dividing by zero.
+ *
+ * Counterpart to Solidity's `%` operator. This function uses a `revert`
+ * opcode (which leaves remaining gas untouched) while Solidity uses an
+ * invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ */
+ function mod(uint256 a, uint256 b) internal pure returns (uint256) {
+ return mod(a, b, "SafeMath: modulo by zero");
+ }
+
+ /**
+ * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
+ * Reverts with custom message when dividing by zero.
+ *
+ * Counterpart to Solidity's `%` operator. This function uses a `revert`
+ * opcode (which leaves remaining gas untouched) while Solidity uses an
+ * invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ *
+ * _Available since v2.4.0._
+ */
+ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+ require(b != 0, errorMessage);
+ return a % b;
+ }
+}
+
+// File: contracts/short-tokens/utils/Math.sol
+
+/// Math.sol -- mixin for inline numerical wizardry
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+pragma solidity ^0.5.0;
+
+
+library DSMath {
+
+ // --- Unsigned Math ----
+ function add(uint x, uint y) internal pure returns (uint z) {
+ require((z = x + y) >= x, "ds-math-add-overflow");
+ }
+ function sub(uint x, uint y) internal pure returns (uint z) {
+ require((z = x - y) <= x, "ds-math-sub-underflow");
+ }
+ function mul(uint x, uint y) internal pure returns (uint z) {
+ require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
+ }
+
+
+ function min(uint x, uint y) internal pure returns (uint z) {
+ return x <= y ? x : y;
+ }
+ function max(uint x, uint y) internal pure returns (uint z) {
+ return x >= y ? x : y;
+ }
+ function imin(int x, int y) internal pure returns (int z) {
+ return x <= y ? x : y;
+ }
+ function imax(int x, int y) internal pure returns (int z) {
+ return x >= y ? x : y;
+ }
+
+ // --- Precise Math ---
+
+ uint public constant WAD = 10 ** 18;
+ uint public constant RAY = 10 ** 27;
+
+ function wmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), WAD / 2) / WAD;
+ }
+ function rmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), RAY / 2) / RAY;
+ }
+ function wdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, WAD), y / 2) / y;
+ }
+ function rdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, RAY), y / 2) / y;
+ }
+
+ function ray(uint _wad) internal pure returns (uint) {
+ return mul(_wad, uint(10 ** 9));
+ }
+
+ // This famous algorithm is called "exponentiation by squaring"
+ // and calculates x^n with x as fixed-point and n as regular unsigned.
+ //
+ // It's O(log n), instead of O(n) for naive repeated multiplication.
+ //
+ // These facts are why it works:
+ //
+ // If n is even, then x^n = (x^2)^(n/2).
+ // If n is odd, then x^n = x * x^(n-1),
+ // and applying the equation for even x gives
+ // x^n = x * (x^2)^((n-1) / 2).
+ //
+ // Also, EVM division is flooring and
+ // floor[(n-1) / 2] = floor[n / 2].
+ //
+ function rpow(uint x, uint n) internal pure returns (uint z) {
+ z = n % 2 != 0 ? x : RAY;
+
+ for (n /= 2; n != 0; n /= 2) {
+ x = rmul(x, x);
+
+ if (n % 2 != 0) {
+ z = rmul(z, x);
+ }
+ }
+ }
+}
+
+// File: contracts/short-tokens/utils/DateTimeLibrary.sol
+
+pragma solidity ^0.5.0;
+
+// ----------------------------------------------------------------------------
+// BokkyPooBah's DateTime Library v1.01
+//
+// A gas-efficient Solidity date and time library
+//
+// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
+//
+// Tested date range 1970/01/01 to 2345/12/31
+//
+// Conventions:
+// Unit | Range | Notes
+// :-------- |:-------------:|:-----
+// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
+// year | 1970 ... 2345 |
+// month | 1 ... 12 |
+// day | 1 ... 31 |
+// hour | 0 ... 23 |
+// minute | 0 ... 59 |
+// second | 0 ... 59 |
+// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday
+//
+//
+// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.
+// ----------------------------------------------------------------------------
+
+library DateTimeLibrary {
+ uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;
+ uint256 constant SECONDS_PER_HOUR = 60 * 60;
+ uint256 constant SECONDS_PER_MINUTE = 60;
+ int256 constant OFFSET19700101 = 2440588;
+
+ uint256 constant DOW_MON = 1;
+ uint256 constant DOW_TUE = 2;
+ uint256 constant DOW_WED = 3;
+ uint256 constant DOW_THU = 4;
+ uint256 constant DOW_FRI = 5;
+ uint256 constant DOW_SAT = 6;
+ uint256 constant DOW_SUN = 7;
+
+ // ------------------------------------------------------------------------
+ // Calculate the number of days from 1970/01/01 to year/month/day using
+ // the date conversion algorithm from
+ // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
+ // and subtracting the offset 2440588 so that 1970/01/01 is day 0
+ //
+ // days = day
+ // - 32075
+ // + 1461 * (year + 4800 + (month - 14) / 12) / 4
+ // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
+ // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
+ // - offset
+ // ------------------------------------------------------------------------
+ function _daysFromDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (uint256 _days)
+ {
+ require(year >= 1970);
+ int256 _year = int256(year);
+ int256 _month = int256(month);
+ int256 _day = int256(day);
+
+ int256 __days = _day -
+ 32075 +
+ (1461 * (_year + 4800 + (_month - 14) / 12)) /
+ 4 +
+ (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /
+ 12 -
+ (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /
+ 4 -
+ OFFSET19700101;
+
+ _days = uint256(__days);
+ }
+
+ // ------------------------------------------------------------------------
+ // Calculate year/month/day from the number of days since 1970/01/01 using
+ // the date conversion algorithm from
+ // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
+ // and adding the offset 2440588 so that 1970/01/01 is day 0
+ //
+ // int L = days + 68569 + offset
+ // int N = 4 * L / 146097
+ // L = L - (146097 * N + 3) / 4
+ // year = 4000 * (L + 1) / 1461001
+ // L = L - 1461 * year / 4 + 31
+ // month = 80 * L / 2447
+ // dd = L - 2447 * month / 80
+ // L = month / 11
+ // month = month + 2 - 12 * L
+ // year = 100 * (N - 49) + year + L
+ // ------------------------------------------------------------------------
+ function _daysToDate(uint256 _days)
+ internal
+ pure
+ returns (uint256 year, uint256 month, uint256 day)
+ {
+ int256 __days = int256(_days);
+
+ int256 L = __days + 68569 + OFFSET19700101;
+ int256 N = (4 * L) / 146097;
+ L = L - (146097 * N + 3) / 4;
+ int256 _year = (4000 * (L + 1)) / 1461001;
+ L = L - (1461 * _year) / 4 + 31;
+ int256 _month = (80 * L) / 2447;
+ int256 _day = L - (2447 * _month) / 80;
+ L = _month / 11;
+ _month = _month + 2 - 12 * L;
+ _year = 100 * (N - 49) + _year + L;
+
+ year = uint256(_year);
+ month = uint256(_month);
+ day = uint256(_day);
+ }
+
+ function daysFromDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (uint256 daysSinceDate)
+ {
+ daysSinceDate = _daysFromDate(year, month, day);
+ }
+ function timestampFromDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (uint256 timestamp)
+ {
+ timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
+ }
+ function timestampFromDateTime(
+ uint256 year,
+ uint256 month,
+ uint256 day,
+ uint256 hour,
+ uint256 minute,
+ uint256 second
+ ) internal pure returns (uint256 timestamp) {
+ timestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ hour *
+ SECONDS_PER_HOUR +
+ minute *
+ SECONDS_PER_MINUTE +
+ second;
+ }
+ function timestampToDate(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 year, uint256 month, uint256 day)
+ {
+ (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function timestampToDateTime(uint256 timestamp)
+ internal
+ pure
+ returns (
+ uint256 year,
+ uint256 month,
+ uint256 day,
+ uint256 hour,
+ uint256 minute,
+ uint256 second
+ )
+ {
+ (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ uint256 secs = timestamp % SECONDS_PER_DAY;
+ hour = secs / SECONDS_PER_HOUR;
+ secs = secs % SECONDS_PER_HOUR;
+ minute = secs / SECONDS_PER_MINUTE;
+ second = secs % SECONDS_PER_MINUTE;
+ }
+
+ function isValidDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (bool valid)
+ {
+ if (year >= 1970 && month > 0 && month <= 12) {
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > 0 && day <= daysInMonth) {
+ valid = true;
+ }
+ }
+ }
+ function isValidDateTime(
+ uint256 year,
+ uint256 month,
+ uint256 day,
+ uint256 hour,
+ uint256 minute,
+ uint256 second
+ ) internal pure returns (bool valid) {
+ if (isValidDate(year, month, day)) {
+ if (hour < 24 && minute < 60 && second < 60) {
+ valid = true;
+ }
+ }
+ }
+ function isLeapYear(uint256 timestamp)
+ internal
+ pure
+ returns (bool leapYear)
+ {
+ (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ leapYear = _isLeapYear(year);
+ }
+ function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {
+ leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
+ }
+ function isWeekDay(uint256 timestamp) internal pure returns (bool weekDay) {
+ weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
+ }
+ function isWeekEnd(uint256 timestamp) internal pure returns (bool weekEnd) {
+ weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
+ }
+ function getDaysInMonth(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 daysInMonth)
+ {
+ (uint256 year, uint256 month, ) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ daysInMonth = _getDaysInMonth(year, month);
+ }
+ function _getDaysInMonth(uint256 year, uint256 month)
+ internal
+ pure
+ returns (uint256 daysInMonth)
+ {
+ if (
+ month == 1 ||
+ month == 3 ||
+ month == 5 ||
+ month == 7 ||
+ month == 8 ||
+ month == 10 ||
+ month == 12
+ ) {
+ daysInMonth = 31;
+ } else if (month != 2) {
+ daysInMonth = 30;
+ } else {
+ daysInMonth = _isLeapYear(year) ? 29 : 28;
+ }
+ }
+ // 1 = Monday, 7 = Sunday
+ function getDayOfWeek(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 dayOfWeek)
+ {
+ uint256 _days = timestamp / SECONDS_PER_DAY;
+ dayOfWeek = ((_days + 3) % 7) + 1;
+ }
+
+ function getYear(uint256 timestamp) internal pure returns (uint256 year) {
+ (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function getMonth(uint256 timestamp) internal pure returns (uint256 month) {
+ (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function getDay(uint256 timestamp) internal pure returns (uint256 day) {
+ (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function getHour(uint256 timestamp) internal pure returns (uint256 hour) {
+ uint256 secs = timestamp % SECONDS_PER_DAY;
+ hour = secs / SECONDS_PER_HOUR;
+ }
+ function getMinute(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 minute)
+ {
+ uint256 secs = timestamp % SECONDS_PER_HOUR;
+ minute = secs / SECONDS_PER_MINUTE;
+ }
+ function getSecond(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 second)
+ {
+ second = timestamp % SECONDS_PER_MINUTE;
+ }
+
+ function addYears(uint256 timestamp, uint256 _years)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ year += _years;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp >= timestamp);
+ }
+ function addMonths(uint256 timestamp, uint256 _months)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ month += _months;
+ year += (month - 1) / 12;
+ month = ((month - 1) % 12) + 1;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp >= timestamp);
+ }
+ function addDays(uint256 timestamp, uint256 _days)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _days * SECONDS_PER_DAY;
+ require(newTimestamp >= timestamp);
+ }
+ function addHours(uint256 timestamp, uint256 _hours)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
+ require(newTimestamp >= timestamp);
+ }
+ function addMinutes(uint256 timestamp, uint256 _minutes)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
+ require(newTimestamp >= timestamp);
+ }
+ function addSeconds(uint256 timestamp, uint256 _seconds)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _seconds;
+ require(newTimestamp >= timestamp);
+ }
+
+ function subYears(uint256 timestamp, uint256 _years)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ year -= _years;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp <= timestamp);
+ }
+ function subMonths(uint256 timestamp, uint256 _months)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ uint256 yearMonth = year * 12 + (month - 1) - _months;
+ year = yearMonth / 12;
+ month = (yearMonth % 12) + 1;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp <= timestamp);
+ }
+ function subDays(uint256 timestamp, uint256 _days)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _days * SECONDS_PER_DAY;
+ require(newTimestamp <= timestamp);
+ }
+ function subHours(uint256 timestamp, uint256 _hours)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
+ require(newTimestamp <= timestamp);
+ }
+ function subMinutes(uint256 timestamp, uint256 _minutes)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
+ require(newTimestamp <= timestamp);
+ }
+ function subSeconds(uint256 timestamp, uint256 _seconds)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _seconds;
+ require(newTimestamp <= timestamp);
+ }
+
+ function diffYears(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _years)
+ {
+ require(fromTimestamp <= toTimestamp);
+ (uint256 fromYear, , ) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
+ (uint256 toYear, , ) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
+ _years = toYear - fromYear;
+ }
+ function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _months)
+ {
+ require(fromTimestamp <= toTimestamp);
+ (uint256 fromYear, uint256 fromMonth, ) = _daysToDate(
+ fromTimestamp / SECONDS_PER_DAY
+ );
+ (uint256 toYear, uint256 toMonth, ) = _daysToDate(
+ toTimestamp / SECONDS_PER_DAY
+ );
+ _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
+ }
+ function diffDays(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _days)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
+ }
+ function diffHours(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _hours)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
+ }
+ function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _minutes)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
+ }
+ function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _seconds)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _seconds = toTimestamp - fromTimestamp;
+ }
+}
+
+// File: contracts/leverage-tokens/Abstract/InterfaceStorageLeverage.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceStorageLeverage {
+ function whitelistedAddresses(address) external view returns (bool);
+
+ function isPaused() external view returns (bool);
+
+ function isShutdown() external view returns (bool);
+
+ function tokenSwapManager() external view returns (address);
+
+ function bridge() external view returns (address);
+
+ function managementFee() external view returns (uint256);
+
+ function getExecutionPrice() external view returns (uint256);
+
+ function getMarkPrice() external view returns (uint256);
+
+ function getTokenValueAfterFees() external view returns (uint256);
+
+ function getNotional() external view returns (uint256);
+
+ function getTokenValue() external view returns (uint256);
+
+ function getChangeInNotional() external view returns (int256);
+
+ function getMintingFee(uint256 cash) external view returns (uint256);
+
+ function minimumMintingFee() external view returns (uint256);
+
+ function minRebalanceAmount() external view returns (uint8);
+
+ function delayedRedemptionsByUser(address) external view returns (uint256);
+
+ function setDelayedRedemptionsByUser(
+ uint256 amountToRedeem,
+ address whitelistedAddress
+ ) external;
+
+ function setOrderByUser(
+ address whitelistedAddress,
+ string calldata orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ uint256 orderIndex,
+ bool overwrite
+ ) external;
+
+ function setAccounting(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ int256 _changeInNotional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external;
+
+ function setAccountingForLastActivityDay(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ int256 _changeInNotional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external;
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceInverseToken.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceInverseToken {
+ function mintTokens(address, uint256) external returns (bool);
+
+ function burnTokens(address, uint256) external returns (bool);
+
+ /**
+ * @dev Returns the amount of tokens in existence.
+ */
+ function totalSupply() external view returns (uint256);
+
+ /**
+ * @dev Returns the amount of tokens owned by `account`.
+ */
+ function balanceOf(address account) external view returns (uint256);
+
+ /**
+ * @dev Moves `amount` tokens from the caller's account to `recipient`.
+ *
+ * Returns a boolean value indicating whether the operation succeeded.
+ *
+ * Emits a {Transfer} event.
+ */
+ function transfer(address recipient, uint256 amount)
+ external
+ returns (bool);
+
+ /**
+ * @dev Returns the remaining number of tokens that `spender` will be
+ * allowed to spend on behalf of `owner` through {transferFrom}. This is
+ * zero by default.
+ *
+ * This value changes when {approve} or {transferFrom} are called.
+ */
+ function allowance(address owner, address spender)
+ external
+ view
+ returns (uint256);
+
+ /**
+ * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
+ *
+ * Returns a boolean value indicating whether the operation succeeded.
+ *
+ * IMPORTANT: Beware that changing an allowance with this method brings the risk
+ * that someone may use both the old and the new allowance by unfortunate
+ * transaction ordering. One possible solution to mitigate this race
+ * condition is to first reduce the spender's allowance to 0 and set the
+ * desired value afterwards:
+ * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+ *
+ * Emits an {Approval} event.
+ */
+ function approve(address spender, uint256 amount) external returns (bool);
+
+ /**
+ * @dev Moves `amount` tokens from `sender` to `recipient` using the
+ * allowance mechanism. `amount` is then deducted from the caller's
+ * allowance.
+ *
+ * Returns a boolean value indicating whether the operation succeeded.
+ *
+ * Emits a {Transfer} event.
+ */
+ function transferFrom(address sender, address recipient, uint256 amount)
+ external
+ returns (bool);
+
+ /**
+ * @dev Emitted when `value` tokens are moved from one account (`from`) to
+ * another (`to`).
+ *
+ * Note that `value` may be zero.
+ */
+ event Transfer(address indexed from, address indexed to, uint256 value);
+
+ /**
+ * @dev Emitted when the allowance of a `spender` for an `owner` is set by
+ * a call to {approve}. `value` is the new allowance.
+ */
+ event Approval(
+ address indexed owner,
+ address indexed spender,
+ uint256 value
+ );
+}
+
+// File: contracts/leverage-tokens/CalculatorLeverage.sol
+
+pragma solidity ^0.5.0;
+
+
+
+
+
+
+
+
+/**
+ * @dev uint256 are expected to use last 18 numbers as decimal points except when specifid differently in @params
+ */
+contract CalculatorLeverage is Initializable {
+ using SafeMath for uint256;
+
+ InterfaceStorageLeverage public persistentStorage;
+ InterfaceInverseToken public inverseToken;
+
+ function initialize(
+ address _persistentStorageAddress,
+ address _inverseTokenAddress
+ ) public initializer {
+ persistentStorage = InterfaceStorageLeverage(_persistentStorageAddress);
+ inverseToken = InterfaceInverseToken(_inverseTokenAddress);
+ }
+
+ // Start fill in the values
+ // Creation 3x Long
+
+ function getTokensCreatedByCash(
+ uint256 mintingPrice,
+ uint256 cash,
+ uint256 gasFee
+ ) public view returns (uint256 tokensCreated) {
+ // Cash Remove Gas Fee
+ // Cash Remove Minting Fee (Cash Proceeds)
+ // Cash Proceeds / Minting Price (# of Tokens to Mint)
+
+ uint256 cashAfterGas = DSMath.sub(cash, gasFee);
+ uint256 cashAfterFee = removeCurrentMintingFeeFromCash(cashAfterGas);
+
+ uint256 tokensMinted = DSMath.wdiv(cashAfterFee, mintingPrice);
+
+ return tokensMinted;
+ }
+
+ function getCashCreatedByTokens(
+ uint256 burningPrice,
+ uint256 elapsedTime,
+ uint256 tokens,
+ uint256 gasFee
+ ) public view returns (uint256 stablecoinAfterFees) {
+ uint256 stablecoin = DSMath.wmul(tokens, burningPrice);
+ uint256 stablecoinAfterGas = DSMath.sub(stablecoin, gasFee);
+ uint256 stablecoinRedeemed = removeCurrentMintingFeeFromCash(
+ stablecoinAfterGas
+ );
+ uint256 managementFeeDaily = DSMath.wdiv(
+ persistentStorage.managementFee(),
+ 365 ether
+ );
+ uint managementFeeHourly = DSMath.wdiv(
+ DSMath.wmul(managementFeeDaily, elapsedTime),
+ 24 ether
+ );
+ uint256 normalizedManagementFee = DSMath.sub(
+ 1 ether,
+ DSMath.wdiv(managementFeeHourly, 100 ether)
+ );
+
+ stablecoinAfterFees = DSMath.wmul(
+ stablecoinRedeemed,
+ normalizedManagementFee
+ );
+ }
+
+ /**
+ * @dev Calculates all new values generated from rebalance
+ * @param _tokenValueNetFees The token value after fees are removed
+ * @param _bestExecutionPrice The best execution price for rebalancing
+ * @param _markPrice The Mark Price
+ * @param _notionalPreRebalance The notional amount before rebalance
+ * @param _targetLeverage The targetLeverage
+ */
+ function calculateRebalanceValues(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notionalPreRebalance,
+ uint256 _targetLeverage
+ )
+ public
+ pure
+ returns (
+ uint256 notional,
+ int256 changeInNotional,
+ int256 tokenValue
+ )
+ {
+ uint256 valueOfNotional = DSMath.wmul(
+ _targetLeverage,
+ _tokenValueNetFees
+ );
+ notional = DSMath.wdiv(valueOfNotional, _markPrice);
+
+ changeInNotional = int256(notional - _notionalPreRebalance);
+ int256 priceSlippage = int256(_markPrice - _bestExecutionPrice);
+ int256 tokenSlippage = wmulInt256(priceSlippage, changeInNotional);
+
+ tokenValue = int256(_tokenValueNetFees) + tokenSlippage;
+ }
+
+ function removeCurrentMintingFeeFromCash(uint256 _cash)
+ public
+ view
+ returns (uint256 cashAfterFee)
+ {
+ uint256 creationFee = persistentStorage.getMintingFee(_cash);
+ uint256 minimumMintingFee = persistentStorage.minimumMintingFee();
+ cashAfterFee = removeMintingFeeFromCash(
+ _cash,
+ creationFee,
+ minimumMintingFee
+ );
+ }
+
+ function removeMintingFeeFromCash(
+ uint256 _cash,
+ uint256 _mintingFee,
+ uint256 _minimumMintingFee
+ ) public pure returns (uint256 cashAfterFee) {
+ uint256 creationFeeInCash = DSMath.wmul(_cash, _mintingFee);
+ if (_minimumMintingFee > creationFeeInCash) {
+ creationFeeInCash = _minimumMintingFee;
+ }
+ cashAfterFee = DSMath.sub(_cash, creationFeeInCash);
+ }
+
+ int256 constant WAD = 10**18;
+
+ function addInt256(int256 x, int256 y) internal pure returns (int256 z) {
+ require((z = x + y) >= x, "ds-math-add-overflow");
+ }
+
+ function mulInt256(int256 x, int256 y) internal pure returns (int256 z) {
+ require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
+ }
+
+ function wmulInt256(int256 x, int256 y) internal pure returns (int256 z) {
+ z = addInt256(mulInt256(x, y), WAD / 2) / WAD;
+ }
+}
diff --git a/flats/CashPool.sol b/flats/CashPool.sol
index 2c2e795..2a7b9a5 100644
--- a/flats/CashPool.sol
+++ b/flats/CashPool.sol
@@ -335,7 +335,7 @@ library SafeMath {
}
}
-// File: contracts/Abstract/InterfaceInverseToken.sol
+// File: contracts/short-tokens/Abstract/InterfaceInverseToken.sol
pragma solidity ^0.5.0;
@@ -426,598 +426,16 @@ interface InterfaceInverseToken {
);
}
-// File: contracts/utils/DateTimeLibrary.sol
+// File: contracts/short-tokens/Abstract/InterfaceKYCVerifier.sol
pragma solidity ^0.5.0;
-// ----------------------------------------------------------------------------
-// BokkyPooBah's DateTime Library v1.01
-//
-// A gas-efficient Solidity date and time library
-//
-// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
-//
-// Tested date range 1970/01/01 to 2345/12/31
-//
-// Conventions:
-// Unit | Range | Notes
-// :-------- |:-------------:|:-----
-// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
-// year | 1970 ... 2345 |
-// month | 1 ... 12 |
-// day | 1 ... 31 |
-// hour | 0 ... 23 |
-// minute | 0 ... 59 |
-// second | 0 ... 59 |
-// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday
-//
-//
-// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.
-// ----------------------------------------------------------------------------
-
-library DateTimeLibrary {
- uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;
- uint256 constant SECONDS_PER_HOUR = 60 * 60;
- uint256 constant SECONDS_PER_MINUTE = 60;
- int256 constant OFFSET19700101 = 2440588;
-
- uint256 constant DOW_MON = 1;
- uint256 constant DOW_TUE = 2;
- uint256 constant DOW_WED = 3;
- uint256 constant DOW_THU = 4;
- uint256 constant DOW_FRI = 5;
- uint256 constant DOW_SAT = 6;
- uint256 constant DOW_SUN = 7;
-
- // ------------------------------------------------------------------------
- // Calculate the number of days from 1970/01/01 to year/month/day using
- // the date conversion algorithm from
- // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
- // and subtracting the offset 2440588 so that 1970/01/01 is day 0
- //
- // days = day
- // - 32075
- // + 1461 * (year + 4800 + (month - 14) / 12) / 4
- // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
- // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
- // - offset
- // ------------------------------------------------------------------------
- function _daysFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 _days)
- {
- require(year >= 1970);
- int256 _year = int256(year);
- int256 _month = int256(month);
- int256 _day = int256(day);
-
- int256 __days = _day -
- 32075 +
- (1461 * (_year + 4800 + (_month - 14) / 12)) /
- 4 +
- (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /
- 12 -
- (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /
- 4 -
- OFFSET19700101;
-
- _days = uint256(__days);
- }
-
- // ------------------------------------------------------------------------
- // Calculate year/month/day from the number of days since 1970/01/01 using
- // the date conversion algorithm from
- // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
- // and adding the offset 2440588 so that 1970/01/01 is day 0
- //
- // int L = days + 68569 + offset
- // int N = 4 * L / 146097
- // L = L - (146097 * N + 3) / 4
- // year = 4000 * (L + 1) / 1461001
- // L = L - 1461 * year / 4 + 31
- // month = 80 * L / 2447
- // dd = L - 2447 * month / 80
- // L = month / 11
- // month = month + 2 - 12 * L
- // year = 100 * (N - 49) + year + L
- // ------------------------------------------------------------------------
- function _daysToDate(uint256 _days)
- internal
- pure
- returns (uint256 year, uint256 month, uint256 day)
- {
- int256 __days = int256(_days);
-
- int256 L = __days + 68569 + OFFSET19700101;
- int256 N = (4 * L) / 146097;
- L = L - (146097 * N + 3) / 4;
- int256 _year = (4000 * (L + 1)) / 1461001;
- L = L - (1461 * _year) / 4 + 31;
- int256 _month = (80 * L) / 2447;
- int256 _day = L - (2447 * _month) / 80;
- L = _month / 11;
- _month = _month + 2 - 12 * L;
- _year = 100 * (N - 49) + _year + L;
-
- year = uint256(_year);
- month = uint256(_month);
- day = uint256(_day);
- }
-
- function daysFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 daysSinceDate)
- {
- daysSinceDate = _daysFromDate(year, month, day);
- }
- function timestampFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 timestamp)
- {
- timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
- }
- function timestampFromDateTime(
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- ) internal pure returns (uint256 timestamp) {
- timestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- hour *
- SECONDS_PER_HOUR +
- minute *
- SECONDS_PER_MINUTE +
- second;
- }
- function timestampToDate(uint256 timestamp)
- internal
- pure
- returns (uint256 year, uint256 month, uint256 day)
- {
- (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function timestampToDateTime(uint256 timestamp)
- internal
- pure
- returns (
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- )
- {
- (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- uint256 secs = timestamp % SECONDS_PER_DAY;
- hour = secs / SECONDS_PER_HOUR;
- secs = secs % SECONDS_PER_HOUR;
- minute = secs / SECONDS_PER_MINUTE;
- second = secs % SECONDS_PER_MINUTE;
- }
-
- function isValidDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (bool valid)
- {
- if (year >= 1970 && month > 0 && month <= 12) {
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > 0 && day <= daysInMonth) {
- valid = true;
- }
- }
- }
- function isValidDateTime(
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- ) internal pure returns (bool valid) {
- if (isValidDate(year, month, day)) {
- if (hour < 24 && minute < 60 && second < 60) {
- valid = true;
- }
- }
- }
- function isLeapYear(uint256 timestamp)
- internal
- pure
- returns (bool leapYear)
- {
- (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- leapYear = _isLeapYear(year);
- }
- function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {
- leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
- }
- function isWeekDay(uint256 timestamp) internal pure returns (bool weekDay) {
- weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
- }
- function isWeekEnd(uint256 timestamp) internal pure returns (bool weekEnd) {
- weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
- }
- function getDaysInMonth(uint256 timestamp)
- internal
- pure
- returns (uint256 daysInMonth)
- {
- (uint256 year, uint256 month, ) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- daysInMonth = _getDaysInMonth(year, month);
- }
- function _getDaysInMonth(uint256 year, uint256 month)
- internal
- pure
- returns (uint256 daysInMonth)
- {
- if (
- month == 1 ||
- month == 3 ||
- month == 5 ||
- month == 7 ||
- month == 8 ||
- month == 10 ||
- month == 12
- ) {
- daysInMonth = 31;
- } else if (month != 2) {
- daysInMonth = 30;
- } else {
- daysInMonth = _isLeapYear(year) ? 29 : 28;
- }
- }
- // 1 = Monday, 7 = Sunday
- function getDayOfWeek(uint256 timestamp)
- internal
- pure
- returns (uint256 dayOfWeek)
- {
- uint256 _days = timestamp / SECONDS_PER_DAY;
- dayOfWeek = ((_days + 3) % 7) + 1;
- }
-
- function getYear(uint256 timestamp) internal pure returns (uint256 year) {
- (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getMonth(uint256 timestamp) internal pure returns (uint256 month) {
- (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getDay(uint256 timestamp) internal pure returns (uint256 day) {
- (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getHour(uint256 timestamp) internal pure returns (uint256 hour) {
- uint256 secs = timestamp % SECONDS_PER_DAY;
- hour = secs / SECONDS_PER_HOUR;
- }
- function getMinute(uint256 timestamp)
- internal
- pure
- returns (uint256 minute)
- {
- uint256 secs = timestamp % SECONDS_PER_HOUR;
- minute = secs / SECONDS_PER_MINUTE;
- }
- function getSecond(uint256 timestamp)
- internal
- pure
- returns (uint256 second)
- {
- second = timestamp % SECONDS_PER_MINUTE;
- }
-
- function addYears(uint256 timestamp, uint256 _years)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- year += _years;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp >= timestamp);
- }
- function addMonths(uint256 timestamp, uint256 _months)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- month += _months;
- year += (month - 1) / 12;
- month = ((month - 1) % 12) + 1;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp >= timestamp);
- }
- function addDays(uint256 timestamp, uint256 _days)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _days * SECONDS_PER_DAY;
- require(newTimestamp >= timestamp);
- }
- function addHours(uint256 timestamp, uint256 _hours)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
- require(newTimestamp >= timestamp);
- }
- function addMinutes(uint256 timestamp, uint256 _minutes)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
- require(newTimestamp >= timestamp);
- }
- function addSeconds(uint256 timestamp, uint256 _seconds)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _seconds;
- require(newTimestamp >= timestamp);
- }
-
- function subYears(uint256 timestamp, uint256 _years)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- year -= _years;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp <= timestamp);
- }
- function subMonths(uint256 timestamp, uint256 _months)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- uint256 yearMonth = year * 12 + (month - 1) - _months;
- year = yearMonth / 12;
- month = (yearMonth % 12) + 1;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp <= timestamp);
- }
- function subDays(uint256 timestamp, uint256 _days)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _days * SECONDS_PER_DAY;
- require(newTimestamp <= timestamp);
- }
- function subHours(uint256 timestamp, uint256 _hours)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
- require(newTimestamp <= timestamp);
- }
- function subMinutes(uint256 timestamp, uint256 _minutes)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
- require(newTimestamp <= timestamp);
- }
- function subSeconds(uint256 timestamp, uint256 _seconds)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _seconds;
- require(newTimestamp <= timestamp);
- }
-
- function diffYears(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _years)
- {
- require(fromTimestamp <= toTimestamp);
- (uint256 fromYear, , ) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
- (uint256 toYear, , ) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
- _years = toYear - fromYear;
- }
- function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _months)
- {
- require(fromTimestamp <= toTimestamp);
- (uint256 fromYear, uint256 fromMonth, ) = _daysToDate(
- fromTimestamp / SECONDS_PER_DAY
- );
- (uint256 toYear, uint256 toMonth, ) = _daysToDate(
- toTimestamp / SECONDS_PER_DAY
- );
- _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
- }
- function diffDays(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _days)
- {
- require(fromTimestamp <= toTimestamp);
- _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
- }
- function diffHours(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _hours)
- {
- require(fromTimestamp <= toTimestamp);
- _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
- }
- function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _minutes)
- {
- require(fromTimestamp <= toTimestamp);
- _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
- }
- function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _seconds)
- {
- require(fromTimestamp <= toTimestamp);
- _seconds = toTimestamp - fromTimestamp;
- }
-}
-
-// File: contracts/utils/Math.sol
-
-/// Math.sol -- mixin for inline numerical wizardry
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-pragma solidity ^0.5.0;
-
-
-library DSMath {
-
- // --- Unsigned Math ----
- function add(uint x, uint y) internal pure returns (uint z) {
- require((z = x + y) >= x, "ds-math-add-overflow");
- }
- function sub(uint x, uint y) internal pure returns (uint z) {
- require((z = x - y) <= x, "ds-math-sub-underflow");
- }
- function mul(uint x, uint y) internal pure returns (uint z) {
- require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
- }
-
-
- function min(uint x, uint y) internal pure returns (uint z) {
- return x <= y ? x : y;
- }
- function max(uint x, uint y) internal pure returns (uint z) {
- return x >= y ? x : y;
- }
- function imin(int x, int y) internal pure returns (int z) {
- return x <= y ? x : y;
- }
- function imax(int x, int y) internal pure returns (int z) {
- return x >= y ? x : y;
- }
- // --- Precise Math ---
-
- uint public constant WAD = 10 ** 18;
- uint public constant RAY = 10 ** 27;
-
- function wmul(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, y), WAD / 2) / WAD;
- }
- function rmul(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, y), RAY / 2) / RAY;
- }
- function wdiv(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, WAD), y / 2) / y;
- }
- function rdiv(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, RAY), y / 2) / y;
- }
-
- function ray(uint _wad) internal pure returns (uint) {
- return mul(_wad, uint(10 ** 9));
- }
-
- // This famous algorithm is called "exponentiation by squaring"
- // and calculates x^n with x as fixed-point and n as regular unsigned.
- //
- // It's O(log n), instead of O(n) for naive repeated multiplication.
- //
- // These facts are why it works:
- //
- // If n is even, then x^n = (x^2)^(n/2).
- // If n is odd, then x^n = x * x^(n-1),
- // and applying the equation for even x gives
- // x^n = x * (x^2)^((n-1) / 2).
- //
- // Also, EVM division is flooring and
- // floor[(n-1) / 2] = floor[n / 2].
- //
- function rpow(uint x, uint n) internal pure returns (uint z) {
- z = n % 2 != 0 ? x : RAY;
-
- for (n /= 2; n != 0; n /= 2) {
- x = rmul(x, x);
-
- if (n % 2 != 0) {
- z = rmul(z, x);
- }
- }
- }
+interface InterfaceKYCVerifier {
+ function isAddressWhitelisted(address) external view returns (bool);
}
-// File: contracts/PersistentStorage.sol
+// File: contracts/short-tokens/CashPool.sol
pragma solidity ^0.5.0;
@@ -1025,504 +443,124 @@ pragma solidity ^0.5.0;
-contract PersistentStorage is Ownable {
- address public tokenSwapManager;
- address public bridge;
-
- bool public isPaused;
- bool public isShutdown;
- struct Accounting {
- uint256 price;
- uint256 cashPositionPerTokenUnit;
- uint256 balancePerTokenUnit;
- uint256 lendingFee;
- }
-
- struct Order {
- string orderType;
- uint256 tokensGiven;
- uint256 tokensRecieved;
- uint256 avgBlendedFee;
- }
-
- uint256 public lastActivityDay;
- uint256 public minRebalanceAmount;
- uint256 private managementFee;
-
- mapping(uint256 => Accounting[]) private accounting;
-
- mapping(address => bool) public whitelistedAddresses;
+contract CashPool is Ownable {
+ using SafeMath for uint256;
+ InterfaceKYCVerifier public kycVerifier;
- uint256[] public mintingFeeBracket;
- mapping(uint256 => uint256) public mintingFee;
+ uint256[2] public percentageOfFundsForColdStorage;
+ address public coldStorage;
- Order[] public allOrders;
- mapping(address => Order[]) public orderByUser;
- mapping(address => uint256) public delayedRedemptionsByUser;
+ mapping(address => bool) public approvedTokenManagers;
- event AccountingValuesSet(uint256 today);
- event RebalanceValuesSet(uint256 newMinRebalanceAmount);
- event ManagementFeeValuesSet(uint256 newManagementFee);
+ event SetPercentageOfFundsForColdStorageEvent(
+ uint256[2] newPercentageOfFundsForColdStorage
+ );
function initialize(
address ownerAddress,
- uint256 _managementFee,
- uint256 _minRebalanceAmount
+ address _kycVerifier,
+ address _coldStorage,
+ uint256[2] memory _percentageOfFundsForColdStorage
) public initializer {
- initialize(ownerAddress);
- managementFee = _managementFee;
- minRebalanceAmount = _minRebalanceAmount;
- mintingFeeBracket.push(50000 ether);
- mintingFeeBracket.push(100000 ether);
- mintingFee[50000 ether] = 3 ether / 1000; //0.3%
- mintingFee[100000 ether] = 2 ether / 1000; //0.2%
- mintingFee[~uint256(0)] = 1 ether / 1000; //0.1% all values higher
- }
-
- function setTokenSwapManager(address _tokenSwapManager) public onlyOwner {
- require(_tokenSwapManager != address(0), "adddress must not be empty");
- tokenSwapManager = _tokenSwapManager;
- }
-
- function setBridge(address _bridge) public onlyOwner {
- require(_bridge != address(0), "adddress must not be empty");
- bridge = _bridge;
- }
-
- function setIsPaused(bool _isPaused) public onlyOwner {
- isPaused = _isPaused;
- }
-
- function shutdown() public onlyOwner {
- isShutdown = true;
- }
-
- /**
- * @dev Throws if called by any account other than the owner.
- */
- modifier onlyOwnerOrTokenSwap() {
require(
- isOwner() || _msgSender() == tokenSwapManager,
- "caller is not the owner or token swap manager"
- );
- _;
- }
-
- function setDelayedRedemptionsByUser(
- uint256 amountToRedeem,
- address whitelistedAddress
- ) public onlyOwnerOrTokenSwap {
- delayedRedemptionsByUser[whitelistedAddress] = amountToRedeem;
- }
-
- function getDelayedRedemptionsByUser(address whitelistedAddress)
- public
- view
- returns (uint256)
- {
- return delayedRedemptionsByUser[whitelistedAddress];
- }
-
- /*
- * Saves order in mapping (address => Order[]) orderByUser
- * overwrite == false, append to Order[]
- * overwrite == true, overwrite element at orderIndex
- */
-
- function setOrderByUser(
- address whitelistedAddress,
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee,
- uint256 orderIndex,
- bool overwrite
- ) public onlyOwnerOrTokenSwap() {
- Order memory newOrder = Order(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee
- );
-
- if (!overwrite) {
- orderByUser[whitelistedAddress].push(newOrder);
- setOrder(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee,
- orderIndex,
- overwrite
- );
- } else {
- orderByUser[whitelistedAddress][orderIndex] = newOrder;
- }
- }
-
- /*
- * Gets Order[] For User Address
- * Return order at Index in Order[]
- */
-
- function getOrderByUser(address whitelistedAddress, uint256 orderIndex)
- public
- view
- returns (
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee
- )
- {
-
- Order storage orderAtIndex
- = orderByUser[whitelistedAddress][orderIndex];
- return (
- orderAtIndex.orderType,
- orderAtIndex.tokensGiven,
- orderAtIndex.tokensRecieved,
- orderAtIndex.avgBlendedFee
- );
- }
-
- /*
- * Save order to allOrders array
- * overwrite == false, append to allOrders array
- * overwrite == true, overwrite element at orderIndex
- */
- function setOrder(
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee,
- uint256 orderIndex,
- bool overwrite
- ) public onlyOwnerOrTokenSwap() {
- Order memory newOrder = Order(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee
- );
-
- if (!overwrite) {
- allOrders.push(newOrder);
- } else {
- allOrders[orderIndex] = newOrder;
- }
- }
-
- /*
- * Get Order
- */
- function getOrder(uint256 index)
- public
- view
- returns (
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee
- )
- {
- Order storage orderAtIndex = allOrders[index];
- return (
- orderAtIndex.orderType,
- orderAtIndex.tokensGiven,
- orderAtIndex.tokensRecieved,
- orderAtIndex.avgBlendedFee
+ ownerAddress != address(0) &&
+ _kycVerifier != address(0) &&
+ _coldStorage != address(0) &&
+ _percentageOfFundsForColdStorage[1] != 0,
+ "params variables cannot be empty but _percentageOfFundsForColdStorage[0]"
);
- }
-
- // @dev Set whitelisted addresses
- function setWhitelistedAddress(address adddressToAdd) public onlyOwner {
- require(adddressToAdd != address(0), "adddress must not be empty");
-
- whitelistedAddresses[adddressToAdd] = true;
- }
-
- // @dev Remove whitelisted addresses
- function removeWhitelistedAddress(address addressToRemove)
- public
- onlyOwner
- {
require(
- whitelistedAddresses[addressToRemove],
- "address must be added to be removed allowed"
+ _percentageOfFundsForColdStorage[0] <=
+ _percentageOfFundsForColdStorage[1],
+ "cannot set more than 100% for coldstorage"
);
-
- delete whitelistedAddresses[addressToRemove];
- }
-
- // @dev Updates whitelisted addresses
- function updateWhitelistedAddress(address oldAddress, address newAddress)
- public
- {
- removeWhitelistedAddress(oldAddress);
- setWhitelistedAddress(newAddress);
+ initialize(ownerAddress);
+ kycVerifier = InterfaceKYCVerifier(_kycVerifier);
+ coldStorage = _coldStorage;
+ percentageOfFundsForColdStorage = _percentageOfFundsForColdStorage;
}
- // @dev Get accounting values for a specific day
- // @param date format as 20200123 for 23th of January 2020
- function getAccounting(uint256 date)
- public
- view
- returns (uint256, uint256, uint256, uint256)
- {
- return (
- accounting[date][accounting[date].length - 1].price,
- accounting[date][accounting[date].length - 1]
- .cashPositionPerTokenUnit,
- accounting[date][accounting[date].length - 1].balancePerTokenUnit,
- accounting[date][accounting[date].length - 1].lendingFee
- );
+ function getBalance(address _token) public view returns (uint256) {
+ InterfaceInverseToken token_ = InterfaceInverseToken(_token);
+ uint256 tokenBalance = token_.balanceOf(address(this));
+ return tokenBalance;
}
- // @dev Set accounting values for the day
- function setAccounting(
- uint256 _price,
- uint256 _cashPositionPerTokenUnit,
- uint256 _balancePerTokenUnit,
- uint256 _lendingFee
- ) external onlyOwnerOrTokenSwap() {
- (uint256 year, uint256 month, uint256 day) = DateTimeLibrary
- .timestampToDate(block.timestamp);
- uint256 today = year * 10000 + month * 100 + day;
- accounting[today].push(
- Accounting(
- _price,
- _cashPositionPerTokenUnit,
- _balancePerTokenUnit,
- _lendingFee
- )
- );
- lastActivityDay = today;
- emit AccountingValuesSet(today);
+ // @dev Sets new coldStorage
+ // @param _newColdStorage Address for new cold storage wallet
+ function setColdStorage(address _newColdStorage) public onlyOwner {
+ require(_newColdStorage != address(0), "address cannot be empty");
+ coldStorage = _newColdStorage;
}
- // @dev Set accounting values for the day
- function setAccountingForLastActivityDay(
- uint256 _price,
- uint256 _cashPositionPerTokenUnit,
- uint256 _balancePerTokenUnit,
- uint256 _lendingFee
- ) external onlyOwnerOrTokenSwap() {
- accounting[lastActivityDay].push(
- Accounting(
- _price,
- _cashPositionPerTokenUnit,
- _balancePerTokenUnit,
- _lendingFee
- )
+ // @dev Sets percentage of funds to stay in contract. Owner only
+ // @param _newPercentageOfFundsForColdStorage List with two elements referencing percentage of funds for cold storage as a fraction
+ // e.g. 1/2 is [1,2]
+ function setPercentageOfFundsForColdStorage(
+ uint256[2] memory _newPercentageOfFundsForColdStorage
+ ) public onlyOwner {
+ require(
+ _newPercentageOfFundsForColdStorage[1] != 0,
+ "denominator should not be zero"
);
- emit AccountingValuesSet(lastActivityDay);
- }
-
- // @dev Set last rebalance information
- function setMinRebalanceAmount(uint256 _minRebalanceAmount)
- external
- onlyOwner
- {
- minRebalanceAmount = _minRebalanceAmount;
-
- emit RebalanceValuesSet(minRebalanceAmount);
- }
-
- // @dev Set last rebalance information
- function setManagementFee(uint256 _managementFee) external onlyOwner {
- managementFee = _managementFee;
- emit ManagementFeeValuesSet(managementFee);
- }
-
- // @dev Returns price
- function getPrice() public view returns (uint256 price) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .price;
- }
-
- // @dev Returns cash position amount
- function getCashPositionPerTokenUnit()
- public
- view
- returns (uint256 amount)
- {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .cashPositionPerTokenUnit;
- }
-
- // @dev Returns borrowed crypto amount
- function getBalancePerTokenUnit() public view returns (uint256 amount) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .balancePerTokenUnit;
- }
-
- // @dev Returns lending fee
- function getLendingFee() public view returns (uint256 lendingRate) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .lendingFee;
- }
-
- // @dev Returns lending fee
- function getManagementFee() public view returns (uint256 lendingRate) {
- return managementFee;
- }
-
- // @dev Sets last minting fee
- function setLastMintingFee(uint256 _mintingFee) public onlyOwner {
- mintingFee[~uint256(0)] = _mintingFee;
- }
-
- // @dev Adds minting fee
- function addMintingFeeBracket(uint256 _mintingFeeLimit, uint256 _mintingFee)
- public
- onlyOwner
- {
require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
- "New minting fee bracket needs to be bigger then last one"
+ _newPercentageOfFundsForColdStorage[0] <=
+ _newPercentageOfFundsForColdStorage[1],
+ "cannot set more than 100% for coldstorage"
);
- mintingFeeBracket.push(_mintingFeeLimit);
- mintingFee[_mintingFeeLimit] = _mintingFee;
- }
-
- // @dev Deletes last minting fee
- function deleteLastMintingFeeBracket() public onlyOwner {
- delete mintingFee[mintingFeeBracket[mintingFeeBracket.length - 1]];
- mintingFeeBracket.length--;
- }
+ percentageOfFundsForColdStorage[0] = _newPercentageOfFundsForColdStorage[0];
+ percentageOfFundsForColdStorage[1] = _newPercentageOfFundsForColdStorage[1];
- // @dev Changes minting fee
- function changeMintingLimit(
- uint256 _position,
- uint256 _mintingFeeLimit,
- uint256 _mintingFee
- ) public onlyOwner {
- require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
- "New minting fee bracket needs to be bigger then last one"
+ emit SetPercentageOfFundsForColdStorageEvent(
+ _newPercentageOfFundsForColdStorage
);
- if (_position != 0) {
- require(
- _mintingFeeLimit > mintingFeeBracket[_position - 1],
- "New minting fee bracket needs to be bigger then last one"
- );
- }
- if (_position < mintingFeeBracket.length - 1) {
- require(
- _mintingFeeLimit < mintingFeeBracket[_position + 1],
- "New minting fee bracket needs to be smaller then next one"
- );
- }
- mintingFeeBracket[_position] = _mintingFeeLimit;
- mintingFee[_mintingFeeLimit] = _mintingFee;
}
- function getMintingFee(uint256 cash) public view returns (uint256) {
- // Define Start + End Index
- uint256 startIndex = 0;
- uint256 endIndex = mintingFeeBracket.length - 1;
- uint256 middleIndex = endIndex / 2;
+ // ############################################## Add/Remove ############################################## //
+ // ############################################## Token ################################################### //
+ // ############################################## Manager ################################################# //
- if (cash <= mintingFeeBracket[middleIndex]) {
- endIndex = middleIndex;
- } else {
- startIndex = middleIndex + 1;
- }
-
- for (uint256 i = startIndex; i <= endIndex; i++) {
- if (cash <= mintingFeeBracket[i]) {
- return mintingFee[mintingFeeBracket[i]];
- }
- }
- return mintingFee[~uint256(0)];
+ function addTokenManager(address tokenManager) public onlyOwner {
+ require(tokenManager != address(0), "adddress must not be empty");
+ approvedTokenManagers[tokenManager] = true;
}
-}
-
-// File: contracts/Abstract/InterfaceStorage.sol
-
-pragma solidity ^0.5.0;
-
-
-interface InterfaceStorage {
- function whitelistedAddresses(address) external view returns (bool);
-}
-
-// File: contracts/KYCVerifier.sol
-
-pragma solidity ^0.5.0;
-
-
-
-contract KYCVerifier is Initializable {
- InterfaceStorage public persistentStorage;
-
- function initialize(address _persistentStorage) public initializer {
- persistentStorage = InterfaceStorage(_persistentStorage);
+ function removeTokenManager(address tokenManager) public onlyOwner {
+ require(tokenManager != address(0), "adddress must not be empty");
+ delete approvedTokenManagers[tokenManager];
}
- function isAddressWhitelisted(address userAddress)
+ function isTokenManager(address potentialAddress)
public
view
returns (bool)
{
- return persistentStorage.whitelistedAddresses(userAddress);
+ require(potentialAddress != address(0), "adddress must not be empty");
+ return approvedTokenManagers[potentialAddress];
}
-}
-
-// File: contracts/CashPool.sol
-
-pragma solidity ^0.5.0;
-
-
-
-
-
-
-contract CashPool is Ownable {
- using SafeMath for uint256;
- KYCVerifier public kycVerifier;
- PersistentStorage public persistentStorage;
-
- uint256[2] public percentageOfFundsForColdStorage;
- address public coldStorage;
-
- event SetPercentageOfFundsForColdStorageEvent(
- uint256[2] newPercentageOfFundsForColdStorage
- );
-
- function initialize(
- address ownerAddress,
- address _kycVerifier,
- address _persistentStorage,
- address _coldStorage,
- uint256[2] memory _percentageOfFundsForColdStorage
- ) public initializer {
+ modifier onlyOwnerOrTokenSwap() {
require(
- ownerAddress != address(0) &&
- _kycVerifier != address(0) &&
- _coldStorage != address(0) &&
- _percentageOfFundsForColdStorage[1] != 0,
- "params variables cannot be empty but _percentageOfFundsForColdStorage[0]"
+ isOwner() || approvedTokenManagers[_msgSender()] == true,
+ "caller is not the owner or an approved token swap manager"
);
- initialize(ownerAddress);
- kycVerifier = KYCVerifier(_kycVerifier);
- persistentStorage = PersistentStorage(_persistentStorage);
- coldStorage = _coldStorage;
- percentageOfFundsForColdStorage = _percentageOfFundsForColdStorage;
+ _;
}
- function getBalance(address _token) public view returns (uint256) {
+ // @dev Move tokens out of cash pool
+ // @param _token ERC20 address
+ // @param destinationAddress address to send to
+ // @param orderAmount amount to transfer from cash pool
+ function moveTokenfromPool(
+ address _token,
+ address destinationAddress,
+ uint256 orderAmount
+ ) public onlyOwnerOrTokenSwap() returns (bool) {
InterfaceInverseToken token_ = InterfaceInverseToken(_token);
- uint256 tokenBalance = token_.balanceOf(address(this));
- return tokenBalance;
+
+ token_.transfer(destinationAddress, orderAmount);
+ return true;
}
// @dev Move tokens to cash pool
@@ -1551,57 +589,4 @@ contract CashPool is Ownable {
return true;
}
-
- // @dev Move tokens out of cash pool
- // @param _token ERC20 address
- // @param destinationAddress address to send to
- // @param orderAmount amount to transfer from cash pool
- function moveTokenfromPool(
- address _token,
- address destinationAddress,
- uint256 orderAmount
- ) public onlyOwnerOrTokenSwap() returns (bool) {
- InterfaceInverseToken token_ = InterfaceInverseToken(_token);
-
- token_.transfer(destinationAddress, orderAmount);
- return true;
- }
-
- modifier onlyOwnerOrTokenSwap() {
- require(
- isOwner() || _msgSender() == persistentStorage.tokenSwapManager(),
- "caller is not the owner or token swap manager"
- );
- _;
- }
-
- // @dev Sets new coldStorage
- // @param _newColdStorage Address for new cold storage wallet
- function setColdStorage(address _newColdStorage) public onlyOwner {
- require(_newColdStorage != address(0), "address cannot be empty");
- coldStorage = _newColdStorage;
- }
-
- // @dev Sets percentage of funds to stay in contract. Owner only
- // @param _newPercentageOfFundsForColdStorage List with two elements referencing percentage of funds for cold storage as a fraction
- // e.g. 1/2 is [1,2]
- function setPercentageOfFundsForColdStorage(
- uint256[2] memory _newPercentageOfFundsForColdStorage
- ) public onlyOwner {
- require(
- _newPercentageOfFundsForColdStorage[1] != 0,
- "denominator should not be zero"
- );
- require(
- _newPercentageOfFundsForColdStorage[0] <=
- _newPercentageOfFundsForColdStorage[1],
- "cannot set more than 100% for coldstorage"
- );
- percentageOfFundsForColdStorage[0] = _newPercentageOfFundsForColdStorage[0];
- percentageOfFundsForColdStorage[1] = _newPercentageOfFundsForColdStorage[1];
-
- emit SetPercentageOfFundsForColdStorageEvent(
- _newPercentageOfFundsForColdStorage
- );
- }
}
diff --git a/flats/CompositionCalculator.sol b/flats/CompositionCalculator.sol
index 4fce2e6..3f97744 100644
--- a/flats/CompositionCalculator.sol
+++ b/flats/CompositionCalculator.sol
@@ -222,7 +222,7 @@ library SafeMath {
}
}
-// File: contracts/utils/Math.sol
+// File: contracts/short-tokens/utils/Math.sol
/// Math.sol -- mixin for inline numerical wizardry
@@ -319,7 +319,7 @@ library DSMath {
}
}
-// File: contracts/utils/DateTimeLibrary.sol
+// File: contracts/short-tokens/utils/DateTimeLibrary.sol
pragma solidity ^0.5.0;
@@ -926,7 +926,7 @@ contract Ownable is Initializable, Context {
uint256[50] private ______gap;
}
-// File: contracts/PersistentStorage.sol
+// File: contracts/short-tokens/PersistentStorage.sol
pragma solidity ^0.5.0;
@@ -957,11 +957,13 @@ contract PersistentStorage is Ownable {
uint256 public lastActivityDay;
uint256 public minRebalanceAmount;
- uint256 private managementFee;
+ uint256 public managementFee;
+ uint256 public minimumMintingFee;
+ uint256 public minimumTrade;
- mapping(uint256 => Accounting[]) private accounting;
+ uint8 public balancePrecision;
- mapping(address => bool) public whitelistedAddresses;
+ mapping(uint256 => Accounting[]) private accounting;
uint256[] public mintingFeeBracket;
mapping(uint256 => uint256) public mintingFee;
@@ -977,16 +979,19 @@ contract PersistentStorage is Ownable {
function initialize(
address ownerAddress,
uint256 _managementFee,
- uint256 _minRebalanceAmount
+ uint256 _minRebalanceAmount,
+ uint8 _balancePrecision,
+ uint256 _lastMintingFee,
+ uint256 _minimumMintingFee,
+ uint256 _minimumTrade
) public initializer {
initialize(ownerAddress);
managementFee = _managementFee;
minRebalanceAmount = _minRebalanceAmount;
- mintingFeeBracket.push(50000 ether);
- mintingFeeBracket.push(100000 ether);
- mintingFee[50000 ether] = 3 ether / 1000; //0.3%
- mintingFee[100000 ether] = 2 ether / 1000; //0.2%
- mintingFee[~uint256(0)] = 1 ether / 1000; //0.1% all values higher
+ mintingFee[~uint256(0)] = _lastMintingFee;
+ balancePrecision = _balancePrecision;
+ minimumMintingFee = _minimumMintingFee;
+ minimumTrade = _minimumTrade;
}
function setTokenSwapManager(address _tokenSwapManager) public onlyOwner {
@@ -1018,6 +1023,14 @@ contract PersistentStorage is Ownable {
_;
}
+ modifier onlyOwnerOrBridge() {
+ require(
+ isOwner() || _msgSender() == bridge,
+ "caller is not the owner or bridge"
+ );
+ _;
+ }
+
function setDelayedRedemptionsByUser(
uint256 amountToRedeem,
address whitelistedAddress
@@ -1025,14 +1038,6 @@ contract PersistentStorage is Ownable {
delayedRedemptionsByUser[whitelistedAddress] = amountToRedeem;
}
- function getDelayedRedemptionsByUser(address whitelistedAddress)
- public
- view
- returns (uint256)
- {
- return delayedRedemptionsByUser[whitelistedAddress];
- }
-
/*
* Saves order in mapping (address => Order[]) orderByUser
* overwrite == false, append to Order[]
@@ -1145,34 +1150,6 @@ contract PersistentStorage is Ownable {
);
}
- // @dev Set whitelisted addresses
- function setWhitelistedAddress(address adddressToAdd) public onlyOwner {
- require(adddressToAdd != address(0), "adddress must not be empty");
-
- whitelistedAddresses[adddressToAdd] = true;
- }
-
- // @dev Remove whitelisted addresses
- function removeWhitelistedAddress(address addressToRemove)
- public
- onlyOwner
- {
- require(
- whitelistedAddresses[addressToRemove],
- "address must be added to be removed allowed"
- );
-
- delete whitelistedAddresses[addressToRemove];
- }
-
- // @dev Updates whitelisted addresses
- function updateWhitelistedAddress(address oldAddress, address newAddress)
- public
- {
- removeWhitelistedAddress(oldAddress);
- setWhitelistedAddress(newAddress);
- }
-
// @dev Get accounting values for a specific day
// @param date format as 20200123 for 23th of January 2020
function getAccounting(uint256 date)
@@ -1277,11 +1254,6 @@ contract PersistentStorage is Ownable {
.lendingFee;
}
- // @dev Returns lending fee
- function getManagementFee() public view returns (uint256 lendingRate) {
- return managementFee;
- }
-
// @dev Sets last minting fee
function setLastMintingFee(uint256 _mintingFee) public onlyOwner {
mintingFee[~uint256(0)] = _mintingFee;
@@ -1293,7 +1265,9 @@ contract PersistentStorage is Ownable {
onlyOwner
{
require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
+ mintingFeeBracket.length == 0 ||
+ _mintingFeeLimit >
+ mintingFeeBracket[mintingFeeBracket.length - 1],
"New minting fee bracket needs to be bigger then last one"
);
mintingFeeBracket.push(_mintingFeeLimit);
@@ -1351,9 +1325,24 @@ contract PersistentStorage is Ownable {
}
return mintingFee[~uint256(0)];
}
+
+ // @dev Sets last balance precision
+ function setLastPrecision(uint8 _balancePrecision) public onlyOwner {
+ balancePrecision = _balancePrecision;
+ }
+
+ // @dev Sets minimum minting fee
+ function setMinimumMintingFee(uint256 _minimumMintingFee) public onlyOwner {
+ minimumMintingFee = _minimumMintingFee;
+ }
+
+ // @dev Sets minimum trade value
+ function setMinimumTrade(uint256 _minimumTrade) public onlyOwner {
+ minimumTrade = _minimumTrade;
+ }
}
-// File: contracts/Abstract/InterfaceInverseToken.sol
+// File: contracts/short-tokens/Abstract/InterfaceInverseToken.sol
pragma solidity ^0.5.0;
@@ -1444,7 +1433,7 @@ interface InterfaceInverseToken {
);
}
-// File: contracts/CompositionCalculator.sol
+// File: contracts/short-tokens/CompositionCalculator.sol
pragma solidity ^0.5.0;
@@ -1561,6 +1550,7 @@ contract CompositionCalculator is Initializable {
* @param _lendingFee The yearly average lending fee for borrowed balance
* @param _days The days since the last fee calculation (Natural number)
* @param _minRebalanceAmount The minimum amount to rebalance
+ * @param _changeInBalancePrecision The change in balance precision
*/
function calculatePCF(
uint256 _cashPosition,
@@ -1568,7 +1558,8 @@ contract CompositionCalculator is Initializable {
uint256 _price,
uint256 _lendingFee,
uint256 _days,
- uint256 _minRebalanceAmount
+ uint256 _minRebalanceAmount,
+ uint256 _changeInBalancePrecision
)
public
pure
@@ -1614,12 +1605,16 @@ contract CompositionCalculator is Initializable {
_balance,
_price
);
-
+ changeInBalance = floor(changeInBalance, _changeInBalancePrecision);
if (changeInBalance < _minRebalanceAmount) {
changeInBalance = 0;
endBalance = _balance;
}
-
+ endBalance = addOrSub(
+ _balance, //cashPositionWithoutFee
+ changeInBalance,
+ isChangeInBalanceNeg
+ );
//result
endCashPosition = addOrSub(
endCashPosition, //cashPositionWithoutFee
@@ -1639,6 +1634,7 @@ contract CompositionCalculator is Initializable {
* @param _price The momentary price of the crypto
* @param _lendingFee The yearly average lending fee for borrowed balance
* @param _days The days since the last fee calculation (Natural number)
+ * @param _changeInBalancePrecision The change in balance precision
*/
function calculatePCFWithoutMin(
//getDeliverables
@@ -1646,7 +1642,8 @@ contract CompositionCalculator is Initializable {
uint256 _balance,
uint256 _price,
uint256 _lendingFee,
- uint256 _days
+ uint256 _days,
+ uint256 _changeInBalancePrecision
)
public
pure
@@ -1666,7 +1663,8 @@ contract CompositionCalculator is Initializable {
_price,
_lendingFee,
_days,
- 0
+ 0,
+ _changeInBalancePrecision
);
}
@@ -1688,22 +1686,15 @@ contract CompositionCalculator is Initializable {
) public pure returns (uint256 tokenAmountCreated) {
require(_spotPrice != 0, "Price cant be zero");
require(_totalTokenSupply != 0, "Token supply cant be zero");
-
uint256 netTokenValue = getNetTokenValue(
_cashPosition,
_balance,
_spotPrice
);
- uint256 netTokenValueTimesTokenAmount = DSMath.wmul(
- netTokenValue,
- _totalTokenSupply
- );
- require(
- netTokenValueTimesTokenAmount != 0,
- "netTokenValueTimesTokenAmount cant be zero"
- );
- tokenAmountCreated = DSMath.wdiv(_cash, netTokenValueTimesTokenAmount);
+ uint256 cashTimesTokenAmount = DSMath.wmul(_cash, _totalTokenSupply);
+
+ tokenAmountCreated = DSMath.wdiv(cashTimesTokenAmount, netTokenValue);
}
/**
@@ -1744,13 +1735,17 @@ contract CompositionCalculator is Initializable {
* @dev Returns cash without fee
* @param _cash The cash provided to create token
* @param _mintingFee The minting fee to remove
+ * @param _minimumMintingFee The minimum minting fee in $ to remove
*/
- function removeMintingFeeFromCash(uint256 _cash, uint256 _mintingFee)
- public
- pure
- returns (uint256 cashAfterFee)
- {
+ function removeMintingFeeFromCash(
+ uint256 _cash,
+ uint256 _mintingFee,
+ uint256 _minimumMintingFee
+ ) public pure returns (uint256 cashAfterFee) {
uint256 creationFeeInCash = DSMath.wmul(_cash, _mintingFee);
+ if (_minimumMintingFee > creationFeeInCash) {
+ creationFeeInCash = _minimumMintingFee;
+ }
cashAfterFee = DSMath.sub(_cash, creationFeeInCash);
}
@@ -1792,7 +1787,12 @@ contract CompositionCalculator is Initializable {
returns (uint256 cashAfterFee)
{
uint256 creationFee = persistentStorage.getMintingFee(_cash);
- cashAfterFee = removeMintingFeeFromCash(_cash, creationFee);
+ uint256 minimumMintingFee = persistentStorage.minimumMintingFee();
+ cashAfterFee = removeMintingFeeFromCash(
+ _cash,
+ creationFee,
+ minimumMintingFee
+ );
}
/**
@@ -1803,9 +1803,11 @@ contract CompositionCalculator is Initializable {
function getCurrentTokenAmountCreatedByCash(
//Create
uint256 _cash,
- uint256 _spotPrice
+ uint256 _spotPrice,
+ uint256 _gasFee
) public view returns (uint256 tokenAmountCreated) {
- uint256 cashAfterFee = removeCurrentMintingFeeFromCash(_cash);
+ uint256 cashAfterGas = DSMath.sub(_cash, _gasFee);
+ uint256 cashAfterFee = removeCurrentMintingFeeFromCash(cashAfterGas);
tokenAmountCreated = getTokenAmountCreatedByCash(
persistentStorage.getCashPositionPerTokenUnit(),
persistentStorage.getBalancePerTokenUnit(),
@@ -1823,11 +1825,9 @@ contract CompositionCalculator is Initializable {
function getCurrentCashAmountCreatedByToken(
//Redeem
uint256 _tokenAmount,
- uint256 _spotPrice
+ uint256 _spotPrice,
+ uint256 _gasFee
) public view returns (uint256 cashFromTokenRedeem) {
- uint256 totalTokenSupply = inverseToken.totalSupply();
- require(totalTokenSupply != 0, "Token supply cant be zero");
-
uint256 lendingFee = persistentStorage.getLendingFee();
uint256 daysSinceLastRebalance = getDaysSinceLastRebalance() + 1;
@@ -1855,6 +1855,8 @@ contract CompositionCalculator is Initializable {
cashFromTokenRedeem = removeCurrentMintingFeeFromCash(
DSMath.sub(cashFromToken, fiatForLendingFee)
);
+
+ cashFromTokenRedeem = DSMath.sub(cashFromTokenRedeem, _gasFee);
}
function getDaysSinceLastRebalance()
@@ -1951,7 +1953,6 @@ contract CompositionCalculator is Initializable {
);
uint256 daysSinceLastRebalance = getDaysSinceLastRebalance();
uint256 minRebalanceAmount = persistentStorage.minRebalanceAmount();
-
return
calculatePCF(
cashPosition,
@@ -1959,7 +1960,8 @@ contract CompositionCalculator is Initializable {
_price,
_lendingFee,
daysSinceLastRebalance,
- minRebalanceAmount
+ minRebalanceAmount,
+ persistentStorage.balancePrecision()
);
}
@@ -1985,4 +1987,12 @@ contract CompositionCalculator is Initializable {
function wdiv(uint256 x, uint256 y) external pure returns (uint256 z) {
z = DSMath.wdiv(x, y);
}
+
+ function floorBalance(uint256 a) public view returns (uint256) {
+ return floor(a, persistentStorage.balancePrecision());
+ }
+
+ function floor(uint256 a, uint256 precision) public pure returns (uint256) {
+ return (a / 10**(precision)) * 10**(precision);
+ }
}
diff --git a/flats/InverseToken.sol b/flats/InverseToken.sol
index 80ea3ae..0b16a90 100644
--- a/flats/InverseToken.sol
+++ b/flats/InverseToken.sol
@@ -1,5 +1,5 @@
-// File: contracts/Token/Sender.sol
+// File: contracts/shared/Token/Sender.sol
pragma solidity ^0.5.0;
@@ -287,1025 +287,69 @@ contract Ownable is Initializable, Context {
uint256[50] private ______gap;
}
-// File: contracts/utils/DateTimeLibrary.sol
+// File: contracts/short-tokens/Abstract/InterfaceStorage.sol
pragma solidity ^0.5.0;
-// ----------------------------------------------------------------------------
-// BokkyPooBah's DateTime Library v1.01
-//
-// A gas-efficient Solidity date and time library
-//
-// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
-//
-// Tested date range 1970/01/01 to 2345/12/31
-//
-// Conventions:
-// Unit | Range | Notes
-// :-------- |:-------------:|:-----
-// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
-// year | 1970 ... 2345 |
-// month | 1 ... 12 |
-// day | 1 ... 31 |
-// hour | 0 ... 23 |
-// minute | 0 ... 59 |
-// second | 0 ... 59 |
-// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday
-//
-//
-// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.
-// ----------------------------------------------------------------------------
-
-library DateTimeLibrary {
- uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;
- uint256 constant SECONDS_PER_HOUR = 60 * 60;
- uint256 constant SECONDS_PER_MINUTE = 60;
- int256 constant OFFSET19700101 = 2440588;
-
- uint256 constant DOW_MON = 1;
- uint256 constant DOW_TUE = 2;
- uint256 constant DOW_WED = 3;
- uint256 constant DOW_THU = 4;
- uint256 constant DOW_FRI = 5;
- uint256 constant DOW_SAT = 6;
- uint256 constant DOW_SUN = 7;
-
- // ------------------------------------------------------------------------
- // Calculate the number of days from 1970/01/01 to year/month/day using
- // the date conversion algorithm from
- // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
- // and subtracting the offset 2440588 so that 1970/01/01 is day 0
- //
- // days = day
- // - 32075
- // + 1461 * (year + 4800 + (month - 14) / 12) / 4
- // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
- // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
- // - offset
- // ------------------------------------------------------------------------
- function _daysFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 _days)
- {
- require(year >= 1970);
- int256 _year = int256(year);
- int256 _month = int256(month);
- int256 _day = int256(day);
-
- int256 __days = _day -
- 32075 +
- (1461 * (_year + 4800 + (_month - 14) / 12)) /
- 4 +
- (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /
- 12 -
- (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /
- 4 -
- OFFSET19700101;
-
- _days = uint256(__days);
- }
-
- // ------------------------------------------------------------------------
- // Calculate year/month/day from the number of days since 1970/01/01 using
- // the date conversion algorithm from
- // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
- // and adding the offset 2440588 so that 1970/01/01 is day 0
- //
- // int L = days + 68569 + offset
- // int N = 4 * L / 146097
- // L = L - (146097 * N + 3) / 4
- // year = 4000 * (L + 1) / 1461001
- // L = L - 1461 * year / 4 + 31
- // month = 80 * L / 2447
- // dd = L - 2447 * month / 80
- // L = month / 11
- // month = month + 2 - 12 * L
- // year = 100 * (N - 49) + year + L
- // ------------------------------------------------------------------------
- function _daysToDate(uint256 _days)
- internal
- pure
- returns (uint256 year, uint256 month, uint256 day)
- {
- int256 __days = int256(_days);
-
- int256 L = __days + 68569 + OFFSET19700101;
- int256 N = (4 * L) / 146097;
- L = L - (146097 * N + 3) / 4;
- int256 _year = (4000 * (L + 1)) / 1461001;
- L = L - (1461 * _year) / 4 + 31;
- int256 _month = (80 * L) / 2447;
- int256 _day = L - (2447 * _month) / 80;
- L = _month / 11;
- _month = _month + 2 - 12 * L;
- _year = 100 * (N - 49) + _year + L;
-
- year = uint256(_year);
- month = uint256(_month);
- day = uint256(_day);
- }
-
- function daysFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 daysSinceDate)
- {
- daysSinceDate = _daysFromDate(year, month, day);
- }
- function timestampFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 timestamp)
- {
- timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
- }
- function timestampFromDateTime(
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- ) internal pure returns (uint256 timestamp) {
- timestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- hour *
- SECONDS_PER_HOUR +
- minute *
- SECONDS_PER_MINUTE +
- second;
- }
- function timestampToDate(uint256 timestamp)
- internal
- pure
- returns (uint256 year, uint256 month, uint256 day)
- {
- (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function timestampToDateTime(uint256 timestamp)
- internal
- pure
- returns (
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- )
- {
- (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- uint256 secs = timestamp % SECONDS_PER_DAY;
- hour = secs / SECONDS_PER_HOUR;
- secs = secs % SECONDS_PER_HOUR;
- minute = secs / SECONDS_PER_MINUTE;
- second = secs % SECONDS_PER_MINUTE;
- }
-
- function isValidDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (bool valid)
- {
- if (year >= 1970 && month > 0 && month <= 12) {
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > 0 && day <= daysInMonth) {
- valid = true;
- }
- }
- }
- function isValidDateTime(
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- ) internal pure returns (bool valid) {
- if (isValidDate(year, month, day)) {
- if (hour < 24 && minute < 60 && second < 60) {
- valid = true;
- }
- }
- }
- function isLeapYear(uint256 timestamp)
- internal
- pure
- returns (bool leapYear)
- {
- (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- leapYear = _isLeapYear(year);
- }
- function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {
- leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
- }
- function isWeekDay(uint256 timestamp) internal pure returns (bool weekDay) {
- weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
- }
- function isWeekEnd(uint256 timestamp) internal pure returns (bool weekEnd) {
- weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
- }
- function getDaysInMonth(uint256 timestamp)
- internal
- pure
- returns (uint256 daysInMonth)
- {
- (uint256 year, uint256 month, ) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- daysInMonth = _getDaysInMonth(year, month);
- }
- function _getDaysInMonth(uint256 year, uint256 month)
- internal
- pure
- returns (uint256 daysInMonth)
- {
- if (
- month == 1 ||
- month == 3 ||
- month == 5 ||
- month == 7 ||
- month == 8 ||
- month == 10 ||
- month == 12
- ) {
- daysInMonth = 31;
- } else if (month != 2) {
- daysInMonth = 30;
- } else {
- daysInMonth = _isLeapYear(year) ? 29 : 28;
- }
- }
- // 1 = Monday, 7 = Sunday
- function getDayOfWeek(uint256 timestamp)
- internal
- pure
- returns (uint256 dayOfWeek)
- {
- uint256 _days = timestamp / SECONDS_PER_DAY;
- dayOfWeek = ((_days + 3) % 7) + 1;
- }
-
- function getYear(uint256 timestamp) internal pure returns (uint256 year) {
- (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getMonth(uint256 timestamp) internal pure returns (uint256 month) {
- (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getDay(uint256 timestamp) internal pure returns (uint256 day) {
- (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getHour(uint256 timestamp) internal pure returns (uint256 hour) {
- uint256 secs = timestamp % SECONDS_PER_DAY;
- hour = secs / SECONDS_PER_HOUR;
- }
- function getMinute(uint256 timestamp)
- internal
- pure
- returns (uint256 minute)
- {
- uint256 secs = timestamp % SECONDS_PER_HOUR;
- minute = secs / SECONDS_PER_MINUTE;
- }
- function getSecond(uint256 timestamp)
- internal
- pure
- returns (uint256 second)
- {
- second = timestamp % SECONDS_PER_MINUTE;
- }
-
- function addYears(uint256 timestamp, uint256 _years)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- year += _years;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp >= timestamp);
- }
- function addMonths(uint256 timestamp, uint256 _months)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- month += _months;
- year += (month - 1) / 12;
- month = ((month - 1) % 12) + 1;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp >= timestamp);
- }
- function addDays(uint256 timestamp, uint256 _days)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _days * SECONDS_PER_DAY;
- require(newTimestamp >= timestamp);
- }
- function addHours(uint256 timestamp, uint256 _hours)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
- require(newTimestamp >= timestamp);
- }
- function addMinutes(uint256 timestamp, uint256 _minutes)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
- require(newTimestamp >= timestamp);
- }
- function addSeconds(uint256 timestamp, uint256 _seconds)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _seconds;
- require(newTimestamp >= timestamp);
- }
-
- function subYears(uint256 timestamp, uint256 _years)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- year -= _years;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp <= timestamp);
- }
- function subMonths(uint256 timestamp, uint256 _months)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- uint256 yearMonth = year * 12 + (month - 1) - _months;
- year = yearMonth / 12;
- month = (yearMonth % 12) + 1;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp <= timestamp);
- }
- function subDays(uint256 timestamp, uint256 _days)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _days * SECONDS_PER_DAY;
- require(newTimestamp <= timestamp);
- }
- function subHours(uint256 timestamp, uint256 _hours)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
- require(newTimestamp <= timestamp);
- }
- function subMinutes(uint256 timestamp, uint256 _minutes)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
- require(newTimestamp <= timestamp);
- }
- function subSeconds(uint256 timestamp, uint256 _seconds)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _seconds;
- require(newTimestamp <= timestamp);
- }
-
- function diffYears(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _years)
- {
- require(fromTimestamp <= toTimestamp);
- (uint256 fromYear, , ) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
- (uint256 toYear, , ) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
- _years = toYear - fromYear;
- }
- function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _months)
- {
- require(fromTimestamp <= toTimestamp);
- (uint256 fromYear, uint256 fromMonth, ) = _daysToDate(
- fromTimestamp / SECONDS_PER_DAY
- );
- (uint256 toYear, uint256 toMonth, ) = _daysToDate(
- toTimestamp / SECONDS_PER_DAY
- );
- _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
- }
- function diffDays(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _days)
- {
- require(fromTimestamp <= toTimestamp);
- _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
- }
- function diffHours(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _hours)
- {
- require(fromTimestamp <= toTimestamp);
- _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
- }
- function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _minutes)
- {
- require(fromTimestamp <= toTimestamp);
- _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
- }
- function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _seconds)
- {
- require(fromTimestamp <= toTimestamp);
- _seconds = toTimestamp - fromTimestamp;
- }
-}
-
-// File: contracts/utils/Math.sol
-
-/// Math.sol -- mixin for inline numerical wizardry
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
-
-pragma solidity ^0.5.0;
-
-
-library DSMath {
-
- // --- Unsigned Math ----
- function add(uint x, uint y) internal pure returns (uint z) {
- require((z = x + y) >= x, "ds-math-add-overflow");
- }
- function sub(uint x, uint y) internal pure returns (uint z) {
- require((z = x - y) <= x, "ds-math-sub-underflow");
- }
- function mul(uint x, uint y) internal pure returns (uint z) {
- require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
- }
-
-
- function min(uint x, uint y) internal pure returns (uint z) {
- return x <= y ? x : y;
- }
- function max(uint x, uint y) internal pure returns (uint z) {
- return x >= y ? x : y;
- }
- function imin(int x, int y) internal pure returns (int z) {
- return x <= y ? x : y;
- }
- function imax(int x, int y) internal pure returns (int z) {
- return x >= y ? x : y;
- }
-
- // --- Precise Math ---
-
- uint public constant WAD = 10 ** 18;
- uint public constant RAY = 10 ** 27;
-
- function wmul(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, y), WAD / 2) / WAD;
- }
- function rmul(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, y), RAY / 2) / RAY;
- }
- function wdiv(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, WAD), y / 2) / y;
- }
- function rdiv(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, RAY), y / 2) / y;
- }
- function ray(uint _wad) internal pure returns (uint) {
- return mul(_wad, uint(10 ** 9));
- }
+interface InterfaceStorage {
+ function whitelistedAddresses(address) external view returns (bool);
- // This famous algorithm is called "exponentiation by squaring"
- // and calculates x^n with x as fixed-point and n as regular unsigned.
- //
- // It's O(log n), instead of O(n) for naive repeated multiplication.
- //
- // These facts are why it works:
- //
- // If n is even, then x^n = (x^2)^(n/2).
- // If n is odd, then x^n = x * x^(n-1),
- // and applying the equation for even x gives
- // x^n = x * (x^2)^((n-1) / 2).
- //
- // Also, EVM division is flooring and
- // floor[(n-1) / 2] = floor[n / 2].
- //
- function rpow(uint x, uint n) internal pure returns (uint z) {
- z = n % 2 != 0 ? x : RAY;
-
- for (n /= 2; n != 0; n /= 2) {
- x = rmul(x, x);
-
- if (n % 2 != 0) {
- z = rmul(z, x);
- }
- }
- }
-}
+ function isPaused() external view returns (bool);
-// File: contracts/PersistentStorage.sol
+ function isShutdown() external view returns (bool);
-pragma solidity ^0.5.0;
+ function tokenSwapManager() external view returns (address);
+ function bridge() external view returns (address);
+ function getCashPositionPerTokenUnit() external view returns (uint256);
+ function getBalancePerTokenUnit() external view returns (uint256);
+ function getMintingFee(uint256 cash) external view returns (uint256);
-contract PersistentStorage is Ownable {
- address public tokenSwapManager;
- address public bridge;
+ function getPrice() external view returns (uint256);
- bool public isPaused;
- bool public isShutdown;
+ function minimumMintingFee() external view returns (uint256);
- struct Accounting {
- uint256 price;
- uint256 cashPositionPerTokenUnit;
- uint256 balancePerTokenUnit;
- uint256 lendingFee;
- }
+ function getLendingFee() external view returns (uint256);
- struct Order {
- string orderType;
- uint256 tokensGiven;
- uint256 tokensRecieved;
- uint256 avgBlendedFee;
- }
-
- uint256 public lastActivityDay;
- uint256 public minRebalanceAmount;
- uint256 private managementFee;
-
- mapping(uint256 => Accounting[]) private accounting;
-
- mapping(address => bool) public whitelistedAddresses;
-
- uint256[] public mintingFeeBracket;
- mapping(uint256 => uint256) public mintingFee;
+ function minRebalanceAmount() external view returns (uint8);
- Order[] public allOrders;
- mapping(address => Order[]) public orderByUser;
- mapping(address => uint256) public delayedRedemptionsByUser;
-
- event AccountingValuesSet(uint256 today);
- event RebalanceValuesSet(uint256 newMinRebalanceAmount);
- event ManagementFeeValuesSet(uint256 newManagementFee);
-
- function initialize(
- address ownerAddress,
- uint256 _managementFee,
- uint256 _minRebalanceAmount
- ) public initializer {
- initialize(ownerAddress);
- managementFee = _managementFee;
- minRebalanceAmount = _minRebalanceAmount;
- mintingFeeBracket.push(50000 ether);
- mintingFeeBracket.push(100000 ether);
- mintingFee[50000 ether] = 3 ether / 1000; //0.3%
- mintingFee[100000 ether] = 2 ether / 1000; //0.2%
- mintingFee[~uint256(0)] = 1 ether / 1000; //0.1% all values higher
- }
-
- function setTokenSwapManager(address _tokenSwapManager) public onlyOwner {
- require(_tokenSwapManager != address(0), "adddress must not be empty");
- tokenSwapManager = _tokenSwapManager;
- }
-
- function setBridge(address _bridge) public onlyOwner {
- require(_bridge != address(0), "adddress must not be empty");
- bridge = _bridge;
- }
-
- function setIsPaused(bool _isPaused) public onlyOwner {
- isPaused = _isPaused;
- }
-
- function shutdown() public onlyOwner {
- isShutdown = true;
- }
-
- /**
- * @dev Throws if called by any account other than the owner.
- */
- modifier onlyOwnerOrTokenSwap() {
- require(
- isOwner() || _msgSender() == tokenSwapManager,
- "caller is not the owner or token swap manager"
- );
- _;
- }
+ function delayedRedemptionsByUser(address) external view returns (uint256);
function setDelayedRedemptionsByUser(
uint256 amountToRedeem,
address whitelistedAddress
- ) public onlyOwnerOrTokenSwap {
- delayedRedemptionsByUser[whitelistedAddress] = amountToRedeem;
- }
-
- function getDelayedRedemptionsByUser(address whitelistedAddress)
- public
- view
- returns (uint256)
- {
- return delayedRedemptionsByUser[whitelistedAddress];
- }
-
- /*
- * Saves order in mapping (address => Order[]) orderByUser
- * overwrite == false, append to Order[]
- * overwrite == true, overwrite element at orderIndex
- */
+ ) external;
function setOrderByUser(
address whitelistedAddress,
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee,
- uint256 orderIndex,
- bool overwrite
- ) public onlyOwnerOrTokenSwap() {
- Order memory newOrder = Order(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee
- );
-
- if (!overwrite) {
- orderByUser[whitelistedAddress].push(newOrder);
- setOrder(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee,
- orderIndex,
- overwrite
- );
- } else {
- orderByUser[whitelistedAddress][orderIndex] = newOrder;
- }
- }
-
- /*
- * Gets Order[] For User Address
- * Return order at Index in Order[]
- */
-
- function getOrderByUser(address whitelistedAddress, uint256 orderIndex)
- public
- view
- returns (
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee
- )
- {
-
- Order storage orderAtIndex
- = orderByUser[whitelistedAddress][orderIndex];
- return (
- orderAtIndex.orderType,
- orderAtIndex.tokensGiven,
- orderAtIndex.tokensRecieved,
- orderAtIndex.avgBlendedFee
- );
- }
-
- /*
- * Save order to allOrders array
- * overwrite == false, append to allOrders array
- * overwrite == true, overwrite element at orderIndex
- */
- function setOrder(
- string memory orderType,
+ string calldata orderType,
uint256 tokensGiven,
uint256 tokensRecieved,
uint256 avgBlendedFee,
uint256 orderIndex,
bool overwrite
- ) public onlyOwnerOrTokenSwap() {
- Order memory newOrder = Order(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee
- );
-
- if (!overwrite) {
- allOrders.push(newOrder);
- } else {
- allOrders[orderIndex] = newOrder;
- }
- }
-
- /*
- * Get Order
- */
- function getOrder(uint256 index)
- public
- view
- returns (
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee
- )
- {
- Order storage orderAtIndex = allOrders[index];
- return (
- orderAtIndex.orderType,
- orderAtIndex.tokensGiven,
- orderAtIndex.tokensRecieved,
- orderAtIndex.avgBlendedFee
- );
- }
-
- // @dev Set whitelisted addresses
- function setWhitelistedAddress(address adddressToAdd) public onlyOwner {
- require(adddressToAdd != address(0), "adddress must not be empty");
+ ) external;
- whitelistedAddresses[adddressToAdd] = true;
- }
-
- // @dev Remove whitelisted addresses
- function removeWhitelistedAddress(address addressToRemove)
- public
- onlyOwner
- {
- require(
- whitelistedAddresses[addressToRemove],
- "address must be added to be removed allowed"
- );
-
- delete whitelistedAddresses[addressToRemove];
- }
-
- // @dev Updates whitelisted addresses
- function updateWhitelistedAddress(address oldAddress, address newAddress)
- public
- {
- removeWhitelistedAddress(oldAddress);
- setWhitelistedAddress(newAddress);
- }
-
- // @dev Get accounting values for a specific day
- // @param date format as 20200123 for 23th of January 2020
- function getAccounting(uint256 date)
- public
- view
- returns (uint256, uint256, uint256, uint256)
- {
- return (
- accounting[date][accounting[date].length - 1].price,
- accounting[date][accounting[date].length - 1]
- .cashPositionPerTokenUnit,
- accounting[date][accounting[date].length - 1].balancePerTokenUnit,
- accounting[date][accounting[date].length - 1].lendingFee
- );
- }
-
- // @dev Set accounting values for the day
function setAccounting(
uint256 _price,
uint256 _cashPositionPerTokenUnit,
uint256 _balancePerTokenUnit,
uint256 _lendingFee
- ) external onlyOwnerOrTokenSwap() {
- (uint256 year, uint256 month, uint256 day) = DateTimeLibrary
- .timestampToDate(block.timestamp);
- uint256 today = year * 10000 + month * 100 + day;
- accounting[today].push(
- Accounting(
- _price,
- _cashPositionPerTokenUnit,
- _balancePerTokenUnit,
- _lendingFee
- )
- );
- lastActivityDay = today;
- emit AccountingValuesSet(today);
- }
+ ) external;
- // @dev Set accounting values for the day
function setAccountingForLastActivityDay(
uint256 _price,
uint256 _cashPositionPerTokenUnit,
uint256 _balancePerTokenUnit,
uint256 _lendingFee
- ) external onlyOwnerOrTokenSwap() {
- accounting[lastActivityDay].push(
- Accounting(
- _price,
- _cashPositionPerTokenUnit,
- _balancePerTokenUnit,
- _lendingFee
- )
- );
- emit AccountingValuesSet(lastActivityDay);
- }
-
- // @dev Set last rebalance information
- function setMinRebalanceAmount(uint256 _minRebalanceAmount)
- external
- onlyOwner
- {
- minRebalanceAmount = _minRebalanceAmount;
-
- emit RebalanceValuesSet(minRebalanceAmount);
- }
-
- // @dev Set last rebalance information
- function setManagementFee(uint256 _managementFee) external onlyOwner {
- managementFee = _managementFee;
- emit ManagementFeeValuesSet(managementFee);
- }
-
- // @dev Returns price
- function getPrice() public view returns (uint256 price) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .price;
- }
-
- // @dev Returns cash position amount
- function getCashPositionPerTokenUnit()
- public
- view
- returns (uint256 amount)
- {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .cashPositionPerTokenUnit;
- }
-
- // @dev Returns borrowed crypto amount
- function getBalancePerTokenUnit() public view returns (uint256 amount) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .balancePerTokenUnit;
- }
-
- // @dev Returns lending fee
- function getLendingFee() public view returns (uint256 lendingRate) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .lendingFee;
- }
-
- // @dev Returns lending fee
- function getManagementFee() public view returns (uint256 lendingRate) {
- return managementFee;
- }
-
- // @dev Sets last minting fee
- function setLastMintingFee(uint256 _mintingFee) public onlyOwner {
- mintingFee[~uint256(0)] = _mintingFee;
- }
-
- // @dev Adds minting fee
- function addMintingFeeBracket(uint256 _mintingFeeLimit, uint256 _mintingFee)
- public
- onlyOwner
- {
- require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
- "New minting fee bracket needs to be bigger then last one"
- );
- mintingFeeBracket.push(_mintingFeeLimit);
- mintingFee[_mintingFeeLimit] = _mintingFee;
- }
-
- // @dev Deletes last minting fee
- function deleteLastMintingFeeBracket() public onlyOwner {
- delete mintingFee[mintingFeeBracket[mintingFeeBracket.length - 1]];
- mintingFeeBracket.length--;
- }
-
- // @dev Changes minting fee
- function changeMintingLimit(
- uint256 _position,
- uint256 _mintingFeeLimit,
- uint256 _mintingFee
- ) public onlyOwner {
- require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
- "New minting fee bracket needs to be bigger then last one"
- );
- if (_position != 0) {
- require(
- _mintingFeeLimit > mintingFeeBracket[_position - 1],
- "New minting fee bracket needs to be bigger then last one"
- );
- }
- if (_position < mintingFeeBracket.length - 1) {
- require(
- _mintingFeeLimit < mintingFeeBracket[_position + 1],
- "New minting fee bracket needs to be smaller then next one"
- );
- }
- mintingFeeBracket[_position] = _mintingFeeLimit;
- mintingFee[_mintingFeeLimit] = _mintingFee;
- }
-
- function getMintingFee(uint256 cash) public view returns (uint256) {
- // Define Start + End Index
- uint256 startIndex = 0;
- uint256 endIndex = mintingFeeBracket.length - 1;
- uint256 middleIndex = endIndex / 2;
-
- if (cash <= mintingFeeBracket[middleIndex]) {
- endIndex = middleIndex;
- } else {
- startIndex = middleIndex + 1;
- }
-
- for (uint256 i = startIndex; i <= endIndex; i++) {
- if (cash <= mintingFeeBracket[i]) {
- return mintingFee[mintingFeeBracket[i]];
- }
- }
- return mintingFee[~uint256(0)];
- }
+ ) external;
}
-// File: contracts/Token/ERC20Detailed.sol
+// File: contracts/shared/Token/ERC20Detailed.sol
pragma solidity ^0.5.0;
@@ -1321,7 +365,7 @@ contract ERC20Detailed is IERC20, Initializable, Ownable {
string private _name;
string private _symbol;
uint8 private _decimals;
- PersistentStorage public _persistenStorage;
+ InterfaceStorage public _persistenStorage;
/**
* @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
@@ -1339,7 +383,7 @@ contract ERC20Detailed is IERC20, Initializable, Ownable {
_name = name;
_symbol = symbol;
_decimals = decimals;
- _persistenStorage = PersistentStorage(persistenStorage);
+ _persistenStorage = InterfaceStorage(persistenStorage);
}
/**
@@ -1533,7 +577,7 @@ library SafeMath {
}
}
-// File: contracts/Token/ERC20.sol
+// File: contracts/shared/Token/ERC20.sol
pragma solidity ^0.5.0;
@@ -1812,7 +856,7 @@ contract ERC20 is Sender, ERC20Detailed {
}
}
-// File: contracts/Token/InverseToken.sol
+// File: contracts/short-tokens/InverseToken.sol
pragma solidity ^0.5.0;
diff --git a/flats/KYCVerifier.sol b/flats/KYCVerifier.sol
index d21dfee..f7e4f04 100644
--- a/flats/KYCVerifier.sol
+++ b/flats/KYCVerifier.sol
@@ -63,27 +63,134 @@ contract Initializable {
uint256[50] private ______gap;
}
-// File: contracts/Abstract/InterfaceStorage.sol
+// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
pragma solidity ^0.5.0;
-interface InterfaceStorage {
- function whitelistedAddresses(address) external view returns (bool);
+/*
+ * @dev Provides information about the current execution context, including the
+ * sender of the transaction and its data. While these are generally available
+ * via msg.sender and msg.data, they should not be accessed in such a direct
+ * manner, since when dealing with GSN meta-transactions the account sending and
+ * paying for execution may not be the actual sender (as far as an application
+ * is concerned).
+ *
+ * This contract is only required for intermediate, library-like contracts.
+ */
+contract Context is Initializable {
+ // Empty internal constructor, to prevent people from mistakenly deploying
+ // an instance of this contract, which should be used via inheritance.
+ constructor () internal { }
+ // solhint-disable-previous-line no-empty-blocks
+
+ function _msgSender() internal view returns (address payable) {
+ return msg.sender;
+ }
+
+ function _msgData() internal view returns (bytes memory) {
+ this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
+ return msg.data;
+ }
+}
+
+// File: @openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol
+
+pragma solidity ^0.5.0;
+
+
+
+/**
+ * @dev Contract module which provides a basic access control mechanism, where
+ * there is an account (an owner) that can be granted exclusive access to
+ * specific functions.
+ *
+ * This module is used through inheritance. It will make available the modifier
+ * `onlyOwner`, which can be aplied to your functions to restrict their use to
+ * the owner.
+ */
+contract Ownable is Initializable, Context {
+ address private _owner;
+
+ event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+
+ /**
+ * @dev Initializes the contract setting the deployer as the initial owner.
+ */
+ function initialize(address sender) public initializer {
+ _owner = sender;
+ emit OwnershipTransferred(address(0), _owner);
+ }
+
+ /**
+ * @dev Returns the address of the current owner.
+ */
+ function owner() public view returns (address) {
+ return _owner;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwner() {
+ require(isOwner(), "Ownable: caller is not the owner");
+ _;
+ }
+
+ /**
+ * @dev Returns true if the caller is the current owner.
+ */
+ function isOwner() public view returns (bool) {
+ return _msgSender() == _owner;
+ }
+
+ /**
+ * @dev Leaves the contract without owner. It will not be possible to call
+ * `onlyOwner` functions anymore. Can only be called by the current owner.
+ *
+ * > Note: 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 Transfers ownership of the contract to a new account (`newOwner`).
+ * Can only be called by the current owner.
+ */
+ function transferOwnership(address newOwner) public onlyOwner {
+ _transferOwnership(newOwner);
+ }
+
+ /**
+ * @dev Transfers ownership of the contract to a new account (`newOwner`).
+ */
+ function _transferOwnership(address newOwner) internal {
+ require(newOwner != address(0), "Ownable: new owner is the zero address");
+ emit OwnershipTransferred(_owner, newOwner);
+ _owner = newOwner;
+ }
+
+ uint256[50] private ______gap;
}
-// File: contracts/KYCVerifier.sol
+// File: contracts/short-tokens/KYCVerifier.sol
pragma solidity ^0.5.0;
+contract KYCVerifier is Ownable {
+ address public bridge;
+ mapping(address => bool) public whitelistedAddresses;
-contract KYCVerifier is Initializable {
- InterfaceStorage public persistentStorage;
+ event WhitelistedAddressAdded(address);
- function initialize(address _persistentStorage) public initializer {
- persistentStorage = InterfaceStorage(_persistentStorage);
+ function initialize(address ownerAddress) public initializer {
+ require(ownerAddress != address(0), "owner adddress must not be empty");
+ Ownable.initialize(ownerAddress);
}
function isAddressWhitelisted(address userAddress)
@@ -91,6 +198,58 @@ contract KYCVerifier is Initializable {
view
returns (bool)
{
- return persistentStorage.whitelistedAddresses(userAddress);
+ return whitelistedAddresses[userAddress];
+ }
+
+ // @dev Set whitelisted addresses
+ function setWhitelistedAddress(address addressToAdd)
+ public
+ onlyOwnerOrBridge
+ {
+ require(addressToAdd != address(0), "adddress must not be empty");
+
+ whitelistedAddresses[addressToAdd] = true;
+
+ emit WhitelistedAddressAdded(addressToAdd);
+ }
+
+ function batchWhitelistedAddress(address[] calldata addresses) external {
+ for (uint8 index = 0; index < addresses.length; index++) {
+ setWhitelistedAddress(addresses[index]);
+ }
+ }
+
+ // @dev Remove whitelisted addresses
+ function removeWhitelistedAddress(address addressToRemove)
+ public
+ onlyOwnerOrBridge
+ {
+ require(
+ whitelistedAddresses[addressToRemove],
+ "address must be added to be removed allowed"
+ );
+
+ delete whitelistedAddresses[addressToRemove];
+ }
+
+ // @dev Updates whitelisted addresses
+ function updateWhitelistedAddress(address oldAddress, address newAddress)
+ public
+ {
+ removeWhitelistedAddress(oldAddress);
+ setWhitelistedAddress(newAddress);
+ }
+
+ function setBridge(address _bridge) public onlyOwner {
+ require(_bridge != address(0), "adddress must not be empty");
+ bridge = _bridge;
+ }
+
+ modifier onlyOwnerOrBridge() {
+ require(
+ isOwner() || _msgSender() == bridge,
+ "caller is not the owner or bridge"
+ );
+ _;
}
}
diff --git a/flats/OwnerMultiSig.sol b/flats/OwnerMultiSig.sol
new file mode 100644
index 0000000..939deec
--- /dev/null
+++ b/flats/OwnerMultiSig.sol
@@ -0,0 +1,463 @@
+
+// File: @openzeppelin/upgrades/contracts/Initializable.sol
+
+pragma solidity >=0.4.24 <0.6.0;
+
+
+/**
+ * @title Initializable
+ *
+ * @dev Helper contract to support initializer functions. To use it, replace
+ * the constructor with a function that has the `initializer` modifier.
+ * WARNING: Unlike constructors, initializer functions must be manually
+ * invoked. This applies both to deploying an Initializable contract, as well
+ * as extending an Initializable contract via inheritance.
+ * WARNING: When used with inheritance, manual care must be taken to not invoke
+ * a parent initializer twice, or ensure that all initializers are idempotent,
+ * because this is not dealt with automatically as with constructors.
+ */
+contract Initializable {
+
+ /**
+ * @dev Indicates that the contract has been initialized.
+ */
+ bool private initialized;
+
+ /**
+ * @dev Indicates that the contract is in the process of being initialized.
+ */
+ bool private initializing;
+
+ /**
+ * @dev Modifier to use in the initializer function of a contract.
+ */
+ modifier initializer() {
+ require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
+
+ bool isTopLevelCall = !initializing;
+ if (isTopLevelCall) {
+ initializing = true;
+ initialized = true;
+ }
+
+ _;
+
+ if (isTopLevelCall) {
+ initializing = false;
+ }
+ }
+
+ /// @dev Returns true if and only if the function is running in the constructor
+ function isConstructor() private view returns (bool) {
+ // extcodesize checks the size of the code stored in an address, and
+ // address returns the current address. Since the code is still not
+ // deployed when running a constructor, any checks on its code size will
+ // yield zero, making it an effective way to detect if a contract is
+ // under construction or not.
+ uint256 cs;
+ assembly { cs := extcodesize(address) }
+ return cs == 0;
+ }
+
+ // Reserved storage space to allow for layout changes in the future.
+ uint256[50] private ______gap;
+}
+
+// File: contracts/shared/AdminMultiSig/OwnerMultiSig.sol
+
+pragma solidity ^0.5.0;
+
+
+
+/// @title Multisignature wallet - Allows multiple parties to agree on transactions before execution.
+/// @author Stefan George -
+contract OwnerMultiSig is Initializable {
+ /*
+ * Events
+ */
+ event Confirmation(address indexed sender, uint256 indexed transactionId);
+ event Revocation(address indexed sender, uint256 indexed transactionId);
+ event Submission(uint256 indexed transactionId);
+ event Execution(uint256 indexed transactionId);
+ event ExecutionFailure(uint256 indexed transactionId);
+ event Deposit(address indexed sender, uint256 value);
+ event OwnerAddition(address indexed owner);
+ event OwnerRemoval(address indexed owner);
+ event RequirementChange(uint256 required);
+
+ /*
+ * Constants
+ */
+ uint256 public constant MAX_OWNER_COUNT = 50;
+
+ /*
+ * Storage
+ */
+ mapping(uint256 => Transaction) public transactions;
+ mapping(uint256 => mapping(address => bool)) public confirmations;
+ mapping(address => bool) public isOwner;
+ address[] public owners;
+ uint256 public required;
+ uint256 public transactionCount;
+ //Txn Struct dest, val, data, and isExecuted
+ struct Transaction {
+ address destination;
+ uint256 value;
+ bytes data;
+ bool executed;
+ }
+
+ /*
+ * Modifiers
+ */
+
+ // Sent by Wallet
+ modifier onlyWallet() {
+ require(msg.sender == address(this));
+ _;
+ }
+ // Check if Owner Does Not Exists
+ modifier ownerDoesNotExist(address owner) {
+ require(!isOwner[owner]);
+ _;
+ }
+ // Check if Owner Exists
+ modifier ownerExists(address owner) {
+ require(isOwner[owner]);
+ _;
+ }
+ // Check for Txn by Txn ID
+ modifier transactionExists(uint256 transactionId) {
+ require(transactions[transactionId].destination != address(0));
+ _;
+ }
+ // Check if Txn ID is Confirmed
+ modifier confirmed(uint256 transactionId, address owner) {
+ require(confirmations[transactionId][owner]);
+ _;
+ }
+ // Not Confirmed Txn ID
+ modifier notConfirmed(uint256 transactionId, address owner) {
+ require(!confirmations[transactionId][owner]);
+ _;
+ }
+ // Check if Txn ID is Executed
+ modifier notExecuted(uint256 transactionId) {
+ require(!transactions[transactionId].executed);
+ _;
+ }
+ // Check if Addr is not 0 address
+ modifier notNull(address _address) {
+ require(_address != address(0));
+ _;
+ }
+ // Check if Requirements have passed
+ modifier validRequirement(uint256 ownerCount, uint256 _required) {
+ require(
+ ownerCount <= MAX_OWNER_COUNT &&
+ _required <= ownerCount &&
+ _required != 0 &&
+ ownerCount != 0
+ );
+ _;
+ }
+
+ // Fallback Function to Depo ETH
+ /// @dev Fallback function allows to deposit ether.
+ function() external payable {
+ if (msg.value > 0) emit Deposit(msg.sender, msg.value);
+ }
+
+ /*
+ * Public functions
+ */
+ /// @dev Contract constructor sets initial owners and required number of confirmations.
+ /// @param _owners List of initial owners.
+ /// @param _required Number of required confirmations.
+ // Number of Confirmations for a Txn
+ function initialize(address[] memory _owners, uint256 _required)
+ public
+ initializer
+ validRequirement(_owners.length, _required)
+ {
+ for (uint256 i = 0; i < _owners.length; i++) {
+ require(!isOwner[_owners[i]] && _owners[i] != address(0));
+ isOwner[_owners[i]] = true;
+ }
+ owners = _owners;
+ required = _required;
+ //
+ }
+
+ /// @dev Allows to add a new owner. Transaction has to be sent by wallet.
+ /// @param owner Address of new owner.
+ function addOwner(address owner)
+ public
+ onlyWallet
+ ownerDoesNotExist(owner)
+ notNull(owner)
+ validRequirement(owners.length + 1, required)
+ {
+ isOwner[owner] = true;
+ owners.push(owner);
+ emit OwnerAddition(owner);
+ }
+
+ /// @dev Allows to remove an owner. Transaction has to be sent by wallet.
+ /// @param owner Address of owner.
+ function removeOwner(address owner) public onlyWallet ownerExists(owner) {
+ isOwner[owner] = false;
+ for (uint256 i = 0; i < owners.length - 1; i++)
+ if (owners[i] == owner) {
+ owners[i] = owners[owners.length - 1];
+ break;
+ }
+ owners.length -= 1;
+ if (required > owners.length) changeRequirement(owners.length);
+ emit OwnerRemoval(owner);
+ }
+
+ /// @dev Allows to replace an owner with a new owner. Transaction has to be sent by wallet.
+ /// @param owner Address of owner to be replaced.
+ /// @param newOwner Address of new owner.
+ function replaceOwner(address owner, address newOwner)
+ public
+ onlyWallet
+ ownerExists(owner)
+ ownerDoesNotExist(newOwner)
+ {
+ for (uint256 i = 0; i < owners.length; i++)
+ if (owners[i] == owner) {
+ owners[i] = newOwner;
+ break;
+ }
+ isOwner[owner] = false;
+ isOwner[newOwner] = true;
+ emit OwnerRemoval(owner);
+ emit OwnerAddition(newOwner);
+ }
+
+ /// @dev Allows to change the number of required confirmations. Transaction has to be sent by wallet.
+ /// @param _required Number of required confirmations.
+ function changeRequirement(uint256 _required)
+ public
+ onlyWallet
+ validRequirement(owners.length, _required)
+ {
+ required = _required;
+ emit RequirementChange(_required);
+ }
+
+ /// @dev Allows an owner to submit and confirm a transaction.
+ /// @param destination Transaction target address.
+ /// @param value Transaction ether value.
+ /// @param data Transaction data payload.
+ /// @return Returns transaction ID.
+ function submitTransaction(
+ address destination,
+ uint256 value,
+ bytes memory data
+ ) public returns (uint256 transactionId) {
+ transactionId = addTransaction(destination, value, data);
+ confirmTransaction(transactionId);
+ }
+
+ /// @dev Allows an owner to confirm a transaction.
+ /// @param transactionId Transaction ID.
+ // Check if Txn is confirmed
+ function confirmTransaction(uint256 transactionId)
+ public
+ ownerExists(msg.sender)
+ transactionExists(transactionId)
+ notConfirmed(transactionId, msg.sender)
+ {
+ confirmations[transactionId][msg.sender] = true;
+ emit Confirmation(msg.sender, transactionId);
+ executeTransaction(transactionId);
+ }
+
+ /// @dev Allows an owner to revoke a confirmation for a transaction.
+ /// @param transactionId Transaction ID.
+ function revokeConfirmation(uint256 transactionId)
+ public
+ ownerExists(msg.sender)
+ confirmed(transactionId, msg.sender)
+ notExecuted(transactionId)
+ {
+ confirmations[transactionId][msg.sender] = false;
+ emit Revocation(msg.sender, transactionId);
+ }
+
+ /// @dev Allows anyone to execute a confirmed transaction.
+ /// @param transactionId Transaction ID.
+ function executeTransaction(uint256 transactionId)
+ public
+ ownerExists(msg.sender)
+ confirmed(transactionId, msg.sender)
+ notExecuted(transactionId)
+ {
+ if (isConfirmed(transactionId)) {
+ Transaction storage txn = transactions[transactionId];
+ txn.executed = true;
+ if (
+ external_call(
+ txn.destination,
+ txn.value,
+ txn.data.length,
+ txn.data
+ )
+ ) emit Execution(transactionId);
+ else {
+ emit ExecutionFailure(transactionId);
+ txn.executed = false;
+ }
+ }
+ }
+
+ // call has been separated into its own function in order to take advantage
+ // of the Solidity's code generator to produce a loop that copies tx.data into memory.
+ function external_call(
+ address destination,
+ uint256 value,
+ uint256 dataLength,
+ bytes memory data
+ ) internal returns (bool) {
+ bool result;
+ assembly {
+ let x := mload(0x40) // "Allocate" memory for output (0x40 is where "free memory" pointer is stored by convention)
+ let d := add(data, 32) // First 32 bytes are the padded length of data, so exclude that
+ result := call(
+ sub(gas, 34710), // 34710 is the value that solidity is currently emitting
+ // It includes callGas (700) + callVeryLow (3, to pay for SUB) + callValueTransferGas (9000) +
+ // callNewAccountGas (25000, in case the destination address does not exist and needs creating)
+ destination,
+ value,
+ d,
+ dataLength, // Size of the input (in bytes) - this is what fixes the padding problem
+ x,
+ 0 // Output is ignored, therefore the output size is zero
+ )
+ }
+ return result;
+ }
+
+ /// @dev Returns the confirmation status of a transaction.
+ /// @param transactionId Transaction ID.
+ /// @return Confirmation status.
+ function isConfirmed(uint256 transactionId) public view returns (bool) {
+ uint256 count = 0;
+ for (uint256 i = 0; i < owners.length; i++) {
+ if (confirmations[transactionId][owners[i]]) count += 1;
+ if (count == required) return true;
+ }
+ }
+
+ /*
+ * Internal functions
+ */
+ /// @dev Adds a new transaction to the transaction mapping, if transaction does not exist yet.
+ /// @param destination Transaction target address.
+ /// @param value Transaction ether value.
+ /// @param data Transaction data payload.
+ /// @return Returns transaction ID.
+ function addTransaction(
+ address destination,
+ uint256 value,
+ bytes memory data
+ ) internal notNull(destination) returns (uint256 transactionId) {
+ transactionId = transactionCount;
+ transactions[transactionId] = Transaction({
+ destination: destination,
+ value: value,
+ data: data,
+ executed: false
+ });
+ transactionCount += 1;
+ emit Submission(transactionId);
+ }
+
+ /*
+ * Web3 call functions
+ */
+ /// @dev Returns number of confirmations of a transaction.
+ /// @param transactionId Transaction ID.
+ /// @return Number of confirmations.
+ function getConfirmationCount(uint256 transactionId)
+ public
+ view
+ returns (uint256 count)
+ {
+ for (uint256 i = 0; i < owners.length; i++)
+ if (confirmations[transactionId][owners[i]]) count += 1;
+ }
+
+ /// @dev Returns total number of transactions after filers are applied.
+ /// @param pending Include pending transactions.
+ /// @param executed Include executed transactions.
+ /// @return Total number of transactions after filters are applied.
+ function getTransactionCount(bool pending, bool executed)
+ public
+ view
+ returns (uint256 count)
+ {
+ for (uint256 i = 0; i < transactionCount; i++)
+ if (
+ (pending && !transactions[i].executed) ||
+ (executed && transactions[i].executed)
+ ) count += 1;
+ }
+
+ /// @dev Returns list of owners.
+ /// @return List of owner addresses.
+ function getOwners() public view returns (address[] memory) {
+ return owners;
+ }
+
+ /// @dev Returns array with owner addresses, which confirmed transaction.
+ /// @param transactionId Transaction ID.
+ /// @return Returns array of owner addresses.
+ function getConfirmations(uint256 transactionId)
+ public
+ view
+ returns (address[] memory _confirmations)
+ {
+ address[] memory confirmationsTemp = new address[](owners.length);
+ uint256 count = 0;
+ uint256 i;
+ for (i = 0; i < owners.length; i++)
+ if (confirmations[transactionId][owners[i]]) {
+ confirmationsTemp[count] = owners[i];
+ count += 1;
+ }
+ _confirmations = new address[](count);
+ for (i = 0; i < count; i++) _confirmations[i] = confirmationsTemp[i];
+ }
+
+ /// @dev Returns list of transaction IDs in defined range.
+ /// @param from Index start position of transaction array.
+ /// @param to Index end position of transaction array.
+ /// @param pending Include pending transactions.
+ /// @param executed Include executed transactions.
+ /// @return Returns array of transaction IDs.
+ function getTransactionIds(
+ uint256 from,
+ uint256 to,
+ bool pending,
+ bool executed
+ ) public view returns (uint256[] memory _transactionIds) {
+ uint256[] memory transactionIdsTemp = new uint256[](transactionCount);
+ uint256 count = 0;
+ uint256 i;
+ for (i = 0; i < transactionCount; i++)
+ if (
+ (pending && !transactions[i].executed) ||
+ (executed && transactions[i].executed)
+ ) {
+ transactionIdsTemp[count] = i;
+ count += 1;
+ }
+ _transactionIds = new uint256[](to - from);
+ for (i = from; i < to; i++)
+ _transactionIds[i - from] = transactionIdsTemp[i];
+ }
+}
diff --git a/flats/PersistentStorage.sol b/flats/PersistentStorage.sol
index fc8cc1d..02bf7f4 100644
--- a/flats/PersistentStorage.sol
+++ b/flats/PersistentStorage.sol
@@ -176,7 +176,7 @@ contract Ownable is Initializable, Context {
uint256[50] private ______gap;
}
-// File: contracts/utils/DateTimeLibrary.sol
+// File: contracts/short-tokens/utils/DateTimeLibrary.sol
pragma solidity ^0.5.0;
@@ -670,7 +670,7 @@ library DateTimeLibrary {
}
}
-// File: contracts/utils/Math.sol
+// File: contracts/short-tokens/utils/Math.sol
/// Math.sol -- mixin for inline numerical wizardry
@@ -767,7 +767,7 @@ library DSMath {
}
}
-// File: contracts/PersistentStorage.sol
+// File: contracts/short-tokens/PersistentStorage.sol
pragma solidity ^0.5.0;
@@ -798,11 +798,13 @@ contract PersistentStorage is Ownable {
uint256 public lastActivityDay;
uint256 public minRebalanceAmount;
- uint256 private managementFee;
+ uint256 public managementFee;
+ uint256 public minimumMintingFee;
+ uint256 public minimumTrade;
- mapping(uint256 => Accounting[]) private accounting;
+ uint8 public balancePrecision;
- mapping(address => bool) public whitelistedAddresses;
+ mapping(uint256 => Accounting[]) private accounting;
uint256[] public mintingFeeBracket;
mapping(uint256 => uint256) public mintingFee;
@@ -818,16 +820,19 @@ contract PersistentStorage is Ownable {
function initialize(
address ownerAddress,
uint256 _managementFee,
- uint256 _minRebalanceAmount
+ uint256 _minRebalanceAmount,
+ uint8 _balancePrecision,
+ uint256 _lastMintingFee,
+ uint256 _minimumMintingFee,
+ uint256 _minimumTrade
) public initializer {
initialize(ownerAddress);
managementFee = _managementFee;
minRebalanceAmount = _minRebalanceAmount;
- mintingFeeBracket.push(50000 ether);
- mintingFeeBracket.push(100000 ether);
- mintingFee[50000 ether] = 3 ether / 1000; //0.3%
- mintingFee[100000 ether] = 2 ether / 1000; //0.2%
- mintingFee[~uint256(0)] = 1 ether / 1000; //0.1% all values higher
+ mintingFee[~uint256(0)] = _lastMintingFee;
+ balancePrecision = _balancePrecision;
+ minimumMintingFee = _minimumMintingFee;
+ minimumTrade = _minimumTrade;
}
function setTokenSwapManager(address _tokenSwapManager) public onlyOwner {
@@ -859,6 +864,14 @@ contract PersistentStorage is Ownable {
_;
}
+ modifier onlyOwnerOrBridge() {
+ require(
+ isOwner() || _msgSender() == bridge,
+ "caller is not the owner or bridge"
+ );
+ _;
+ }
+
function setDelayedRedemptionsByUser(
uint256 amountToRedeem,
address whitelistedAddress
@@ -866,14 +879,6 @@ contract PersistentStorage is Ownable {
delayedRedemptionsByUser[whitelistedAddress] = amountToRedeem;
}
- function getDelayedRedemptionsByUser(address whitelistedAddress)
- public
- view
- returns (uint256)
- {
- return delayedRedemptionsByUser[whitelistedAddress];
- }
-
/*
* Saves order in mapping (address => Order[]) orderByUser
* overwrite == false, append to Order[]
@@ -986,34 +991,6 @@ contract PersistentStorage is Ownable {
);
}
- // @dev Set whitelisted addresses
- function setWhitelistedAddress(address adddressToAdd) public onlyOwner {
- require(adddressToAdd != address(0), "adddress must not be empty");
-
- whitelistedAddresses[adddressToAdd] = true;
- }
-
- // @dev Remove whitelisted addresses
- function removeWhitelistedAddress(address addressToRemove)
- public
- onlyOwner
- {
- require(
- whitelistedAddresses[addressToRemove],
- "address must be added to be removed allowed"
- );
-
- delete whitelistedAddresses[addressToRemove];
- }
-
- // @dev Updates whitelisted addresses
- function updateWhitelistedAddress(address oldAddress, address newAddress)
- public
- {
- removeWhitelistedAddress(oldAddress);
- setWhitelistedAddress(newAddress);
- }
-
// @dev Get accounting values for a specific day
// @param date format as 20200123 for 23th of January 2020
function getAccounting(uint256 date)
@@ -1118,11 +1095,6 @@ contract PersistentStorage is Ownable {
.lendingFee;
}
- // @dev Returns lending fee
- function getManagementFee() public view returns (uint256 lendingRate) {
- return managementFee;
- }
-
// @dev Sets last minting fee
function setLastMintingFee(uint256 _mintingFee) public onlyOwner {
mintingFee[~uint256(0)] = _mintingFee;
@@ -1134,7 +1106,9 @@ contract PersistentStorage is Ownable {
onlyOwner
{
require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
+ mintingFeeBracket.length == 0 ||
+ _mintingFeeLimit >
+ mintingFeeBracket[mintingFeeBracket.length - 1],
"New minting fee bracket needs to be bigger then last one"
);
mintingFeeBracket.push(_mintingFeeLimit);
@@ -1192,4 +1166,19 @@ contract PersistentStorage is Ownable {
}
return mintingFee[~uint256(0)];
}
+
+ // @dev Sets last balance precision
+ function setLastPrecision(uint8 _balancePrecision) public onlyOwner {
+ balancePrecision = _balancePrecision;
+ }
+
+ // @dev Sets minimum minting fee
+ function setMinimumMintingFee(uint256 _minimumMintingFee) public onlyOwner {
+ minimumMintingFee = _minimumMintingFee;
+ }
+
+ // @dev Sets minimum trade value
+ function setMinimumTrade(uint256 _minimumTrade) public onlyOwner {
+ minimumTrade = _minimumTrade;
+ }
}
diff --git a/flats/StorageLeverage.sol b/flats/StorageLeverage.sol
new file mode 100644
index 0000000..4e03ae8
--- /dev/null
+++ b/flats/StorageLeverage.sol
@@ -0,0 +1,1224 @@
+
+// File: @openzeppelin/upgrades/contracts/Initializable.sol
+
+pragma solidity >=0.4.24 <0.6.0;
+
+
+/**
+ * @title Initializable
+ *
+ * @dev Helper contract to support initializer functions. To use it, replace
+ * the constructor with a function that has the `initializer` modifier.
+ * WARNING: Unlike constructors, initializer functions must be manually
+ * invoked. This applies both to deploying an Initializable contract, as well
+ * as extending an Initializable contract via inheritance.
+ * WARNING: When used with inheritance, manual care must be taken to not invoke
+ * a parent initializer twice, or ensure that all initializers are idempotent,
+ * because this is not dealt with automatically as with constructors.
+ */
+contract Initializable {
+
+ /**
+ * @dev Indicates that the contract has been initialized.
+ */
+ bool private initialized;
+
+ /**
+ * @dev Indicates that the contract is in the process of being initialized.
+ */
+ bool private initializing;
+
+ /**
+ * @dev Modifier to use in the initializer function of a contract.
+ */
+ modifier initializer() {
+ require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
+
+ bool isTopLevelCall = !initializing;
+ if (isTopLevelCall) {
+ initializing = true;
+ initialized = true;
+ }
+
+ _;
+
+ if (isTopLevelCall) {
+ initializing = false;
+ }
+ }
+
+ /// @dev Returns true if and only if the function is running in the constructor
+ function isConstructor() private view returns (bool) {
+ // extcodesize checks the size of the code stored in an address, and
+ // address returns the current address. Since the code is still not
+ // deployed when running a constructor, any checks on its code size will
+ // yield zero, making it an effective way to detect if a contract is
+ // under construction or not.
+ uint256 cs;
+ assembly { cs := extcodesize(address) }
+ return cs == 0;
+ }
+
+ // Reserved storage space to allow for layout changes in the future.
+ uint256[50] private ______gap;
+}
+
+// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
+
+pragma solidity ^0.5.0;
+
+
+/*
+ * @dev Provides information about the current execution context, including the
+ * sender of the transaction and its data. While these are generally available
+ * via msg.sender and msg.data, they should not be accessed in such a direct
+ * manner, since when dealing with GSN meta-transactions the account sending and
+ * paying for execution may not be the actual sender (as far as an application
+ * is concerned).
+ *
+ * This contract is only required for intermediate, library-like contracts.
+ */
+contract Context is Initializable {
+ // Empty internal constructor, to prevent people from mistakenly deploying
+ // an instance of this contract, which should be used via inheritance.
+ constructor () internal { }
+ // solhint-disable-previous-line no-empty-blocks
+
+ function _msgSender() internal view returns (address payable) {
+ return msg.sender;
+ }
+
+ function _msgData() internal view returns (bytes memory) {
+ this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
+ return msg.data;
+ }
+}
+
+// File: @openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol
+
+pragma solidity ^0.5.0;
+
+
+
+/**
+ * @dev Contract module which provides a basic access control mechanism, where
+ * there is an account (an owner) that can be granted exclusive access to
+ * specific functions.
+ *
+ * This module is used through inheritance. It will make available the modifier
+ * `onlyOwner`, which can be aplied to your functions to restrict their use to
+ * the owner.
+ */
+contract Ownable is Initializable, Context {
+ address private _owner;
+
+ event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+
+ /**
+ * @dev Initializes the contract setting the deployer as the initial owner.
+ */
+ function initialize(address sender) public initializer {
+ _owner = sender;
+ emit OwnershipTransferred(address(0), _owner);
+ }
+
+ /**
+ * @dev Returns the address of the current owner.
+ */
+ function owner() public view returns (address) {
+ return _owner;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwner() {
+ require(isOwner(), "Ownable: caller is not the owner");
+ _;
+ }
+
+ /**
+ * @dev Returns true if the caller is the current owner.
+ */
+ function isOwner() public view returns (bool) {
+ return _msgSender() == _owner;
+ }
+
+ /**
+ * @dev Leaves the contract without owner. It will not be possible to call
+ * `onlyOwner` functions anymore. Can only be called by the current owner.
+ *
+ * > Note: 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 Transfers ownership of the contract to a new account (`newOwner`).
+ * Can only be called by the current owner.
+ */
+ function transferOwnership(address newOwner) public onlyOwner {
+ _transferOwnership(newOwner);
+ }
+
+ /**
+ * @dev Transfers ownership of the contract to a new account (`newOwner`).
+ */
+ function _transferOwnership(address newOwner) internal {
+ require(newOwner != address(0), "Ownable: new owner is the zero address");
+ emit OwnershipTransferred(_owner, newOwner);
+ _owner = newOwner;
+ }
+
+ uint256[50] private ______gap;
+}
+
+// File: contracts/short-tokens/utils/DateTimeLibrary.sol
+
+pragma solidity ^0.5.0;
+
+// ----------------------------------------------------------------------------
+// BokkyPooBah's DateTime Library v1.01
+//
+// A gas-efficient Solidity date and time library
+//
+// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
+//
+// Tested date range 1970/01/01 to 2345/12/31
+//
+// Conventions:
+// Unit | Range | Notes
+// :-------- |:-------------:|:-----
+// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
+// year | 1970 ... 2345 |
+// month | 1 ... 12 |
+// day | 1 ... 31 |
+// hour | 0 ... 23 |
+// minute | 0 ... 59 |
+// second | 0 ... 59 |
+// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday
+//
+//
+// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.
+// ----------------------------------------------------------------------------
+
+library DateTimeLibrary {
+ uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;
+ uint256 constant SECONDS_PER_HOUR = 60 * 60;
+ uint256 constant SECONDS_PER_MINUTE = 60;
+ int256 constant OFFSET19700101 = 2440588;
+
+ uint256 constant DOW_MON = 1;
+ uint256 constant DOW_TUE = 2;
+ uint256 constant DOW_WED = 3;
+ uint256 constant DOW_THU = 4;
+ uint256 constant DOW_FRI = 5;
+ uint256 constant DOW_SAT = 6;
+ uint256 constant DOW_SUN = 7;
+
+ // ------------------------------------------------------------------------
+ // Calculate the number of days from 1970/01/01 to year/month/day using
+ // the date conversion algorithm from
+ // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
+ // and subtracting the offset 2440588 so that 1970/01/01 is day 0
+ //
+ // days = day
+ // - 32075
+ // + 1461 * (year + 4800 + (month - 14) / 12) / 4
+ // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
+ // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
+ // - offset
+ // ------------------------------------------------------------------------
+ function _daysFromDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (uint256 _days)
+ {
+ require(year >= 1970);
+ int256 _year = int256(year);
+ int256 _month = int256(month);
+ int256 _day = int256(day);
+
+ int256 __days = _day -
+ 32075 +
+ (1461 * (_year + 4800 + (_month - 14) / 12)) /
+ 4 +
+ (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /
+ 12 -
+ (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /
+ 4 -
+ OFFSET19700101;
+
+ _days = uint256(__days);
+ }
+
+ // ------------------------------------------------------------------------
+ // Calculate year/month/day from the number of days since 1970/01/01 using
+ // the date conversion algorithm from
+ // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
+ // and adding the offset 2440588 so that 1970/01/01 is day 0
+ //
+ // int L = days + 68569 + offset
+ // int N = 4 * L / 146097
+ // L = L - (146097 * N + 3) / 4
+ // year = 4000 * (L + 1) / 1461001
+ // L = L - 1461 * year / 4 + 31
+ // month = 80 * L / 2447
+ // dd = L - 2447 * month / 80
+ // L = month / 11
+ // month = month + 2 - 12 * L
+ // year = 100 * (N - 49) + year + L
+ // ------------------------------------------------------------------------
+ function _daysToDate(uint256 _days)
+ internal
+ pure
+ returns (uint256 year, uint256 month, uint256 day)
+ {
+ int256 __days = int256(_days);
+
+ int256 L = __days + 68569 + OFFSET19700101;
+ int256 N = (4 * L) / 146097;
+ L = L - (146097 * N + 3) / 4;
+ int256 _year = (4000 * (L + 1)) / 1461001;
+ L = L - (1461 * _year) / 4 + 31;
+ int256 _month = (80 * L) / 2447;
+ int256 _day = L - (2447 * _month) / 80;
+ L = _month / 11;
+ _month = _month + 2 - 12 * L;
+ _year = 100 * (N - 49) + _year + L;
+
+ year = uint256(_year);
+ month = uint256(_month);
+ day = uint256(_day);
+ }
+
+ function daysFromDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (uint256 daysSinceDate)
+ {
+ daysSinceDate = _daysFromDate(year, month, day);
+ }
+ function timestampFromDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (uint256 timestamp)
+ {
+ timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
+ }
+ function timestampFromDateTime(
+ uint256 year,
+ uint256 month,
+ uint256 day,
+ uint256 hour,
+ uint256 minute,
+ uint256 second
+ ) internal pure returns (uint256 timestamp) {
+ timestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ hour *
+ SECONDS_PER_HOUR +
+ minute *
+ SECONDS_PER_MINUTE +
+ second;
+ }
+ function timestampToDate(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 year, uint256 month, uint256 day)
+ {
+ (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function timestampToDateTime(uint256 timestamp)
+ internal
+ pure
+ returns (
+ uint256 year,
+ uint256 month,
+ uint256 day,
+ uint256 hour,
+ uint256 minute,
+ uint256 second
+ )
+ {
+ (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ uint256 secs = timestamp % SECONDS_PER_DAY;
+ hour = secs / SECONDS_PER_HOUR;
+ secs = secs % SECONDS_PER_HOUR;
+ minute = secs / SECONDS_PER_MINUTE;
+ second = secs % SECONDS_PER_MINUTE;
+ }
+
+ function isValidDate(uint256 year, uint256 month, uint256 day)
+ internal
+ pure
+ returns (bool valid)
+ {
+ if (year >= 1970 && month > 0 && month <= 12) {
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > 0 && day <= daysInMonth) {
+ valid = true;
+ }
+ }
+ }
+ function isValidDateTime(
+ uint256 year,
+ uint256 month,
+ uint256 day,
+ uint256 hour,
+ uint256 minute,
+ uint256 second
+ ) internal pure returns (bool valid) {
+ if (isValidDate(year, month, day)) {
+ if (hour < 24 && minute < 60 && second < 60) {
+ valid = true;
+ }
+ }
+ }
+ function isLeapYear(uint256 timestamp)
+ internal
+ pure
+ returns (bool leapYear)
+ {
+ (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ leapYear = _isLeapYear(year);
+ }
+ function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {
+ leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
+ }
+ function isWeekDay(uint256 timestamp) internal pure returns (bool weekDay) {
+ weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
+ }
+ function isWeekEnd(uint256 timestamp) internal pure returns (bool weekEnd) {
+ weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
+ }
+ function getDaysInMonth(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 daysInMonth)
+ {
+ (uint256 year, uint256 month, ) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ daysInMonth = _getDaysInMonth(year, month);
+ }
+ function _getDaysInMonth(uint256 year, uint256 month)
+ internal
+ pure
+ returns (uint256 daysInMonth)
+ {
+ if (
+ month == 1 ||
+ month == 3 ||
+ month == 5 ||
+ month == 7 ||
+ month == 8 ||
+ month == 10 ||
+ month == 12
+ ) {
+ daysInMonth = 31;
+ } else if (month != 2) {
+ daysInMonth = 30;
+ } else {
+ daysInMonth = _isLeapYear(year) ? 29 : 28;
+ }
+ }
+ // 1 = Monday, 7 = Sunday
+ function getDayOfWeek(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 dayOfWeek)
+ {
+ uint256 _days = timestamp / SECONDS_PER_DAY;
+ dayOfWeek = ((_days + 3) % 7) + 1;
+ }
+
+ function getYear(uint256 timestamp) internal pure returns (uint256 year) {
+ (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function getMonth(uint256 timestamp) internal pure returns (uint256 month) {
+ (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function getDay(uint256 timestamp) internal pure returns (uint256 day) {
+ (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);
+ }
+ function getHour(uint256 timestamp) internal pure returns (uint256 hour) {
+ uint256 secs = timestamp % SECONDS_PER_DAY;
+ hour = secs / SECONDS_PER_HOUR;
+ }
+ function getMinute(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 minute)
+ {
+ uint256 secs = timestamp % SECONDS_PER_HOUR;
+ minute = secs / SECONDS_PER_MINUTE;
+ }
+ function getSecond(uint256 timestamp)
+ internal
+ pure
+ returns (uint256 second)
+ {
+ second = timestamp % SECONDS_PER_MINUTE;
+ }
+
+ function addYears(uint256 timestamp, uint256 _years)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ year += _years;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp >= timestamp);
+ }
+ function addMonths(uint256 timestamp, uint256 _months)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ month += _months;
+ year += (month - 1) / 12;
+ month = ((month - 1) % 12) + 1;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp >= timestamp);
+ }
+ function addDays(uint256 timestamp, uint256 _days)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _days * SECONDS_PER_DAY;
+ require(newTimestamp >= timestamp);
+ }
+ function addHours(uint256 timestamp, uint256 _hours)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
+ require(newTimestamp >= timestamp);
+ }
+ function addMinutes(uint256 timestamp, uint256 _minutes)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
+ require(newTimestamp >= timestamp);
+ }
+ function addSeconds(uint256 timestamp, uint256 _seconds)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp + _seconds;
+ require(newTimestamp >= timestamp);
+ }
+
+ function subYears(uint256 timestamp, uint256 _years)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ year -= _years;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp <= timestamp);
+ }
+ function subMonths(uint256 timestamp, uint256 _months)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ (uint256 year, uint256 month, uint256 day) = _daysToDate(
+ timestamp / SECONDS_PER_DAY
+ );
+ uint256 yearMonth = year * 12 + (month - 1) - _months;
+ year = yearMonth / 12;
+ month = (yearMonth % 12) + 1;
+ uint256 daysInMonth = _getDaysInMonth(year, month);
+ if (day > daysInMonth) {
+ day = daysInMonth;
+ }
+ newTimestamp =
+ _daysFromDate(year, month, day) *
+ SECONDS_PER_DAY +
+ (timestamp % SECONDS_PER_DAY);
+ require(newTimestamp <= timestamp);
+ }
+ function subDays(uint256 timestamp, uint256 _days)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _days * SECONDS_PER_DAY;
+ require(newTimestamp <= timestamp);
+ }
+ function subHours(uint256 timestamp, uint256 _hours)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
+ require(newTimestamp <= timestamp);
+ }
+ function subMinutes(uint256 timestamp, uint256 _minutes)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
+ require(newTimestamp <= timestamp);
+ }
+ function subSeconds(uint256 timestamp, uint256 _seconds)
+ internal
+ pure
+ returns (uint256 newTimestamp)
+ {
+ newTimestamp = timestamp - _seconds;
+ require(newTimestamp <= timestamp);
+ }
+
+ function diffYears(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _years)
+ {
+ require(fromTimestamp <= toTimestamp);
+ (uint256 fromYear, , ) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
+ (uint256 toYear, , ) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
+ _years = toYear - fromYear;
+ }
+ function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _months)
+ {
+ require(fromTimestamp <= toTimestamp);
+ (uint256 fromYear, uint256 fromMonth, ) = _daysToDate(
+ fromTimestamp / SECONDS_PER_DAY
+ );
+ (uint256 toYear, uint256 toMonth, ) = _daysToDate(
+ toTimestamp / SECONDS_PER_DAY
+ );
+ _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
+ }
+ function diffDays(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _days)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
+ }
+ function diffHours(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _hours)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
+ }
+ function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _minutes)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
+ }
+ function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)
+ internal
+ pure
+ returns (uint256 _seconds)
+ {
+ require(fromTimestamp <= toTimestamp);
+ _seconds = toTimestamp - fromTimestamp;
+ }
+}
+
+// File: contracts/short-tokens/utils/Math.sol
+
+/// Math.sol -- mixin for inline numerical wizardry
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+pragma solidity ^0.5.0;
+
+
+library DSMath {
+
+ // --- Unsigned Math ----
+ function add(uint x, uint y) internal pure returns (uint z) {
+ require((z = x + y) >= x, "ds-math-add-overflow");
+ }
+ function sub(uint x, uint y) internal pure returns (uint z) {
+ require((z = x - y) <= x, "ds-math-sub-underflow");
+ }
+ function mul(uint x, uint y) internal pure returns (uint z) {
+ require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
+ }
+
+
+ function min(uint x, uint y) internal pure returns (uint z) {
+ return x <= y ? x : y;
+ }
+ function max(uint x, uint y) internal pure returns (uint z) {
+ return x >= y ? x : y;
+ }
+ function imin(int x, int y) internal pure returns (int z) {
+ return x <= y ? x : y;
+ }
+ function imax(int x, int y) internal pure returns (int z) {
+ return x >= y ? x : y;
+ }
+
+ // --- Precise Math ---
+
+ uint public constant WAD = 10 ** 18;
+ uint public constant RAY = 10 ** 27;
+
+ function wmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), WAD / 2) / WAD;
+ }
+ function rmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), RAY / 2) / RAY;
+ }
+ function wdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, WAD), y / 2) / y;
+ }
+ function rdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, RAY), y / 2) / y;
+ }
+
+ function ray(uint _wad) internal pure returns (uint) {
+ return mul(_wad, uint(10 ** 9));
+ }
+
+ // This famous algorithm is called "exponentiation by squaring"
+ // and calculates x^n with x as fixed-point and n as regular unsigned.
+ //
+ // It's O(log n), instead of O(n) for naive repeated multiplication.
+ //
+ // These facts are why it works:
+ //
+ // If n is even, then x^n = (x^2)^(n/2).
+ // If n is odd, then x^n = x * x^(n-1),
+ // and applying the equation for even x gives
+ // x^n = x * (x^2)^((n-1) / 2).
+ //
+ // Also, EVM division is flooring and
+ // floor[(n-1) / 2] = floor[n / 2].
+ //
+ function rpow(uint x, uint n) internal pure returns (uint z) {
+ z = n % 2 != 0 ? x : RAY;
+
+ for (n /= 2; n != 0; n /= 2) {
+ x = rmul(x, x);
+
+ if (n % 2 != 0) {
+ z = rmul(z, x);
+ }
+ }
+ }
+}
+
+// File: contracts/leverage-tokens/StorageLeverage.sol
+
+pragma solidity ^0.5.0;
+
+
+
+
+
+contract StorageLeverage is Ownable {
+ address public tokenSwapManager;
+ address public bridge;
+
+ bool public isPaused;
+ bool public isShutdown;
+
+ struct Accounting {
+ uint256 tokenValueNetFees;
+ uint256 bestExecutionPrice;
+ uint256 markPrice;
+ uint256 notional;
+ int256 changeInNotional;
+ uint256 tokenValue;
+ uint256 effectiveFundingRate;
+ }
+
+ struct Order {
+ string orderType;
+ uint256 tokensGiven;
+ uint256 tokensRecieved;
+ uint256 mintingPrice;
+ }
+
+ uint256 public lastActivityDay;
+ uint256 public minRebalanceAmount;
+ uint256 public managementFee;
+ uint256 public minimumMintingFee;
+ uint256 public minimumTrade;
+
+ uint8 public balancePrecision;
+
+ mapping(uint256 => Accounting[]) private accounting;
+
+ uint256[] public mintingFeeBracket;
+ mapping(uint256 => uint256) public mintingFee;
+
+ Order[] public allOrders;
+ mapping(address => Order[]) public orderByUser;
+ mapping(address => uint256) public delayedRedemptionsByUser;
+
+ event AccountingValuesSet(uint256 today);
+ event RebalanceValuesSet(uint256 newMinRebalanceAmount);
+ event ManagementFeeValuesSet(uint256 newManagementFee);
+
+ function initialize(
+ address ownerAddress,
+ uint256 _managementFee,
+ uint256 _minRebalanceAmount,
+ uint8 _balancePrecision,
+ uint256 _lastMintingFee,
+ uint256 _minimumMintingFee,
+ uint256 _minimumTrade
+ ) public initializer {
+ initialize(ownerAddress);
+ managementFee = _managementFee;
+ minRebalanceAmount = _minRebalanceAmount;
+ mintingFee[~uint256(0)] = _lastMintingFee;
+ balancePrecision = _balancePrecision;
+ minimumMintingFee = _minimumMintingFee;
+ minimumTrade = _minimumTrade;
+ }
+
+ function setTokenSwapManager(address _tokenSwapManager) public onlyOwner {
+ require(_tokenSwapManager != address(0), "adddress must not be empty");
+ tokenSwapManager = _tokenSwapManager;
+ }
+
+ function setBridge(address _bridge) public onlyOwner {
+ require(_bridge != address(0), "adddress must not be empty");
+ bridge = _bridge;
+ }
+
+ function setIsPaused(bool _isPaused) public onlyOwner {
+ isPaused = _isPaused;
+ }
+
+ function shutdown() public onlyOwner {
+ isShutdown = true;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwnerOrTokenSwap() {
+ require(
+ isOwner() || _msgSender() == tokenSwapManager,
+ "caller is not the owner or token swap manager"
+ );
+ _;
+ }
+
+ modifier onlyOwnerOrBridge() {
+ require(
+ isOwner() || _msgSender() == bridge,
+ "caller is not the owner or bridge"
+ );
+ _;
+ }
+
+ function setDelayedRedemptionsByUser(
+ uint256 amountToRedeem,
+ address whitelistedAddress
+ ) public onlyOwnerOrTokenSwap {
+ delayedRedemptionsByUser[whitelistedAddress] = amountToRedeem;
+ }
+
+ /*
+ * Saves order in mapping (address => Order[]) orderByUser
+ * overwrite == false, append to Order[]
+ * overwrite == true, overwrite element at orderIndex
+ */
+
+ function setOrderByUser(
+ address whitelistedAddress,
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ uint256 orderIndex,
+ bool overwrite
+ ) public onlyOwnerOrTokenSwap() {
+ Order memory newOrder = Order(
+ orderType,
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice
+ );
+
+ if (!overwrite) {
+ orderByUser[whitelistedAddress].push(newOrder);
+ setOrder(
+ orderType,
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice,
+ orderIndex,
+ overwrite
+ );
+ } else {
+ orderByUser[whitelistedAddress][orderIndex] = newOrder;
+ }
+ }
+
+ /*
+ * Gets Order[] For User Address
+ * Return order at Index in Order[]
+ */
+
+ function getOrderByUser(address whitelistedAddress, uint256 orderIndex)
+ public
+ view
+ returns (
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice
+ )
+ {
+
+ Order storage orderAtIndex
+ = orderByUser[whitelistedAddress][orderIndex];
+ return (
+ orderAtIndex.orderType,
+ orderAtIndex.tokensGiven,
+ orderAtIndex.tokensRecieved,
+ orderAtIndex.mintingPrice
+ );
+ }
+
+ /*
+ * Save order to allOrders array
+ * overwrite == false, append to allOrders array
+ * overwrite == true, overwrite element at orderIndex
+ */
+ function setOrder(
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ uint256 orderIndex,
+ bool overwrite
+ ) public onlyOwnerOrTokenSwap() {
+ Order memory newOrder = Order(
+ orderType,
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice
+ );
+
+ if (!overwrite) {
+ allOrders.push(newOrder);
+ } else {
+ allOrders[orderIndex] = newOrder;
+ }
+ }
+
+ /*
+ * Get Order
+ */
+ function getOrder(uint256 index)
+ public
+ view
+ returns (
+ string memory orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice
+ )
+ {
+ Order storage orderAtIndex = allOrders[index];
+ return (
+ orderAtIndex.orderType,
+ orderAtIndex.tokensGiven,
+ orderAtIndex.tokensRecieved,
+ orderAtIndex.mintingPrice
+ );
+ }
+
+ // @dev Get accounting values for a specific day
+ // @param date format as 20200123 for 23th of January 2020
+ function getAccounting(uint256 date)
+ public
+ view
+ returns (
+ uint256,
+ uint256,
+ uint256,
+ uint256,
+ int256,
+ uint256,
+ uint256
+ )
+ {
+ Accounting[] storage accountingsForDate = accounting[date];
+ uint256 lastIndex = accountingsForDate.length - 1;
+ return (
+ accountingsForDate[lastIndex].tokenValueNetFees,
+ accountingsForDate[lastIndex].bestExecutionPrice,
+ accountingsForDate[lastIndex].markPrice,
+ accountingsForDate[lastIndex].notional,
+ accountingsForDate[lastIndex].changeInNotional,
+ accountingsForDate[lastIndex].tokenValue,
+ accountingsForDate[lastIndex].effectiveFundingRate
+ );
+ }
+
+ // @dev Set accounting values for the day
+ function setAccounting(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ int256 _changeInNotional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external onlyOwnerOrTokenSwap() {
+ (uint256 year, uint256 month, uint256 day) = DateTimeLibrary
+ .timestampToDate(block.timestamp);
+ uint256 today = year * 10000 + month * 100 + day;
+ accounting[today].push(
+ Accounting(
+ _tokenValueNetFees,
+ _bestExecutionPrice,
+ _markPrice,
+ _notional,
+ _changeInNotional,
+ _tokenValue,
+ _effectiveFundingRate
+ )
+ );
+ lastActivityDay = today;
+ emit AccountingValuesSet(today);
+ }
+
+ // @dev Set accounting values for the day
+ function setAccountingForLastActivityDay(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ int256 _changeInNotional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external onlyOwnerOrTokenSwap() {
+ accounting[lastActivityDay].push(
+ Accounting(
+ _tokenValueNetFees,
+ _bestExecutionPrice,
+ _markPrice,
+ _notional,
+ _changeInNotional,
+ _tokenValue,
+ _effectiveFundingRate
+ )
+ );
+ emit AccountingValuesSet(lastActivityDay);
+ }
+
+ // @dev Set last rebalance information
+ function setMinRebalanceAmount(uint256 _minRebalanceAmount)
+ external
+ onlyOwner
+ {
+ minRebalanceAmount = _minRebalanceAmount;
+
+ emit RebalanceValuesSet(minRebalanceAmount);
+ }
+
+ // @dev Set last rebalance information
+ function setManagementFee(uint256 _managementFee) external onlyOwner {
+ managementFee = _managementFee;
+ emit ManagementFeeValuesSet(managementFee);
+ }
+
+ // @dev Returns price
+ function getExecutionPrice() public view returns (uint256 price) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .bestExecutionPrice;
+ }
+
+ // @dev Returns price
+ function getMarkPrice() public view returns (uint256 price) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .markPrice;
+ }
+
+ // @dev Returns token value net of fees
+ function getTokenValueAfterFees() public view returns (uint256 amount) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .tokenValueNetFees;
+ }
+
+ // @dev Returns notional amount
+ function getNotional() public view returns (uint256 amount) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .notional;
+ }
+
+ // @dev Returns change in notional
+ function getChangeInNotional() public view returns (int256 amount) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .changeInNotional;
+ }
+
+ // @dev Returns token value
+ function getTokenValue() public view returns (uint256 lendingRate) {
+ return
+ accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
+ .tokenValue;
+ }
+
+ // @dev Sets last minting fee
+ function setLastMintingFee(uint256 _mintingFee) public onlyOwner {
+ mintingFee[~uint256(0)] = _mintingFee;
+ }
+
+ // @dev Adds minting fee
+ function addMintingFeeBracket(uint256 _mintingFeeLimit, uint256 _mintingFee)
+ public
+ onlyOwner
+ {
+ require(
+ mintingFeeBracket.length == 0 ||
+ _mintingFeeLimit >
+ mintingFeeBracket[mintingFeeBracket.length - 1],
+ "New minting fee bracket needs to be bigger then last one"
+ );
+ mintingFeeBracket.push(_mintingFeeLimit);
+ mintingFee[_mintingFeeLimit] = _mintingFee;
+ }
+
+ // @dev Deletes last minting fee
+ function deleteLastMintingFeeBracket() public onlyOwner {
+ delete mintingFee[mintingFeeBracket[mintingFeeBracket.length - 1]];
+ mintingFeeBracket.length--;
+ }
+
+ // @dev Changes minting fee
+ function changeMintingLimit(
+ uint256 _position,
+ uint256 _mintingFeeLimit,
+ uint256 _mintingFee
+ ) public onlyOwner {
+ require(
+ _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
+ "New minting fee bracket needs to be bigger then last one"
+ );
+ if (_position != 0) {
+ require(
+ _mintingFeeLimit > mintingFeeBracket[_position - 1],
+ "New minting fee bracket needs to be bigger then last one"
+ );
+ }
+ if (_position < mintingFeeBracket.length - 1) {
+ require(
+ _mintingFeeLimit < mintingFeeBracket[_position + 1],
+ "New minting fee bracket needs to be smaller then next one"
+ );
+ }
+ mintingFeeBracket[_position] = _mintingFeeLimit;
+ mintingFee[_mintingFeeLimit] = _mintingFee;
+ }
+
+ function getMintingFee(uint256 cash) public view returns (uint256) {
+ // Define Start + End Index
+ uint256 startIndex = 0;
+ if (mintingFeeBracket.length > 0) {
+ uint256 endIndex = mintingFeeBracket.length - 1;
+ uint256 middleIndex = endIndex / 2;
+
+ if (cash <= mintingFeeBracket[middleIndex]) {
+ endIndex = middleIndex;
+ } else {
+ startIndex = middleIndex + 1;
+ }
+
+ for (uint256 i = startIndex; i <= endIndex; i++) {
+ if (cash <= mintingFeeBracket[i]) {
+ return mintingFee[mintingFeeBracket[i]];
+ }
+ }
+ }
+
+ return mintingFee[~uint256(0)];
+ }
+
+ // @dev Sets last balance precision
+ function setLastPrecision(uint8 _balancePrecision) public onlyOwner {
+ balancePrecision = _balancePrecision;
+ }
+
+ // @dev Sets minimum minting fee
+ function setMinimumMintingFee(uint256 _minimumMintingFee) public onlyOwner {
+ minimumMintingFee = _minimumMintingFee;
+ }
+
+ // @dev Sets minimum trade value
+ function setMinimumTrade(uint256 _minimumTrade) public onlyOwner {
+ minimumTrade = _minimumTrade;
+ }
+}
diff --git a/flats/TokenSwapLeverage.sol b/flats/TokenSwapLeverage.sol
new file mode 100644
index 0000000..fa79071
--- /dev/null
+++ b/flats/TokenSwapLeverage.sol
@@ -0,0 +1,1522 @@
+
+// File: @openzeppelin/upgrades/contracts/Initializable.sol
+
+pragma solidity >=0.4.24 <0.6.0;
+
+
+/**
+ * @title Initializable
+ *
+ * @dev Helper contract to support initializer functions. To use it, replace
+ * the constructor with a function that has the `initializer` modifier.
+ * WARNING: Unlike constructors, initializer functions must be manually
+ * invoked. This applies both to deploying an Initializable contract, as well
+ * as extending an Initializable contract via inheritance.
+ * WARNING: When used with inheritance, manual care must be taken to not invoke
+ * a parent initializer twice, or ensure that all initializers are idempotent,
+ * because this is not dealt with automatically as with constructors.
+ */
+contract Initializable {
+
+ /**
+ * @dev Indicates that the contract has been initialized.
+ */
+ bool private initialized;
+
+ /**
+ * @dev Indicates that the contract is in the process of being initialized.
+ */
+ bool private initializing;
+
+ /**
+ * @dev Modifier to use in the initializer function of a contract.
+ */
+ modifier initializer() {
+ require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
+
+ bool isTopLevelCall = !initializing;
+ if (isTopLevelCall) {
+ initializing = true;
+ initialized = true;
+ }
+
+ _;
+
+ if (isTopLevelCall) {
+ initializing = false;
+ }
+ }
+
+ /// @dev Returns true if and only if the function is running in the constructor
+ function isConstructor() private view returns (bool) {
+ // extcodesize checks the size of the code stored in an address, and
+ // address returns the current address. Since the code is still not
+ // deployed when running a constructor, any checks on its code size will
+ // yield zero, making it an effective way to detect if a contract is
+ // under construction or not.
+ uint256 cs;
+ assembly { cs := extcodesize(address) }
+ return cs == 0;
+ }
+
+ // Reserved storage space to allow for layout changes in the future.
+ uint256[50] private ______gap;
+}
+
+// File: solidity-util/lib/Strings.sol
+
+pragma solidity ^0.5.0;
+
+/**
+ * Strings Library
+ *
+ * In summary this is a simple library of string functions which make simple
+ * string operations less tedious in solidity.
+ *
+ * Please be aware these functions can be quite gas heavy so use them only when
+ * necessary not to clog the blockchain with expensive transactions.
+ *
+ * @author James Lockhart
+ */
+library Strings {
+
+ /**
+ * Concat (High gas cost)
+ *
+ * Appends two strings together and returns a new value
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string which will be the concatenated
+ * prefix
+ * @param _value The value to be the concatenated suffix
+ * @return string The resulting string from combinging the base and value
+ */
+ function concat(string memory _base, string memory _value)
+ internal
+ pure
+ returns (string memory) {
+ bytes memory _baseBytes = bytes(_base);
+ bytes memory _valueBytes = bytes(_value);
+
+ assert(_valueBytes.length > 0);
+
+ string memory _tmpValue = new string(_baseBytes.length +
+ _valueBytes.length);
+ bytes memory _newValue = bytes(_tmpValue);
+
+ uint i;
+ uint j;
+
+ for (i = 0; i < _baseBytes.length; i++) {
+ _newValue[j++] = _baseBytes[i];
+ }
+
+ for (i = 0; i < _valueBytes.length; i++) {
+ _newValue[j++] = _valueBytes[i];
+ }
+
+ return string(_newValue);
+ }
+
+ /**
+ * Index Of
+ *
+ * Locates and returns the position of a character within a string
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string acting as the haystack to be
+ * searched
+ * @param _value The needle to search for, at present this is currently
+ * limited to one character
+ * @return int The position of the needle starting from 0 and returning -1
+ * in the case of no matches found
+ */
+ function indexOf(string memory _base, string memory _value)
+ internal
+ pure
+ returns (int) {
+ return _indexOf(_base, _value, 0);
+ }
+
+ /**
+ * Index Of
+ *
+ * Locates and returns the position of a character within a string starting
+ * from a defined offset
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string acting as the haystack to be
+ * searched
+ * @param _value The needle to search for, at present this is currently
+ * limited to one character
+ * @param _offset The starting point to start searching from which can start
+ * from 0, but must not exceed the length of the string
+ * @return int The position of the needle starting from 0 and returning -1
+ * in the case of no matches found
+ */
+ function _indexOf(string memory _base, string memory _value, uint _offset)
+ internal
+ pure
+ returns (int) {
+ bytes memory _baseBytes = bytes(_base);
+ bytes memory _valueBytes = bytes(_value);
+
+ assert(_valueBytes.length == 1);
+
+ for (uint i = _offset; i < _baseBytes.length; i++) {
+ if (_baseBytes[i] == _valueBytes[0]) {
+ return int(i);
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * Length
+ *
+ * Returns the length of the specified string
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string to be measured
+ * @return uint The length of the passed string
+ */
+ function length(string memory _base)
+ internal
+ pure
+ returns (uint) {
+ bytes memory _baseBytes = bytes(_base);
+ return _baseBytes.length;
+ }
+
+ /**
+ * Sub String
+ *
+ * Extracts the beginning part of a string based on the desired length
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string that will be used for
+ * extracting the sub string from
+ * @param _length The length of the sub string to be extracted from the base
+ * @return string The extracted sub string
+ */
+ function substring(string memory _base, int _length)
+ internal
+ pure
+ returns (string memory) {
+ return _substring(_base, _length, 0);
+ }
+
+ /**
+ * Sub String
+ *
+ * Extracts the part of a string based on the desired length and offset. The
+ * offset and length must not exceed the lenth of the base string.
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string that will be used for
+ * extracting the sub string from
+ * @param _length The length of the sub string to be extracted from the base
+ * @param _offset The starting point to extract the sub string from
+ * @return string The extracted sub string
+ */
+ function _substring(string memory _base, int _length, int _offset)
+ internal
+ pure
+ returns (string memory) {
+ bytes memory _baseBytes = bytes(_base);
+
+ assert(uint(_offset + _length) <= _baseBytes.length);
+
+ string memory _tmp = new string(uint(_length));
+ bytes memory _tmpBytes = bytes(_tmp);
+
+ uint j = 0;
+ for (uint i = uint(_offset); i < uint(_offset + _length); i++) {
+ _tmpBytes[j++] = _baseBytes[i];
+ }
+
+ return string(_tmpBytes);
+ }
+
+ /**
+ * String Split (Very high gas cost)
+ *
+ * Splits a string into an array of strings based off the delimiter value.
+ * Please note this can be quite a gas expensive function due to the use of
+ * storage so only use if really required.
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string value to be split.
+ * @param _value The delimiter to split the string on which must be a single
+ * character
+ * @return string[] An array of values split based off the delimiter, but
+ * do not container the delimiter.
+ */
+ function split(string memory _base, string memory _value)
+ internal
+ pure
+ returns (string[] memory splitArr) {
+ bytes memory _baseBytes = bytes(_base);
+
+ uint _offset = 0;
+ uint _splitsCount = 1;
+ while (_offset < _baseBytes.length - 1) {
+ int _limit = _indexOf(_base, _value, _offset);
+ if (_limit == -1)
+ break;
+ else {
+ _splitsCount++;
+ _offset = uint(_limit) + 1;
+ }
+ }
+
+ splitArr = new string[](_splitsCount);
+
+ _offset = 0;
+ _splitsCount = 0;
+ while (_offset < _baseBytes.length - 1) {
+
+ int _limit = _indexOf(_base, _value, _offset);
+ if (_limit == - 1) {
+ _limit = int(_baseBytes.length);
+ }
+
+ string memory _tmp = new string(uint(_limit) - _offset);
+ bytes memory _tmpBytes = bytes(_tmp);
+
+ uint j = 0;
+ for (uint i = _offset; i < uint(_limit); i++) {
+ _tmpBytes[j++] = _baseBytes[i];
+ }
+ _offset = uint(_limit) + 1;
+ splitArr[_splitsCount++] = string(_tmpBytes);
+ }
+ return splitArr;
+ }
+
+ /**
+ * Compare To
+ *
+ * Compares the characters of two strings, to ensure that they have an
+ * identical footprint
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string base to compare against
+ * @param _value The string the base is being compared to
+ * @return bool Simply notates if the two string have an equivalent
+ */
+ function compareTo(string memory _base, string memory _value)
+ internal
+ pure
+ returns (bool) {
+ bytes memory _baseBytes = bytes(_base);
+ bytes memory _valueBytes = bytes(_value);
+
+ if (_baseBytes.length != _valueBytes.length) {
+ return false;
+ }
+
+ for (uint i = 0; i < _baseBytes.length; i++) {
+ if (_baseBytes[i] != _valueBytes[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Compare To Ignore Case (High gas cost)
+ *
+ * Compares the characters of two strings, converting them to the same case
+ * where applicable to alphabetic characters to distinguish if the values
+ * match.
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string base to compare against
+ * @param _value The string the base is being compared to
+ * @return bool Simply notates if the two string have an equivalent value
+ * discarding case
+ */
+ function compareToIgnoreCase(string memory _base, string memory _value)
+ internal
+ pure
+ returns (bool) {
+ bytes memory _baseBytes = bytes(_base);
+ bytes memory _valueBytes = bytes(_value);
+
+ if (_baseBytes.length != _valueBytes.length) {
+ return false;
+ }
+
+ for (uint i = 0; i < _baseBytes.length; i++) {
+ if (_baseBytes[i] != _valueBytes[i] &&
+ _upper(_baseBytes[i]) != _upper(_valueBytes[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Upper
+ *
+ * Converts all the values of a string to their corresponding upper case
+ * value.
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string base to convert to upper case
+ * @return string
+ */
+ function upper(string memory _base)
+ internal
+ pure
+ returns (string memory) {
+ bytes memory _baseBytes = bytes(_base);
+ for (uint i = 0; i < _baseBytes.length; i++) {
+ _baseBytes[i] = _upper(_baseBytes[i]);
+ }
+ return string(_baseBytes);
+ }
+
+ /**
+ * Lower
+ *
+ * Converts all the values of a string to their corresponding lower case
+ * value.
+ *
+ * @param _base When being used for a data type this is the extended object
+ * otherwise this is the string base to convert to lower case
+ * @return string
+ */
+ function lower(string memory _base)
+ internal
+ pure
+ returns (string memory) {
+ bytes memory _baseBytes = bytes(_base);
+ for (uint i = 0; i < _baseBytes.length; i++) {
+ _baseBytes[i] = _lower(_baseBytes[i]);
+ }
+ return string(_baseBytes);
+ }
+
+ /**
+ * Upper
+ *
+ * Convert an alphabetic character to upper case and return the original
+ * value when not alphabetic
+ *
+ * @param _b1 The byte to be converted to upper case
+ * @return bytes1 The converted value if the passed value was alphabetic
+ * and in a lower case otherwise returns the original value
+ */
+ function _upper(bytes1 _b1)
+ private
+ pure
+ returns (bytes1) {
+
+ if (_b1 >= 0x61 && _b1 <= 0x7A) {
+ return bytes1(uint8(_b1) - 32);
+ }
+
+ return _b1;
+ }
+
+ /**
+ * Lower
+ *
+ * Convert an alphabetic character to lower case and return the original
+ * value when not alphabetic
+ *
+ * @param _b1 The byte to be converted to lower case
+ * @return bytes1 The converted value if the passed value was alphabetic
+ * and in a upper case otherwise returns the original value
+ */
+ function _lower(bytes1 _b1)
+ private
+ pure
+ returns (bytes1) {
+
+ if (_b1 >= 0x41 && _b1 <= 0x5A) {
+ return bytes1(uint8(_b1) + 32);
+ }
+
+ return _b1;
+ }
+}
+
+// File: @openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol
+
+pragma solidity ^0.5.0;
+
+/**
+ * @dev Wrappers over Solidity's arithmetic operations with added overflow
+ * checks.
+ *
+ * Arithmetic operations in Solidity wrap on overflow. This can easily result
+ * in bugs, because programmers usually assume that an overflow raises an
+ * error, which is the standard behavior in high level programming languages.
+ * `SafeMath` restores this intuition by reverting the transaction when an
+ * operation overflows.
+ *
+ * Using this library instead of the unchecked operations eliminates an entire
+ * class of bugs, so it's recommended to use it always.
+ */
+library SafeMath {
+ /**
+ * @dev Returns the addition of two unsigned integers, reverting on
+ * overflow.
+ *
+ * Counterpart to Solidity's `+` operator.
+ *
+ * Requirements:
+ * - Addition cannot overflow.
+ */
+ function add(uint256 a, uint256 b) internal pure returns (uint256) {
+ uint256 c = a + b;
+ require(c >= a, "SafeMath: addition overflow");
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the subtraction of two unsigned integers, reverting on
+ * overflow (when the result is negative).
+ *
+ * Counterpart to Solidity's `-` operator.
+ *
+ * Requirements:
+ * - Subtraction cannot overflow.
+ */
+ function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+ return sub(a, b, "SafeMath: subtraction overflow");
+ }
+
+ /**
+ * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
+ * overflow (when the result is negative).
+ *
+ * Counterpart to Solidity's `-` operator.
+ *
+ * Requirements:
+ * - Subtraction cannot overflow.
+ *
+ * _Available since v2.4.0._
+ */
+ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+ require(b <= a, errorMessage);
+ uint256 c = a - b;
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the multiplication of two unsigned integers, reverting on
+ * overflow.
+ *
+ * Counterpart to Solidity's `*` operator.
+ *
+ * Requirements:
+ * - Multiplication cannot 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-contracts/pull/522
+ if (a == 0) {
+ return 0;
+ }
+
+ uint256 c = a * b;
+ require(c / a == b, "SafeMath: multiplication overflow");
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the integer division of two unsigned integers. Reverts on
+ * division by zero. The result is rounded towards zero.
+ *
+ * Counterpart to Solidity's `/` operator. Note: this function uses a
+ * `revert` opcode (which leaves remaining gas untouched) while Solidity
+ * uses an invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ */
+ function div(uint256 a, uint256 b) internal pure returns (uint256) {
+ return div(a, b, "SafeMath: division by zero");
+ }
+
+ /**
+ * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
+ * division by zero. The result is rounded towards zero.
+ *
+ * Counterpart to Solidity's `/` operator. Note: this function uses a
+ * `revert` opcode (which leaves remaining gas untouched) while Solidity
+ * uses an invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ *
+ * _Available since v2.4.0._
+ */
+ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+ // Solidity only automatically asserts when dividing by 0
+ require(b > 0, errorMessage);
+ uint256 c = a / b;
+ // assert(a == b * c + a % b); // There is no case in which this doesn't hold
+
+ return c;
+ }
+
+ /**
+ * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
+ * Reverts when dividing by zero.
+ *
+ * Counterpart to Solidity's `%` operator. This function uses a `revert`
+ * opcode (which leaves remaining gas untouched) while Solidity uses an
+ * invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ */
+ function mod(uint256 a, uint256 b) internal pure returns (uint256) {
+ return mod(a, b, "SafeMath: modulo by zero");
+ }
+
+ /**
+ * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
+ * Reverts with custom message when dividing by zero.
+ *
+ * Counterpart to Solidity's `%` operator. This function uses a `revert`
+ * opcode (which leaves remaining gas untouched) while Solidity uses an
+ * invalid opcode to revert (consuming all remaining gas).
+ *
+ * Requirements:
+ * - The divisor cannot be zero.
+ *
+ * _Available since v2.4.0._
+ */
+ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
+ require(b != 0, errorMessage);
+ return a % b;
+ }
+}
+
+// File: @openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol
+
+pragma solidity ^0.5.0;
+
+
+/*
+ * @dev Provides information about the current execution context, including the
+ * sender of the transaction and its data. While these are generally available
+ * via msg.sender and msg.data, they should not be accessed in such a direct
+ * manner, since when dealing with GSN meta-transactions the account sending and
+ * paying for execution may not be the actual sender (as far as an application
+ * is concerned).
+ *
+ * This contract is only required for intermediate, library-like contracts.
+ */
+contract Context is Initializable {
+ // Empty internal constructor, to prevent people from mistakenly deploying
+ // an instance of this contract, which should be used via inheritance.
+ constructor () internal { }
+ // solhint-disable-previous-line no-empty-blocks
+
+ function _msgSender() internal view returns (address payable) {
+ return msg.sender;
+ }
+
+ function _msgData() internal view returns (bytes memory) {
+ this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
+ return msg.data;
+ }
+}
+
+// File: @openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol
+
+pragma solidity ^0.5.0;
+
+
+
+/**
+ * @dev Contract module which provides a basic access control mechanism, where
+ * there is an account (an owner) that can be granted exclusive access to
+ * specific functions.
+ *
+ * This module is used through inheritance. It will make available the modifier
+ * `onlyOwner`, which can be aplied to your functions to restrict their use to
+ * the owner.
+ */
+contract Ownable is Initializable, Context {
+ address private _owner;
+
+ event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
+
+ /**
+ * @dev Initializes the contract setting the deployer as the initial owner.
+ */
+ function initialize(address sender) public initializer {
+ _owner = sender;
+ emit OwnershipTransferred(address(0), _owner);
+ }
+
+ /**
+ * @dev Returns the address of the current owner.
+ */
+ function owner() public view returns (address) {
+ return _owner;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwner() {
+ require(isOwner(), "Ownable: caller is not the owner");
+ _;
+ }
+
+ /**
+ * @dev Returns true if the caller is the current owner.
+ */
+ function isOwner() public view returns (bool) {
+ return _msgSender() == _owner;
+ }
+
+ /**
+ * @dev Leaves the contract without owner. It will not be possible to call
+ * `onlyOwner` functions anymore. Can only be called by the current owner.
+ *
+ * > Note: 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 Transfers ownership of the contract to a new account (`newOwner`).
+ * Can only be called by the current owner.
+ */
+ function transferOwnership(address newOwner) public onlyOwner {
+ _transferOwnership(newOwner);
+ }
+
+ /**
+ * @dev Transfers ownership of the contract to a new account (`newOwner`).
+ */
+ function _transferOwnership(address newOwner) internal {
+ require(newOwner != address(0), "Ownable: new owner is the zero address");
+ emit OwnershipTransferred(_owner, newOwner);
+ _owner = newOwner;
+ }
+
+ uint256[50] private ______gap;
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceCashPool.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceCashPool {
+ function kycVerifier() external view returns (address);
+
+ function moveTokenToPool(
+ address _token,
+ address whiteListedAddress,
+ uint256 orderAmount
+ ) external returns (bool);
+
+ function moveTokenfromPool(
+ address _token,
+ address destinationAddress,
+ uint256 orderAmount
+ ) external returns (bool);
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceKYCVerifier.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceKYCVerifier {
+ function isAddressWhitelisted(address) external view returns (bool);
+}
+
+// File: contracts/leverage-tokens/Abstract/InterfaceCalculator.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceCalculator {
+
+ function getTokensCreatedByCash(
+ uint256 mintingPrice,
+ uint256 cash,
+ uint256 gasFee
+ ) external view returns (uint256 tokensCreated) ;
+
+ function getCashCreatedByTokens(
+ uint256 burningPrice,
+ uint256 elapsedTime,
+ uint256 tokens,
+ uint256 gasFee
+ ) external view returns (uint256 stablecoinRedeemed);
+
+
+ function calculateRebalanceValues(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notionalPreRebalance,
+ uint256 _targetLeverage
+ ) external view
+ returns (
+ uint256 notional,
+ int256 changeInNotional,
+ uint256 tokenValue
+ );
+
+
+ function removeCurrentMintingFeeFromCash(uint256 _cash)
+ external view returns (uint256 cashAfterFee);
+
+ function removeMintingFeeFromCash(
+ uint256 _cash,
+ uint256 _mintingFee,
+ uint256 _minimumMintingFee
+ ) external pure returns (uint256 cashAfterFee);
+
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceERC20.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceERC20 {
+ function decimals() external view returns (uint8);
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceInverseToken.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceInverseToken {
+ function mintTokens(address, uint256) external returns (bool);
+
+ function burnTokens(address, uint256) external returns (bool);
+
+ /**
+ * @dev Returns the amount of tokens in existence.
+ */
+ function totalSupply() external view returns (uint256);
+
+ /**
+ * @dev Returns the amount of tokens owned by `account`.
+ */
+ function balanceOf(address account) external view returns (uint256);
+
+ /**
+ * @dev Moves `amount` tokens from the caller's account to `recipient`.
+ *
+ * Returns a boolean value indicating whether the operation succeeded.
+ *
+ * Emits a {Transfer} event.
+ */
+ function transfer(address recipient, uint256 amount)
+ external
+ returns (bool);
+
+ /**
+ * @dev Returns the remaining number of tokens that `spender` will be
+ * allowed to spend on behalf of `owner` through {transferFrom}. This is
+ * zero by default.
+ *
+ * This value changes when {approve} or {transferFrom} are called.
+ */
+ function allowance(address owner, address spender)
+ external
+ view
+ returns (uint256);
+
+ /**
+ * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
+ *
+ * Returns a boolean value indicating whether the operation succeeded.
+ *
+ * IMPORTANT: Beware that changing an allowance with this method brings the risk
+ * that someone may use both the old and the new allowance by unfortunate
+ * transaction ordering. One possible solution to mitigate this race
+ * condition is to first reduce the spender's allowance to 0 and set the
+ * desired value afterwards:
+ * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+ *
+ * Emits an {Approval} event.
+ */
+ function approve(address spender, uint256 amount) external returns (bool);
+
+ /**
+ * @dev Moves `amount` tokens from `sender` to `recipient` using the
+ * allowance mechanism. `amount` is then deducted from the caller's
+ * allowance.
+ *
+ * Returns a boolean value indicating whether the operation succeeded.
+ *
+ * Emits a {Transfer} event.
+ */
+ function transferFrom(address sender, address recipient, uint256 amount)
+ external
+ returns (bool);
+
+ /**
+ * @dev Emitted when `value` tokens are moved from one account (`from`) to
+ * another (`to`).
+ *
+ * Note that `value` may be zero.
+ */
+ event Transfer(address indexed from, address indexed to, uint256 value);
+
+ /**
+ * @dev Emitted when the allowance of a `spender` for an `owner` is set by
+ * a call to {approve}. `value` is the new allowance.
+ */
+ event Approval(
+ address indexed owner,
+ address indexed spender,
+ uint256 value
+ );
+}
+
+// File: contracts/leverage-tokens/Abstract/InterfaceStorageLeverage.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceStorageLeverage {
+ function whitelistedAddresses(address) external view returns (bool);
+
+ function isPaused() external view returns (bool);
+
+ function isShutdown() external view returns (bool);
+
+ function tokenSwapManager() external view returns (address);
+
+ function bridge() external view returns (address);
+
+ function managementFee() external view returns (uint256);
+
+ function getExecutionPrice() external view returns (uint256);
+
+ function getMarkPrice() external view returns (uint256);
+
+ function getTokenValueAfterFees() external view returns (uint256);
+
+ function getNotional() external view returns (uint256);
+
+ function getTokenValue() external view returns (uint256);
+
+ function getChangeInNotional() external view returns (int256);
+
+ function getMintingFee(uint256 cash) external view returns (uint256);
+
+ function minimumMintingFee() external view returns (uint256);
+
+ function minRebalanceAmount() external view returns (uint8);
+
+ function delayedRedemptionsByUser(address) external view returns (uint256);
+
+ function setDelayedRedemptionsByUser(
+ uint256 amountToRedeem,
+ address whitelistedAddress
+ ) external;
+
+ function setOrderByUser(
+ address whitelistedAddress,
+ string calldata orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ uint256 orderIndex,
+ bool overwrite
+ ) external;
+
+ function setAccounting(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ int256 _changeInNotional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external;
+
+ function setAccountingForLastActivityDay(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notional,
+ int256 _changeInNotional,
+ uint256 _tokenValue,
+ uint256 _effectiveFundingRate
+ ) external;
+}
+
+// File: contracts/short-tokens/utils/Math.sol
+
+/// Math.sol -- mixin for inline numerical wizardry
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+pragma solidity ^0.5.0;
+
+
+library DSMath {
+
+ // --- Unsigned Math ----
+ function add(uint x, uint y) internal pure returns (uint z) {
+ require((z = x + y) >= x, "ds-math-add-overflow");
+ }
+ function sub(uint x, uint y) internal pure returns (uint z) {
+ require((z = x - y) <= x, "ds-math-sub-underflow");
+ }
+ function mul(uint x, uint y) internal pure returns (uint z) {
+ require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
+ }
+
+
+ function min(uint x, uint y) internal pure returns (uint z) {
+ return x <= y ? x : y;
+ }
+ function max(uint x, uint y) internal pure returns (uint z) {
+ return x >= y ? x : y;
+ }
+ function imin(int x, int y) internal pure returns (int z) {
+ return x <= y ? x : y;
+ }
+ function imax(int x, int y) internal pure returns (int z) {
+ return x >= y ? x : y;
+ }
+
+ // --- Precise Math ---
+
+ uint public constant WAD = 10 ** 18;
+ uint public constant RAY = 10 ** 27;
+
+ function wmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), WAD / 2) / WAD;
+ }
+ function rmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), RAY / 2) / RAY;
+ }
+ function wdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, WAD), y / 2) / y;
+ }
+ function rdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, RAY), y / 2) / y;
+ }
+
+ function ray(uint _wad) internal pure returns (uint) {
+ return mul(_wad, uint(10 ** 9));
+ }
+
+ // This famous algorithm is called "exponentiation by squaring"
+ // and calculates x^n with x as fixed-point and n as regular unsigned.
+ //
+ // It's O(log n), instead of O(n) for naive repeated multiplication.
+ //
+ // These facts are why it works:
+ //
+ // If n is even, then x^n = (x^2)^(n/2).
+ // If n is odd, then x^n = x * x^(n-1),
+ // and applying the equation for even x gives
+ // x^n = x * (x^2)^((n-1) / 2).
+ //
+ // Also, EVM division is flooring and
+ // floor[(n-1) / 2] = floor[n / 2].
+ //
+ function rpow(uint x, uint n) internal pure returns (uint z) {
+ z = n % 2 != 0 ? x : RAY;
+
+ for (n /= 2; n != 0; n /= 2) {
+ x = rmul(x, x);
+
+ if (n % 2 != 0) {
+ z = rmul(z, x);
+ }
+ }
+ }
+}
+
+// File: contracts/leverage-tokens/TokenSwapLeverage.sol
+
+pragma solidity ^0.5.0;
+
+
+
+
+
+
+
+
+
+
+
+
+
+contract TokenSwapLeverage is Initializable, Ownable {
+ using Strings for string;
+ using SafeMath for uint256;
+
+ address public inverseToken;
+
+ InterfaceERC20 public erc20;
+ InterfaceKYCVerifier public kycVerifier;
+ InterfaceCashPool public cashPool;
+ InterfaceStorageLeverage public persistentStorage;
+ InterfaceCalculator public compositionCalculator;
+
+ event SuccessfulOrder(
+ string orderType,
+ address whitelistedAddress,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ address stablecoin,
+ uint256 price
+ );
+
+ event RebalanceEvent(
+ uint256 tokenValueNetFees,
+ uint256 bestExecutionPrice,
+ uint256 markPrice,
+ uint256 notional,
+ int256 changeInNotional,
+ uint256 tokenValue,
+ uint256 effectiveFundingRate
+ );
+
+ function initialize(
+ address _owner,
+ address _inverseToken,
+ address _cashPool,
+ address _storage,
+ address _compositionCalculator
+ ) public initializer {
+ initialize(_owner);
+
+ require(
+ _owner != address(0) &&
+ _inverseToken != address(0) &&
+ _cashPool != address(0) &&
+ _storage != address(0) &&
+ _compositionCalculator != address(0),
+ "addresses cannot be zero"
+ );
+
+ inverseToken = _inverseToken;
+
+ cashPool = InterfaceCashPool(_cashPool);
+ persistentStorage = InterfaceStorageLeverage(_storage);
+ kycVerifier = InterfaceKYCVerifier(address(cashPool.kycVerifier()));
+ compositionCalculator = InterfaceCalculator(_compositionCalculator);
+ }
+
+ //////////////// Create + Redeem Order Request ////////////////
+ //////////////// Create: Recieve Inverse Token ////////////////
+ //////////////// Redeem: Recieve Stable Coin ////////////////
+
+ function createOrder(
+ bool success,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 mintingPrice,
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 gasFee
+ ) public onlyOwnerOrBridge() notPausedOrShutdown() returns (bool retVal) {
+ // Require is Whitelisted
+ require(
+ kycVerifier.isAddressWhitelisted(whitelistedAddress),
+ "only whitelisted address may place orders"
+ );
+
+ // Return Funds if Bridge Pass an Error
+ if (!success) {
+ transferTokenFromPool(
+ stablecoin,
+ whitelistedAddress,
+ normalizeStablecoin(tokensGiven, stablecoin)
+ );
+ return false;
+ }
+
+ // Check Tokens Recieved with Composition Calculator
+ uint256 _tokensRecieved = compositionCalculator.getTokensCreatedByCash(
+ mintingPrice,
+ tokensGiven,
+ gasFee
+ );
+ require(
+ _tokensRecieved == tokensRecieved,
+ "tokens created must equal tokens recieved"
+ );
+
+ // Save Order to Storage and Lock Funds for 1 Hour
+ persistentStorage.setOrderByUser(
+ whitelistedAddress,
+ "CREATE",
+ tokensGiven,
+ tokensRecieved,
+ mintingPrice,
+ 0,
+ false
+ );
+
+ // Write Successful Order to Log
+ writeOrderResponse(
+ "CREATE",
+ whitelistedAddress,
+ tokensGiven,
+ tokensRecieved,
+ stablecoin,
+ mintingPrice
+ );
+
+ // Mint Tokens to Address
+ InterfaceInverseToken token = InterfaceInverseToken(inverseToken);
+ token.mintTokens(whitelistedAddress, tokensRecieved);
+
+ return true;
+ }
+
+ function redeemOrder(
+ bool success,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 burningPrice,
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 gasFee,
+ uint256 elapsedTime
+ ) public onlyOwnerOrBridge() notPausedOrShutdown() returns (bool retVal) {
+ // Require Whitelisted
+ require(
+ kycVerifier.isAddressWhitelisted(whitelistedAddress),
+ "only whitelisted address may place orders"
+ );
+
+ // Return Funds if Bridge Pass an Error
+ if (!success) {
+ transferTokenFromPool(
+ inverseToken,
+ whitelistedAddress,
+ tokensGiven
+ );
+ return false;
+ }
+
+ // Check Cash Recieved with Composition Calculator
+ uint256 _tokensRecieved = compositionCalculator.getCashCreatedByTokens(
+ burningPrice,
+ elapsedTime,
+ tokensGiven,
+ gasFee
+ );
+ require(
+ _tokensRecieved == tokensRecieved,
+ "cash redeemed must equal tokens recieved"
+ );
+
+ // Save To Storage
+ persistentStorage.setOrderByUser(
+ whitelistedAddress,
+ "REDEEM",
+ tokensGiven,
+ tokensRecieved,
+ burningPrice,
+ 0,
+ false
+ );
+
+ // Redeem Stablecoin or Perform Delayed Settlement
+ redeemFunds(
+ tokensGiven,
+ tokensRecieved,
+ whitelistedAddress,
+ stablecoin,
+ burningPrice
+ );
+
+ // Burn Tokens to Address
+ InterfaceInverseToken token = InterfaceInverseToken(inverseToken);
+ token.burnTokens(address(cashPool), tokensGiven);
+
+ return true;
+ }
+
+ function writeOrderResponse(
+ string memory orderType,
+ address whiteListedAddress,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ address stablecoin,
+ uint256 price
+ ) internal {
+ require(
+ tokensGiven != 0 && tokensRecieved != 0,
+ "amount must be greater than 0"
+ );
+
+ emit SuccessfulOrder(
+ orderType,
+ whiteListedAddress,
+ tokensGiven,
+ tokensRecieved,
+ stablecoin,
+ price
+ );
+ }
+
+ function settleDelayedFunds(
+ uint256 tokensToRedeem,
+ address whitelistedAddress,
+ address stablecoin
+ ) public onlyOwnerOrBridge notPausedOrShutdown {
+ require(
+ kycVerifier.isAddressWhitelisted(whitelistedAddress),
+ "only whitelisted may redeem funds"
+ );
+
+ bool isSufficientFunds = isHotWalletSufficient(
+ tokensToRedeem,
+ stablecoin
+ );
+ require(
+ isSufficientFunds == true,
+ "not enough funds in the hot wallet"
+ );
+
+ uint256 tokensOutstanding = persistentStorage.delayedRedemptionsByUser(
+ whitelistedAddress
+ );
+ uint256 tokensRemaining = DSMath.sub(tokensOutstanding, tokensToRedeem);
+
+ persistentStorage.setDelayedRedemptionsByUser(
+ tokensRemaining,
+ whitelistedAddress
+ );
+ transferTokenFromPool(
+ stablecoin,
+ whitelistedAddress,
+ normalizeStablecoin(tokensToRedeem, stablecoin)
+ );
+ }
+
+ function redeemFunds(
+ uint256 tokensGiven,
+ uint256 tokensToRedeem,
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 price
+ ) internal {
+ bool isSufficientFunds = isHotWalletSufficient(
+ tokensToRedeem,
+ stablecoin
+ );
+
+ if (isSufficientFunds) {
+ transferTokenFromPool(
+ stablecoin,
+ whitelistedAddress,
+ normalizeStablecoin(tokensToRedeem, stablecoin)
+ );
+ writeOrderResponse(
+ "REDEEM",
+ whitelistedAddress,
+ tokensGiven,
+ tokensToRedeem,
+ stablecoin,
+ price
+ );
+ } else {
+ uint256 tokensOutstanding = persistentStorage
+ .delayedRedemptionsByUser(whitelistedAddress);
+ tokensOutstanding = DSMath.add(tokensOutstanding, tokensToRedeem);
+ persistentStorage.setDelayedRedemptionsByUser(
+ tokensOutstanding,
+ whitelistedAddress
+ );
+ writeOrderResponse(
+ "REDEEM_NO_SETTLEMENT",
+ whitelistedAddress,
+ tokensGiven,
+ tokensToRedeem,
+ stablecoin,
+ price
+ );
+ }
+ }
+
+ function isHotWalletSufficient(uint256 tokensToRedeem, address stablecoin)
+ internal
+ returns (bool)
+ {
+ InterfaceInverseToken _stablecoin = InterfaceInverseToken(stablecoin);
+ uint256 stablecoinBalance = _stablecoin.balanceOf(address(cashPool));
+
+ if (normalizeStablecoin(tokensToRedeem, stablecoin) > stablecoinBalance)
+ return false;
+ return true;
+ }
+
+ function normalizeStablecoin(uint256 stablecoinValue, address stablecoin)
+ internal
+ returns (uint256)
+ {
+ erc20 = InterfaceERC20(stablecoin);
+ uint256 exponent = 18 - erc20.decimals();
+ return stablecoinValue / 10**exponent; // 6 decimal stable coin = 10**12
+ }
+
+ //////////////// Daily Rebalance ////////////////
+ //////////////// Threshold Rebalance ////////////////
+
+ /**
+ * @dev Performs rebalance calculations and saves them in persistent storages
+ * @param _tokenValueNetFees The token value after fees are removed
+ * @param _bestExecutionPrice The best execution price for rebalancing
+ * @param _markPrice The Mark Price
+ * @param _notionalPreRebalance The notional amount before rebalance
+ * @param _targetLeverage The targetLeverage
+ * @param _effectiveFundingRate The effectiveFundingRate
+ */
+ function rebalance(
+ uint256 _tokenValueNetFees,
+ uint256 _bestExecutionPrice,
+ uint256 _markPrice,
+ uint256 _notionalPreRebalance,
+ uint256 _targetLeverage,
+ uint256 _effectiveFundingRate
+ ) public onlyOwnerOrBridge() notPausedOrShutdown() {
+ (
+ uint256 notional,
+ int256 changeInNotional,
+ uint256 tokenValue
+ ) = compositionCalculator.calculateRebalanceValues(
+ _tokenValueNetFees,
+ _bestExecutionPrice,
+ _markPrice,
+ _notionalPreRebalance,
+ _targetLeverage
+ );
+
+ persistentStorage.setAccounting(
+ _tokenValueNetFees,
+ _bestExecutionPrice,
+ _markPrice,
+ notional,
+ changeInNotional,
+ tokenValue,
+ _effectiveFundingRate
+ );
+ emit RebalanceEvent(
+ _tokenValueNetFees,
+ _bestExecutionPrice,
+ _markPrice,
+ notional,
+ changeInNotional,
+ tokenValue,
+ _effectiveFundingRate
+ );
+ }
+
+ //////////////// Transfer Stablecoin Out of Pool ////////////////
+ //////////////// Transfer Stablecoin In of Pool ////////////////
+ //////////////// Transfer InverseToken Out of Pool ////////////////
+ //////////////// Transfer InverseToken In of Pool ////////////////
+
+ function transferTokenToPool(
+ address tokenContract,
+ address whiteListedAddress,
+ uint256 orderAmount
+ ) internal returns (bool) {
+ // Check orderAmount <= availableAmount
+ // Transfer USDC to Stablecoin Cash Pool
+ return
+ cashPool.moveTokenToPool(
+ tokenContract,
+ whiteListedAddress,
+ orderAmount
+ );
+ }
+
+ function transferTokenFromPool(
+ address tokenContract,
+ address destinationAddress,
+ uint256 orderAmount
+ ) internal returns (bool) {
+ // Check orderAmount <= availableAmount
+ // Transfer USDC to Destination Address
+ return
+ cashPool.moveTokenfromPool(
+ tokenContract,
+ destinationAddress,
+ orderAmount
+ );
+ }
+
+ function setCashPool(address _cashPool) public onlyOwner {
+ require(_cashPool != address(0), "adddress must not be empty");
+ cashPool = InterfaceCashPool(_cashPool);
+ }
+
+ function setStorage(address _storage) public onlyOwner {
+ require(_storage != address(0), "adddress must not be empty");
+ persistentStorage = InterfaceStorageLeverage(_storage);
+ }
+
+ function setKycVerfier(address _kycVerifier) public onlyOwner {
+ require(_kycVerifier != address(0), "adddress must not be empty");
+ kycVerifier = InterfaceKYCVerifier(_kycVerifier);
+ }
+
+ function setCalculator(address _calculator) public onlyOwner {
+ require(_calculator != address(0), "adddress must not be empty");
+ compositionCalculator = InterfaceCalculator(_calculator);
+ }
+
+ modifier onlyOwnerOrBridge() {
+ require(
+ isOwner() || _msgSender() == persistentStorage.bridge(),
+ "caller is not the owner or bridge"
+ );
+ _;
+ }
+
+ modifier notPausedOrShutdown() {
+ require(persistentStorage.isPaused() == false, "contract is paused");
+ require(
+ persistentStorage.isShutdown() == false,
+ "contract is shutdown"
+ );
+ _;
+ }
+}
diff --git a/flats/TokenSwapManager.sol b/flats/TokenSwapManager.sol
index 32c7414..352a3a7 100644
--- a/flats/TokenSwapManager.sol
+++ b/flats/TokenSwapManager.sol
@@ -719,7 +719,70 @@ contract Ownable is Initializable, Context {
uint256[50] private ______gap;
}
-// File: contracts/Abstract/InterfaceInverseToken.sol
+// File: contracts/short-tokens/Abstract/InterfaceCashPool.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceCashPool {
+ function kycVerifier() external view returns (address);
+
+ function moveTokenToPool(
+ address _token,
+ address whiteListedAddress,
+ uint256 orderAmount
+ ) external returns (bool);
+
+ function moveTokenfromPool(
+ address _token,
+ address destinationAddress,
+ uint256 orderAmount
+ ) external returns (bool);
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceKYCVerifier.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceKYCVerifier {
+ function isAddressWhitelisted(address) external view returns (bool);
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceCompositionCalculator.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceCompositionCalculator {
+ function getCurrentCashAmountCreatedByToken(
+ uint256 _tokenAmount,
+ uint256 _spotPrice,
+ uint256 _gasFee
+ ) external view returns (uint256);
+
+ function getCurrentTokenAmountCreatedByCash(
+ uint256 _cash,
+ uint256 _spotPrice,
+ uint256 _gasFee
+ ) external view returns (uint256);
+
+ function calculateDailyPCF(uint256 _price, uint256 _lendingFee)
+ external
+ view
+ returns (uint256, uint256, uint256, uint256, uint256, bool);
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceERC20.sol
+
+pragma solidity ^0.5.0;
+
+
+interface InterfaceERC20 {
+ function decimals() external view returns (uint8);
+}
+
+// File: contracts/short-tokens/Abstract/InterfaceInverseToken.sol
pragma solidity ^0.5.0;
@@ -810,1730 +873,166 @@ interface InterfaceInverseToken {
);
}
-// File: contracts/utils/DateTimeLibrary.sol
-
-pragma solidity ^0.5.0;
-
-// ----------------------------------------------------------------------------
-// BokkyPooBah's DateTime Library v1.01
-//
-// A gas-efficient Solidity date and time library
-//
-// https://github.com/bokkypoobah/BokkyPooBahsDateTimeLibrary
-//
-// Tested date range 1970/01/01 to 2345/12/31
-//
-// Conventions:
-// Unit | Range | Notes
-// :-------- |:-------------:|:-----
-// timestamp | >= 0 | Unix timestamp, number of seconds since 1970/01/01 00:00:00 UTC
-// year | 1970 ... 2345 |
-// month | 1 ... 12 |
-// day | 1 ... 31 |
-// hour | 0 ... 23 |
-// minute | 0 ... 59 |
-// second | 0 ... 59 |
-// dayOfWeek | 1 ... 7 | 1 = Monday, ..., 7 = Sunday
-//
-//
-// Enjoy. (c) BokkyPooBah / Bok Consulting Pty Ltd 2018-2019. The MIT Licence.
-// ----------------------------------------------------------------------------
-
-library DateTimeLibrary {
- uint256 constant SECONDS_PER_DAY = 24 * 60 * 60;
- uint256 constant SECONDS_PER_HOUR = 60 * 60;
- uint256 constant SECONDS_PER_MINUTE = 60;
- int256 constant OFFSET19700101 = 2440588;
-
- uint256 constant DOW_MON = 1;
- uint256 constant DOW_TUE = 2;
- uint256 constant DOW_WED = 3;
- uint256 constant DOW_THU = 4;
- uint256 constant DOW_FRI = 5;
- uint256 constant DOW_SAT = 6;
- uint256 constant DOW_SUN = 7;
-
- // ------------------------------------------------------------------------
- // Calculate the number of days from 1970/01/01 to year/month/day using
- // the date conversion algorithm from
- // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
- // and subtracting the offset 2440588 so that 1970/01/01 is day 0
- //
- // days = day
- // - 32075
- // + 1461 * (year + 4800 + (month - 14) / 12) / 4
- // + 367 * (month - 2 - (month - 14) / 12 * 12) / 12
- // - 3 * ((year + 4900 + (month - 14) / 12) / 100) / 4
- // - offset
- // ------------------------------------------------------------------------
- function _daysFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 _days)
- {
- require(year >= 1970);
- int256 _year = int256(year);
- int256 _month = int256(month);
- int256 _day = int256(day);
-
- int256 __days = _day -
- 32075 +
- (1461 * (_year + 4800 + (_month - 14) / 12)) /
- 4 +
- (367 * (_month - 2 - ((_month - 14) / 12) * 12)) /
- 12 -
- (3 * ((_year + 4900 + (_month - 14) / 12) / 100)) /
- 4 -
- OFFSET19700101;
-
- _days = uint256(__days);
- }
-
- // ------------------------------------------------------------------------
- // Calculate year/month/day from the number of days since 1970/01/01 using
- // the date conversion algorithm from
- // http://aa.usno.navy.mil/faq/docs/JD_Formula.php
- // and adding the offset 2440588 so that 1970/01/01 is day 0
- //
- // int L = days + 68569 + offset
- // int N = 4 * L / 146097
- // L = L - (146097 * N + 3) / 4
- // year = 4000 * (L + 1) / 1461001
- // L = L - 1461 * year / 4 + 31
- // month = 80 * L / 2447
- // dd = L - 2447 * month / 80
- // L = month / 11
- // month = month + 2 - 12 * L
- // year = 100 * (N - 49) + year + L
- // ------------------------------------------------------------------------
- function _daysToDate(uint256 _days)
- internal
- pure
- returns (uint256 year, uint256 month, uint256 day)
- {
- int256 __days = int256(_days);
-
- int256 L = __days + 68569 + OFFSET19700101;
- int256 N = (4 * L) / 146097;
- L = L - (146097 * N + 3) / 4;
- int256 _year = (4000 * (L + 1)) / 1461001;
- L = L - (1461 * _year) / 4 + 31;
- int256 _month = (80 * L) / 2447;
- int256 _day = L - (2447 * _month) / 80;
- L = _month / 11;
- _month = _month + 2 - 12 * L;
- _year = 100 * (N - 49) + _year + L;
-
- year = uint256(_year);
- month = uint256(_month);
- day = uint256(_day);
- }
-
- function daysFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 daysSinceDate)
- {
- daysSinceDate = _daysFromDate(year, month, day);
- }
- function timestampFromDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (uint256 timestamp)
- {
- timestamp = _daysFromDate(year, month, day) * SECONDS_PER_DAY;
- }
- function timestampFromDateTime(
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- ) internal pure returns (uint256 timestamp) {
- timestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- hour *
- SECONDS_PER_HOUR +
- minute *
- SECONDS_PER_MINUTE +
- second;
- }
- function timestampToDate(uint256 timestamp)
- internal
- pure
- returns (uint256 year, uint256 month, uint256 day)
- {
- (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function timestampToDateTime(uint256 timestamp)
- internal
- pure
- returns (
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- )
- {
- (year, month, day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- uint256 secs = timestamp % SECONDS_PER_DAY;
- hour = secs / SECONDS_PER_HOUR;
- secs = secs % SECONDS_PER_HOUR;
- minute = secs / SECONDS_PER_MINUTE;
- second = secs % SECONDS_PER_MINUTE;
- }
-
- function isValidDate(uint256 year, uint256 month, uint256 day)
- internal
- pure
- returns (bool valid)
- {
- if (year >= 1970 && month > 0 && month <= 12) {
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > 0 && day <= daysInMonth) {
- valid = true;
- }
- }
- }
- function isValidDateTime(
- uint256 year,
- uint256 month,
- uint256 day,
- uint256 hour,
- uint256 minute,
- uint256 second
- ) internal pure returns (bool valid) {
- if (isValidDate(year, month, day)) {
- if (hour < 24 && minute < 60 && second < 60) {
- valid = true;
- }
- }
- }
- function isLeapYear(uint256 timestamp)
- internal
- pure
- returns (bool leapYear)
- {
- (uint256 year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- leapYear = _isLeapYear(year);
- }
- function _isLeapYear(uint256 year) internal pure returns (bool leapYear) {
- leapYear = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
- }
- function isWeekDay(uint256 timestamp) internal pure returns (bool weekDay) {
- weekDay = getDayOfWeek(timestamp) <= DOW_FRI;
- }
- function isWeekEnd(uint256 timestamp) internal pure returns (bool weekEnd) {
- weekEnd = getDayOfWeek(timestamp) >= DOW_SAT;
- }
- function getDaysInMonth(uint256 timestamp)
- internal
- pure
- returns (uint256 daysInMonth)
- {
- (uint256 year, uint256 month, ) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- daysInMonth = _getDaysInMonth(year, month);
- }
- function _getDaysInMonth(uint256 year, uint256 month)
- internal
- pure
- returns (uint256 daysInMonth)
- {
- if (
- month == 1 ||
- month == 3 ||
- month == 5 ||
- month == 7 ||
- month == 8 ||
- month == 10 ||
- month == 12
- ) {
- daysInMonth = 31;
- } else if (month != 2) {
- daysInMonth = 30;
- } else {
- daysInMonth = _isLeapYear(year) ? 29 : 28;
- }
- }
- // 1 = Monday, 7 = Sunday
- function getDayOfWeek(uint256 timestamp)
- internal
- pure
- returns (uint256 dayOfWeek)
- {
- uint256 _days = timestamp / SECONDS_PER_DAY;
- dayOfWeek = ((_days + 3) % 7) + 1;
- }
-
- function getYear(uint256 timestamp) internal pure returns (uint256 year) {
- (year, , ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getMonth(uint256 timestamp) internal pure returns (uint256 month) {
- (, month, ) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getDay(uint256 timestamp) internal pure returns (uint256 day) {
- (, , day) = _daysToDate(timestamp / SECONDS_PER_DAY);
- }
- function getHour(uint256 timestamp) internal pure returns (uint256 hour) {
- uint256 secs = timestamp % SECONDS_PER_DAY;
- hour = secs / SECONDS_PER_HOUR;
- }
- function getMinute(uint256 timestamp)
- internal
- pure
- returns (uint256 minute)
- {
- uint256 secs = timestamp % SECONDS_PER_HOUR;
- minute = secs / SECONDS_PER_MINUTE;
- }
- function getSecond(uint256 timestamp)
- internal
- pure
- returns (uint256 second)
- {
- second = timestamp % SECONDS_PER_MINUTE;
- }
-
- function addYears(uint256 timestamp, uint256 _years)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- year += _years;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp >= timestamp);
- }
- function addMonths(uint256 timestamp, uint256 _months)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- month += _months;
- year += (month - 1) / 12;
- month = ((month - 1) % 12) + 1;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp >= timestamp);
- }
- function addDays(uint256 timestamp, uint256 _days)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _days * SECONDS_PER_DAY;
- require(newTimestamp >= timestamp);
- }
- function addHours(uint256 timestamp, uint256 _hours)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _hours * SECONDS_PER_HOUR;
- require(newTimestamp >= timestamp);
- }
- function addMinutes(uint256 timestamp, uint256 _minutes)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _minutes * SECONDS_PER_MINUTE;
- require(newTimestamp >= timestamp);
- }
- function addSeconds(uint256 timestamp, uint256 _seconds)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp + _seconds;
- require(newTimestamp >= timestamp);
- }
-
- function subYears(uint256 timestamp, uint256 _years)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- year -= _years;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp <= timestamp);
- }
- function subMonths(uint256 timestamp, uint256 _months)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- (uint256 year, uint256 month, uint256 day) = _daysToDate(
- timestamp / SECONDS_PER_DAY
- );
- uint256 yearMonth = year * 12 + (month - 1) - _months;
- year = yearMonth / 12;
- month = (yearMonth % 12) + 1;
- uint256 daysInMonth = _getDaysInMonth(year, month);
- if (day > daysInMonth) {
- day = daysInMonth;
- }
- newTimestamp =
- _daysFromDate(year, month, day) *
- SECONDS_PER_DAY +
- (timestamp % SECONDS_PER_DAY);
- require(newTimestamp <= timestamp);
- }
- function subDays(uint256 timestamp, uint256 _days)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _days * SECONDS_PER_DAY;
- require(newTimestamp <= timestamp);
- }
- function subHours(uint256 timestamp, uint256 _hours)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _hours * SECONDS_PER_HOUR;
- require(newTimestamp <= timestamp);
- }
- function subMinutes(uint256 timestamp, uint256 _minutes)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _minutes * SECONDS_PER_MINUTE;
- require(newTimestamp <= timestamp);
- }
- function subSeconds(uint256 timestamp, uint256 _seconds)
- internal
- pure
- returns (uint256 newTimestamp)
- {
- newTimestamp = timestamp - _seconds;
- require(newTimestamp <= timestamp);
- }
-
- function diffYears(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _years)
- {
- require(fromTimestamp <= toTimestamp);
- (uint256 fromYear, , ) = _daysToDate(fromTimestamp / SECONDS_PER_DAY);
- (uint256 toYear, , ) = _daysToDate(toTimestamp / SECONDS_PER_DAY);
- _years = toYear - fromYear;
- }
- function diffMonths(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _months)
- {
- require(fromTimestamp <= toTimestamp);
- (uint256 fromYear, uint256 fromMonth, ) = _daysToDate(
- fromTimestamp / SECONDS_PER_DAY
- );
- (uint256 toYear, uint256 toMonth, ) = _daysToDate(
- toTimestamp / SECONDS_PER_DAY
- );
- _months = toYear * 12 + toMonth - fromYear * 12 - fromMonth;
- }
- function diffDays(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _days)
- {
- require(fromTimestamp <= toTimestamp);
- _days = (toTimestamp - fromTimestamp) / SECONDS_PER_DAY;
- }
- function diffHours(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _hours)
- {
- require(fromTimestamp <= toTimestamp);
- _hours = (toTimestamp - fromTimestamp) / SECONDS_PER_HOUR;
- }
- function diffMinutes(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _minutes)
- {
- require(fromTimestamp <= toTimestamp);
- _minutes = (toTimestamp - fromTimestamp) / SECONDS_PER_MINUTE;
- }
- function diffSeconds(uint256 fromTimestamp, uint256 toTimestamp)
- internal
- pure
- returns (uint256 _seconds)
- {
- require(fromTimestamp <= toTimestamp);
- _seconds = toTimestamp - fromTimestamp;
- }
-}
-
-// File: contracts/utils/Math.sol
-
-/// Math.sol -- mixin for inline numerical wizardry
-
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see .
+// File: contracts/short-tokens/Abstract/InterfaceStorage.sol
pragma solidity ^0.5.0;
-library DSMath {
-
- // --- Unsigned Math ----
- function add(uint x, uint y) internal pure returns (uint z) {
- require((z = x + y) >= x, "ds-math-add-overflow");
- }
- function sub(uint x, uint y) internal pure returns (uint z) {
- require((z = x - y) <= x, "ds-math-sub-underflow");
- }
- function mul(uint x, uint y) internal pure returns (uint z) {
- require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
- }
-
-
- function min(uint x, uint y) internal pure returns (uint z) {
- return x <= y ? x : y;
- }
- function max(uint x, uint y) internal pure returns (uint z) {
- return x >= y ? x : y;
- }
- function imin(int x, int y) internal pure returns (int z) {
- return x <= y ? x : y;
- }
- function imax(int x, int y) internal pure returns (int z) {
- return x >= y ? x : y;
- }
-
- // --- Precise Math ---
-
- uint public constant WAD = 10 ** 18;
- uint public constant RAY = 10 ** 27;
-
- function wmul(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, y), WAD / 2) / WAD;
- }
- function rmul(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, y), RAY / 2) / RAY;
- }
- function wdiv(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, WAD), y / 2) / y;
- }
- function rdiv(uint x, uint y) internal pure returns (uint z) {
- z = add(mul(x, RAY), y / 2) / y;
- }
-
- function ray(uint _wad) internal pure returns (uint) {
- return mul(_wad, uint(10 ** 9));
- }
-
- // This famous algorithm is called "exponentiation by squaring"
- // and calculates x^n with x as fixed-point and n as regular unsigned.
- //
- // It's O(log n), instead of O(n) for naive repeated multiplication.
- //
- // These facts are why it works:
- //
- // If n is even, then x^n = (x^2)^(n/2).
- // If n is odd, then x^n = x * x^(n-1),
- // and applying the equation for even x gives
- // x^n = x * (x^2)^((n-1) / 2).
- //
- // Also, EVM division is flooring and
- // floor[(n-1) / 2] = floor[n / 2].
- //
- function rpow(uint x, uint n) internal pure returns (uint z) {
- z = n % 2 != 0 ? x : RAY;
-
- for (n /= 2; n != 0; n /= 2) {
- x = rmul(x, x);
-
- if (n % 2 != 0) {
- z = rmul(z, x);
- }
- }
- }
-}
-
-// File: contracts/PersistentStorage.sol
-
-pragma solidity ^0.5.0;
-
-
-
-
-
-contract PersistentStorage is Ownable {
- address public tokenSwapManager;
- address public bridge;
-
- bool public isPaused;
- bool public isShutdown;
-
- struct Accounting {
- uint256 price;
- uint256 cashPositionPerTokenUnit;
- uint256 balancePerTokenUnit;
- uint256 lendingFee;
- }
-
- struct Order {
- string orderType;
- uint256 tokensGiven;
- uint256 tokensRecieved;
- uint256 avgBlendedFee;
- }
-
- uint256 public lastActivityDay;
- uint256 public minRebalanceAmount;
- uint256 private managementFee;
-
- mapping(uint256 => Accounting[]) private accounting;
-
- mapping(address => bool) public whitelistedAddresses;
-
- uint256[] public mintingFeeBracket;
- mapping(uint256 => uint256) public mintingFee;
-
- Order[] public allOrders;
- mapping(address => Order[]) public orderByUser;
- mapping(address => uint256) public delayedRedemptionsByUser;
-
- event AccountingValuesSet(uint256 today);
- event RebalanceValuesSet(uint256 newMinRebalanceAmount);
- event ManagementFeeValuesSet(uint256 newManagementFee);
-
- function initialize(
- address ownerAddress,
- uint256 _managementFee,
- uint256 _minRebalanceAmount
- ) public initializer {
- initialize(ownerAddress);
- managementFee = _managementFee;
- minRebalanceAmount = _minRebalanceAmount;
- mintingFeeBracket.push(50000 ether);
- mintingFeeBracket.push(100000 ether);
- mintingFee[50000 ether] = 3 ether / 1000; //0.3%
- mintingFee[100000 ether] = 2 ether / 1000; //0.2%
- mintingFee[~uint256(0)] = 1 ether / 1000; //0.1% all values higher
- }
-
- function setTokenSwapManager(address _tokenSwapManager) public onlyOwner {
- require(_tokenSwapManager != address(0), "adddress must not be empty");
- tokenSwapManager = _tokenSwapManager;
- }
-
- function setBridge(address _bridge) public onlyOwner {
- require(_bridge != address(0), "adddress must not be empty");
- bridge = _bridge;
- }
-
- function setIsPaused(bool _isPaused) public onlyOwner {
- isPaused = _isPaused;
- }
-
- function shutdown() public onlyOwner {
- isShutdown = true;
- }
-
- /**
- * @dev Throws if called by any account other than the owner.
- */
- modifier onlyOwnerOrTokenSwap() {
- require(
- isOwner() || _msgSender() == tokenSwapManager,
- "caller is not the owner or token swap manager"
- );
- _;
- }
-
- function setDelayedRedemptionsByUser(
- uint256 amountToRedeem,
- address whitelistedAddress
- ) public onlyOwnerOrTokenSwap {
- delayedRedemptionsByUser[whitelistedAddress] = amountToRedeem;
- }
-
- function getDelayedRedemptionsByUser(address whitelistedAddress)
- public
- view
- returns (uint256)
- {
- return delayedRedemptionsByUser[whitelistedAddress];
- }
-
- /*
- * Saves order in mapping (address => Order[]) orderByUser
- * overwrite == false, append to Order[]
- * overwrite == true, overwrite element at orderIndex
- */
-
- function setOrderByUser(
- address whitelistedAddress,
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee,
- uint256 orderIndex,
- bool overwrite
- ) public onlyOwnerOrTokenSwap() {
- Order memory newOrder = Order(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee
- );
-
- if (!overwrite) {
- orderByUser[whitelistedAddress].push(newOrder);
- setOrder(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee,
- orderIndex,
- overwrite
- );
- } else {
- orderByUser[whitelistedAddress][orderIndex] = newOrder;
- }
- }
-
- /*
- * Gets Order[] For User Address
- * Return order at Index in Order[]
- */
-
- function getOrderByUser(address whitelistedAddress, uint256 orderIndex)
- public
- view
- returns (
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee
- )
- {
-
- Order storage orderAtIndex
- = orderByUser[whitelistedAddress][orderIndex];
- return (
- orderAtIndex.orderType,
- orderAtIndex.tokensGiven,
- orderAtIndex.tokensRecieved,
- orderAtIndex.avgBlendedFee
- );
- }
-
- /*
- * Save order to allOrders array
- * overwrite == false, append to allOrders array
- * overwrite == true, overwrite element at orderIndex
- */
- function setOrder(
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee,
- uint256 orderIndex,
- bool overwrite
- ) public onlyOwnerOrTokenSwap() {
- Order memory newOrder = Order(
- orderType,
- tokensGiven,
- tokensRecieved,
- avgBlendedFee
- );
-
- if (!overwrite) {
- allOrders.push(newOrder);
- } else {
- allOrders[orderIndex] = newOrder;
- }
- }
-
- /*
- * Get Order
- */
- function getOrder(uint256 index)
- public
- view
- returns (
- string memory orderType,
- uint256 tokensGiven,
- uint256 tokensRecieved,
- uint256 avgBlendedFee
- )
- {
- Order storage orderAtIndex = allOrders[index];
- return (
- orderAtIndex.orderType,
- orderAtIndex.tokensGiven,
- orderAtIndex.tokensRecieved,
- orderAtIndex.avgBlendedFee
- );
- }
-
- // @dev Set whitelisted addresses
- function setWhitelistedAddress(address adddressToAdd) public onlyOwner {
- require(adddressToAdd != address(0), "adddress must not be empty");
-
- whitelistedAddresses[adddressToAdd] = true;
- }
-
- // @dev Remove whitelisted addresses
- function removeWhitelistedAddress(address addressToRemove)
- public
- onlyOwner
- {
- require(
- whitelistedAddresses[addressToRemove],
- "address must be added to be removed allowed"
- );
-
- delete whitelistedAddresses[addressToRemove];
- }
-
- // @dev Updates whitelisted addresses
- function updateWhitelistedAddress(address oldAddress, address newAddress)
- public
- {
- removeWhitelistedAddress(oldAddress);
- setWhitelistedAddress(newAddress);
- }
-
- // @dev Get accounting values for a specific day
- // @param date format as 20200123 for 23th of January 2020
- function getAccounting(uint256 date)
- public
- view
- returns (uint256, uint256, uint256, uint256)
- {
- return (
- accounting[date][accounting[date].length - 1].price,
- accounting[date][accounting[date].length - 1]
- .cashPositionPerTokenUnit,
- accounting[date][accounting[date].length - 1].balancePerTokenUnit,
- accounting[date][accounting[date].length - 1].lendingFee
- );
- }
-
- // @dev Set accounting values for the day
- function setAccounting(
- uint256 _price,
- uint256 _cashPositionPerTokenUnit,
- uint256 _balancePerTokenUnit,
- uint256 _lendingFee
- ) external onlyOwnerOrTokenSwap() {
- (uint256 year, uint256 month, uint256 day) = DateTimeLibrary
- .timestampToDate(block.timestamp);
- uint256 today = year * 10000 + month * 100 + day;
- accounting[today].push(
- Accounting(
- _price,
- _cashPositionPerTokenUnit,
- _balancePerTokenUnit,
- _lendingFee
- )
- );
- lastActivityDay = today;
- emit AccountingValuesSet(today);
- }
-
- // @dev Set accounting values for the day
- function setAccountingForLastActivityDay(
- uint256 _price,
- uint256 _cashPositionPerTokenUnit,
- uint256 _balancePerTokenUnit,
- uint256 _lendingFee
- ) external onlyOwnerOrTokenSwap() {
- accounting[lastActivityDay].push(
- Accounting(
- _price,
- _cashPositionPerTokenUnit,
- _balancePerTokenUnit,
- _lendingFee
- )
- );
- emit AccountingValuesSet(lastActivityDay);
- }
-
- // @dev Set last rebalance information
- function setMinRebalanceAmount(uint256 _minRebalanceAmount)
- external
- onlyOwner
- {
- minRebalanceAmount = _minRebalanceAmount;
-
- emit RebalanceValuesSet(minRebalanceAmount);
- }
-
- // @dev Set last rebalance information
- function setManagementFee(uint256 _managementFee) external onlyOwner {
- managementFee = _managementFee;
- emit ManagementFeeValuesSet(managementFee);
- }
-
- // @dev Returns price
- function getPrice() public view returns (uint256 price) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .price;
- }
-
- // @dev Returns cash position amount
- function getCashPositionPerTokenUnit()
- public
- view
- returns (uint256 amount)
- {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .cashPositionPerTokenUnit;
- }
-
- // @dev Returns borrowed crypto amount
- function getBalancePerTokenUnit() public view returns (uint256 amount) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .balancePerTokenUnit;
- }
-
- // @dev Returns lending fee
- function getLendingFee() public view returns (uint256 lendingRate) {
- return
- accounting[lastActivityDay][accounting[lastActivityDay].length - 1]
- .lendingFee;
- }
-
- // @dev Returns lending fee
- function getManagementFee() public view returns (uint256 lendingRate) {
- return managementFee;
- }
-
- // @dev Sets last minting fee
- function setLastMintingFee(uint256 _mintingFee) public onlyOwner {
- mintingFee[~uint256(0)] = _mintingFee;
- }
-
- // @dev Adds minting fee
- function addMintingFeeBracket(uint256 _mintingFeeLimit, uint256 _mintingFee)
- public
- onlyOwner
- {
- require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
- "New minting fee bracket needs to be bigger then last one"
- );
- mintingFeeBracket.push(_mintingFeeLimit);
- mintingFee[_mintingFeeLimit] = _mintingFee;
- }
-
- // @dev Deletes last minting fee
- function deleteLastMintingFeeBracket() public onlyOwner {
- delete mintingFee[mintingFeeBracket[mintingFeeBracket.length - 1]];
- mintingFeeBracket.length--;
- }
-
- // @dev Changes minting fee
- function changeMintingLimit(
- uint256 _position,
- uint256 _mintingFeeLimit,
- uint256 _mintingFee
- ) public onlyOwner {
- require(
- _mintingFeeLimit > mintingFeeBracket[mintingFeeBracket.length - 1],
- "New minting fee bracket needs to be bigger then last one"
- );
- if (_position != 0) {
- require(
- _mintingFeeLimit > mintingFeeBracket[_position - 1],
- "New minting fee bracket needs to be bigger then last one"
- );
- }
- if (_position < mintingFeeBracket.length - 1) {
- require(
- _mintingFeeLimit < mintingFeeBracket[_position + 1],
- "New minting fee bracket needs to be smaller then next one"
- );
- }
- mintingFeeBracket[_position] = _mintingFeeLimit;
- mintingFee[_mintingFeeLimit] = _mintingFee;
- }
-
- function getMintingFee(uint256 cash) public view returns (uint256) {
- // Define Start + End Index
- uint256 startIndex = 0;
- uint256 endIndex = mintingFeeBracket.length - 1;
- uint256 middleIndex = endIndex / 2;
-
- if (cash <= mintingFeeBracket[middleIndex]) {
- endIndex = middleIndex;
- } else {
- startIndex = middleIndex + 1;
- }
-
- for (uint256 i = startIndex; i <= endIndex; i++) {
- if (cash <= mintingFeeBracket[i]) {
- return mintingFee[mintingFeeBracket[i]];
- }
- }
- return mintingFee[~uint256(0)];
- }
-}
-
-// File: contracts/Abstract/InterfaceStorage.sol
-
-pragma solidity ^0.5.0;
-
-
-interface InterfaceStorage {
- function whitelistedAddresses(address) external view returns (bool);
-}
-
-// File: contracts/KYCVerifier.sol
-
-pragma solidity ^0.5.0;
-
-
-
-
-contract KYCVerifier is Initializable {
- InterfaceStorage public persistentStorage;
-
- function initialize(address _persistentStorage) public initializer {
- persistentStorage = InterfaceStorage(_persistentStorage);
- }
-
- function isAddressWhitelisted(address userAddress)
- public
- view
- returns (bool)
- {
- return persistentStorage.whitelistedAddresses(userAddress);
- }
-}
-
-// File: contracts/CashPool.sol
-
-pragma solidity ^0.5.0;
-
-
-
-
-
-
-
-contract CashPool is Ownable {
- using SafeMath for uint256;
- KYCVerifier public kycVerifier;
- PersistentStorage public persistentStorage;
-
- uint256[2] public percentageOfFundsForColdStorage;
- address public coldStorage;
-
- event SetPercentageOfFundsForColdStorageEvent(
- uint256[2] newPercentageOfFundsForColdStorage
- );
-
- function initialize(
- address ownerAddress,
- address _kycVerifier,
- address _persistentStorage,
- address _coldStorage,
- uint256[2] memory _percentageOfFundsForColdStorage
- ) public initializer {
- require(
- ownerAddress != address(0) &&
- _kycVerifier != address(0) &&
- _coldStorage != address(0) &&
- _percentageOfFundsForColdStorage[1] != 0,
- "params variables cannot be empty but _percentageOfFundsForColdStorage[0]"
- );
- initialize(ownerAddress);
- kycVerifier = KYCVerifier(_kycVerifier);
- persistentStorage = PersistentStorage(_persistentStorage);
- coldStorage = _coldStorage;
- percentageOfFundsForColdStorage = _percentageOfFundsForColdStorage;
- }
-
- function getBalance(address _token) public view returns (uint256) {
- InterfaceInverseToken token_ = InterfaceInverseToken(_token);
- uint256 tokenBalance = token_.balanceOf(address(this));
- return tokenBalance;
- }
-
- // @dev Move tokens to cash pool
- // @param _token ERC20 address
- // @param whiteListedAddress address allowed to transfer to pool
- // @param orderAmount amount to transfer to cash pool
- function moveTokenToPool(
- address _token,
- address whiteListedAddress,
- uint256 orderAmount
- ) public onlyOwnerOrTokenSwap() returns (bool) {
- InterfaceInverseToken token_ = InterfaceInverseToken(_token);
- require(
- kycVerifier.isAddressWhitelisted(whiteListedAddress),
- "only whitelisted address are allowed to move tokens to pool"
- );
-
- uint256 percentageNumerator = percentageOfFundsForColdStorage[0];
- uint256 percentageDenominator = percentageOfFundsForColdStorage[1];
- uint256 amountForColdStorage = orderAmount.mul(percentageNumerator).div(
- percentageDenominator
- );
-
- token_.transferFrom(whiteListedAddress, address(this), orderAmount);
- token_.transfer(coldStorage, amountForColdStorage);
-
- return true;
- }
-
- // @dev Move tokens out of cash pool
- // @param _token ERC20 address
- // @param destinationAddress address to send to
- // @param orderAmount amount to transfer from cash pool
- function moveTokenfromPool(
- address _token,
- address destinationAddress,
- uint256 orderAmount
- ) public onlyOwnerOrTokenSwap() returns (bool) {
- InterfaceInverseToken token_ = InterfaceInverseToken(_token);
-
- token_.transfer(destinationAddress, orderAmount);
- return true;
- }
-
- modifier onlyOwnerOrTokenSwap() {
- require(
- isOwner() || _msgSender() == persistentStorage.tokenSwapManager(),
- "caller is not the owner or token swap manager"
- );
- _;
- }
-
- // @dev Sets new coldStorage
- // @param _newColdStorage Address for new cold storage wallet
- function setColdStorage(address _newColdStorage) public onlyOwner {
- require(_newColdStorage != address(0), "address cannot be empty");
- coldStorage = _newColdStorage;
- }
-
- // @dev Sets percentage of funds to stay in contract. Owner only
- // @param _newPercentageOfFundsForColdStorage List with two elements referencing percentage of funds for cold storage as a fraction
- // e.g. 1/2 is [1,2]
- function setPercentageOfFundsForColdStorage(
- uint256[2] memory _newPercentageOfFundsForColdStorage
- ) public onlyOwner {
- require(
- _newPercentageOfFundsForColdStorage[1] != 0,
- "denominator should not be zero"
- );
- require(
- _newPercentageOfFundsForColdStorage[0] <=
- _newPercentageOfFundsForColdStorage[1],
- "cannot set more than 100% for coldstorage"
- );
- percentageOfFundsForColdStorage[0] = _newPercentageOfFundsForColdStorage[0];
- percentageOfFundsForColdStorage[1] = _newPercentageOfFundsForColdStorage[1];
-
- emit SetPercentageOfFundsForColdStorageEvent(
- _newPercentageOfFundsForColdStorage
- );
- }
-}
-
-// File: contracts/CompositionCalculator.sol
-
-pragma solidity ^0.5.0;
-
-
-
-
-
-
-
-
-/**
- * @dev uint256 are expected to use last 18 numbers as decimal points except when specifid differently in @params
- */
-contract CompositionCalculator is Initializable {
- using SafeMath for uint256;
-
- PersistentStorage public persistentStorage;
- InterfaceInverseToken public inverseToken;
-
- function initialize(
- address _persistentStorageAddress,
- address _inverseTokenAddress
- ) public initializer {
- persistentStorage = PersistentStorage(_persistentStorageAddress);
- inverseToken = InterfaceInverseToken(_inverseTokenAddress);
- }
-
- //*************************************************************************
- //************************** Pure Functions *******************************
- //*************************************************************************
-
- /**
- * @dev Returns NetTokenValue for the given values
- * @param _cashPosition The yearly average lending fee for borrowed balance
- * @param _balance The balance (dept/borrow) in crypto
- * @param _price The momentary price of the crypto
- */
- function getNetTokenValue(
- uint256 _cashPosition,
- uint256 _balance,
- uint256 _price
- ) public pure returns (uint256 netTokenValue) {
- // Calculate NetTokenValue of Product
- uint256 balanceWorth = DSMath.wmul(_balance, _price);
- require(
- _cashPosition > balanceWorth,
- "The cash position needs to be bigger then the borrowed crypto is worth"
- );
- netTokenValue = DSMath.sub(_cashPosition, balanceWorth);
- }
-
- /**
- * @dev Returns the crypto amount to pay as lending fee
- * @param _lendingFee The yearly average lending fee for borrowed balance
- * @param _balance The balance (dept/borrow) in crypto
- * @param _days The days since the last fee calculation (Natural number)
- */
- function getLendingFeeInCrypto(
- uint256 _lendingFee,
- uint256 _balance,
- uint256 _days
- ) public pure returns (uint256 feeInCrypto) {
- uint256 lendingFeePercent = DSMath.wdiv(_lendingFee, 100 ether);
- uint256 feePerDay = DSMath.wdiv(lendingFeePercent, 365 ether);
- uint256 feeInCryptoForOneDay = DSMath.wmul(feePerDay, _balance);
- feeInCrypto = DSMath.mul(_days, feeInCryptoForOneDay);
- }
-
- /**
- * Returns the change of balance with decimal at 18
- * @param _netTokenValue The current netTokenValue of the product
- * @param _balance The balance (dept/borrow) in crypto
- * @param _price The momentary price of the crypto
- */
- function getNeededChangeInBalanceToRebalance(
- uint256 _netTokenValue,
- uint256 _balance,
- uint256 _price
- ) public pure returns (uint256 changeInBalance, bool isNegative) {
- require(_price != 0, "Price cant be zero");
-
- uint256 newAccountBalance = DSMath.wdiv(_netTokenValue, _price);
- isNegative = newAccountBalance < _balance;
- if (!isNegative) {
- changeInBalance = DSMath.sub(newAccountBalance, _balance);
- } else {
- changeInBalance = DSMath.sub(_balance, newAccountBalance);
- }
- }
+interface InterfaceStorage {
+ function whitelistedAddresses(address) external view returns (bool);
- /**
- * @param _a First number
- * @param _b Second number
- * @param _isBNegative Is number b negative
- */
- function addOrSub(
- //getDeliverables
- uint256 _a,
- uint256 _b,
- bool _isBNegative
- ) internal pure returns (uint256) {
- if (_isBNegative) {
- return _a.sub(_b);
- } else {
- return _a.add(_b);
- }
- }
+ function isPaused() external view returns (bool);
- /**
- * @dev Returns imported values for a PCF
- * @param _cashPosition The total cash position on the token
- * @param _balance The balance (dept/borrow) in crypto
- * @param _price The momentary price of the crypto
- * @param _lendingFee The yearly average lending fee for borrowed balance
- * @param _days The days since the last fee calculation (Natural number)
- * @param _minRebalanceAmount The minimum amount to rebalance
- */
- function calculatePCF(
- uint256 _cashPosition,
- uint256 _balance,
- uint256 _price,
- uint256 _lendingFee,
- uint256 _days,
- uint256 _minRebalanceAmount
- )
- public
- pure
- returns (
- uint256 endNetTokenValue,
- uint256 endBalance,
- uint256 endCashPosition,
- uint256 feeInFiat,
- uint256 changeInBalance,
- bool isChangeInBalanceNeg
- )
- {
- require(_price != 0, "Price cant be zero");
- // Update Calculation for NetTokenValue, Cash Position, Loan Positions, and Accrued Fees
-
- //remove fees
- uint256 feeInCrypto = getLendingFeeInCrypto(
- _lendingFee,
- _balance,
- _days
- );
- require(
- feeInCrypto <= _balance,
- "The balance cant be smaller then the fee"
- );
+ function isShutdown() external view returns (bool);
- feeInFiat = DSMath.wmul(feeInCrypto, _price);
+ function tokenSwapManager() external view returns (address);
- //cashPositionWithoutFee
- endCashPosition = DSMath.sub(_cashPosition, feeInFiat);
+ function bridge() external view returns (address);
- //calculte change in balance (rebalance)
- endBalance = DSMath.wdiv(
- getNetTokenValue(endCashPosition, _balance, _price),
- _price
- );
+ function getCashPositionPerTokenUnit() external view returns (uint256);
- (
- changeInBalance,
- isChangeInBalanceNeg
- ) = getNeededChangeInBalanceToRebalance(
- getNetTokenValue(endCashPosition, _balance, _price),
- _balance,
- _price
- );
+ function getBalancePerTokenUnit() external view returns (uint256);
- if (changeInBalance < _minRebalanceAmount) {
- changeInBalance = 0;
- endBalance = _balance;
- }
+ function getMintingFee(uint256 cash) external view returns (uint256);
- //result
- endCashPosition = addOrSub(
- endCashPosition, //cashPositionWithoutFee
- DSMath.wmul(changeInBalance, _price),
- isChangeInBalanceNeg
- );
- endNetTokenValue = getNetTokenValue(
- endCashPosition,
- endBalance,
- _price
- );
- }
+ function getPrice() external view returns (uint256);
- /**
- * @param _cashPosition The total cash position on the token
- * @param _balance The balance (dept/borrow) in crypto
- * @param _price The momentary price of the crypto
- * @param _lendingFee The yearly average lending fee for borrowed balance
- * @param _days The days since the last fee calculation (Natural number)
- */
- function calculatePCFWithoutMin(
- //getDeliverables
- uint256 _cashPosition,
- uint256 _balance,
- uint256 _price,
- uint256 _lendingFee,
- uint256 _days
- )
- public
- pure
- returns (
- uint256 endNetTokenValue,
- uint256 endBalance,
- uint256 endCashPosition,
- uint256 feeInFiat,
- uint256 changeInBalance,
- bool isChangeInBalanceNeg
- )
- {
- return
- calculatePCF(
- _cashPosition,
- _balance,
- _price,
- _lendingFee,
- _days,
- 0
- );
- }
+ function minimumMintingFee() external view returns (uint256);
- /**
- * @dev Returns the amount of token created by cash at price
- * @param _cashPosition The total cash position on the token
- * @param _balance The balance (dept/borrow) in crypto
- * @param _totalTokenSupply The total token supply
- * @param _cash The cash provided to create token
- * @param _spotPrice The momentary price of the crypto
- */
- function getTokenAmountCreatedByCash(
- //Create
- uint256 _cashPosition,
- uint256 _balance,
- uint256 _totalTokenSupply,
- uint256 _cash,
- uint256 _spotPrice
- ) public pure returns (uint256 tokenAmountCreated) {
- require(_spotPrice != 0, "Price cant be zero");
- require(_totalTokenSupply != 0, "Token supply cant be zero");
-
- uint256 netTokenValue = getNetTokenValue(
- _cashPosition,
- _balance,
- _spotPrice
- );
- uint256 netTokenValueTimesTokenAmount = DSMath.wmul(
- netTokenValue,
- _totalTokenSupply
- );
- require(
- netTokenValueTimesTokenAmount != 0,
- "netTokenValueTimesTokenAmount cant be zero"
- );
+ function getLendingFee() external view returns (uint256);
- tokenAmountCreated = DSMath.wdiv(_cash, netTokenValueTimesTokenAmount);
- }
+ function minRebalanceAmount() external view returns (uint8);
- /**
- * @dev Returns the amount of cash redeemed at price
- * @param _cashPosition The total cash position on the token
- * @param _balance The balance (dept/borrow) in crypto
- * @param _totalTokenSupply The total token supply
- * @param _tokenAmount The token provided to redeem cash
- * @param _spotPrice The momentary price of the crypto
- */
- function getCashAmountCreatedByToken(
- //Redeem
- uint256 _cashPosition,
- uint256 _balance,
- uint256 _totalTokenSupply,
- uint256 _tokenAmount,
- uint256 _spotPrice
- ) public pure returns (uint256 cashFromTokenRedeem) {
- require(_spotPrice != 0, "Price cant be zero");
- require(_totalTokenSupply != 0, "Token supply cant be zero");
-
- uint256 netTokenValue = getNetTokenValue(
- _cashPosition,
- _balance,
- _spotPrice
- );
- uint256 netTokenValueTimesTokenAmount = DSMath.wmul(
- netTokenValue,
- _tokenAmount
- );
- cashFromTokenRedeem = DSMath.wdiv(
- netTokenValueTimesTokenAmount,
- _totalTokenSupply
- );
- }
+ function delayedRedemptionsByUser(address) external view returns (uint256);
- /**
- * @dev Returns cash without fee
- * @param _cash The cash provided to create token
- * @param _mintingFee The minting fee to remove
- */
- function removeMintingFeeFromCash(uint256 _cash, uint256 _mintingFee)
- public
- pure
- returns (uint256 cashAfterFee)
- {
- uint256 creationFeeInCash = DSMath.wmul(_cash, _mintingFee);
- cashAfterFee = DSMath.sub(_cash, creationFeeInCash);
- }
+ function setDelayedRedemptionsByUser(
+ uint256 amountToRedeem,
+ address whitelistedAddress
+ ) external;
- //*************************************************************************
- //***************** Get values for last PCF *******************************
- //*************************************************************************
+ function setOrderByUser(
+ address whitelistedAddress,
+ string calldata orderType,
+ uint256 tokensGiven,
+ uint256 tokensRecieved,
+ uint256 avgBlendedFee,
+ uint256 orderIndex,
+ bool overwrite
+ ) external;
- /**
- * @dev Returns the current NetTokenValue
- */
- function getCurrentNetTokenValue()
- public
- view
- returns (uint256 netTokenValue)
- {
- uint256 totalTokenSupply = inverseToken.totalSupply();
- require(totalTokenSupply != 0, "Token supply cant be zero");
+ function setAccounting(
+ uint256 _price,
+ uint256 _cashPositionPerTokenUnit,
+ uint256 _balancePerTokenUnit,
+ uint256 _lendingFee
+ ) external;
- uint256 cashPosition = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getCashPositionPerTokenUnit()
- );
- uint256 balance = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getBalancePerTokenUnit()
- );
- uint256 price = persistentStorage.getPrice();
+ function setAccountingForLastActivityDay(
+ uint256 _price,
+ uint256 _cashPositionPerTokenUnit,
+ uint256 _balancePerTokenUnit,
+ uint256 _lendingFee
+ ) external;
+}
- netTokenValue = getNetTokenValue(cashPosition, balance, price);
- }
+// File: contracts/short-tokens/utils/Math.sol
- /**
- * @dev Returns cash without fee
- * @param _cash The cash provided to create token
- */
- function removeCurrentMintingFeeFromCash(uint256 _cash)
- public
- view
- returns (uint256 cashAfterFee)
- {
- uint256 creationFee = persistentStorage.getMintingFee(_cash);
- cashAfterFee = removeMintingFeeFromCash(_cash, creationFee);
- }
+/// Math.sol -- mixin for inline numerical wizardry
- /**
- * @dev Returns the amount of token created by cash
- * @param _cash The cash provided to create token
- * @param _spotPrice The momentary price of the crypto
- */
- function getCurrentTokenAmountCreatedByCash(
- //Create
- uint256 _cash,
- uint256 _spotPrice
- ) public view returns (uint256 tokenAmountCreated) {
- uint256 cashAfterFee = removeCurrentMintingFeeFromCash(_cash);
- tokenAmountCreated = getTokenAmountCreatedByCash(
- persistentStorage.getCashPositionPerTokenUnit(),
- persistentStorage.getBalancePerTokenUnit(),
- 1 ether,
- cashAfterFee,
- _spotPrice
- );
- }
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
- /**
- * @dev Returns the amount of cash redeemed at spot price
- * @param _tokenAmount The token provided to redeem cash
- * @param _spotPrice The momentary price of the crypto
- */
- function getCurrentCashAmountCreatedByToken(
- //Redeem
- uint256 _tokenAmount,
- uint256 _spotPrice
- ) public view returns (uint256 cashFromTokenRedeem) {
- uint256 totalTokenSupply = inverseToken.totalSupply();
- require(totalTokenSupply != 0, "Token supply cant be zero");
-
- uint256 lendingFee = persistentStorage.getLendingFee();
- uint256 daysSinceLastRebalance = getDaysSinceLastRebalance() + 1;
-
- uint256 cryptoForLendingFee = getLendingFeeInCrypto(
- lendingFee,
- DSMath.wmul(
- _tokenAmount,
- persistentStorage.getBalancePerTokenUnit()
- ),
- daysSinceLastRebalance
- );
- uint256 fiatForLendingFee = DSMath.wmul(
- cryptoForLendingFee,
- _spotPrice
- );
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
- uint256 cashFromToken = getCashAmountCreatedByToken(
- persistentStorage.getCashPositionPerTokenUnit(),
- persistentStorage.getBalancePerTokenUnit(),
- 1 ether,
- _tokenAmount,
- _spotPrice
- );
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
- cashFromTokenRedeem = removeCurrentMintingFeeFromCash(
- DSMath.sub(cashFromToken, fiatForLendingFee)
- );
- }
+pragma solidity ^0.5.0;
- function getDaysSinceLastRebalance()
- public
- view
- returns (uint256 daysSinceLastRebalance)
- {
- uint256 lastRebalanceDay = persistentStorage.lastActivityDay();
- uint256 year = lastRebalanceDay.div(10000);
- uint256 month = lastRebalanceDay.div(100) - year.mul(100);
- uint256 day = lastRebalanceDay - year.mul(10000) - month.mul(100);
- uint256 startDate = DateTimeLibrary.timestampFromDate(year, month, day);
- daysSinceLastRebalance = (block.timestamp - startDate) / 60 / 60 / 24;
+library DSMath {
+
+ // --- Unsigned Math ----
+ function add(uint x, uint y) internal pure returns (uint z) {
+ require((z = x + y) >= x, "ds-math-add-overflow");
+ }
+ function sub(uint x, uint y) internal pure returns (uint z) {
+ require((z = x - y) <= x, "ds-math-sub-underflow");
+ }
+ function mul(uint x, uint y) internal pure returns (uint z) {
+ require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}
- /**
- * @dev Returns the lending fee in crypto
- */
- function getCurrentLendingFeeInCrypto()
- public
- view
- returns (uint256 cryptoForLendingFee)
- {
- uint256 totalTokenSupply = inverseToken.totalSupply();
- uint256 lendingFee = persistentStorage.getLendingFee();
- uint256 balance = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getBalancePerTokenUnit()
- );
- uint256 daysSinceLastRebalance = getDaysSinceLastRebalance();
- cryptoForLendingFee = getLendingFeeInCrypto(
- lendingFee,
- balance,
- daysSinceLastRebalance
- );
+ function min(uint x, uint y) internal pure returns (uint z) {
+ return x <= y ? x : y;
}
-
- /**
- * @dev Returns balance change needed to perform to have a balanced cashposition to balance ratio
- */
- function getCurrentNeededChangeInBalanceToRebalance(uint256 _price)
- public
- view
- returns (uint256 neededChangeInBalance, bool isNegative)
- {
- uint256 totalTokenSupply = inverseToken.totalSupply();
- uint256 netTokenValue = getCurrentNetTokenValue();
- uint256 balance = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getBalancePerTokenUnit()
- );
- return
- getNeededChangeInBalanceToRebalance(netTokenValue, balance, _price);
+ function max(uint x, uint y) internal pure returns (uint z) {
+ return x >= y ? x : y;
}
-
- /**
- * @dev Returns total balance
- */
- function getTotalBalance() external view returns (uint256 totalBalance) {
- uint256 totalTokenSupply = inverseToken.totalSupply();
- totalBalance = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getBalancePerTokenUnit()
- );
+ function imin(int x, int y) internal pure returns (int z) {
+ return x <= y ? x : y;
+ }
+ function imax(int x, int y) internal pure returns (int z) {
+ return x >= y ? x : y;
}
- /**
- * @dev Returns current PCF values for the given price
- * @param _price The momentary price of the crypto
- */
- function calculateDailyPCF(uint256 _price, uint256 _lendingFee)
- public
- view
- returns (
- uint256 endNetTokenValue,
- uint256 endBalance,
- uint256 endCashPosition,
- uint256 feeInFiat,
- uint256 changeInBalance,
- bool isChangeInBalanceNeg
- )
- {
- require(_price != 0, "Price cant be zero");
- uint256 totalTokenSupply = inverseToken.totalSupply();
- uint256 balance = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getBalancePerTokenUnit()
- );
- uint256 cashPosition = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getCashPositionPerTokenUnit()
- );
- uint256 daysSinceLastRebalance = getDaysSinceLastRebalance();
- uint256 minRebalanceAmount = persistentStorage.minRebalanceAmount();
+ // --- Precise Math ---
- return
- calculatePCF(
- cashPosition,
- balance,
- _price,
- _lendingFee,
- daysSinceLastRebalance,
- minRebalanceAmount
- );
- }
+ uint public constant WAD = 10 ** 18;
+ uint public constant RAY = 10 ** 27;
- /**
- * @dev Returns total cash position
- */
- function getTotalCashPosition()
- public
- view
- returns (uint256 totalCashPosition)
- {
- uint256 totalTokenSupply = inverseToken.totalSupply();
- totalCashPosition = DSMath.wmul(
- totalTokenSupply,
- persistentStorage.getCashPositionPerTokenUnit()
- );
+ function wmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), WAD / 2) / WAD;
+ }
+ function rmul(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, y), RAY / 2) / RAY;
+ }
+ function wdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, WAD), y / 2) / y;
+ }
+ function rdiv(uint x, uint y) internal pure returns (uint z) {
+ z = add(mul(x, RAY), y / 2) / y;
}
- function wmul(uint256 x, uint256 y) external pure returns (uint256 z) {
- z = DSMath.wmul(x, y);
+ function ray(uint _wad) internal pure returns (uint) {
+ return mul(_wad, uint(10 ** 9));
}
- function wdiv(uint256 x, uint256 y) external pure returns (uint256 z) {
- z = DSMath.wdiv(x, y);
+ // This famous algorithm is called "exponentiation by squaring"
+ // and calculates x^n with x as fixed-point and n as regular unsigned.
+ //
+ // It's O(log n), instead of O(n) for naive repeated multiplication.
+ //
+ // These facts are why it works:
+ //
+ // If n is even, then x^n = (x^2)^(n/2).
+ // If n is odd, then x^n = x * x^(n-1),
+ // and applying the equation for even x gives
+ // x^n = x * (x^2)^((n-1) / 2).
+ //
+ // Also, EVM division is flooring and
+ // floor[(n-1) / 2] = floor[n / 2].
+ //
+ function rpow(uint x, uint n) internal pure returns (uint z) {
+ z = n % 2 != 0 ? x : RAY;
+
+ for (n /= 2; n != 0; n /= 2) {
+ x = rmul(x, x);
+
+ if (n % 2 != 0) {
+ z = rmul(z, x);
+ }
+ }
}
}
-// File: contracts/TokenSwapManager.sol
+// File: contracts/short-tokens/TokenSwapManager.sol
pragma solidity ^0.5.0;
@@ -2548,23 +1047,25 @@ pragma solidity ^0.5.0;
+
contract TokenSwapManager is Initializable, Ownable {
using Strings for string;
using SafeMath for uint256;
- address public stablecoin;
address public inverseToken;
- KYCVerifier public kycVerifier;
- CashPool public cashPool;
- PersistentStorage public persistentStorage;
- CompositionCalculator public compositionCalculator;
+ InterfaceERC20 public erc20;
+ InterfaceKYCVerifier public kycVerifier;
+ InterfaceCashPool public cashPool;
+ InterfaceStorage public persistentStorage;
+ InterfaceCompositionCalculator public compositionCalculator;
event SuccessfulOrder(
string orderType,
address whitelistedAddress,
uint256 tokensGiven,
- uint256 tokensRecieved
+ uint256 tokensRecieved,
+ address stablecoin
);
event RebalanceEvent(
@@ -2576,31 +1077,29 @@ contract TokenSwapManager is Initializable, Ownable {
function initialize(
address _owner,
- address _stablecoin,
address _inverseToken,
address _cashPool,
+ address _persistentStorage,
address _compositionCalculator
) public initializer {
initialize(_owner);
require(
_owner != address(0) &&
- _stablecoin != address(0) &&
_inverseToken != address(0) &&
_cashPool != address(0) &&
_compositionCalculator != address(0),
"addresses cannot be zero"
);
- stablecoin = _stablecoin;
inverseToken = _inverseToken;
- cashPool = CashPool(_cashPool);
- persistentStorage = PersistentStorage(
- address(cashPool.persistentStorage())
+ cashPool = InterfaceCashPool(_cashPool);
+ persistentStorage = InterfaceStorage(_persistentStorage);
+ kycVerifier = InterfaceKYCVerifier(address(cashPool.kycVerifier()));
+ compositionCalculator = InterfaceCompositionCalculator(
+ _compositionCalculator
);
- kycVerifier = KYCVerifier(address(cashPool.kycVerifier()));
- compositionCalculator = CompositionCalculator(_compositionCalculator);
}
//////////////// Create + Redeem Order Request ////////////////
@@ -2613,7 +1112,9 @@ contract TokenSwapManager is Initializable, Ownable {
uint256 tokensRecieved,
uint256 avgBlendedFee,
uint256 executionPrice,
- address whitelistedAddress
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 gasFee
) public onlyOwnerOrBridge() notPausedOrShutdown() returns (bool retVal) {
// Require is Whitelisted
require(
@@ -2626,14 +1127,18 @@ contract TokenSwapManager is Initializable, Ownable {
transferTokenFromPool(
stablecoin,
whitelistedAddress,
- normalizeUSDC(tokensGiven)
+ normalizeStablecoin(tokensGiven, stablecoin)
);
return false;
}
// Check Tokens Recieved with Composition Calculator
uint256 _tokensRecieved = compositionCalculator
- .getCurrentTokenAmountCreatedByCash(tokensGiven, executionPrice);
+ .getCurrentTokenAmountCreatedByCash(
+ tokensGiven,
+ executionPrice,
+ gasFee
+ );
require(
_tokensRecieved == tokensRecieved,
"tokens created must equal tokens recieved"
@@ -2655,7 +1160,8 @@ contract TokenSwapManager is Initializable, Ownable {
"CREATE",
whitelistedAddress,
tokensGiven,
- tokensRecieved
+ tokensRecieved,
+ stablecoin
);
// Mint Tokens to Address
@@ -2671,7 +1177,9 @@ contract TokenSwapManager is Initializable, Ownable {
uint256 tokensRecieved,
uint256 avgBlendedFee,
uint256 executionPrice,
- address whitelistedAddress
+ address whitelistedAddress,
+ address stablecoin,
+ uint256 gasFee
) public onlyOwnerOrBridge() notPausedOrShutdown() returns (bool retVal) {
// Require Whitelisted
require(
@@ -2691,7 +1199,11 @@ contract TokenSwapManager is Initializable, Ownable {
// Check Cash Recieved with Composition Calculator
uint256 _tokensRecieved = compositionCalculator
- .getCurrentCashAmountCreatedByToken(tokensGiven, executionPrice);
+ .getCurrentCashAmountCreatedByToken(
+ tokensGiven,
+ executionPrice,
+ gasFee
+ );
require(
_tokensRecieved == tokensRecieved,
"cash redeemed must equal tokens recieved"
@@ -2709,7 +1221,12 @@ contract TokenSwapManager is Initializable, Ownable {
);
// Redeem Stablecoin or Perform Delayed Settlement
- redeemFunds(tokensGiven, tokensRecieved, whitelistedAddress);
+ redeemFunds(
+ tokensGiven,
+ tokensRecieved,
+ whitelistedAddress,
+ stablecoin
+ );
// Burn Tokens to Address
InterfaceInverseToken token = InterfaceInverseToken(inverseToken);
@@ -2722,7 +1239,8 @@ contract TokenSwapManager is Initializable, Ownable {
string memory orderType,
address whiteListedAddress,
uint256 tokensGiven,
- uint256 tokensRecieved
+ uint256 tokensRecieved,
+ address stablecoin
) internal {
require(
tokensGiven != 0 && tokensRecieved != 0,
@@ -2733,27 +1251,33 @@ contract TokenSwapManager is Initializable, Ownable {
orderType,
whiteListedAddress,
tokensGiven,
- tokensRecieved
+ tokensRecieved,
+ stablecoin
);
}
function settleDelayedFunds(
uint256 tokensToRedeem,
- address whitelistedAddress
- ) public onlyOwnerOrBridge {
+ address whitelistedAddress,
+ address stablecoin
+ ) public onlyOwnerOrBridge notPausedOrShutdown {
require(
kycVerifier.isAddressWhitelisted(whitelistedAddress),
"only whitelisted may redeem funds"
);
- bool isSufficientFunds = isHotWalletSufficient(tokensToRedeem);
+ bool isSufficientFunds = isHotWalletSufficient(
+ tokensToRedeem,
+ stablecoin
+ );
require(
isSufficientFunds == true,
"not enough funds in the hot wallet"
);
- uint256 tokensOutstanding = persistentStorage
- .getDelayedRedemptionsByUser(whitelistedAddress);
+ uint256 tokensOutstanding = persistentStorage.delayedRedemptionsByUser(
+ whitelistedAddress
+ );
uint256 tokensRemaining = DSMath.sub(tokensOutstanding, tokensToRedeem);
persistentStorage.setDelayedRedemptionsByUser(
@@ -2763,60 +1287,71 @@ contract TokenSwapManager is Initializable, Ownable {
transferTokenFromPool(
stablecoin,
whitelistedAddress,
- normalizeUSDC(tokensToRedeem)
+ normalizeStablecoin(tokensToRedeem, stablecoin)
);
}
function redeemFunds(
uint256 tokensGiven,
uint256 tokensToRedeem,
- address whitelistedAddress
+ address whitelistedAddress,
+ address stablecoin
) internal {
- bool isSufficientFunds = isHotWalletSufficient(tokensToRedeem);
+ bool isSufficientFunds = isHotWalletSufficient(
+ tokensToRedeem,
+ stablecoin
+ );
if (isSufficientFunds) {
transferTokenFromPool(
stablecoin,
whitelistedAddress,
- normalizeUSDC(tokensToRedeem)
+ normalizeStablecoin(tokensToRedeem, stablecoin)
);
writeOrderResponse(
"REDEEM",
whitelistedAddress,
tokensGiven,
- tokensToRedeem
+ tokensToRedeem,
+ stablecoin
);
} else {
uint256 tokensOutstanding = persistentStorage
- .getDelayedRedemptionsByUser(whitelistedAddress);
+ .delayedRedemptionsByUser(whitelistedAddress);
tokensOutstanding = DSMath.add(tokensOutstanding, tokensToRedeem);
persistentStorage.setDelayedRedemptionsByUser(
tokensOutstanding,
whitelistedAddress
);
writeOrderResponse(
- "REDEEM NO SETTLEMENT",
+ "REDEEM_NO_SETTLEMENT",
whitelistedAddress,
tokensGiven,
- tokensToRedeem
+ tokensToRedeem,
+ stablecoin
);
}
}
- function isHotWalletSufficient(uint256 tokensToRedeem)
+ function isHotWalletSufficient(uint256 tokensToRedeem, address stablecoin)
internal
- view
returns (bool)
{
InterfaceInverseToken _stablecoin = InterfaceInverseToken(stablecoin);
uint256 stablecoinBalance = _stablecoin.balanceOf(address(cashPool));
- if (normalizeUSDC(tokensToRedeem) > stablecoinBalance) return false;
+ if (normalizeStablecoin(tokensToRedeem, stablecoin) > stablecoinBalance)
+ return false;
return true;
}
- function normalizeUSDC(uint256 usdcValue) public pure returns (uint256) {
- return usdcValue / 10**12;
+ function normalizeStablecoin(uint256 stablecoinValue, address stablecoin)
+ internal
+ returns (uint256)
+ {
+ erc20 = InterfaceERC20(stablecoin);
+ uint256 exponent = 18 - erc20.decimals();
+ return stablecoinValue / 10**exponent; // 6 decimal stable coin = 10**12
}
//////////////// Daily Rebalance ////////////////
diff --git a/flats/USDC.sol b/flats/USDC.sol
index 6bd60f4..b16f571 100644
--- a/flats/USDC.sol
+++ b/flats/USDC.sol
@@ -1,5 +1,5 @@
-// File: contracts/Token/USDC.sol
+// File: contracts/shared/Token/USDC.sol
/**
*Submitted for verification at Etherscan.io on 2020-03-03
diff --git a/flats/USDT.sol b/flats/USDT.sol
new file mode 100644
index 0000000..d07cbf7
--- /dev/null
+++ b/flats/USDT.sol
@@ -0,0 +1,656 @@
+
+// File: contracts/shared/Token/USDT.sol
+
+/**
+ *Submitted for verification at Etherscan.io on 2020-03-03
+ */
+
+/**
+ *Submitted for verification at Etherscan.io on 2018-08-03
+ */
+
+pragma solidity ^0.5.0;
+
+
+// File: contracts/OwnableUSDCT.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title OwnableUSDCT
+ * @dev The OwnableUSDCT contract from https://github.com/zeppelinos/labs/blob/master/upgradeability_ownership/contracts/ownership/Ownable.sol
+ * branch: master commit: 3887ab77b8adafba4a26ace002f3a684c1a3388b modified to:
+ * 1) Add emit prefix to OwnershipTransferred event (7/13/18)
+ * 2) Replace constructor with constructor syntax (7/13/18)
+ * 3) consolidate OwnableUSDCTStorage into this contract
+ */
+contract OwnableUSDCT {
+ // Owner of the contract
+ address private _owner;
+
+ /**
+ * @dev Event to show ownership has been transferred
+ * @param previousOwner representing the address of the previous owner
+ * @param newOwner representing the address of the new owner
+ */
+ event OwnershipTransferred(address previousOwner, address newOwner);
+
+ /**
+ * @dev The constructor sets the original owner of the contract to the sender account.
+ */
+ function initialize() public {
+ setOwner(msg.sender);
+ }
+
+ /**
+ * @dev Tells the address of the owner
+ * @return the address of the owner
+ */
+ function owner() public view returns (address) {
+ return _owner;
+ }
+
+ /**
+ * @dev Sets a new owner address
+ */
+ function setOwner(address newOwner) internal {
+ _owner = newOwner;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the owner.
+ */
+ modifier onlyOwner() {
+ require(msg.sender == owner());
+ _;
+ }
+
+ /**
+ * @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 {
+ require(newOwner != address(0));
+ emit OwnershipTransferred(owner(), newOwner);
+ setOwner(newOwner);
+ }
+}
+
+
+// File: contracts/Blacklistable.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title Blacklistable Token
+ * @dev Allows accounts to be blacklisted by a "blacklister" role
+ */
+contract Blacklistable is OwnableUSDCT {
+ address public blacklister;
+ mapping(address => bool) internal blacklisted;
+
+ event Blacklisted(address indexed _account);
+ event UnBlacklisted(address indexed _account);
+ event BlacklisterChanged(address indexed newBlacklister);
+
+ /**
+ * @dev Throws if called by any account other than the blacklister
+ */
+ modifier onlyBlacklister() {
+ require(msg.sender == blacklister);
+ _;
+ }
+
+ /**
+ * @dev Throws if argument account is blacklisted
+ * @param _account The address to check
+ */
+ modifier notBlacklisted(address _account) {
+ require(blacklisted[_account] == false);
+ _;
+ }
+
+ /**
+ * @dev Checks if account is blacklisted
+ * @param _account The address to check
+ */
+ function isBlacklisted(address _account) public view returns (bool) {
+ return blacklisted[_account];
+ }
+
+ /**
+ * @dev Adds account to blacklist
+ * @param _account The address to blacklist
+ */
+ function blacklist(address _account) public onlyBlacklister {
+ blacklisted[_account] = true;
+ emit Blacklisted(_account);
+ }
+
+ /**
+ * @dev Removes account from blacklist
+ * @param _account The address to remove from the blacklist
+ */
+ function unBlacklist(address _account) public onlyBlacklister {
+ blacklisted[_account] = false;
+ emit UnBlacklisted(_account);
+ }
+
+ function updateBlacklister(address _newBlacklister) public onlyOwner {
+ require(_newBlacklister != address(0));
+ blacklister = _newBlacklister;
+ emit BlacklisterChanged(blacklister);
+ }
+}
+
+
+// File: contracts/Pausable.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title Pausable
+ * @dev Base contract which allows children to implement an emergency stop mechanism.
+ * Based on openzeppelin tag v1.10.0 commit: feb665136c0dae9912e08397c1a21c4af3651ef3
+ * Modifications:
+ * 1) Added pauser role, switched pause/unpause to be onlyPauser (6/14/2018)
+ * 2) Removed whenNotPause/whenPaused from pause/unpause (6/14/2018)
+ * 3) Removed whenPaused (6/14/2018)
+ * 4) Switches ownable library to use zeppelinos (7/12/18)
+ * 5) Remove constructor (7/13/18)
+ */
+contract Pausable is OwnableUSDCT {
+ event Pause();
+ event Unpause();
+ event PauserChanged(address indexed newAddress);
+
+ address public pauser;
+ bool public paused = false;
+
+ /**
+ * @dev Modifier to make a function callable only when the contract is not paused.
+ */
+ modifier whenNotPaused() {
+ require(!paused);
+ _;
+ }
+
+ /**
+ * @dev throws if called by any account other than the pauser
+ */
+ modifier onlyPauser() {
+ require(msg.sender == pauser);
+ _;
+ }
+
+ /**
+ * @dev called by the owner to pause, triggers stopped state
+ */
+ function pause() public onlyPauser {
+ paused = true;
+ emit Pause();
+ }
+
+ /**
+ * @dev called by the owner to unpause, returns to normal state
+ */
+ function unpause() public onlyPauser {
+ paused = false;
+ emit Unpause();
+ }
+
+ /**
+ * @dev update the pauser role
+ */
+ function updatePauser(address _newPauser) public onlyOwner {
+ require(_newPauser != address(0));
+ pauser = _newPauser;
+ emit PauserChanged(pauser);
+ }
+}
+
+
+// File: openzeppelin-solidity/contracts/math/SafeMath.sol
+
+/**
+ * @title SafeMath
+ * @dev Math operations with safety checks that throw on error
+ */
+library SafeMath {
+ /**
+ * @dev Multiplies two numbers, throws on overflow.
+ */
+ function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
+ // Gas optimization: this is cheaper than asserting '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;
+ }
+
+ c = a * b;
+ assert(c / a == b);
+ return c;
+ }
+
+ /**
+ * @dev Integer division of two numbers, truncating the quotient.
+ */
+ function div(uint256 a, uint256 b) internal pure returns (uint256) {
+ // assert(b > 0); // Solidity automatically throws when dividing by 0
+ // uint256 c = a / b;
+ // assert(a == b * c + a % b); // There is no case in which this doesn't hold
+ return a / b;
+ }
+
+ /**
+ * @dev Subtracts two numbers, throws on overflow (i.e. if subtrahend is greater than minuend).
+ */
+ function sub(uint256 a, uint256 b) internal pure returns (uint256) {
+ assert(b <= a);
+ return a - b;
+ }
+
+ /**
+ * @dev Adds two numbers, throws on overflow.
+ */
+ function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
+ c = a + b;
+ assert(c >= a);
+ return c;
+ }
+}
+
+
+// File: openzeppelin-solidity/contracts/token/ERC20/ERC20Basic.sol
+
+/**
+ * @title ERC20Basic
+ * @dev Simpler version of ERC20 interface
+ * See https://github.com/ethereum/EIPs/issues/179
+ */
+contract ERC20Basic {
+ function totalSupply() public view returns (uint256);
+
+ function balanceOf(address who) public view returns (uint256);
+
+ function transfer(address to, uint256 value) public returns (bool);
+
+ event Transfer(address indexed from, address indexed to, uint256 value);
+}
+
+
+// File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol
+
+/**
+ * @title ERC20 interface
+ * @dev see https://github.com/ethereum/EIPs/issues/20
+ */
+contract ERC20T is ERC20Basic {
+ function allowance(address owner, address spender)
+ public
+ view
+ returns (uint256);
+
+ function transferFrom(address from, address to, uint256 value)
+ public
+ returns (bool);
+
+ function approve(address spender, uint256 value) public returns (bool);
+
+ event Approval(
+ address indexed owner,
+ address indexed spender,
+ uint256 value
+ );
+}
+
+
+// File: contracts/FiatTokenV1.sol
+
+/**
+ * Copyright CENTRE SECZ 2018
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is furnished to
+ * do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/**
+ * @title FiatToken
+ * @dev ERC20 Token backed by fiat reserves
+ */
+contract USDT is OwnableUSDCT, ERC20T, Pausable, Blacklistable {
+ using SafeMath for uint256;
+
+ string public name;
+ string public symbol;
+ uint8 public decimals;
+ string public currency;
+ address public masterMinter;
+ bool internal initialized;
+ uint256 internal totalSupply_;
+
+ mapping(address => uint256) internal balances;
+ mapping(address => mapping(address => uint256)) internal allowed;
+ mapping(address => bool) internal minters;
+ mapping(address => uint256) internal minterAllowed;
+
+ event Mint(address indexed minter, address indexed to, uint256 amount);
+ event Burn(address indexed burner, uint256 amount);
+ event MinterConfigured(address indexed minter, uint256 minterAllowedAmount);
+ event MinterRemoved(address indexed oldMinter);
+ event MasterMinterChanged(address indexed newMasterMinter);
+
+ function initialize(
+ string memory _name,
+ string memory _symbol,
+ string memory _currency,
+ uint8 _decimals,
+ address _masterMinter,
+ address _pauser,
+ address _blacklister,
+ address _owner
+ ) public {
+ require(!initialized);
+ require(_masterMinter != address(0));
+ require(_pauser != address(0));
+ require(_blacklister != address(0));
+ require(_owner != address(0));
+
+ totalSupply_ = 0;
+ name = _name;
+ symbol = _symbol;
+ currency = _currency;
+ decimals = _decimals;
+ masterMinter = _masterMinter;
+ pauser = _pauser;
+ blacklister = _blacklister;
+ setOwner(_owner);
+ initialized = true;
+ }
+
+ /**
+ * @dev Throws if called by any account other than a minter
+ */
+ modifier onlyMinters() {
+ require(minters[msg.sender] == true);
+ _;
+ }
+
+ /**
+ * @dev Function to mint tokens
+ * @param _to The address that will receive the minted tokens.
+ * @param _amount The amount of tokens to mint. Must be less than or equal to the minterAllowance of the caller.
+ * @return A boolean that indicates if the operation was successful.
+ */
+ function mint(address _to, uint256 _amount)
+ public
+ whenNotPaused
+ onlyMinters
+ notBlacklisted(msg.sender)
+ notBlacklisted(_to)
+ returns (bool)
+ {
+ require(_to != address(0));
+ require(_amount > 0);
+
+ uint256 mintingAllowedAmount = minterAllowed[msg.sender];
+ require(_amount <= mintingAllowedAmount);
+
+ totalSupply_ = totalSupply_.add(_amount);
+ balances[_to] = balances[_to].add(_amount);
+ minterAllowed[msg.sender] = mintingAllowedAmount.sub(_amount);
+ emit Mint(msg.sender, _to, _amount);
+ emit Transfer(address(0), _to, _amount);
+ return true;
+ }
+
+ /**
+ * @dev Throws if called by any account other than the masterMinter
+ */
+ modifier onlyMasterMinter() {
+ require(msg.sender == masterMinter);
+ _;
+ }
+
+ /**
+ * @dev Get minter allowance for an account
+ * @param minter The address of the minter
+ */
+ function minterAllowance(address minter) public view returns (uint256) {
+ return minterAllowed[minter];
+ }
+
+ /**
+ * @dev Checks if account is a minter
+ * @param account The address to check
+ */
+ function isMinter(address account) public view returns (bool) {
+ return minters[account];
+ }
+
+ /**
+ * @dev Get allowed amount for an account
+ * @param owner address The account owner
+ * @param spender address The account spender
+ */
+ function allowance(address owner, address spender)
+ public
+ view
+ returns (uint256)
+ {
+ return allowed[owner][spender];
+ }
+
+ /**
+ * @dev Get totalSupply of token
+ */
+ function totalSupply() public view returns (uint256) {
+ return totalSupply_;
+ }
+
+ /**
+ * @dev Get token balance of an account
+ * @param account address The account
+ */
+ function balanceOf(address account) public view returns (uint256) {
+ return balances[account];
+ }
+
+ /**
+ * @dev Adds blacklisted check to approve
+ * @return True if the operation was successful.
+ */
+ function approve(address _spender, uint256 _value)
+ public
+ whenNotPaused
+ notBlacklisted(msg.sender)
+ notBlacklisted(_spender)
+ returns (bool)
+ {
+ allowed[msg.sender][_spender] = _value;
+ emit Approval(msg.sender, _spender, _value);
+ return true;
+ }
+
+ /**
+ * @dev Transfer tokens from one address to another.
+ * @param _from address The address which you want to send tokens from
+ * @param _to address The address which you want to transfer to
+ * @param _value uint256 the amount of tokens to be transferred
+ * @return bool success
+ */
+ function transferFrom(address _from, address _to, uint256 _value)
+ public
+ whenNotPaused
+ notBlacklisted(_to)
+ notBlacklisted(msg.sender)
+ notBlacklisted(_from)
+ returns (bool)
+ {
+ require(_to != address(0));
+ require(_value <= balances[_from]);
+ require(_value <= allowed[_from][msg.sender]);
+
+ balances[_from] = balances[_from].sub(_value);
+ balances[_to] = balances[_to].add(_value);
+ allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
+ emit Transfer(_from, _to, _value);
+ return true;
+ }
+
+ /**
+ * @dev transfer token for a specified address
+ * @param _to The address to transfer to.
+ * @param _value The amount to be transferred.
+ * @return bool success
+ */
+ function transfer(address _to, uint256 _value)
+ public
+ whenNotPaused
+ notBlacklisted(msg.sender)
+ notBlacklisted(_to)
+ returns (bool)
+ {
+ require(_to != address(0));
+ require(_value <= balances[msg.sender]);
+
+ balances[msg.sender] = balances[msg.sender].sub(_value);
+ balances[_to] = balances[_to].add(_value);
+ emit Transfer(msg.sender, _to, _value);
+ return true;
+ }
+
+ /**
+ * @dev Function to add/update a new minter
+ * @param minter The address of the minter
+ * @param minterAllowedAmount The minting amount allowed for the minter
+ * @return True if the operation was successful.
+ */
+ function configureMinter(address minter, uint256 minterAllowedAmount)
+ public
+ whenNotPaused
+ onlyMasterMinter
+ returns (bool)
+ {
+ minters[minter] = true;
+ minterAllowed[minter] = minterAllowedAmount;
+ emit MinterConfigured(minter, minterAllowedAmount);
+ return true;
+ }
+
+ /**
+ * @dev Function to remove a minter
+ * @param minter The address of the minter to remove
+ * @return True if the operation was successful.
+ */
+ function removeMinter(address minter)
+ public
+ onlyMasterMinter
+ returns (bool)
+ {
+ minters[minter] = false;
+ minterAllowed[minter] = 0;
+ emit MinterRemoved(minter);
+ return true;
+ }
+
+ /**
+ * @dev allows a minter to burn some of its own tokens
+ * Validates that caller is a minter and that sender is not blacklisted
+ * amount is less than or equal to the minter's account balance
+ * @param _amount uint256 the amount of tokens to be burned
+ */
+ function burn(uint256 _amount)
+ public
+ whenNotPaused
+ onlyMinters
+ notBlacklisted(msg.sender)
+ {
+ uint256 balance = balances[msg.sender];
+ require(_amount > 0);
+ require(balance >= _amount);
+
+ totalSupply_ = totalSupply_.sub(_amount);
+ balances[msg.sender] = balance.sub(_amount);
+ emit Burn(msg.sender, _amount);
+ emit Transfer(msg.sender, address(0), _amount);
+ }
+
+ function updateMasterMinter(address _newMasterMinter) public onlyOwner {
+ require(_newMasterMinter != address(0));
+ masterMinter = _newMasterMinter;
+ emit MasterMinterChanged(masterMinter);
+ }
+}
diff --git a/scripts/deployContracts.sh b/scripts/deployContracts.sh
new file mode 100755
index 0000000..5ecf001
--- /dev/null
+++ b/scripts/deployContracts.sh
@@ -0,0 +1,302 @@
+#!/usr/bin/env bash
+
+function deployContracts() {
+
+ if [ "$network" == "development" ]; then
+ # Check if ganache is running and terminate if not
+ isGanacheRunning="$(pgrep node | xargs ps -fp | grep -c ganache)"
+
+ if [ "$isGanacheRunning" == 1 ]; then
+ echo "### Ganache is running. Can move on to compile ###"
+ else
+ echo "### Ganache isn't running. Please run Ganache (npm run dev) before running this script ###"
+ exit
+ fi
+ else
+ echo "### Deploying contract on ${network} network ###"
+ fi
+
+ managementFee=11000000000000000000
+ minRebalanceAmount=1000000000000000000
+ inverseTokenName=$2
+ lastMintingFee=1000000000000000;
+ balancePrecision=12;
+ minimumMintingFee=0; # $5
+ minimumTrade=50000000000000000000; # $50
+
+
+ if [ "$isLeverageToken" == "true" ]; then
+ echo
+ echo "### Deploying StorageLeverage ###"
+ echo
+ persistenStorageContractName="StorageLeverage"
+ persistentStorageAddress="$(exec npx oz create StorageLeverage -n ${network} --init --args $ownerAddress,$managementFee,$minRebalanceAmount,$balancePrecision,$lastMintingFee,$minimumMintingFee,$minimumTrade --no-interactive --force)"
+ else
+ echo
+ echo "### Compiling and deploying persistentStorage ###"
+ echo
+ persistenStorageContractName="PersistentStorage"
+ persistentStorageAddress="$(exec npx oz create PersistentStorage -n ${network} --init --args $ownerAddress,$managementFee,$minRebalanceAmount,$balancePrecision,$lastMintingFee,$minimumMintingFee,$minimumTrade --no-interactive --force)"
+ fi
+
+ echo
+ echo "### Compiling and deploying InverseToken ###"
+ echo
+ inverseTokenAddress="$(exec npx oz create InverseToken -n ${network} --init --args $inverseTokenName,$inverseTokenName,18,$persistentStorageAddress,$ownerAddress --no-interactive --force)"
+
+
+ if [ "$isLeverageToken" == "true" ]; then
+ echo
+ echo "### Deploying CalculatorLeverage ###"
+ echo
+ compositionCalculatorContractName="CalculatorLeverage"
+ compositionCalculatorAddress="$(exec npx oz create CalculatorLeverage -n ${network} --init --args $persistentStorageAddress,$inverseTokenAddress --no-interactive --force)"
+ else
+ echo
+ echo "### Compiling and deploying CompositionCalculator ###"
+ echo
+ compositionCalculatorContractName="CompositionCalculator"
+ compositionCalculatorAddress="$(exec npx oz create CompositionCalculator -n ${network} --init --args $persistentStorageAddress,$inverseTokenAddress --no-interactive --force)"
+ fi
+
+
+ if [ "$isLeverageToken" == "true" ]; then
+ echo
+ echo "### Deploying TokenSwapLeverage ###"
+ echo
+ tsmContractName="TokenSwapLeverage"
+ tsmAddress="$(exec npx oz create TokenSwapLeverage -n ${network} --init --args $ownerAddress,$inverseTokenAddress,$cashPoolAddress,$persistentStorageAddress,$compositionCalculatorAddress --no-interactive --force)"
+ else
+ echo
+ echo "### Compiling and deploying TokenSwapManager ###"
+ echo
+ tsmContractName="TokenSwapManager"
+ tsmAddress="$(exec npx oz create TokenSwapManager -n ${network} --init --args $ownerAddress,$inverseTokenAddress,$cashPoolAddress,$persistentStorageAddress,$compositionCalculatorAddress --no-interactive --force)"
+ fi
+
+ if [ "$isLeverageToken" == "true" ]; then
+ echo
+ echo "### Deploying TokenSwapLeverage ###"
+ echo
+ stablecoinContractName="USDT"
+ stablecoinContractAddress=${USDTAddress}
+ else
+ echo
+ echo "### Compiling and deploying TokenSwapManager ###"
+ echo
+ stablecoinContractName="USDC"
+ stablecoinContractAddress=${USDCAddress}
+
+ fi
+
+
+ json='{"KYCVerifier":"'${kycVerifierAddress}'","'${persistenStorageContractName}'":"'${persistentStorageAddress}'","'${tsmContractName}'":"'${tsmAddress}'","'${stablecoinContractName}'":"'${stablecoinContractAddress}'","InverseToken":"'${inverseTokenAddress}'","CashPool":"'${cashPoolAddress}'","'${compositionCalculatorContractName}'":"'${compositionCalculatorAddress}'","OwnerMultiSig":"'${ownerAddress}'","ColdStorageMultiSig":"'${coldStorageAddress}'"}'
+
+ if [ "$network" == "mainnet" ]; then
+ echo
+ echo "Saving contract addresses to S3 for ${network}"
+ node '../App/backend/scripts/addContractAddressesToS3.js' ${inverseTokenName}contractsAddresses $json $network
+ echo
+ elif [ "$network" == "rinkeby" ]; then
+ echo
+ echo "Saving contract addresses to S3 for ${network}"
+ node '../App/backend/scripts/addContractAddressesToS3.js' ${inverseTokenName}contractsAddresses $json $network
+ echo
+ else
+ echo
+ echo "### Saving contracts file locally for ${network} network ###"
+ printf $json > /tmp/${inverseTokenName}contractsAddresses.json
+ echo
+ fi
+}
+
+function setIsDappMode() {
+ printf '{ "isDappMode":"%s" }\n' "$isDappMode" > /tmp/isDappMode.json
+}
+
+function flattenContracts() {
+ echo
+ echo "### Flatten contracts into /flat ###"
+ echo
+
+ npm run flatten
+}
+
+function setUpContractsWithMintInverseTokens() {
+ product=$1
+ network=$2
+ isLeverageToken=$3
+ # Now invoke the script that whitelists ownerAddress
+ echo
+ echo "Mint initial inverse Tokens, set bridgeAddress on KycVerifier contract, KYC whitelist ownerAddress and set Token SwapManager Address in PersistentStorage"
+ echo
+ node '../App/backend/scripts/setUpContractsWithMintInverseTokens.js' $product $network $isLeverageToken
+}
+
+function resetContractsInDB() {
+ echo "### Setting contracts data into Jasper DB ###"
+ cd ../App/backend && \
+ exec npm run db:reset
+
+ cd ../../Contracts
+}
+
+function createLendingRates() {
+ echo "### Trying to set Lending Rates for 30 days at 2% from Blockfi ###"
+ echo "### Make sure your backend is running ###"
+ echo "### ONLY CREATING FOR BTCSHORT ###"
+
+ exec npm run script:createLendingRates
+ echo "done"
+}
+
+function createFirstRebalanceInDBForInverseNonLeveraged() {
+ echo "### Trying to set Setup Contracts in DB with initial minted token and price ###"
+ echo "### Make sure your backend is running ###"
+ echo "### ONLY CREATING FOR BTCSHORT ###"
+ exec npm run script:createFirstRebalanceInDBForInverseNonLeveraged
+ echo "done"
+}
+
+# Main
+network=$1
+products=$2 # "BTC2L BTCSHORT", space separated string or "all" for all contracts
+
+
+rm build/contracts/*.json
+
+npx oz push -n $network
+ # deploy all contracts
+echo
+echo "### Compiling and deploying contracts ###"
+
+if [ "$network" == "mainnet" ]; then
+ echo
+ ownerAddress="0x786ea62bC54613C3639A6275f88D3b86Ce3a0D45"
+ echo "OwnerMultisig for ${network} network is ${ownerAddress}"
+ echo
+ coldStorageAddress="0x555EAc576C533579834aE1a6AF6FC1d1925E7D71"
+ echo "ColdStorageMultisig for ${network} network is ${coldStorageAddress}"
+ echo
+ # source: https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
+ USDCAddress="0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
+ echo "USDC for ${network} network is ${USDCAddress}"
+ echo
+ # source: https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7
+ USDTAddress="0xdac17f958d2ee523a2206206994597c13d831ec7"
+ echo "USDC for ${network} network is ${USDCAddress}"
+ echo
+ kycVerifierAddress="0xE2E2fB43412818f9827B9e0943C031a518Ef451c"
+ echo "KycVerifier for ${network} network is ${kycVerifierAddress}"
+ echo
+ cashPoolAddress="0x98EABA2684b49BbDe9842cd48f3bAE8cAA31dac4"
+ echo "CashPool for ${network} network is ${cashPoolAddress}"
+elif [ "$network" == "rinkeby" ]; then
+ echo
+ ownerAddress="0xa86223E26a74Eb292f23793BE18a3D1A3d5E1495"
+ echo "OwnerMultisig for ${network} network is ${ownerAddress}"
+ echo
+ coldStorageAddress="0x296DFFEf40DfE7b6C356419aF879eB5Cb3fd64E6"
+ echo "ColdStorageMultisig for ${network} network is ${coldStorageAddress}"
+ echo
+ USDCAddress="0x3033f7d702C602BaF4b2D340B8A3DD29f3d91DEf"
+ echo "USDC for ${network} network is ${USDCAddress}"
+ echo
+ USDTAddress="0x8175a4d188cF8317CC1Bf3505C2f709764076987"
+ echo "USDT for ${network} network is ${USDTAddress}"
+ echo
+ kycVerifierAddress="0x3B1110fc383ADC316133bc16fDb2137731ef5B88"
+ echo "KycVerifier for ${network} network is ${kycVerifierAddress}"
+ echo
+ cashPoolAddress="0xDDeF3814b8D6907f61C90A5cC90F38e33fC7Dc36"
+ echo "CashPool for ${network} network is ${cashPoolAddress}"
+else
+ if [ "$network" != 'development' ]; then
+ echo
+ echo "Missing network. Must be development, rinkeby or mainnet "
+ exit
+ fi
+ echo
+ echo "Deploying contracts through Multisig owner (full dapp mode)"
+ isDappMode=true
+ signers="[0x2a78B494852fc74c25B70eC69a78D7332c7Dc87d,0x7e56019B6c9FDA6eE3B98B860E51195d42861355,0xC3902c4e6cbD0AaB4E88bdDBAD75Ac13c9287ABf]"
+ requiredSignature=2
+ ownerAddress="$(exec npx oz create OwnerMultiSig -n ${network} --init --args $signers,$requiredSignature --no-interactive --force)"
+ coldStorageAddress="$(exec npx oz create OwnerMultiSig -n ${network} --init --args $signers,$requiredSignature --no-interactive --force)"
+ echo "Deployed ownerMultisig at ${ownerAddress} and coldStorage at ${coldStorageAddress}s"
+ echo
+ echo "### Compiling and deploying StableCoin (USDC) in ${network} network ###"
+ echo
+ USDCAddress="$(exec npx oz create USDC -n ${network} --init --args 'USDC','USDC','USD',6,$ownerAddress,$ownerAddress,$ownerAddress,$ownerAddress --no-interactive --force)"
+ echo
+ echo "### Deployed StableCoin (USDC) at ${USDCAddress} ###"
+ echo
+
+ echo "### Compiling and deploying Tether USD (USDT) in ${network} network ###"
+ echo
+ USDTAddress="$(exec npx oz create USDT -n ${network} --init --args 'USDT','USDT','USD',6,$ownerAddress,$ownerAddress,$ownerAddress,$ownerAddress --no-interactive --force)"
+ echo
+ echo "### Deployed Tether USD (USDT) at ${USDTAddress} ###"
+ echo
+
+ echo "### Compiling and deploying KYCVerifier ###"
+ echo
+ kycVerifierAddress="$(exec npx oz create KYCVerifier -n ${network} --init --args $ownerAddress --no-interactive --force)"
+
+ echo
+ echo "### Compiling and deploying CashPool ###"
+ echo
+ zeroPercentInFraction=[0,1]
+ cashPoolAddress="$(exec npx oz create CashPool -n ${network} --init --args $ownerAddress,$kycVerifierAddress,$coldStorageAddress,$zeroPercentInFraction --no-interactive --force)"
+fi
+
+
+if [ "${products}" == "all" ]; then
+ echo "Inside products all"
+ products="BTCSHORT ETHSHORT BCHSHORT BTC2S BTC2L BTC3S BTC3L ETH2S ETH2L ETH3S ETH3L"
+fi
+
+echo "Products ${products}"
+
+for product in $products
+do
+ echo
+ echo
+ if [[ "BTC2S BTC2L BTC3S BTC3L ETH2S ETH2L ETH3S ETH3L" =~ $product ]]; then
+ isLeverageToken="true"
+ echo "Starting deployment for ${product} in ${network} network; IsLeverageToken: true"
+ else
+ isLeverageToken=""
+ echo "Starting deployment for ${product} in ${network} network; Product ${product}; IsLeverageToken: false"
+ fi
+
+ deployContracts $network $product $isLeverageToken
+ setIsDappMode
+ flattenContracts
+ setUpContractsWithMintInverseTokens $product $network $isLeverageToken
+done
+
+
+if [ "$network" == "mainnet" ]; then
+ echo
+ echo "### Saving contract ABIs to S3 for ${network} ###"
+ node '../App/backend/scripts/addContractsAbiToS3.js' $network
+ echo
+elif [ "$network" == "rinkeby" ]; then
+ echo
+ echo "### Saving contract ABIs to S3 for ${network} ###"
+ node '../App/backend/scripts/addContractsAbiToS3.js' $network
+ echo
+fi
+
+if [ "$network" == "development" ]; then
+ if [ -d "../App/backend" ]; then
+ resetContractsInDB
+ createLendingRates
+ createFirstRebalanceInDBForInverseNonLeveraged
+ else
+ echo "Your setup seems to be missing the backend of Jasper. Not seeding DB data"
+ fi
+else
+ echo "Not running db reset because it is in ${network} network. It is only used for development network"
+fi
diff --git a/scripts/flatten.sh b/scripts/flatten.sh
index 61934c7..3416e9f 100755
--- a/scripts/flatten.sh
+++ b/scripts/flatten.sh
@@ -2,11 +2,16 @@
mkdir flats
rm -rf flats/*
-./node_modules/.bin/truffle-flattener contracts/CompositionCalculator.sol > flats/CompositionCalculator.sol
-./node_modules/.bin/truffle-flattener contracts/TokenSwapManager.sol > flats/TokenSwapManager.sol
-./node_modules/.bin/truffle-flattener contracts/PersistentStorage.sol > flats/PersistentStorage.sol
-./node_modules/.bin/truffle-flattener contracts/Token/USDC.sol > flats/USDC.sol
-./node_modules/.bin/truffle-flattener contracts/Token/InverseToken.sol > flats/InverseToken.sol
-./node_modules/.bin/truffle-flattener contracts/CashPool.sol > flats/CashPool.sol
-./node_modules/.bin/truffle-flattener contracts/KYCVerifier.sol > flats/KYCVerifier.sol
+./node_modules/.bin/truffle-flattener contracts/short-tokens/CompositionCalculator.sol > flats/CompositionCalculator.sol
+./node_modules/.bin/truffle-flattener contracts/short-tokens/TokenSwapManager.sol > flats/TokenSwapManager.sol
+./node_modules/.bin/truffle-flattener contracts/leverage-tokens/TokenSwapLeverage.sol > flats/TokenSwapLeverage.sol
+./node_modules/.bin/truffle-flattener contracts/leverage-tokens/CalculatorLeverage.sol > flats/CalculatorLeverage.sol
+./node_modules/.bin/truffle-flattener contracts/leverage-tokens/StorageLeverage.sol > flats/StorageLeverage.sol
+./node_modules/.bin/truffle-flattener contracts/short-tokens/PersistentStorage.sol > flats/PersistentStorage.sol
+./node_modules/.bin/truffle-flattener contracts/shared/Token/USDC.sol > flats/USDC.sol
+./node_modules/.bin/truffle-flattener contracts/shared/Token/USDT.sol > flats/USDT.sol
+./node_modules/.bin/truffle-flattener contracts/short-tokens/InverseToken.sol > flats/InverseToken.sol
+./node_modules/.bin/truffle-flattener contracts/short-tokens/CashPool.sol > flats/CashPool.sol
+./node_modules/.bin/truffle-flattener contracts/short-tokens/KYCVerifier.sol > flats/KYCVerifier.sol
+./node_modules/.bin/truffle-flattener contracts/shared/AdminMultiSig/OwnerMultiSig.sol > flats/OwnerMultiSig.sol
diff --git a/test/CalculatorLeverage.test.js b/test/CalculatorLeverage.test.js
new file mode 100644
index 0000000..d4de248
--- /dev/null
+++ b/test/CalculatorLeverage.test.js
@@ -0,0 +1,96 @@
+const { accounts, contract } = require("@openzeppelin/test-environment");
+const { expect } = require("chai");
+const BigNumber = require("bignumber.js");
+
+const { time, expectRevert, ether, BN } = require("@openzeppelin/test-helpers");
+
+const InverseToken = contract.fromArtifact("InverseToken");
+const CalculatorLeverage = contract.fromArtifact("CalculatorLeverage");
+const StorageLeverage = contract.fromArtifact("StorageLeverage");
+
+const getNumberWithDecimal = num =>
+ new BigNumber(num)
+ .div(new BigNumber("10").pow(new BigNumber("18")))
+ .toFixed(18);
+const getEth = num =>
+ new BigNumber(num).times(new BigNumber("10").pow("18")).integerValue();
+
+describe("CalculatorLeverage", function() {
+ const [owner] = accounts;
+ this.timeout(5000);
+
+ beforeEach(async function() {
+ this.storage = await StorageLeverage.new({ from: owner });
+
+ const managementFee = ether("10.95");
+ const minRebalanceAmount = ether("1");
+ const lastMintingFee = ether("0.001");
+ const balancePrecision = 12;
+ const minimumMintingFee = ether("0");
+ const minimumTrade = ether("50");
+ await this.storage.initialize(
+ owner,
+ managementFee,
+ minRebalanceAmount,
+ balancePrecision,
+ lastMintingFee,
+ minimumMintingFee,
+ minimumTrade
+ );
+ await this.storage.addMintingFeeBracket(ether("50000"), ether("0.003"), {
+ from: owner
+ }); //0.3%
+ await this.storage.addMintingFeeBracket(ether("100000"), ether("0.002"), {
+ from: owner
+ }); //0
+ this.token = await InverseToken.new({ from: owner });
+ await this.token.initialize(
+ "InverseToken",
+ "IT",
+ 18,
+ this.storage.address,
+ owner
+ );
+
+ this.contract = await CalculatorLeverage.new({ from: owner });
+ await this.contract.initialize(this.storage.address, this.token.address);
+ });
+
+ describe("#getTokenAmountCreatedByCash", function() {
+ it("Correct Tokens Created From Creation Order", async function() {
+ const mintPrice = getEth(1000);
+ const tokensGiven = getEth(10);
+ const gasFee = getEth(1);
+
+ // (TokensGiven - GasFee - MintingFee) / MintPrice
+ const expectedTokensCreated = "8973000000000000";
+
+ const tokenCreated = await this.contract.getTokensCreatedByCash(
+ mintPrice,
+ tokensGiven,
+ gasFee
+ );
+ expect(tokenCreated.toString()).to.be.equal(expectedTokensCreated);
+ });
+ });
+
+ describe("#getCashAmountCreatedByToken", function() {
+ it("Correct Stablecoin Recieved from Redemption Order", async function() {
+ const burnPrice = getEth(1000);
+ const timeElapsed = getEth(1);
+ const tokensGiven = getEth(10);
+ const gasFee = getEth(1);
+
+ // (TokensGiven * BurnPrice) - GasFee - MintingFee - ManagementFee
+ const expectedTokensCreated = "9968878387462500000000";
+
+ const tokenCreated = await this.contract.getCashCreatedByTokens(
+ burnPrice,
+ timeElapsed,
+ tokensGiven,
+ gasFee
+ );
+ expect(tokenCreated.toString()).to.be.equal(expectedTokensCreated);
+ });
+ });
+});
diff --git a/test/CashPool.test.js b/test/CashPool.test.js
index cb9670e..08e7a4a 100644
--- a/test/CashPool.test.js
+++ b/test/CashPool.test.js
@@ -48,7 +48,7 @@ describe("CashPool", function() {
{ from: owner }
); //0.2%
await persistentStorage.setTokenSwapManager(tokenSwap, { from: owner });
-
+
// initialize token
token = await ERC20WithMinting.new({ from: owner });
await token.initialize(
@@ -69,10 +69,10 @@ describe("CashPool", function() {
await this.contract.initialize(
owner,
kycVerifier.address,
- persistentStorage.address,
coldStorage,
sixtyPercentInArrayFraction
);
+ await this.contract.addTokenManager(tokenSwap, {from: owner})
});
describe("#moveTokenToPool", function() {
@@ -90,7 +90,7 @@ describe("CashPool", function() {
amountOfTokensToPool,
{ from: user }
),
- "caller is not the owner or token swap manager"
+ "caller is not the owner or an approved token swap manager"
);
});
@@ -153,7 +153,7 @@ describe("CashPool", function() {
amountOfTokensToPool,
{ from: anotherUser }
),
- "caller is not the owner or token swap manager"
+ "caller is not the owner or an approved token swap manager"
);
});
diff --git a/test/KYCVerifier.test.js b/test/KYCVerifier.test.js
index b856fa5..27fe269 100644
--- a/test/KYCVerifier.test.js
+++ b/test/KYCVerifier.test.js
@@ -47,7 +47,9 @@ describe("KYCVerifier", function() {
});
it("allows bridge to whitelisted address", async function() {
- await this.contract.setWhitelistedAddress(notOwner, { from: bridge });
+ await this.contract.setWhitelistedAddress(notOwner, {
+ from: bridge,
+ });
const isAddressAdded = await this.contract.whitelistedAddresses(notOwner);
expect(isAddressAdded).to.be.true;
@@ -55,13 +57,55 @@ describe("KYCVerifier", function() {
});
it("adds whitelisted address", async function() {
- await this.contract.setWhitelistedAddress(notOwner, { from: owner });
+ await this.contract.setWhitelistedAddress(notOwner, {
+ from: owner,
+ });
const isAddressAdded = await this.contract.whitelistedAddresses(notOwner);
expect(isAddressAdded).to.be.true;
});
});
+ describe("#batchWhitelistedAddress", function() {
+ it("does not allow a non owner to add multiple addresses", async function() {
+ await expectRevert(
+ this.contract.batchWhitelistedAddress([notOwner, owner], {
+ from: notOwner,
+ }),
+ "caller is not the owner or bridge"
+ );
+ });
+
+ it("does not allow an empty address to be whitelisted", async function() {
+ await expectRevert(
+ this.contract.batchWhitelistedAddress(
+ [owner, "0x0000000000000000000000000000000000000000"],
+ { from: owner }
+ ),
+ "adddress must not be empty"
+ );
+ });
+
+ it("allows bridge or owner to batch whitelisted addresses", async function() {
+ // as bridge
+ await this.contract.batchWhitelistedAddress([notOwner, owner], {
+ from: bridge,
+ });
+
+ let isAddressAdded = await this.contract.whitelistedAddresses(notOwner);
+ expect(isAddressAdded).to.be.true;
+ await this.contract.removeWhitelistedAddress(notOwner, { from: bridge });
+
+ // as owner
+ await this.contract.batchWhitelistedAddress([notOwner, owner, unlistedUser], {
+ from: owner,
+ });
+
+ isAddressAdded = await this.contract.whitelistedAddresses(unlistedUser);
+ expect(isAddressAdded).to.be.true;
+ });
+ });
+
describe("#removeWhitelistedAddress", function() {
beforeEach(async function() {
await this.contract.setWhitelistedAddress(notOwner, { from: owner });
diff --git a/test/Modifier.test.js b/test/Modifier.test.js
index d589abd..627425e 100644
--- a/test/Modifier.test.js
+++ b/test/Modifier.test.js
@@ -74,7 +74,6 @@ describe("Modifier", function() {
await this.cashPool.initialize(
owner,
this.kycVerifier.address,
- this.storage.address,
coldStorage,
sixtyPercentInArrayFraction
);
@@ -94,6 +93,7 @@ describe("Modifier", function() {
owner,
this.inverseToken.address,
this.cashPool.address,
+ this.storage.address,
this.compositionCalculator.address
);
await this.storage.setTokenSwapManager(this.tokenSwapManager.address, {
@@ -282,7 +282,7 @@ describe("Modifier", function() {
this.cashPool.moveTokenfromPool(this.inverseToken.address, bridge, 5, {
from: user
}),
- "caller is not the owner or token swap manager"
+ "caller is not the owner or an approved token swap manager"
);
});
});
diff --git a/test/StorageLeverage.test.js b/test/StorageLeverage.test.js
new file mode 100644
index 0000000..823e74c
--- /dev/null
+++ b/test/StorageLeverage.test.js
@@ -0,0 +1,60 @@
+const { accounts, contract } = require("@openzeppelin/test-environment");
+const { expect } = require("chai");
+const BigNumber = require("bignumber.js");
+
+const {
+ expectEvent,
+ expectRevert,
+ time,
+ ether,
+ BN,
+} = require("@openzeppelin/test-helpers");
+
+const StorageLeverage = contract.fromArtifact("StorageLeverage");
+
+const getDateForBlockTime = async () => {
+ const latestBlockTime = await time.latest();
+ const latestBlockTimestamp = latestBlockTime.toNumber() * 1000;
+
+ const dateObj = new Date(latestBlockTimestamp);
+ const month = dateObj.getUTCMonth() + 1; //months from 1-12
+ const day = dateObj.getUTCDate();
+ const year = dateObj.getUTCFullYear();
+
+ return year * 10000 + month * 100 + day;
+};
+describe("PersistentStorage", function() {
+ const [owner, notOwner, notListed, bridge] = accounts;
+ const managementFee = ether("7");
+ const minRebalanceAmount = ether("1");
+ const lastMintingFee = ether("0.001");
+ const balancePrecision = 12;
+ const minimumMintingFee = ether("5");
+ const minimumTrade = ether("50");
+ beforeEach(async function() {
+ this.contract = await StorageLeverage.new({ from: owner });
+ await this.contract.initialize(
+ owner,
+ managementFee,
+ minRebalanceAmount,
+ balancePrecision,
+ lastMintingFee,
+ minimumMintingFee,
+ minimumTrade
+ );
+
+ await this.contract.setBridge(bridge, { from: owner });
+ });
+
+ describe("#getMintingFee", function() {
+ it("gets minting fee", async function() {
+ const expectedTotalFee = ether("0.001");
+ const cash = ether("49000");
+
+ const resultMintingFee = await this.contract.getMintingFee(cash, {
+ from: notListed,
+ });
+ expect(resultMintingFee).to.be.bignumber.equal(expectedTotalFee);
+ });
+ });
+});
diff --git a/test/TestStorageExample/Storage.test.js b/test/TestStorageExample/Storage.test.js
deleted file mode 100644
index 7493e66..0000000
--- a/test/TestStorageExample/Storage.test.js
+++ /dev/null
@@ -1,94 +0,0 @@
-const { accounts, contract } = require("@openzeppelin/test-environment");
-const { expect } = require("chai");
-
-const { expectEvent, expectRevert } = require("@openzeppelin/test-helpers");
-
-const Storage = contract.fromArtifact("Storage");
-
-describe("Storage", function() {
- const [owner, notOwner] = accounts;
-
- beforeEach(async function() {
- this.contract = await Storage.new({ from: owner });
- await this.contract.initialize(owner);
- });
-
- describe("#insertInstrument", function() {
- it("does not allow a non owner to add an instrument", async function() {
- await expectRevert(
- this.contract.insertInstrument("ABTC", { from: notOwner }),
- "Ownable: caller is not the owner"
- );
- });
-
- it("is not able to add the same instrument twice", async function() {
- this.contract.insertInstrument("AETH", { from: owner });
-
- await expectRevert(
- this.contract.insertInstrument("AETH", { from: owner }),
- "instrument already exists"
- );
- });
-
- it("adds instrument", async function() {
- const instrumentCounter = (
- await this.contract.instrumentCounter()
- ).toNumber();
- const incrementedCounter = instrumentCounter + 1;
-
- await this.contract.insertInstrument("ABTC", { from: owner });
-
- const abtcIndex = (
- await this.contract.getInstrumentIndex("ABTC")
- ).toNumber();
-
- expect(abtcIndex).to.equal(incrementedCounter);
- expect((await this.contract.instrumentCounter()).toNumber()).to.equal(
- incrementedCounter
- );
- });
-
- it("emits AddInstrument", async function() {
- const receipt = await this.contract.insertInstrument("HODL", {
- from: owner
- });
-
- expectEvent(receipt, "AddInstrument", { addedInstrument: "HODL" });
- });
- });
-
- describe("#addAllowedInstruments", function() {
- beforeEach(async function() {
- await this.contract.insertInstrument("ABTC", { from: owner });
- });
-
- it("prohibits a non owner from adding allowed instruments to user", async function() {
- await expectRevert(
- this.contract.addAllowedInstruments(notOwner, "ABTC", {
- from: notOwner
- }),
- "Ownable: caller is not the owner"
- );
- });
-
- it("does not allow an instrument which has not been added yet", async function() {
- await expectRevert(
- this.contract.addAllowedInstruments(notOwner, "AETH", { from: owner }),
- "instrument does not exist"
- );
- });
-
- it("adds allowed instrument to user", async function() {
- const abtcIndex = await this.contract.getInstrumentIndex("ABTC");
-
- await this.contract.addAllowedInstruments(notOwner, "ABTC", {
- from: owner
- });
- const userAllowedInstruments = await this.contract.getUserAllowedInstruments(
- notOwner
- );
-
- expect(userAllowedInstruments).to.deep.equal([abtcIndex]);
- });
- });
-});
diff --git a/test/TestStorageExample/StorageVersionUpgradeExample.test.js b/test/TestStorageExample/StorageVersionUpgradeExample.test.js
deleted file mode 100644
index c41910d..0000000
--- a/test/TestStorageExample/StorageVersionUpgradeExample.test.js
+++ /dev/null
@@ -1,43 +0,0 @@
-const { accounts, contract } = require("@openzeppelin/test-environment");
-const { expect } = require("chai");
-
-const Storage_V0 = contract.fromArtifact("Storage_V0");
-const Storage_V1 = contract.fromArtifact("Storage_V1");
-
-describe("Storage_V0", function() {
- const [owner] = accounts;
-
- beforeEach(async function() {
- this.contract = await Storage_V0.new({ from: owner });
- await this.contract.initialize(owner);
- });
-
- it("shows intrument counter Storage.sol", async function() {
- await this.contract.insertInstrument("HODL", { from: owner });
-
- const instrumentCounter = (
- await this.contract.instrumentCounter()
- );
- const result = await this.contract.test_storage_v0();
-
- expect(instrumentCounter).to.deep.equal(result);
- });
-});
-
-describe("Storage_V1", function() {
- const [owner] = accounts;
-
- beforeEach(async function() {
- this.contract = await Storage_V1.new({ from: owner });
- await this.contract.initialize(owner);
- });
-
- it("shows intrument counter Storage.solm plus 1", async function() {
- const instrumentCounter = (
- await this.contract.instrumentCounter()
- ).toNumber();
- const result = (await this.contract.test_storage_v1()).toNumber();
-
- expect(instrumentCounter + 1).to.deep.equal(result);
- });
-});
diff --git a/test/TokenSwapLeverage.test.js b/test/TokenSwapLeverage.test.js
new file mode 100644
index 0000000..5a67c06
--- /dev/null
+++ b/test/TokenSwapLeverage.test.js
@@ -0,0 +1,417 @@
+const { accounts, contract } = require("@openzeppelin/test-environment");
+const { expect } = require("chai");
+const {
+ expectRevert,
+ expectEvent,
+ time,
+ ether
+} = require("@openzeppelin/test-helpers");
+
+const BigNumber = require("bignumber.js");
+
+const ERC20WithMinting = contract.fromArtifact("InverseToken");
+const PersistentStorage = contract.fromArtifact("StorageLeverage");
+const KYCVerifier = contract.fromArtifact("KYCVerifier");
+const CashPool = contract.fromArtifact("CashPool");
+const TokenSwapManager = contract.fromArtifact("TokenSwapLeverage");
+const CompositionCalculator = contract.fromArtifact("CalculatorLeverage");
+const sixtyPercentInArrayFraction = [3, 5];
+
+const getEth = num =>
+ new BigNumber(num).times(new BigNumber("10").pow("18")).integerValue();
+
+const getUsdc = num =>
+ new BigNumber(num).times(new BigNumber("10").pow("6")).integerValue();
+
+const normalizeUsdc = num =>
+ new BigNumber(num).dividedBy(new BigNumber("10").pow("12")).integerValue();
+
+describe("TokenSwapLeverage", async function() {
+ const [owner, user, bridge] = accounts;
+ const coldStorage = accounts[9];
+
+ beforeEach(async function() {
+ // Initialize Persistent Storage
+ this.storage = await PersistentStorage.new({ from: owner });
+ const managementFee = ether("10.95");
+ const minRebalanceAmount = ether("1");
+ const lastMintingFee = ether("0.001");
+ const balancePrecision = 12;
+ const minimumMintingFee = ether("0");
+ const minimumTrade = ether("50");
+
+ await this.storage.initialize(
+ owner,
+ managementFee,
+ minRebalanceAmount,
+ balancePrecision,
+ lastMintingFee,
+ minimumMintingFee,
+ minimumTrade
+ );
+ await this.storage.addMintingFeeBracket(ether("50000"), ether("0.003"), {
+ from: owner
+ }); //0.3%
+ await this.storage.addMintingFeeBracket(ether("100000"), ether("0.002"), {
+ from: owner
+ }); //0.2%
+ // Inverse Token + Stablecoin Initialize
+ this.inverseToken = await ERC20WithMinting.new({ from: owner });
+ await this.inverseToken.initialize(
+ "InverseToken",
+ "IT",
+ 18,
+ this.storage.address,
+ owner
+ );
+
+ this.stablecoin = await ERC20WithMinting.new({ from: owner });
+ await this.stablecoin.initialize(
+ "Stablecoin",
+ "USDC",
+ 6,
+ this.storage.address,
+ owner
+ );
+
+ // Initialize KYC Verifier
+ this.kycVerifier = await KYCVerifier.new({ from: owner });
+ await this.kycVerifier.initialize(owner);
+
+ // Initialize Cash Pool
+ this.cashPool = await CashPool.new({ from: owner });
+ await this.cashPool.initialize(
+ owner,
+ this.kycVerifier.address,
+ coldStorage,
+ sixtyPercentInArrayFraction
+ );
+
+ // Deploy CompositionCalculator
+ this.compositionCalculator = await CompositionCalculator.new({
+ from: owner
+ });
+ await this.compositionCalculator.initialize(
+ this.storage.address,
+ this.inverseToken.address
+ );
+
+ // Initialize TSM
+ this.tokenSwapManager = await TokenSwapManager.new({ from: owner });
+ await this.tokenSwapManager.initialize(
+ owner,
+ this.inverseToken.address,
+ this.cashPool.address,
+ this.storage.address,
+ this.compositionCalculator.address
+ );
+ await this.storage.setTokenSwapManager(this.tokenSwapManager.address, {
+ from: owner
+ });
+ await this.cashPool.addTokenManager(this.tokenSwapManager.address, {
+ from: owner
+ });
+
+ await this.storage.setBridge(bridge, { from: owner });
+
+ this.timeout(11500000);
+ });
+
+ describe("#successfulCreateOrder", function() {
+ const totalTokenSupply = getEth(1);
+ const price = getEth(1000);
+ const tokensGiven = getEth(10);
+ const tokensRecieved = getEth(0.00997);
+
+ beforeEach(async function() {
+ await this.kycVerifier.setWhitelistedAddress(user, { from: owner });
+ await this.inverseToken.mintTokens(owner, totalTokenSupply, {
+ from: owner
+ });
+
+ this.receipt = await this.tokenSwapManager.createOrder(
+ true, // Order Type
+ tokensGiven, // Tokens Given
+ tokensRecieved, // Tokens Recieved
+ price,
+ user, // Whitelisted User
+ this.stablecoin.address, // Stablecoin Address
+ 0,
+ { from: bridge } // Sent From Bridge
+ );
+ });
+
+ it("emits successful order event", async function() {
+ expectEvent(this.receipt, "SuccessfulOrder", {
+ orderType: "CREATE",
+ whitelistedAddress: user,
+ tokensGiven: tokensGiven.toString(),
+ tokensRecieved: tokensRecieved.toString(),
+ stablecoin: this.stablecoin.address,
+ price: "1000000000000000000000"
+ });
+ });
+
+ it("successfully mint tokens to user address", async function() {
+ const balance = await this.inverseToken.balanceOf(user);
+ expect(balance.toString()).to.be.equal(tokensRecieved.toString());
+ });
+ });
+
+ describe("#unsuccessfulCreateOrder", function() {
+ const totalTokenSupply = getEth(1);
+ const price = getEth(1000);
+ const tokensGiven = getEth(10);
+ const tokensRecieved = getEth(0.00000997);
+
+ beforeEach(async function() {
+ await this.inverseToken.mintTokens(owner, totalTokenSupply, {
+ from: owner
+ });
+ });
+
+ it("throws error when user is not whitelisted", async function() {
+ await expectRevert(
+ this.tokenSwapManager.createOrder(
+ true,
+ tokensGiven,
+ tokensRecieved,
+ price,
+ user,
+ this.stablecoin.address, // Stablecoin Address
+ 0,
+ { from: bridge }
+ ),
+ "only whitelisted address may place orders"
+ );
+ });
+
+ it("throws error from trading engine: return user funds", async function() {
+ const usdcAmount = getUsdc(10);
+ const usdcAmountWithDecimals = getEth(10);
+ await this.kycVerifier.setWhitelistedAddress(user, { from: owner });
+ await this.stablecoin.mintTokens(this.cashPool.address, usdcAmount, {
+ from: owner
+ });
+ await this.tokenSwapManager.createOrder(
+ false,
+ usdcAmountWithDecimals,
+ usdcAmountWithDecimals,
+ price,
+ user,
+ this.stablecoin.address, // Stablecoin Address
+ 0,
+ {
+ from: bridge
+ }
+ );
+
+ const userReturnedBalance = await this.stablecoin.balanceOf(user);
+ expect(userReturnedBalance.toString()).to.be.equal(usdcAmount.toString());
+ });
+ });
+
+ describe("#successfulRedemptionOrders", function() {
+ const totalTokenSupply = getEth(100);
+ const stablecoinsToMint = getEth(10000);
+
+ const price = getEth(1000);
+ const tokensGiven = getEth(10);
+ const tokensRecieved = getEth(9969.875375);
+ const timeElapsed = getEth(1);
+
+
+ beforeEach(async function() {
+ await this.kycVerifier.setWhitelistedAddress(user, { from: owner });
+
+ await this.inverseToken.mintTokens(
+ this.cashPool.address,
+ totalTokenSupply,
+ {
+ from: owner
+ }
+ );
+
+ await this.stablecoin.mintTokens(
+ this.cashPool.address,
+ stablecoinsToMint,
+ {
+ from: owner
+ }
+ );
+
+ this.receipt = await this.tokenSwapManager.redeemOrder(
+ true, // Order Type
+ tokensGiven, // Tokens Given
+ tokensRecieved, // Tokens Recieved
+ price,
+ user, // Whitelisted User
+ this.stablecoin.address, // Stablecoin Address
+ 0,
+ timeElapsed,
+ { from: bridge } // Sent From Bridge
+ );
+ });
+
+ it("emits successful order event", async function() {
+ expectEvent(this.receipt, "SuccessfulOrder", {
+ orderType: "REDEEM",
+ whitelistedAddress: user,
+ tokensGiven: tokensGiven.toString(),
+ tokensRecieved: "9969875375000000000000",
+ stablecoin: this.stablecoin.address,
+ price: "1000000000000000000000"
+ });
+ });
+
+ it("successfully redeem after creation order", async function() {
+ const tokensSent = getEth(10);
+ const tokensCreated = getEth(0.00997);
+
+ await this.tokenSwapManager.createOrder(
+ true,
+ tokensSent,
+ tokensCreated,
+ price,
+ user,
+ this.stablecoin.address, // Stablecoin Address
+ 0,
+ { from: bridge }
+ );
+ const receipt = await this.tokenSwapManager.redeemOrder(
+ true,
+ tokensGiven,
+ tokensRecieved,
+ price,
+ user,
+ this.stablecoin.address, // Stablecoin Address
+ 0,
+ timeElapsed,
+ { from: bridge }
+ );
+ expectEvent(receipt, "SuccessfulOrder", {
+ orderType: "REDEEM",
+ whitelistedAddress: user,
+ tokensGiven: tokensGiven.toString(),
+ tokensRecieved: "9969875375000000000000",
+ stablecoin: this.stablecoin.address,
+ price: "1000000000000000000000"
+ });
+ });
+
+ it("successfully burn tokens from cash pool", async function() {
+ const balance = await this.inverseToken.balanceOf(this.cashPool.address);
+ expect(balance.toString()).to.be.equal(
+ new BigNumber(totalTokenSupply).minus(tokensGiven).toString()
+ );
+ });
+ });
+
+ describe("#delayedRedemptionOrder", function() {
+ const totalTokenSupply = getEth(100);
+ const stablecoinsToMint = getUsdc(10000);
+
+ const price = getEth(1000);
+ const tokensGiven = getEth(10);
+ const tokensRecieved = getEth(9969.875375);
+ const timeElapsed = getEth(1);
+
+
+ beforeEach(async function() {
+ await this.kycVerifier.setWhitelistedAddress(user, { from: owner });
+ await this.inverseToken.mintTokens(
+ this.cashPool.address,
+ totalTokenSupply,
+ {
+ from: owner
+ }
+ );
+
+ this.receipt = await this.tokenSwapManager.redeemOrder(
+ true, // Order Type
+ tokensGiven, // Tokens Given
+ tokensRecieved, // Tokens Recieved
+ price,
+ user, // Whitelisted User
+ this.stablecoin.address, // Stablecoin Address
+ 0,
+ timeElapsed,
+ { from: bridge } // Sent From Bridge
+ );
+ });
+
+ it("executes redemption without settlement", async function() {
+ expectEvent(this.receipt, "SuccessfulOrder", {
+ orderType: "REDEEM_NO_SETTLEMENT",
+ whitelistedAddress: user,
+ tokensGiven: tokensGiven.toString(),
+ tokensRecieved: "9969875375000000000000",
+ stablecoin: this.stablecoin.address,
+ price: "1000000000000000000000"
+ });
+ });
+
+ it("settles redemption at a later date", async function() {
+ await this.stablecoin.mintTokens(
+ this.cashPool.address,
+ stablecoinsToMint,
+ { from: owner }
+ );
+ await this.tokenSwapManager.settleDelayedFunds(
+ tokensRecieved,
+ user,
+ this.stablecoin.address,
+ {
+ from: bridge
+ }
+ );
+ const balance = await this.stablecoin.balanceOf(user);
+ const normalizedUSDC = normalizeUsdc(tokensRecieved);
+ expect(balance.toString()).to.be.equal(normalizedUSDC.toString());
+ });
+ });
+
+ describe("#unsuccessfulRedemptionOrder", function() {
+ it("throws error when user is not whitelisted", async function() {
+ await expectRevert(
+ this.tokenSwapManager.redeemOrder(
+ true,
+ 10,
+ 10,
+ 1000,
+ user,
+ this.stablecoin.address,
+ 0,
+ 1,
+ {
+ from: bridge
+ }
+ ),
+ "only whitelisted address may place orders"
+ );
+ });
+
+ it("throws error from trading engine: return user funds", async function() {
+ await this.kycVerifier.setWhitelistedAddress(user, { from: owner });
+ await this.inverseToken.mintTokens(this.cashPool.address, 10, {
+ from: owner
+ });
+ await this.tokenSwapManager.redeemOrder(
+ false,
+ 10,
+ 10,
+ 1000,
+ user,
+ this.stablecoin.address,
+ 0,
+ 1,
+ {
+ from: bridge
+ }
+ );
+
+ const userReturnedBalance = await this.inverseToken.balanceOf(user);
+ expect(userReturnedBalance.toNumber()).to.be.equal(10);
+ });
+ });
+});
diff --git a/test/TokenSwapManager.test.js b/test/TokenSwapManager.test.js
index 923c56f..bc77cd6 100644
--- a/test/TokenSwapManager.test.js
+++ b/test/TokenSwapManager.test.js
@@ -82,7 +82,6 @@ describe("TokenSwapManager", function() {
await this.cashPool.initialize(
owner,
this.kycVerifier.address,
- this.storage.address,
coldStorage,
sixtyPercentInArrayFraction
);
@@ -102,11 +101,14 @@ describe("TokenSwapManager", function() {
owner,
this.inverseToken.address,
this.cashPool.address,
+ this.storage.address,
this.compositionCalculator.address
);
await this.storage.setTokenSwapManager(this.tokenSwapManager.address, {
from: owner
});
+ await this.cashPool.addTokenManager(this.tokenSwapManager.address, {from: owner})
+
await this.storage.setBridge(bridge, { from: owner });
this.timeout(115000);
diff --git a/test/upgradeToCashPoolv2.test.js b/test/upgradeToCashPoolv2.test.js
new file mode 100644
index 0000000..91aedfb
--- /dev/null
+++ b/test/upgradeToCashPoolv2.test.js
@@ -0,0 +1,357 @@
+// Skip this test - for reference material only
+const Web3 = require("web3");
+const { provider } = require("@openzeppelin/test-environment");
+const {
+ Contracts,
+ ZWeb3,
+ SimpleProject,
+} = require("@openzeppelin/upgrades");
+const { ether, expectRevert } = require("@openzeppelin/test-helpers");
+
+const { expect } = require("chai");
+
+describe.skip("CashPoolv2", async function() {
+ this.timeout(50000);
+
+ beforeEach(async function() {
+ // Create web3 provider
+ const web3 = new Web3(provider);
+ ZWeb3.initialize(web3.currentProvider);
+
+ [
+ this.proxyAdmin,
+ this.owner,
+ this.user,
+ this.anotherUser,
+ this.tokenSwap,
+ this.coldStorage,
+ this.tokenManager1,
+ this.tokenManager2,
+ this.user,
+ ] = await web3.eth.getAccounts();
+
+ const CashPool = Contracts.getFromLocal("CashPool");
+ const CashPoolv2 = Contracts.getFromLocal("CashPoolv2");
+ const ERC20WithMinting = Contracts.getFromLocal("InverseToken");
+ const PersistentStorage = Contracts.getFromLocal("PersistentStorage");
+ const KYCVerifier = Contracts.getFromLocal("KYCVerifier");
+ const sixtyPercentInArrayFraction = [3, 5];
+
+ this.amountOfTokensToPool = 5;
+ this.userResultTokenBalance = "15"; // 10 from minting plus 5 from amountOfTokensToPool when transfer happens in tests
+
+ // Create an OpenZeppelin project
+ const project = new SimpleProject(
+ "BTCShort",
+ {},
+ { from: this.proxyAdmin }
+ );
+
+ // Initialize Params for Persistent Storage
+ const managementFee = ether("7").toString();
+ const minRebalanceAmount = ether("1").toString();
+ const lastMintingFee = ether("0.001").toString();
+ const balancePrecision = 12;
+ const minimumMintingFee = ether("5").toString();
+ const minimumTrade = ether("50").toString();
+
+ // Deploy an instance of Persistent Storage
+ this.persistentStorage = await project.createProxy(PersistentStorage, {
+ packageName: null,
+ contractName: "PersistentStorage",
+ initMethod: "initialize",
+ initArgs: [
+ this.owner,
+ managementFee,
+ minRebalanceAmount,
+ balancePrecision,
+ lastMintingFee,
+ minimumMintingFee,
+ minimumTrade,
+ ],
+ });
+
+ // Set 0.2%
+ await this.persistentStorage.methods
+ .addMintingFeeBracket(
+ ether("100000").toString(),
+ ether("0.002").toString()
+ )
+ .send({ from: this.owner });
+ await this.persistentStorage.methods
+ .setTokenSwapManager(this.tokenSwap)
+ .send({ from: this.owner });
+
+ // Deploy an instance of the Token
+ this.token = await project.createProxy(ERC20WithMinting, {
+ packageName: null,
+ contractName: "InverseToken",
+ initMethod: "initialize",
+ initArgs: [
+ "Test Token",
+ "TT",
+ 18,
+ this.persistentStorage.address,
+ this.owner,
+ ],
+ });
+ // Mint Tokens
+ await this.token.methods
+ .mintTokens(this.user, 10)
+ .send({ from: this.owner });
+
+ // Deploy an instance of Kyc Verfier
+ this.kycVerifier = await project.createProxy(KYCVerifier, {
+ packageName: null,
+ contractName: "KYCVerifier",
+ initMethod: "initialize",
+ initArgs: [this.owner],
+ });
+ await this.kycVerifier.methods
+ .setWhitelistedAddress(this.user)
+ .send({ from: this.owner });
+
+ // Deploy Cash Pool
+ this.cashPool = await project.createProxy(CashPool, {
+ packageName: null,
+ contractName: "CashPool",
+ initMethod: "initialize",
+ initArgs: [
+ this.owner,
+ this.kycVerifier.address,
+ this.persistentStorage.address,
+ this.coldStorage,
+ sixtyPercentInArrayFraction,
+ ],
+ });
+
+ // Upgrade to Cash Pool v2
+ this.cashPoolv2 = await project.upgradeProxy(
+ this.cashPool.options.address,
+ CashPoolv2,
+ {
+ packageName: null,
+ contractName: "CashPoolv2",
+ initMethod: null,
+ initArgs: null,
+ }
+ );
+ // Add Token Manager 2 to the Mapping
+ await this.cashPoolv2.methods
+ .addTokenManager(this.tokenManager2)
+ .send({ from: this.owner });
+ });
+
+ it("maintains the owner address after upgrade", async function() {
+ const implementationOwner = await this.cashPoolv2.methods
+ .owner()
+ .call({ from: this.owner });
+ expect(this.owner).to.be.equal(implementationOwner);
+ });
+
+ it("maintains the proxy address after upgrade", async function() {
+ const upgradeAddress = this.cashPoolv2.options.address;
+ expect(this.cashPool.options.address).to.be.equal(upgradeAddress);
+ });
+
+ it("adds token manager address to mapping", async function() {
+ await this.cashPoolv2.methods
+ .addTokenManager(this.tokenManager1)
+ .send({ from: this.owner });
+ const isAdded = await this.cashPoolv2.methods
+ .isTokenManager(this.tokenManager1)
+ .call({ from: this.owner });
+ expect(isAdded).to.be.equal(true);
+ });
+
+ it("removes token manager address from mapping", async function() {
+ await this.cashPoolv2.methods
+ .removeTokenManager(this.tokenManager2)
+ .send({ from: this.owner });
+ const isRemoved = await this.cashPoolv2.methods
+ .isTokenManager(this.tokenManager2)
+ .call({ from: this.owner });
+ expect(isRemoved).to.be.equal(false);
+ });
+
+ it("moves tokens to pool", async function() {
+ await this.token.methods
+ .approve(this.cashPoolv2.options.address, this.amountOfTokensToPool)
+ .send({ from: this.user });
+
+ await this.cashPoolv2.methods
+ .moveTokenToPoolv2(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.owner, gas: 6500000 });
+
+ const poolFundsInTokens = await this.token.methods
+ .balanceOf(this.cashPoolv2.options.address)
+ .call({ from: this.owner });
+ const coldStorageAddress = await this.cashPoolv2.methods
+ .coldStorage()
+ .call({ from: this.owner });
+ const coldStorageTokens = await this.token.methods
+ .balanceOf(coldStorageAddress)
+ .call({ from: this.owner });
+
+ expect(poolFundsInTokens).to.be.bignumber.equal(
+ (this.amountOfTokensToPool - coldStorageTokens).toString()
+ );
+ });
+
+ it("moves tokens to pool from approved token manager", async function() {
+ await this.token.methods
+ .approve(this.cashPoolv2.options.address, this.amountOfTokensToPool)
+ .send({ from: this.user });
+
+ await this.cashPoolv2.methods
+ .moveTokenToPoolv2(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.tokenManager2, gas: 6500000 });
+
+ const poolFundsInTokens = await this.token.methods
+ .balanceOf(this.cashPoolv2.options.address)
+ .call({ from: this.owner });
+ const coldStorageAddress = await this.cashPoolv2.methods
+ .coldStorage()
+ .call({ from: this.owner });
+ const coldStorageTokens = await this.token.methods
+ .balanceOf(coldStorageAddress)
+ .call({ from: this.owner });
+
+ expect(poolFundsInTokens).to.be.bignumber.equal(
+ (this.amountOfTokensToPool - coldStorageTokens).toString()
+ );
+ });
+
+ it("rejects movement of tokens to pool from non-approved token manager", async function() {
+ await this.token.methods
+ .approve(this.cashPoolv2.options.address, this.amountOfTokensToPool)
+ .send({ from: this.user });
+
+ await expectRevert(
+ this.cashPoolv2.methods
+ .moveTokenToPoolv2(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.tokenManager1, gas: 6500000 }),
+ "caller is not the owner or an approved token swap manager"
+ );
+ });
+
+ it("moves tokens from pool (by owner)", async function() {
+ await this.token.methods
+ .mintTokens(this.cashPoolv2.options.address, 10)
+ .send({ from: this.owner });
+
+ await this.cashPoolv2.methods
+ .moveTokenfromPoolv2(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.owner, gas: 6500000 });
+
+ const userBalance = await this.token.methods
+ .balanceOf(this.user)
+ .call({ from: this.owner });
+ expect(userBalance.toString()).to.be.bignumber.equal(
+ this.userResultTokenBalance
+ );
+ });
+
+ it("moves tokens from pool (by approved token manager)", async function() {
+ await this.token.methods
+ .mintTokens(this.cashPoolv2.options.address, 10)
+ .send({ from: this.owner });
+
+ await this.cashPoolv2.methods
+ .moveTokenfromPoolv2(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.tokenManager2, gas: 6500000 });
+
+ const userBalance = await this.token.methods
+ .balanceOf(this.user)
+ .call({ from: this.owner });
+ expect(userBalance.toString()).to.be.bignumber.equal(
+ this.userResultTokenBalance
+ );
+ });
+
+ it("rejects movement of tokens from pool (by non-approved token manager)", async function() {
+ await this.token.methods
+ .mintTokens(this.cashPoolv2.options.address, 10)
+ .send({ from: this.owner });
+
+ await expectRevert(
+ this.cashPoolv2.methods
+ .moveTokenfromPoolv2(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.tokenManager1, gas: 6500000 }),
+ "caller is not the owner or an approved token swap manager"
+ );
+ });
+
+ it("moves tokens to pool (v1 code)", async function() {
+ await this.token.methods
+ .approve(this.cashPoolv2.options.address, this.amountOfTokensToPool)
+ .send({ from: this.user });
+
+ await this.cashPoolv2.methods
+ .moveTokenToPool(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.tokenSwap, gas: 6500000 });
+
+ const poolFundsInTokens = await this.token.methods
+ .balanceOf(this.cashPoolv2.options.address)
+ .call({ from: this.owner });
+ const coldStorageAddress = await this.cashPoolv2.methods
+ .coldStorage()
+ .call({ from: this.owner });
+ const coldStorageTokens = await this.token.methods
+ .balanceOf(coldStorageAddress)
+ .call({ from: this.owner });
+
+ expect(poolFundsInTokens).to.be.bignumber.equal(
+ (this.amountOfTokensToPool - coldStorageTokens).toString()
+ );
+ });
+
+ it("moves tokens from pool (v1 code)", async function() {
+ await this.token.methods
+ .mintTokens(this.cashPoolv2.options.address, 10)
+ .send({ from: this.owner });
+
+ await this.cashPoolv2.methods
+ .moveTokenfromPool(
+ this.token.options.address,
+ this.user,
+ this.amountOfTokensToPool
+ )
+ .send({ from: this.tokenSwap, gas: 6500000 });
+
+ const userBalance = await this.token.methods
+ .balanceOf(this.user)
+ .call({ from: this.owner });
+ expect(userBalance.toString()).to.be.bignumber.equal(
+ this.userResultTokenBalance
+ );
+ });
+});