diff --git a/.env.example b/.env.example deleted file mode 100644 index 443a978..0000000 --- a/.env.example +++ /dev/null @@ -1,24 +0,0 @@ -MORPHER_DEPLOYER= -MORPHER_DEPLOYER_KEY= - -INFURA_PROJECT_ID= - -MORPHER_OWNER= -MORPHER_TREASURY= - -MORPHER_ADMINISTRATOR= -MORPHER_ADMINISTRATOR_KEY= - -CALLBACK_ADDRESS= -CALLBACK_ADDRESS_KEY= - -GAS_COLLECTION= -GAS_COLLECTION_KEY= - -SIDECHAIN_OPERATOR= -SIDECHAIN_OPERATOR_KEY= - -AIRDROP_ADMIN= -AIRDROP_ADMIN_KEY= - -#COLDSTORAGE_OWNER_ADDRESS=...? #used for the oracle \ No newline at end of file diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 52031de..0000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.sol linguist-language=Solidity diff --git a/.soliumignore b/.soliumignore deleted file mode 100644 index 15c47af..0000000 --- a/.soliumignore +++ /dev/null @@ -1,2 +0,0 @@ -node_modules -contracts/Migrations.sol diff --git a/.soliumrc.json b/.soliumrc.json deleted file mode 100644 index 6c461a5..0000000 --- a/.soliumrc.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "extends": "solium:recommended", - "plugins": [ - "security" - ], - "rules": { - "quotes": [ - "error", - "double" - ], - "indentation": [ - "warning", - 4 - ], - "linebreak-style": [ - "warning", - "unix" - ] - } -} \ No newline at end of file diff --git a/README.md b/README.md index 719eda9..e7ff7d6 100644 --- a/README.md +++ b/README.md @@ -13,181 +13,66 @@ Morpher rebuilds financial markets from the ground up on the Ethereum Blockchain # Audit -Morpher Smart Contracts are fully and regularly audited. 🙌 +The Non-Proxy versions of Morpher Smart Contracts are fully and regularly audited. 🙌 * Audited by Solidified on April 12, 2021. [Full Report](./docs/solidified-audit-12.04.2021.pdf) * Audited by Capacity on April 20, 2020. [Full Report](./docs/Capacity-MorpherAudit2Result.pdf) + # Proxied vs Non-Proxied versions -# Getting Started -## Prerequisites -* Install Ganache https://www.trufflesuite.com/ganache on your system and run it successfully on port 7545. -* Install Node.js, Npm and build-essential (Linux and MacOS package to build Web3 C/C++ files) on your computer. -* Git clone this repo and `cd` into it. +There are two versions of the smart contracts: -## How to run the Tests -Run the following commands to start the test suite. 😎 -* `npm install` to install all the truffle/node dependencies. -* Rename .env.example to .env and input all the required variables. If you're testing locally with Ganache, you only need to input `MORPHER_DEPLOYER` and `MORPHER_DEPLOYER_KEY` which is the first account you see in the Ganache GUI. -* When everything is configured correctly, run the last command: `./node_modules/truffle/build/cli.bundled.js test --network local -` +## Non Proxied Contracts -If you want to see exactly what assertions are being made, you can take a look at the `test` folder. +Initially MorpherProtocol was written in a non-proxy way using Solidity 0.5 and the Eternal Storage Pattern. -### Example In the Ubuntu Linux terminal: +Contracts are residing on [the nonproxy-master Branch](/Morpher-io/MorpherProtocol/tree/dev) -`curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -` +## Proxied Contracts -`sudo apt-get install nodejs build-essential` +# Contracts -`git clone https://github.com/Morpher-io/MorpherProtocol.git` +## Interfaces +IERC20.sol: Interface for the ERC20 Token -`cd MorpherProtocol` +IMorpherStaking.sol: Interface for the Staking functionality -`npm install` +IMorpherState.sol: Interface for the State functions -`cp .env.example .env` +IMorpherToken.sol: Interface for the Morpher Token -Then open .env and input Ganache Accounts for MORPHER_DEPLOYER and MORPHER_DEPLOYER_KEY +## Auxiliary Contracts +MerkleProof.sol: Calculate the Merkle Proof -`./node_modules/truffle/build/cli.bundled.js test --network local` +Migrations.sol: Used by Truffle to store migrations on chain +Ownable.sol: Ownable functionality +SafeMath.sol: Prevent Integer Overflows/Underflows in Solidity < 0.8 -# Smart Contract Components +## Morpher Core Contracts -There are several components playing together, these are here described in more detail in order of their importance. +MorpherBridge.sol: Functionality to bridge tokens between chains in a trustless way -## MorpherState.sol +MorpherOracle.sol: Pricing Oracle Functionality that accepts high frequency price ticks from external trusted data sources +MorpherStaking.sol: Staking functionality for MPH -This smart contract is _the brain_ of what happens with users, balances, trading and governance. It is the central point which stores balances for each user. It also stores the addresses of other smart contracts, such as the ERC20 token, the bridge or the governance functionality. +MorpherState.sol: Storing Data On Chain using the Eternal Storage Pattern (only non-proxied) -## MorpherToken.sol +MorpherToken.sol: ERC20 Interface for the Morpher Token -It is the ERC20 Compatible token for Morpher. All the balances are stored in MorpherState.sol, it's just the interface. +MorpherTradeEngine.sol: Processing Trades, calculating the position value -## MorpherTradeEngine.sol +## Morpher Auxiliary Contracts -This is the contract that processes and stores orders. The orders can only be given by the oracle smart contract. This smart contract is the trusted entity taking prices from outside into the sandboxed blockchain. +MorpherAdmin.sol: - -## MorpherBridge.sol - -Morpher Bridge takes care of bridging functionality from Main-Chain to Side-Chain and vice versa. It contains functionality to burn tokens upon deposit on the main-chain and credit (or mint) tokens on the side-chain. It can also take the merkle-proofs from the side-chain and let you withdraw tokens on the main-chain. - -If side chain operator doesn't write a merkle root hash to main chain for more than 72 hours positions and balaces from side chain can be transferred to main chain. - -## MorpherGovernance.sol - -Every user able and willig to lock up sufficient token can become a validator of the Morpher protocol. Validators function similiar to a board of directors and vote on the protocol Administrator and the Oracle contract. - -## MorpherAirdrop.sol - -Holds the Airdrop Token balance on contract address. AirdropAdmin can authorize addresses to receive airdrop. Users have to claim their airdrop actively or Admin initiates transfer. - -## MorpherEscrow.sol - -Escrow contract to safely store and release the token allocated to Morpher at protocol inception. - -## MorpherOracle.sol - -The oracle initates a new trade by calling trade engine and requesting a new orderId. An event is fired by the contract notifying the oracle operator to query a price/liquidation unchecked for a market/user and return the information via the callback function. Since calling the callback function requires gas, the user must send a fixed amount of Ether when creating their order. - -## Important Functionality - -MorpherState, by default, doesn't let anyone transfer tokens. This has to be enabled, but is disabled by default. By calling the following functions the access to transfers, minting, burning and creating positions will be enabled: - -``` -grantAccess(morpherTokenAddress) -grantAccess(morpherTradeEngineAddress) -grantAccess(morpherBridgeAddress) -grantAccess(morpherGovernanceAddress) -``` - -To let a sidechain operator set the amount of tokens on a sidechain, it has to be set by the owner initially: -``` -setSideChainOperator(sideChainOperatorAddress) -``` - -For sidechain operations (only relevant on sidechain) some transfers need to be enabled: -``` -enableTransfers(addressOfDeployer) -enableTransfers(morpherAirdropAddress) -``` - -Initially the governance contract did not vote on an administrator or oracle yet. To have an Admin or Oracle until there is a vote in the governance contract two addresses need to be set: -``` -setGovernanceContract(addressOfDeployer) -setAdministrator(addressOfDeployer) -``` - -To set the protocol contracts in state, the following functions need to be called: - -``` -setTokenContract(morpherTokenAddress) -setMorpherBridge(bridgeAddress) -setOracleContract(oracleAddress) -``` - -To enable "CRYPTO_BTC" and "CRYPTO_ETH" as markets for testing purposes; -``` -activateMarket(0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9) -activateMarket(0x5376ff169a3705b2003892fe730060ee74ec83e5701da29318221aa782271779) -``` - -To set the governance properly _on main chain only_: -``` -setGovernanceContract(morpherGovernanceAddress) -``` - -And to transfer the ownership, potentially to a 0x0 address: - -``` -transferOwnership(ownerAddress) -``` - -# Deployed Contracts - -The Smart Contracts are deployed on the Ethereum Mainnet and on the Morpher Sidechain. - -## Sidechain Deployments - -* MorpherState: [0xB4881186b9E52F8BD6EC5F19708450cE57b24370](https://scan.morpher.com/address/0xb4881186b9e52f8bd6ec5f19708450ce57b24370) -* MorpherToken: [0xC44628734a9432a3DAA302E11AfbdFa8361424A5](https://scan.morpher.com/address/0xC44628734a9432a3DAA302E11AfbdFa8361424A5) -* MorpherTradeEngine: [0xA162F61a747663088ef69D3f94db414a71C5DcF7](https://scan.morpher.com/address/0xA162F61a747663088ef69D3f94db414a71C5DcF7) -* MorpherBridge: [0x01B4854bf6eb61Dac40f2AE1e1c1CD8be3a6BaDf](https://scan.morpher.com/address/0x01B4854bf6eb61Dac40f2AE1e1c1CD8be3a6BaDf) -* MorpherAirdrop: [0x6306037eaD1FC236F4aabC8c826F351c9F45d409](https://scan.morpher.com/address/0x6306037eaD1FC236F4aabC8c826F351c9F45d409) -* MorpherEscrow: [0x3CBC7e439FD0A98182622136d38EBa03Aac17A72](https://scan.morpher.com/address/0x3CBC7e439FD0A98182622136d38EBa03Aac17A72) -* MorpherOracle: [0xf8B5b1699A00EDfdB6F15524646Bd5071bA419Fb](https://scan.morpher.com/address/0xf8B5b1699A00EDfdB6F15524646Bd5071bA419Fb) - - -## Mainchain Deployments - -* MorpherState: [0x1f426C51F0Ef7655A6f4c3Eb58017d2F1c381bfF](https://etherscan.io/address/0x1f426C51F0Ef7655A6f4c3Eb58017d2F1c381bfF) -* MorpherToken: [0x6369c3DadfC00054A42BA8B2c09c48131dd4Aa38](https://etherscan.io/address/0x6369c3DadfC00054A42BA8B2c09c48131dd4Aa38) -* MorpherTradeEngine: [0x62e26AB4444E24E42e63A0857bF56Ea1c70AAEc8](https://etherscan.io/address/0x62e26AB4444E24E42e63A0857bF56Ea1c70AAEc8) -* MorpherBridge: [0xa937787581b17236f3efb2618c38270baac685ba](https://etherscan.io/address/0xa937787581b17236f3efb2618c38270baac685ba) -* MorpherGovernance: [0x51c5cE7C4926D5cA74f4824e11a062f1Ef491762](https://etherscan.io/address/0x51c5cE7C4926D5cA74f4824e11a062f1Ef491762) -* MorpherAirdrop: [0x6306037eaD1FC236F4aabC8c826F351c9F45d409](https://etherscan.io/address/0x6306037eaD1FC236F4aabC8c826F351c9F45d409) -* MorpherEscrow: [0x161Ba24A3F9f90b531f6C0a2E0abb392DDBb8f6c](https://etherscan.io/address/0x161Ba24A3F9f90b531f6C0a2E0abb392DDBb8f6c) -* MorpherOracle: [0x73b7631c508db9E389edF6aBb3C4a48da0444553](https://etherscan.io/address/0x73b7631c508db9E389edF6aBb3C4a48da0444553) -* MorpherMintingLimiter: [0x30D90c8a36FFF52F1C8C05b54d4CE79610431Ec0](https://etherscan.io/address/0x30d90c8a36fff52f1c8c05b54d4ce79610431ec0) -* MorpherStaking: [0xBaF121D02E6948D3A089F99dDc522eb2A4a1b1fE](https://etherscan.io/address/0xBaF121D02E6948D3A089F99dDc522eb2A4a1b1fE) - -## Polygon Deployments - -Contracts on Polygon are all Proxied contracts through the OpenZeppelin Transparent Proxy - -ℹ️ The code for the contracts is currently residing on the dev-branch, as they greatly diverge from the current master branch. - -* MorpherState: [0x1ce1efda5d52dE421BD3BC1CCc85977D7a0a0F1e](https://polygonscan.com/address/0x1ce1efda5d52dE421BD3BC1CCc85977D7a0a0F1e) -* MorpherToken: [0x65C9e3289e5949134759119DBc9F862E8d6F2fBE](https://polygonscan.com/token/0x65C9e3289e5949134759119DBc9F862E8d6F2fBE) -* MorpherTradeEngine: [0x005cb9Ad7C713bfF25ED07F3d9e1C3945e543cd5](https://polygonscan.com/address/0x005cb9Ad7C713bfF25ED07F3d9e1C3945e543cd5) -* MorpherBridge: [0xE409f27e977E6bC10cc0a064eD3004F78A40A648](https://polygonscan.com/address/0xE409f27e977E6bC10cc0a064eD3004F78A40A648) -* MorpherOracle: [0x21Fd95b46FC655BfF75a8E74267Cfdc7efEBdb6A](https://polygonscan.com/address/0x21Fd95b46FC655BfF75a8E74267Cfdc7efEBdb6A) -* MorpherMintingLimiter: [0xf8B5b1699A00EDfdB6F15524646Bd5071bA419Fb](https://polygonscan.com/address/0xf8B5b1699A00EDfdB6F15524646Bd5071bA419Fb) -* MorpherStaking: [0x0Fc936d3008d08F065BfD37FCAF7aa8515525417](https://polygonscan.com/address/0x0Fc936d3008d08F065BfD37FCAF7aa8515525417) -* MorpherAccessControl: [0x139950831d8338487db6807c6FdAeD1827726dF2](https://polygonscan.com/address/0x139950831d8338487db6807c6FdAeD1827726dF2) - OpenZeppelin Access Control Structure -* MorpherUserBlocking: [0x92Ea01229335854000dc648Fcf4Ea2931A78c363](https://polygonscan.com/address/0x92Ea01229335854000dc648Fcf4Ea2931A78c363) +MorpherAdministratorProxy.sol +MorpherAirdrop.sol +MorpherEscrow.sol +MorpherFaucet.sol +MorpherGovernance.sol +MorpherMintingLimiter.sol +MorpherUserBlocking.sol diff --git a/contracts/IERC20.sol b/contracts/IERC20.sol deleted file mode 100644 index fede053..0000000 --- a/contracts/IERC20.sol +++ /dev/null @@ -1,76 +0,0 @@ -pragma solidity 0.5.16; - -/** - * @dev Interface of the ERC20 standard as defined in the EIP. Does not include - * the optional functions; to access them see {ERC20Detailed}. - */ -interface IERC20 { - /** - * @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); -} diff --git a/contracts/IMorpherStaking.sol b/contracts/IMorpherStaking.sol deleted file mode 100644 index 79622af..0000000 --- a/contracts/IMorpherStaking.sol +++ /dev/null @@ -1,22 +0,0 @@ -pragma solidity 0.5.16; -contract IMorpherStaking { - - function lastReward() public view returns (uint256); - - function totalShares() public view returns (uint256); - - function interestRate() public view returns (uint256); - - function lockupPeriod() public view returns (uint256); - - function minimumStake() public view returns (uint256); - - function stakingAdmin() public view returns (address); - - function updatePoolShareValue() public returns (uint256 _newPoolShareValue) ; - - function stake(uint256 _amount) public returns (uint256 _poolShares); - - function unStake(uint256 _numOfShares) public returns (uint256 _amount); - -} diff --git a/contracts/IMorpherState.sol b/contracts/IMorpherState.sol deleted file mode 100644 index 46c6902..0000000 --- a/contracts/IMorpherState.sol +++ /dev/null @@ -1,99 +0,0 @@ -pragma solidity 0.5.16; - -contract IMorpherState { - function setPosition( - address _address, - bytes32 _marketId, - uint256 _timeStamp, - uint256 _longShares, - uint256 _shortShares, - uint256 _meanEntryPrice, - uint256 _meanEntrySpread, - uint256 _meanEntryLeverage, - uint256 _liquidationPrice - ) public; - - function getPosition( - address _address, - bytes32 _marketId - ) public view returns ( - uint256 _longShares, - uint256 _shortShares, - uint256 _meanEntryPrice, - uint256 _meanEntrySpread, - uint256 _meanEntryLeverage, - uint256 _liquidationPrice - ); - - function getLastUpdated(address _address, bytes32 _marketId) public view returns (uint256 _lastUpdated); - - function transfer(address _from, address _to, uint256 _token) public; - - function balanceOf(address _tokenOwner) public view returns (uint256 balance); - - function mint(address _address, uint256 _token) public; - - function burn(address _address, uint256 _token) public; - - function getSideChainOperator() public view returns (address _address); - - function inactivityPeriod() public view returns (uint256); - - function getSideChainMerkleRootWrittenAtTime() public view returns(uint256 _sideChainMerkleRoot); - - function fastTransfersEnabled() public view returns(bool); - - function mainChain() public view returns(bool); - - function setInactivityPeriod(uint256 _periodLength) public; - - function disableFastWithdraws() public; - - function setSideChainMerkleRoot(bytes32 _sideChainMerkleRoot) public; - - function resetLast24HoursAmountWithdrawn() public; - - function set24HourWithdrawLimit(uint256 _limit) public; - - function getTokenSentToLinkedChain(address _address) public view returns (uint256 _token); - - function getTokenClaimedOnThisChain(address _address) public view returns (uint256 _token); - - function getTokenSentToLinkedChainTime(address _address) public view returns (uint256 _timeStamp); - - function lastWithdrawLimitReductionTime() public view returns (uint256); - - function withdrawLimit24Hours() public view returns (uint256); - - function update24HoursWithdrawLimit(uint256 _amount) public; - - function last24HoursAmountWithdrawn() public view returns (uint256); - - function setTokenSentToLinkedChain(address _address, uint256 _token) public; - - function setTokenClaimedOnThisChain(address _address, uint256 _token) public; - - function add24HoursWithdrawn(uint256 _amount) public; - - function getPositionHash( - address _address, - bytes32 _marketId, - uint256 _timeStamp, - uint256 _longShares, - uint256 _shortShares, - uint256 _meanEntryPrice, - uint256 _meanEntrySpread, - uint256 _meanEntryLeverage, - uint256 _liquidationPrice - ) public pure returns (bytes32 _hash); - - function getPositionClaimedOnMainChain(bytes32 _positionHash) public view returns (bool _alreadyClaimed); - - function setPositionClaimedOnMainChain(bytes32 _positionHash) public; - - function getBalanceHash(address _address, uint256 _balance) public pure returns (bytes32 _hash); - - function getSideChainMerkleRoot() public view returns(bytes32 _sideChainMerkleRoot); - - function getBridgeNonce() public returns (uint256 _nonce); -} \ No newline at end of file diff --git a/contracts/IMorpherToken.sol b/contracts/IMorpherToken.sol deleted file mode 100644 index 963eb4a..0000000 --- a/contracts/IMorpherToken.sol +++ /dev/null @@ -1,8 +0,0 @@ -pragma solidity 0.5.16; - -interface IMorpherToken { - /** - * Emits a {Transfer} event in ERC-20 token contract. - */ - function emitTransfer(address _from, address _to, uint256 _amount) external; -} diff --git a/contracts/MerkleProof.sol b/contracts/MerkleProof.sol deleted file mode 100644 index 7f5216a..0000000 --- a/contracts/MerkleProof.sol +++ /dev/null @@ -1,31 +0,0 @@ -pragma solidity 0.5.16; - -/** - * @dev These functions deal with verification of Merkle trees (hash trees), - */ -library MerkleProof { - /** - * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree - * defined by `root`. For this, a `proof` must be provided, containing - * sibling hashes on the branch from the leaf to the root of the tree. Each - * pair of leaves and each pair of pre-images are assumed to be sorted. - */ - function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { - require(proof.length < 100, "MerkleProof: proof too long. Use only sibling hashes."); - bytes32 computedHash = leaf; - for (uint256 i = 0; i < proof.length; i++) { - bytes32 proofElement = proof[i]; - - if (computedHash < proofElement) { - // Hash(current computed hash + current element of the proof) - computedHash = keccak256(abi.encodePacked(computedHash, proofElement)); - } else { - // Hash(current element of the proof + current computed hash) - computedHash = keccak256(abi.encodePacked(proofElement, computedHash)); - } - } - - // Check if the computed hash (root) is equal to the provided root - return computedHash == root; - } -} diff --git a/contracts/Migrations.sol b/contracts/Migrations.sol deleted file mode 100644 index 61109dd..0000000 --- a/contracts/Migrations.sol +++ /dev/null @@ -1,23 +0,0 @@ -pragma solidity 0.5.16; - -contract Migrations { - address public owner; - uint public last_completed_migration; - - modifier restricted() { - if (msg.sender == owner) _; - } - - constructor() public { - owner = msg.sender; - } - - function setCompleted(uint completed) public restricted { - last_completed_migration = completed; - } - - function upgrade(address new_address) public restricted { - Migrations upgraded = Migrations(new_address); - upgraded.setCompleted(last_completed_migration); - } -} diff --git a/contracts/MorpherAdmin.sol b/contracts/MorpherAdmin.sol deleted file mode 100644 index 3f42861..0000000 --- a/contracts/MorpherAdmin.sol +++ /dev/null @@ -1,232 +0,0 @@ -pragma solidity 0.5.16; - -import "./SafeMath.sol"; -import "./MorpherState.sol"; -import "./MorpherTradeEngine.sol"; - -// ---------------------------------------------------------------------------------- -// Administrator of the Morpher platform -// ---------------------------------------------------------------------------------- - -contract MorpherAdmin { - MorpherState state; - MorpherTradeEngine tradeEngine; - using SafeMath for uint256; - - event AdminLiquidationOrderCreated( - bytes32 indexed _orderId, - address indexed _address, - bytes32 indexed _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage - ); - - event AddressPositionMigrationComplete(address _owner, bytes32 _oldMarketId, bytes32 _newMarketId); - event AllPositionMigrationsComplete(bytes32 _oldMarketId, bytes32 _newMarketId); - event AllPositionMigrationIncomplete(bytes32 _oldMarketId, bytes32 _newMarketId, uint _maxIx); - -// ---------------------------------------------------------------------------- -// Precision of prices and leverage -// ---------------------------------------------------------------------------- - - modifier onlyAdministrator { - require(msg.sender == state.getAdministrator(), "Function can only be called by the Administrator."); - _; - } - - constructor(address _stateAddress, address _tradeEngine) public { - state = MorpherState(_stateAddress); - tradeEngine = MorpherTradeEngine(_tradeEngine); - } - -// ---------------------------------------------------------------------------- -// Administrative functions -// Set state address and maximum permitted leverage on platform -// ---------------------------------------------------------------------------- - function setMorpherState(address _stateAddress) public onlyAdministrator { - state = MorpherState(_stateAddress); - } - - function setMorpherTradeEngine(address _tradeEngine) public onlyAdministrator { - tradeEngine = MorpherTradeEngine(_tradeEngine); - } - - function migratePositionsToNewMarket(bytes32 _oldMarketId, bytes32 _newMarketId) public onlyAdministrator { - require(state.getMarketActive(_oldMarketId) == false, "Market must be paused to process market migration."); - require(state.getMarketActive(_newMarketId) == false, "Market must be paused to process market migration."); - - uint256 maxMarketAddressIndex = state.getMaxMappingIndex(_oldMarketId); - address[] memory addresses = new address[](maxMarketAddressIndex); - for (uint256 i = 1; i <= maxMarketAddressIndex; i++) { - addresses[i-1] = state.getExposureMappingAddress(_oldMarketId, i); //changing on position delete - } - for(uint256 i = 0; i < addresses.length; i++) { - address _address = addresses[i]; //normalize back to 0-based index - (uint longShares, uint shortShares, uint meanEntryPrice, uint meanEntrySpread, uint meanEntryLeverage, uint liquidationPrice) = state.getPosition(_address, _oldMarketId); - if(longShares > 0 || shortShares > 0) { - state.setPosition(_address, _newMarketId, block.timestamp, longShares, shortShares, meanEntryPrice, meanEntrySpread, meanEntryLeverage, liquidationPrice); //create a new position for the new market with the same parameters - state.setPosition(_address, _oldMarketId, block.timestamp, 0,0,0,0,0,0); //delete the current position - emit AddressPositionMigrationComplete(_address, _oldMarketId, _newMarketId); - } - - if(gasleft() < 500000 && (i+1) < addresses.length) { //stop if there's not enough gas to write the next transaction - emit AllPositionMigrationIncomplete(_oldMarketId, _newMarketId, i); - return; - } - } - - emit AllPositionMigrationsComplete(_oldMarketId, _newMarketId); - } - - -// ---------------------------------------------------------------------------------- -// stockSplits(bytes32 _marketId, uint256 _fromIx, uint256 _toIx, uint256 _nominator, uint256 _denominator) -// Experimental and untested -// ---------------------------------------------------------------------------------- - - function stockSplits(bytes32 _marketId, uint256 _fromIx, uint256 _toIx, uint256 _nominator, uint256 _denominator) public onlyAdministrator { - require(state.getMarketActive(_marketId) == false, "Market must be paused to process stock splits."); - // If no _fromIx and _toIx specified, do entire _list - if (_fromIx == 0) { - _fromIx = 1; - } - if (_toIx == 0) { - _toIx = state.getMaxMappingIndex(_marketId); - } - uint256 _positionLongShares; - uint256 _positionShortShares; - uint256 _positionAveragePrice; - uint256 _positionAverageSpread; - uint256 _positionAverageLeverage; - uint256 _liquidationPrice; - address _address; - - for (uint256 i = _fromIx; i <= _toIx; i++) { - // GET position from state - // multiply with nominator, divide by denominator (longShares/shortShares/meanEntry/meanSpread) - // Write back to state - _address = state.getExposureMappingAddress(_marketId, i); - (_positionLongShares, _positionShortShares, _positionAveragePrice, _positionAverageSpread, _positionAverageLeverage, _liquidationPrice) = state.getPosition(_address, _marketId); - _positionLongShares = _positionLongShares.mul(_denominator).div(_nominator); - _positionShortShares = _positionShortShares.mul(_denominator).div(_nominator); - _positionAveragePrice = _positionAveragePrice.mul(_nominator).div(_denominator); - _positionAverageSpread = _positionAverageSpread.mul(_nominator).div(_denominator); - if (_positionShortShares > 0) { - _liquidationPrice = getLiquidationPriceInternal(false, _address, _marketId); - } else { - _liquidationPrice = getLiquidationPriceInternal(true, _address, _marketId); - } - state.setPosition(_address, _marketId, now, _positionLongShares, _positionShortShares, _positionAveragePrice, _positionAverageSpread, _positionAverageLeverage, _liquidationPrice); - } - } - -// ---------------------------------------------------------------------------------- -// contractRolls(bytes32 _marketId, uint256 _fromIx, uint256 _toIx, uint256 _rollUp, uint256 _rollDown) -// Experimental and untested -// ---------------------------------------------------------------------------------- - function contractRolls(bytes32 _marketId, uint256 _fromIx, uint256 _toIx, uint256 _rollUp, uint256 _rollDown) public onlyAdministrator { - // If no _fromIx and _toIx specified, do entire _list - // dividends set meanEntry down, rolls either up or down - require(state.getMarketActive(_marketId) == false, "Market must be paused to process rolls."); - // If no _fromIx and _toIx specified, do entire _list - if (_fromIx == 0) { - _fromIx = 1; - } - if (_toIx == 0) { - _toIx = state.getMaxMappingIndex(_marketId); - } - uint256 _positionLongShares; - uint256 _positionShortShares; - uint256 _positionAveragePrice; - uint256 _positionAverageSpread; - uint256 _positionAverageLeverage; - uint256 _liquidationPrice; - address _address; - - for (uint256 i = _fromIx; i <= _toIx; i++) { - _address = state.getExposureMappingAddress(_marketId, i); - (_positionLongShares, _positionShortShares, _positionAveragePrice, _positionAverageSpread, _positionAverageLeverage, _liquidationPrice) = state.getPosition(_address, _marketId); - _positionAveragePrice = _positionAveragePrice.add(_rollUp).sub(_rollDown); - if (_positionShortShares > 0) { - _liquidationPrice = getLiquidationPriceInternal(false, _address, _marketId); - } else { - _liquidationPrice = getLiquidationPriceInternal(true, _address, _marketId); - } - state.setPosition(_address, _marketId, now, _positionLongShares, _positionShortShares, _positionAveragePrice, _positionAverageSpread, _positionAverageLeverage, _liquidationPrice); - } - } - -/** - * Stack too deep error - */ - function getLiquidationPriceInternal(bool isLong, address _userAddress, bytes32 _marketId) internal view returns (uint) { - ( , , uint price, , uint leverage, ) = state.getPosition(_userAddress, _marketId); - return tradeEngine.getLiquidationPrice(price, leverage, isLong, state.getLastUpdated(_userAddress, _marketId)); - } - -// ---------------------------------------------------------------------------------- -// delistMarket(bytes32 _marketId) -// Administrator closes out all existing positions on _marketId market at current prices -// ---------------------------------------------------------------------------------- - function delistMarket(bytes32 _marketId, uint256 _fromIx, uint256 _toIx) public onlyAdministrator { - require(state.getMarketActive(_marketId) == true, "Market must be active to process position liquidations."); - // If no _fromIx and _toIx specified, do entire _list - if (_fromIx == 0) { - _fromIx = 1; - } - if (_toIx == 0) { - _toIx = state.getMaxMappingIndex(_marketId); - } - address _address; - for (uint256 i = _fromIx; i <= _toIx; i++) { - _address = state.getExposureMappingAddress(_marketId, i); - adminLiquidationOrder(_address, _marketId); - } - } - -// ---------------------------------------------------------------------------------- -// delistMarket(bytes32 _marketId) -// Administrator closes out an existing positions on _marketId market at current price -// ---------------------------------------------------------------------------------- - function adminLiquidationOrder( - address _address, - bytes32 _marketId - ) public onlyAdministrator returns (bytes32 _orderId) { - uint256 _positionLongShares = state.getLongShares(_address, _marketId); - uint256 _positionShortShares = state.getShortShares(_address, _marketId); - if (_positionLongShares > 0) { - _orderId = tradeEngine.requestOrderId(_address, _marketId, _positionLongShares, 0, false, 10**8); - emit AdminLiquidationOrderCreated(_orderId, _address, _marketId, _positionLongShares, 0, false, 10**8); - } - if (_positionShortShares > 0) { - _orderId = tradeEngine.requestOrderId(_address, _marketId, _positionShortShares, 0, true, 10**8); - emit AdminLiquidationOrderCreated(_orderId, _address, _marketId, _positionShortShares, 0, true, 10**8); - } - return _orderId; - } - -// ---------------------------------------------------------------------------------- -// payOperatingReward() -// Calls paying of operating reward in state -// ---------------------------------------------------------------------------------- - function payOperatingReward() public view { - if (state.mainChain() == true) { - uint256 _lastRewardTime = state.lastRewardTime(); - if (now > _lastRewardTime) { - for (uint256 i = 1; i <= now.sub(state.lastRewardTime()).div(86400); i++) { - state.payOperatingReward; - } - } - } - } - -// ---------------------------------------------------------------------------------- -// stockDividends() -// May want to add support for dividends later -// ---------------------------------------------------------------------------------- -/* function stockDividends(bytes32 _marketId, uint256 _fromIx, uint256 _toIx, uint256 _meanEntryUp, uint256 _meanEntryDown) public onlyOracle returns (bool _success){ - } -*/ -} diff --git a/contracts/MorpherAdministratorProxy.sol b/contracts/MorpherAdministratorProxy.sol deleted file mode 100644 index 08fb0bf..0000000 --- a/contracts/MorpherAdministratorProxy.sol +++ /dev/null @@ -1,26 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; - -contract MorpherAdministratorProxy is Ownable { - - address public morpherStateAddress; - - constructor(address _morpherAdministrator, address _morpherStateAddress) public { - transferOwnership(_morpherAdministrator); - morpherStateAddress = _morpherStateAddress; - } - - function bulkActivateMarkets(bytes32[] memory _marketHashes) public onlyOwner { - for(uint i = 0; i < _marketHashes.length; i++) { - bytes memory payload = abi.encodeWithSignature("activateMarket(bytes32)", _marketHashes[i]); - (bool success, ) = morpherStateAddress.call(payload); - require(success, "MorpherAdministratorProxy: Failed to activate Market"); - } - } - - function () external payable onlyOwner { - (bool success, ) = morpherStateAddress.call.value(msg.value)(msg.data); - require(success, "MorpherAdministratorProxy: Failed to forward call"); - } -} \ No newline at end of file diff --git a/contracts/MorpherAirdrop.sol b/contracts/MorpherAirdrop.sol deleted file mode 100644 index 6748dce..0000000 --- a/contracts/MorpherAirdrop.sol +++ /dev/null @@ -1,138 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./IERC20.sol"; - -// ---------------------------------------------------------------------------------- -// Holds the Airdrop Token balance on contract address -// AirdropAdmin can authorize addresses to receive airdrop. -// Users have to claim their airdrop actively or Admin initiates transfer. -// ---------------------------------------------------------------------------------- - -contract MorpherAirdrop is Ownable { - using SafeMath for uint256; - -// ---------------------------------------------------------------------------- -// Mappings for authorized / claimed airdrop -// ---------------------------------------------------------------------------- - mapping(address => uint256) private airdropClaimed; - mapping(address => uint256) private airdropAuthorized; - - uint256 public totalAirdropAuthorized; - uint256 public totalAirdropClaimed; - - address public airdropAdmin; - address public morpherToken; - -// ---------------------------------------------------------------------------- -// Events -// ---------------------------------------------------------------------------- - event AirdropSent(address indexed _operator, address indexed _recipient, uint256 _amountClaimed, uint256 _amountAuthorized); - event SetAirdropAuthorized(address indexed _recipient, uint256 _amountClaimed, uint256 _amountAuthorized); - - constructor(address _airdropAdminAddress, address _morpherToken, address _coldStorageOwnerAddress) public { - setAirdropAdmin(_airdropAdminAddress); - setMorpherTokenAddress(_morpherToken); - transferOwnership(_coldStorageOwnerAddress); - } - - modifier onlyAirdropAdmin { - require(msg.sender == airdropAdmin, "MorpherAirdrop: can only be called by Airdrop Administrator."); - _; - } - -// ---------------------------------------------------------------------------- -// Administrative functions -// ---------------------------------------------------------------------------- - function setAirdropAdmin(address _address) public onlyOwner { - airdropAdmin = _address; - } - - function setMorpherTokenAddress(address _address) public onlyOwner { - morpherToken = _address; - } - -// ---------------------------------------------------------------------------- -// Get airdrop amount authorized for or claimed by address -// ---------------------------------------------------------------------------- - function getAirdropClaimed(address _userAddress) public view returns (uint256 _amount) { - return airdropClaimed[_userAddress]; - } - - function getAirdropAuthorized(address _userAddress) public view returns (uint256 _balance) { - return airdropAuthorized[_userAddress]; - } - - function getAirdrop(address _userAddress) public view returns(uint256 _claimed, uint256 _authorized) { - return (airdropClaimed[_userAddress], airdropAuthorized[_userAddress]); - } - -// ---------------------------------------------------------------------------- -// Airdrop Administrator can authorize airdrop amount per address -// ---------------------------------------------------------------------------- - function setAirdropAuthorized(address _userAddress, uint256 _authorized) public onlyAirdropAdmin { - // Can only set authorized amount to be higher than claimed - require(_authorized >= airdropClaimed[_userAddress], "MorpherAirdrop: airdrop authorized must be larger than claimed."); - // Authorized amount can be higher or lower than previously authorized amount, adjust accordingly - totalAirdropAuthorized = totalAirdropAuthorized.sub(getAirdropAuthorized(_userAddress)).add(_authorized); - airdropAuthorized[_userAddress] = _authorized; - emit SetAirdropAuthorized(_userAddress, airdropClaimed[_userAddress], _authorized); - } - -// ---------------------------------------------------------------------------- -// User claims their entire airdrop -// ---------------------------------------------------------------------------- - function claimAirdrop() public { - uint256 _amount = airdropAuthorized[msg.sender].sub(airdropClaimed[msg.sender]); - _sendAirdrop(msg.sender, _amount); - } - -// ---------------------------------------------------------------------------- -// User claims part of their airdrop -// ---------------------------------------------------------------------------- - function claimSomeAirdrop(uint256 _amount) public { - _sendAirdrop(msg.sender, _amount); - } - -// ---------------------------------------------------------------------------- -// Administrator sends user their entire airdrop -// ---------------------------------------------------------------------------- - function adminSendAirdrop(address _recipient) public onlyAirdropAdmin { - uint256 _amount = airdropAuthorized[_recipient].sub(airdropClaimed[_recipient]); - _sendAirdrop(_recipient, _amount); - } - -// ---------------------------------------------------------------------------- -// Administrator sends user part of their airdrop -// ---------------------------------------------------------------------------- - function adminSendSomeAirdrop(address _recipient, uint256 _amount) public onlyAirdropAdmin { - _sendAirdrop(_recipient, _amount); - } - -// ---------------------------------------------------------------------------- -// Administrator sends user entire airdrop -// ---------------------------------------------------------------------------- - function _sendAirdrop(address _recipient, uint256 _amount) private { - require(airdropAuthorized[_recipient] >= airdropClaimed[_recipient].add(_amount), "MorpherAirdrop: amount exceeds authorized airdrop amount."); - airdropClaimed[_recipient] = airdropClaimed[_recipient].add(_amount); - totalAirdropClaimed = totalAirdropClaimed.add(_amount); - IERC20(morpherToken).transfer(_recipient, _amount); - emit AirdropSent(msg.sender, _recipient, airdropClaimed[_recipient], airdropAuthorized[_recipient]); - } - -// ---------------------------------------------------------------------------- -// Administrator sends user part of their airdrop -// ---------------------------------------------------------------------------- - function adminAuthorizeAndSend(address _recipient, uint256 _amount) public onlyAirdropAdmin { - setAirdropAuthorized(_recipient, getAirdropAuthorized(_recipient).add(_amount)); - _sendAirdrop(_recipient, _amount); - } - -// ------------------------------------------------------------------------ -// Don't accept ETH -// ------------------------------------------------------------------------ - function () external payable { - revert("MorpherAirdrop: you can't deposit Ether here"); - } -} diff --git a/contracts/MorpherBridge.sol b/contracts/MorpherBridge.sol deleted file mode 100644 index c66d4a8..0000000 --- a/contracts/MorpherBridge.sol +++ /dev/null @@ -1,365 +0,0 @@ -// ------------------------------------------------------------------------ -// MorpherBridge -// Handles deposit to and withdraws from the side chain, writing of the merkle -// root to the main chain by the side chain operator, and enforces a rolling 24 hours -// token withdraw limit from side chain to main chain. -// If side chain operator doesn't write a merkle root hash to main chain for more than -// 72 hours positions and balaces from side chain can be transferred to main chain. -// ------------------------------------------------------------------------ - -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./IMorpherState.sol"; -import "./MerkleProof.sol"; -import "./MorpherUserBlocking.sol"; - -contract MorpherBridge is Ownable { - - IMorpherState state; - MorpherBridge previousBridge; - MorpherUserBlocking userBlocking; - using SafeMath for uint256; - - mapping(address => mapping(uint256 => uint256)) public withdrawalPerDay; //[address][day] = withdrawalAmount - mapping(address => mapping(uint256 => uint256)) public withdrawalPerMonth; //[address][month] = withdrawalAmount - mapping(address => mapping(uint256 => uint256)) public withdrawalPerYear; //[address][year] = withdrawalAmount - - uint256 public withdrawalLimitDaily = 200000 * (10**18); //200k MPH per day - uint256 public withdrawalLimitMonthly = 1000000 * (10 ** 18); //1M MPH per month - uint256 public withdrawalLimitYearly = 5000000 * (10 ** 18); //5M MPH per year - - event TransferToLinkedChain( - address indexed from, - uint256 tokens, - uint256 totalTokenSent, - uint256 timeStamp, - uint256 transferNonce, - bytes32 indexed transferHash - ); - event TrustlessWithdrawFromSideChain(address indexed from, uint256 tokens); - event OperatorChainTransfer(address indexed from, uint256 tokens, bytes32 sidechainTransactionHash); - event ClaimFailedTransferToSidechain(address indexed from, uint256 tokens); - event PositionRecoveryFromSideChain(address indexed from, bytes32 positionHash); - event TokenRecoveryFromSideChain(address indexed from, bytes32 positionHash); - event SideChainMerkleRootUpdated(bytes32 _rootHash); - event WithdrawLimitReset(); - event WithdrawLimitChanged(uint256 _withdrawLimit); - event WithdrawLimitDailyChanged(uint256 _oldLimit, uint256 _newLimit); - event WithdrawLimitMonthlyChanged(uint256 _oldLimit, uint256 _newLimit); - event WithdrawLimitYearlyChanged(uint256 _oldLimit, uint256 _newLimit); - event LinkState(address _address); - event LinkMorpherUserBlocking(address _address); - - constructor(address _stateAddress, address _coldStorageOwnerAddress, address _previousBridgeAddress, address _userBlockingAddress) public { - setMorpherState(_stateAddress); - previousBridge = MorpherBridge(_previousBridgeAddress); - setUserBlockingAddress(_userBlockingAddress); - transferOwnership(_coldStorageOwnerAddress); - } - - modifier onlySideChainOperator { - require(msg.sender == state.getSideChainOperator(), "MorpherBridge: Function can only be called by Sidechain Operator."); - _; - } - - modifier sideChainInactive { - require(now - state.inactivityPeriod() > state.getSideChainMerkleRootWrittenAtTime(), "MorpherBridge: Function can only be called if sidechain is inactive."); - _; - } - - modifier fastTransfers { - require(state.fastTransfersEnabled() == true, "MorpherBridge: Fast transfers have been disabled permanently."); - _; - } - - modifier onlyMainchain { - require(state.mainChain() == true, "MorpherBridge: Function can only be executed on Ethereum." ); - _; - } - - modifier userNotBlocked { - require(!userBlocking.userIsBlocked(msg.sender), "MorpherBridge: User is blocked"); - _; - } - - // ------------------------------------------------------------------------ - // Links Token Contract with State - // ------------------------------------------------------------------------ - function setMorpherState(address _stateAddress) public onlyOwner { - state = IMorpherState(_stateAddress); - emit LinkState(_stateAddress); - } - - function setUserBlockingAddress(address _userBlockingAddress) public onlyOwner { - userBlocking = MorpherUserBlocking(_userBlockingAddress); - emit LinkMorpherUserBlocking(_userBlockingAddress); - } - - - function setInactivityPeriod(uint256 _periodInSeconds) private { - state.setInactivityPeriod(_periodInSeconds); - } - - function disableFastTransfers() public onlyOwner { - state.disableFastWithdraws(); - } - - function updateSideChainMerkleRoot(bytes32 _rootHash) public onlySideChainOperator { - state.setSideChainMerkleRoot(_rootHash); - emit SideChainMerkleRootUpdated(_rootHash); - } - - function resetLast24HoursAmountWithdrawn() public onlySideChainOperator { - state.resetLast24HoursAmountWithdrawn(); - emit WithdrawLimitReset(); - } - - function set24HourWithdrawLimit(uint256 _withdrawLimit) public onlySideChainOperator { - state.set24HourWithdrawLimit(_withdrawLimit); - emit WithdrawLimitChanged(_withdrawLimit); - } - - function updateWithdrawLimitDaily(uint256 _withdrawLimit) public onlySideChainOperator { - emit WithdrawLimitDailyChanged(withdrawalLimitDaily, _withdrawLimit); - withdrawalLimitDaily = _withdrawLimit; - } - - function updateWithdrawLimitMonthly(uint256 _withdrawLimit) public onlySideChainOperator { - emit WithdrawLimitMonthlyChanged(withdrawalLimitMonthly, _withdrawLimit); - withdrawalLimitMonthly = _withdrawLimit; - } - function updateWithdrawLimitYearly(uint256 _withdrawLimit) public onlySideChainOperator { - emit WithdrawLimitYearlyChanged(withdrawalLimitYearly, _withdrawLimit); - withdrawalLimitYearly = _withdrawLimit; - } - - function getTokenSentToLinkedChain(address _address) public view returns (uint256 _token) { - return state.getTokenSentToLinkedChain(_address); - } - - function getTokenClaimedOnThisChain(address _address) public view returns (uint256 _token) { - return state.getTokenClaimedOnThisChain(_address); - } - - function getTokenSentToLinkedChainTime(address _address) public view returns (uint256 _time) { - return state.getTokenSentToLinkedChainTime(_address); - } - - // ------------------------------------------------------------------------ - // verifyWithdrawOk(uint256 _amount) - // Checks if creating _amount token on main chain does not violate the 24 hour transfer limit - // ------------------------------------------------------------------------ - function verifyWithdrawOk(uint256 _amount) public returns (bool _authorized) { - uint256 _lastWithdrawLimitReductionTime = state.lastWithdrawLimitReductionTime(); - uint256 _withdrawLimit24Hours = state.withdrawLimit24Hours(); - - if (now > _lastWithdrawLimitReductionTime) { - uint256 _timePassed = now.sub(_lastWithdrawLimitReductionTime); - state.update24HoursWithdrawLimit(_timePassed.mul(_withdrawLimit24Hours).div(1 days)); - } - - if (state.last24HoursAmountWithdrawn().add(_amount) <= _withdrawLimit24Hours) { - return true; - } else { - return false; - } - } - - function isNotDailyLimitExceeding(uint256 _amount) public view returns(bool) { - return (getWithdrawalPerDay(msg.sender).add(_amount) <= withdrawalLimitDaily); - } - function isNotMonthlyLimitExceeding(uint256 _amount) public view returns(bool) { - return (getWithdrawalPerMonth(msg.sender).add(_amount) <= withdrawalLimitMonthly); - } - function isNotYearlyLimitExceeding(uint256 _amount) public view returns(bool) { - return (getWithdrawalPerYear(msg.sender).add(_amount) <= withdrawalLimitYearly); - } - - function verifyUpdateDailyLimit(uint256 _amount) public { - require(isNotDailyLimitExceeding(_amount), "MorpherBridge: Withdrawal Amount exceeds daily limit"); - withdrawalPerDay[msg.sender][block.timestamp / 1 days] = getWithdrawalPerDay(msg.sender).add(_amount); - } - - function verifyUpdateMonthlyLimit(uint256 _amount) public { - require(isNotMonthlyLimitExceeding(_amount), "MorpherBridge: Withdrawal Amount exceeds monthly limit"); - withdrawalPerMonth[msg.sender][block.timestamp / 30 days] = getWithdrawalPerMonth(msg.sender).add(_amount); - } - - function verifyUpdateYearlyLimit(uint256 _amount) public { - require(isNotYearlyLimitExceeding(_amount), "MorpherBridge: Withdrawal Amount exceeds yearly limit"); - withdrawalPerYear[msg.sender][block.timestamp / 365 days] = getWithdrawalPerYear(msg.sender).add(_amount); - } - - function getWithdrawalPerDay(address _user) public view returns(uint) { - if(address(previousBridge) != address(0) && withdrawalPerDay[_user][block.timestamp / 1 days] == 0) { - return previousBridge.withdrawalPerDay(_user, block.timestamp / 1 days); //if bridge is re-deployed this needs to change to previousBridge.getWithdrawalPerDay - } - return withdrawalPerDay[_user][block.timestamp / 1 days]; - } - function getWithdrawalPerMonth(address _user) public view returns(uint) { - if(address(previousBridge) != address(0) && withdrawalPerMonth[_user][block.timestamp / 30 days] == 0) { - return previousBridge.withdrawalPerMonth(_user, block.timestamp / 30 days); //if bridge is re-deployed this needs to change to previousBridge.getWithdrawalPerDay - } - return withdrawalPerMonth[_user][block.timestamp / 30 days]; - } - function getWithdrawalPerYear(address _user) public view returns(uint) { - if(address(previousBridge) != address(0) && withdrawalPerYear[_user][block.timestamp / 365 days] == 0) { - return previousBridge.withdrawalPerYear(_user, block.timestamp / 365 days); //if bridge is re-deployed this needs to change to previousBridge.getWithdrawalPerDay - } - return withdrawalPerYear[_user][block.timestamp / 365 days]; - } - - // ------------------------------------------------------------------------ - // transferToSideChain(uint256 _tokens) - // Transfer token to Morpher's side chain to trade without fees and near instant - // settlement. - // - Owner's account must have sufficient balance to transfer - // - 0 value transfers are not supported - // Token are burned on the main chain and are created and credited to msg.sender - // on the side chain - // ------------------------------------------------------------------------ - function transferToSideChain(uint256 _tokens) public userNotBlocked { - require(_tokens >= 0, "MorpherBridge: Amount of tokens must be positive."); - require(state.balanceOf(msg.sender) >= _tokens, "MorpherBridge: Insufficient balance."); - verifyUpdateDailyLimit(_tokens); - verifyUpdateMonthlyLimit(_tokens); - verifyUpdateYearlyLimit(_tokens); - state.burn(msg.sender, _tokens); - uint256 _newTokenSentToLinkedChain = getTokenSentToLinkedChain(msg.sender).add(_tokens); - uint256 _transferNonce = state.getBridgeNonce(); - uint256 _timeStamp = now; - bytes32 _transferHash = keccak256( - abi.encodePacked( - msg.sender, - _tokens, - _newTokenSentToLinkedChain, - _timeStamp, - _transferNonce - ) - ); - state.setTokenSentToLinkedChain(msg.sender, _newTokenSentToLinkedChain); - emit TransferToLinkedChain(msg.sender, _tokens, _newTokenSentToLinkedChain, _timeStamp, _transferNonce, _transferHash); - } - - // ------------------------------------------------------------------------ - // fastTransferFromSideChain(uint256 _numOfToken, uint256 _tokenBurnedOnLinkedChain, bytes32[] memory _proof) - // The sidechain operator can credit users with token they burend on the sidechain. Transfers - // happen immediately. To be removed after Beta. - // ------------------------------------------------------------------------ - function fastTransferFromSideChain(address _address, uint256 _numOfToken, uint256 _tokenBurnedOnLinkedChain, bytes32 _sidechainTransactionHash) public onlySideChainOperator fastTransfers { - uint256 _tokenClaimed = state.getTokenClaimedOnThisChain(_address); - require(verifyWithdrawOk(_numOfToken), "MorpherBridge: Withdraw amount exceeds permitted 24 hour limit. Please try again in a few hours."); - require(_tokenClaimed.add(_numOfToken) <= _tokenBurnedOnLinkedChain, "MorpherBridge: Token amount exceeds token deleted on linked chain."); - _chainTransfer(_address, _tokenClaimed, _numOfToken); - emit OperatorChainTransfer(_address, _numOfToken, _sidechainTransactionHash); - } - - // ------------------------------------------------------------------------ - // trustlessTransferFromSideChain(uint256 _numOfToken, uint256 _claimLimit, bytes32[] memory _proof) - // Performs a merkle proof on the number of token that have been burned by the user on the side chain. - // If the number of token claimed on the main chain is less than the number of burned token on the side chain - // the difference (or less) can be claimed on the main chain. - // ------------------------------------------------------------------------ - function trustlessTransferFromLinkedChain(uint256 _numOfToken, uint256 _claimLimit, bytes32[] memory _proof) public userNotBlocked { - bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _claimLimit)); - uint256 _tokenClaimed = state.getTokenClaimedOnThisChain(msg.sender); - require(mProof(_proof, leaf), "MorpherBridge: Merkle Proof failed. Please make sure you entered the correct claim limit."); - require(verifyWithdrawOk(_numOfToken), "MorpherBridge: Withdraw amount exceeds permitted 24 hour limit. Please try again in a few hours."); - verifyUpdateDailyLimit(_numOfToken); - verifyUpdateMonthlyLimit(_numOfToken); - verifyUpdateYearlyLimit(_numOfToken); - require(_tokenClaimed.add(_numOfToken) <= _claimLimit, "MorpherBridge: Token amount exceeds token deleted on linked chain."); - _chainTransfer(msg.sender, _tokenClaimed, _numOfToken); - emit TrustlessWithdrawFromSideChain(msg.sender, _numOfToken); - } - - // ------------------------------------------------------------------------ - // _chainTransfer(address _address, uint256 _tokenClaimed, uint256 _numOfToken) - // Creates token on the chain for the user after proving their distruction on the - // linked chain has been proven before - // ------------------------------------------------------------------------ - function _chainTransfer(address _address, uint256 _tokenClaimed, uint256 _numOfToken) private { - state.setTokenClaimedOnThisChain(_address, _tokenClaimed.add(_numOfToken)); - state.add24HoursWithdrawn(_numOfToken); - state.mint(_address, _numOfToken); - } - - // ------------------------------------------------------------------------ - // claimFailedTransferToSidechain(uint256 _wrongSideChainBalance, bytes32[] memory _proof) - // If token sent to side chain were not credited to the user on the side chain within inactivityPeriod - // they can reclaim the token on the main chain by submitting the proof that their - // side chain balance is less than the number of token sent from main chain. - // ------------------------------------------------------------------------ - function claimFailedTransferToSidechain(uint256 _wrongSideChainBalance, bytes32[] memory _proof) public userNotBlocked { - bytes32 leaf = keccak256(abi.encodePacked(msg.sender, _wrongSideChainBalance)); - uint256 _tokenSentToLinkedChain = getTokenSentToLinkedChain(msg.sender); - uint256 _tokenSentToLinkedChainTime = getTokenSentToLinkedChainTime(msg.sender); - uint256 _inactivityPeriod = state.inactivityPeriod(); - - require(now > _tokenSentToLinkedChainTime.add(_inactivityPeriod), "MorpherBridge: Failed deposits can only be claimed after inactivity period."); - require(_wrongSideChainBalance < _tokenSentToLinkedChain, "MorpherBridge: Other chain credit is greater equal to wrongSideChainBalance."); - require(verifyWithdrawOk(_tokenSentToLinkedChain.sub(_wrongSideChainBalance)), "MorpherBridge: Claim amount exceeds permitted 24 hour limit."); - require(mProof(_proof, leaf), "MorpherBridge: Merkle Proof failed. Enter total amount of deposits on side chain."); - - uint256 _claimAmount = _tokenSentToLinkedChain.sub(_wrongSideChainBalance); - state.setTokenSentToLinkedChain(msg.sender, _tokenSentToLinkedChain.sub(_claimAmount)); - state.add24HoursWithdrawn(_claimAmount); - state.mint(msg.sender, _claimAmount); - emit ClaimFailedTransferToSidechain(msg.sender, _claimAmount); - } - - // ------------------------------------------------------------------------ - // recoverPositionFromSideChain(bytes32[] memory _proof, bytes32 _leaf, bytes32 _marketId, uint256 _timeStamp, uint256 _longShares, uint256 _shortShares, uint256 _meanEntryPrice, uint256 _meanEntrySpread, uint256 _meanEntryLeverage) - // Failsafe against side chain operator becoming inactive or withholding Times (Time withhold attack). - // After 72 hours of no update of the side chain merkle root users can withdraw their last recorded - // positions from side chain to main chain. Overwrites eventually existing position on main chain. - // ------------------------------------------------------------------------ - function recoverPositionFromSideChain( - bytes32[] memory _proof, - bytes32 _leaf, - bytes32 _marketId, - uint256 _timeStamp, - uint256 _longShares, - uint256 _shortShares, - uint256 _meanEntryPrice, - uint256 _meanEntrySpread, - uint256 _meanEntryLeverage, - uint256 _liquidationPrice - ) public sideChainInactive userNotBlocked onlyMainchain { - require(_leaf == state.getPositionHash(msg.sender, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice), "MorpherBridge: leaf does not equal position hash."); - require(state.getPositionClaimedOnMainChain(_leaf) == false, "MorpherBridge: Position already transferred."); - require(mProof(_proof,_leaf) == true, "MorpherBridge: Merkle proof failed."); - state.setPositionClaimedOnMainChain(_leaf); - state.setPosition(msg.sender, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice); - emit PositionRecoveryFromSideChain(msg.sender, _leaf); - // Remark: After resuming operations side chain operator has 72 hours to sync and eliminate transferred positions on side chain to avoid double spend - } - - // ------------------------------------------------------------------------ - // recoverTokenFromSideChain(bytes32[] memory _proof, bytes32 _leaf, bytes32 _marketId, uint256 _timeStamp, uint256 _longShares, uint256 _shortShares, uint256 _meanEntryPrice, uint256 _meanEntrySpread, uint256 _meanEntryLeverage) - // Failsafe against side chain operator becoming inactive or withholding times (time withhold attack). - // After 72 hours of no update of the side chain merkle root users can withdraw their last recorded - // token balance from side chain to main chain. - // ------------------------------------------------------------------------ - function recoverTokenFromSideChain(bytes32[] memory _proof, bytes32 _leaf, uint256 _balance) public sideChainInactive userNotBlocked onlyMainchain { - // Require side chain root hash not set on Mainchain for more than 72 hours (=3 days) - require(_leaf == state.getBalanceHash(msg.sender, _balance), "MorpherBridge: Wrong balance."); - require(state.getPositionClaimedOnMainChain(_leaf) == false, "MorpherBridge: Token already transferred."); - require(mProof(_proof,_leaf) == true, "MorpherBridge: Merkle proof failed."); - require(verifyWithdrawOk(_balance), "MorpherBridge: Withdraw amount exceeds permitted 24 hour limit."); - state.setPositionClaimedOnMainChain(_leaf); - _chainTransfer(msg.sender, state.getTokenClaimedOnThisChain(msg.sender), _balance); - emit TokenRecoveryFromSideChain(msg.sender, _leaf); - // Remark: Side chain operator must adjust side chain balances for token recoveries before restarting operations to avoid double spend - } - - // ------------------------------------------------------------------------ - // mProof(bytes32[] memory _proof, bytes32 _leaf) - // Computes merkle proof against the root hash of the sidechain stored in Morpher state - // ------------------------------------------------------------------------ - function mProof(bytes32[] memory _proof, bytes32 _leaf) public view returns(bool _isTrue) { - return MerkleProof.verify(_proof, state.getSideChainMerkleRoot(), _leaf); - } -} diff --git a/contracts/MorpherEscrow.sol b/contracts/MorpherEscrow.sol deleted file mode 100644 index 70b4326..0000000 --- a/contracts/MorpherEscrow.sol +++ /dev/null @@ -1,60 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./IERC20.sol"; - -// ---------------------------------------------------------------------------------- -// Escrow contract to safely store and release the token allocated to Morpher at -// protocol inception -// ---------------------------------------------------------------------------------- - -contract MorpherEscrow is Ownable{ - using SafeMath for uint256; - - uint256 public lastEscrowTransferTime; - address public recipient; - address public morpherToken; - - uint256 public constant RELEASEAMOUNT = 10**25; - uint256 public constant RELEASEPERIOD = 30 days; - - event EscrowReleased(uint256 _released, uint256 _leftInEscrow); - - constructor(address _recipientAddress, address _morpherToken, address _coldStorageOwnerAddress) public { - setRecipientAddress(_recipientAddress); - setMorpherTokenAddress(_morpherToken); - lastEscrowTransferTime = now; - transferOwnership(_coldStorageOwnerAddress); - } - - // ---------------------------------------------------------------------------------- - // Owner can modify recipient address and update morpherToken adddress - // ---------------------------------------------------------------------------------- - function setRecipientAddress(address _recipientAddress) public onlyOwner { - recipient = _recipientAddress; - } - - function setMorpherTokenAddress(address _address) public onlyOwner { - morpherToken = _address; - } - - // ---------------------------------------------------------------------------------- - // Anyone can release funds from escrow if enough time has elapsed - // Every 30 days 1% of the total initial supply or 10m token are released to Morpher - // ---------------------------------------------------------------------------------- - function releaseFromEscrow() public { - require(IERC20(morpherToken).balanceOf(address(this)) > 0, "No funds left in escrow."); - uint256 _releasedAmount; - if (now > lastEscrowTransferTime.add(RELEASEPERIOD)) { - if (IERC20(morpherToken).balanceOf(address(this)) > RELEASEAMOUNT) { - _releasedAmount = RELEASEAMOUNT; - } else { - _releasedAmount = IERC20(morpherToken).balanceOf(address(this)); - } - IERC20(morpherToken).transfer(recipient, _releasedAmount); - lastEscrowTransferTime = lastEscrowTransferTime.add(RELEASEPERIOD); - emit EscrowReleased(_releasedAmount, IERC20(morpherToken).balanceOf(address(this))); - } - } -} diff --git a/contracts/MorpherFaucet.sol b/contracts/MorpherFaucet.sol deleted file mode 100644 index d4470e3..0000000 --- a/contracts/MorpherFaucet.sol +++ /dev/null @@ -1,52 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./MorpherToken.sol"; - -// ---------------------------------------------------------------------------------- -// Holds the Faucet Token balance on contract addressrdrop. -// Users can topup to fillUpAmount -// ---------------------------------------------------------------------------------- - -contract MorpherFaucet is Ownable { - using SafeMath for uint256; - - MorpherToken morpherToken; - - uint public fillUpAmount; //100 * 10**18; //fill up to 100 MPH. - - event MorpherFaucetTopUp(address indexed _receiver, uint _amount); - event MorpherFaucetFillUpAmountChanged(uint _oldAmount, uint _newAmount); - - constructor(address payable _morpherToken, address _coldStorageOwnerAddress, uint _fillUpAmount) public { - morpherToken = MorpherToken(_morpherToken); - transferOwnership(_coldStorageOwnerAddress); - setFillUpAmount(_fillUpAmount); - } - - function setMorpherTokenAddress(address payable _address) public onlyOwner { - morpherToken = MorpherToken(_address); - } - - function setFillUpAmount(uint _newFillUpAmount) public onlyOwner { - emit MorpherFaucetFillUpAmountChanged(fillUpAmount, _newFillUpAmount); - fillUpAmount = _newFillUpAmount; - } - - - /** - * Only important function: User can top-up to his max amount. Needs to have less than fillUpAmount, otherwise it will fail. - */ - function topUpToken() public { - require(morpherToken.balanceOf(msg.sender) < fillUpAmount, "FILLUP_AMOUNT_REACHED"); - morpherToken.transfer(msg.sender, fillUpAmount.sub(morpherToken.balanceOf(msg.sender))); - emit MorpherFaucetTopUp(msg.sender, fillUpAmount.sub(morpherToken.balanceOf(msg.sender))); - } - - function () external payable { - revert("MorpherFaucet: you can't deposit Ether here"); - } - - -} diff --git a/contracts/MorpherGovernance.sol b/contracts/MorpherGovernance.sol deleted file mode 100644 index c4be645..0000000 --- a/contracts/MorpherGovernance.sol +++ /dev/null @@ -1,207 +0,0 @@ -pragma solidity 0.5.16; -// ------------------------------------------------------------------------ -// Morpher Governance (MAIN CHAIN ONLY) -// -// Every user able and willig to lock up sufficient token can become a validator -// of the Morpher protocol. Validators function similiar to a board of directors -// and vote on the protocol Administrator and the Oracle contract. -// The Administrator (=Protocol CEO) has the power to add/delete markets and to -// pause the contracts to allow for updates. -// The Oracle contract is the address of the contract allowed to fetch prices -// from outside the smart contract. -// -// It becomes progressively harder to become a valdiator. Each new validator -// has to lock up (numberOfValidators + 1) * 10m Morpher token. Upon stepping -// down as validator only 99% of the locked up token are returned, the other 1% -// are burned. -// -// Governance is expected to become more sophisticated in the future -// ------------------------------------------------------------------------ - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./MorpherState.sol"; - -contract MorpherGovernance is Ownable { - - using SafeMath for uint256; - MorpherState state; - - event BecomeValidator(address indexed _sender, uint256 indexed _myValidatorIndex); - event StepDownAsValidator(address indexed _sender, uint256 indexed _myValidatorIndex); - event ElectedAdministrator(address indexed _administratorAddress, uint256 _votes); - event ElectedOracle(address indexed _oracleAddress, uint256 _votes); - - uint256 public constant MINVALIDATORLOCKUP = 10**25; - uint256 public constant MAXVALIDATORS = 21; - uint256 public constant VALIDATORWARMUPPERIOD = 7 days; - - uint256 public numberOfValidators; - uint256 public lastValidatorJoined; - uint256 public rewardBasisPoints; - - address public morpherToken; - - mapping(address => uint256) private validatorIndex; - mapping(address => uint256) private validatorJoinedAtTime; - mapping(uint256 => address) private validatorAddress; - mapping(address => address) private oracleVote; - mapping(address => address) private administratorVote; - mapping(address => uint256) private countVotes; - - constructor(address _stateAddress, address _coldStorageOwnerAddress) public { - setMorpherState(_stateAddress); - transferOwnership(_coldStorageOwnerAddress); - } - - modifier onlyValidator() { - require(isValidator(msg.sender), "MorpherGovernance: Only Validators can invoke that function."); - _; - } - - function setMorpherState(address _stateAddress) private { - state = MorpherState(_stateAddress); - } - - function setMorpherTokenAddress(address _address) public onlyOwner { - morpherToken = _address; - } - - function getValidatorAddress(uint256 _index) public view returns (address _address) { - return validatorAddress[_index]; - } - - function getValidatorIndex(address _address) public view returns (uint256 _index) { - return validatorIndex[_address]; - } - - function isValidator(address _address) public view returns (bool) { - return validatorIndex[_address] > 0; - } - - function setOracle(address _oracleAddress) private { - state.setOracleContract(_oracleAddress); - } - - function setAdministrator(address _administratorAddress) private { - state.setAdministrator(_administratorAddress); - } - - function getMorpherAdministrator() public view returns (address _address) { - return state.getAdministrator(); - } - - function getMorpherOracle() public view returns (address _address) { - return state.getOracleContract(); - } - - function getOracleVote(address _address) public view returns (address _votedOracleAddress) { - return oracleVote[_address]; - } - - function becomeValidator() public { - // To become a validator you have to lock up 10m * (number of validators + 1) Morpher Token in escrow - // After a warmup period of 7 days the new validator can vote on Oracle contract and protocol Administrator - uint256 _requiredAmount = MINVALIDATORLOCKUP.mul(numberOfValidators.add(1)); - require(state.balanceOf(msg.sender) >= _requiredAmount, "MorpherGovernance: Insufficient balance to become Validator."); - require(isValidator(msg.sender) == false, "MorpherGovernance: Address is already Validator."); - require(numberOfValidators <= MAXVALIDATORS, "MorpherGovernance: number of Validators can not exceed Max Validators."); - state.transfer(msg.sender, address(this), _requiredAmount); - numberOfValidators = numberOfValidators.add(1); - validatorIndex[msg.sender] = numberOfValidators; - validatorJoinedAtTime[msg.sender] = now; - lastValidatorJoined = now; - validatorAddress[numberOfValidators] = msg.sender; - emit BecomeValidator(msg.sender, numberOfValidators); - } - - function stepDownValidator() public onlyValidator { - // Stepping down as validator nullifies the validator's votes and releases his token - // from escrow. If the validator stepping down is not the validator that joined last, - // all validators who joined after the validator stepping down receive 10^7 * 0.99 token from - // escrow, and their validator ordinal number is reduced by one. E.g. if validator 3 of 5 steps down - // validator 4 becomes validator 3, and validator 5 becomes validator 4. Both receive 10^7 * 0.99 token - // from escrow, as their new position requires fewer token in lockup. 1% of the token released from escrow - // are burned for every validator receiving a payout. - // Burning prevents vote delay attacks: validators stepping down and re-joining could - // delay votes for VALIDATORWARMUPPERIOD. - uint256 _myValidatorIndex = validatorIndex[msg.sender]; - require(state.balanceOf(address(this)) >= MINVALIDATORLOCKUP.mul(numberOfValidators), "MorpherGovernance: Escrow does not have enough funds. Should not happen."); - // Stepping down as validator potentially releases token to the other validatorAddresses - for (uint256 i = _myValidatorIndex; i < numberOfValidators; i++) { - validatorAddress[i] = validatorAddress[i+1]; - validatorIndex[validatorAddress[i]] = i; - // Release 9.9m of token to every validator moving up, burn 0.1m token - state.transfer(address(this), validatorAddress[i], MINVALIDATORLOCKUP.div(100).mul(99)); - state.burn(address(this), MINVALIDATORLOCKUP.div(100)); - } - // Release 99% of escrow token of validator dropping out, burn 1% - validatorAddress[numberOfValidators] = address(0); - validatorIndex[msg.sender] = 0; - validatorJoinedAtTime[msg.sender] = 0; - oracleVote[msg.sender] = address(0); - administratorVote[msg.sender] = address(0); - numberOfValidators = numberOfValidators.sub(1); - countOracleVote(); - countAdministratorVote(); - state.transfer(address(this), msg.sender, MINVALIDATORLOCKUP.mul(_myValidatorIndex).div(100).mul(99)); - state.burn(address(this), MINVALIDATORLOCKUP.mul(_myValidatorIndex).div(100)); - emit StepDownAsValidator(msg.sender, validatorIndex[msg.sender]); - } - - function voteOracle(address _oracleAddress) public onlyValidator { - require(validatorJoinedAtTime[msg.sender].add(VALIDATORWARMUPPERIOD) < now, "MorpherGovernance: Validator was just appointed and is not eligible to vote yet."); - require(lastValidatorJoined.add(VALIDATORWARMUPPERIOD) < now, "MorpherGovernance: New validator joined the board recently, please wait for the end of the warm up period."); - oracleVote[msg.sender] = _oracleAddress; - // Count Oracle Votes - (address _votedOracleAddress, uint256 _votes) = countOracleVote(); - emit ElectedOracle(_votedOracleAddress, _votes); - } - - function voteAdministrator(address _administratorAddress) public onlyValidator { - require(validatorJoinedAtTime[msg.sender].add(VALIDATORWARMUPPERIOD) < now, "MorpherGovernance: Validator was just appointed and is not eligible to vote yet."); - require(lastValidatorJoined.add(VALIDATORWARMUPPERIOD) < now, "MorpherGovernance: New validator joined the board recently, please wait for the end of the warm up period."); - administratorVote[msg.sender] = _administratorAddress; - // Count Administrator Votes - (address _appointedAdministrator, uint256 _votes) = countAdministratorVote(); - emit ElectedAdministrator(_appointedAdministrator, _votes); - } - - function countOracleVote() public returns (address _votedOracleAddress, uint256 _votes) { - // Count oracle votes - for (uint256 i = 1; i <= numberOfValidators; i++) { - countVotes[oracleVote[validatorAddress[i]]]++; - if (countVotes[oracleVote[validatorAddress[i]]] > _votes) { - _votes = countVotes[oracleVote[validatorAddress[i]]]; - _votedOracleAddress = oracleVote[validatorAddress[i]]; - } - } - // Evaluate: Simple majority of Validators resets oracleAddress - if (_votes > numberOfValidators.div(2)) { - setOracle(_votedOracleAddress); - } - for (uint256 i = 1; i <= numberOfValidators; i++) { - countVotes[administratorVote[validatorAddress[i]]] = 0; - } - return(_votedOracleAddress, _votes); - } - - function countAdministratorVote() public returns (address _appointedAdministrator, uint256 _votes) { - // Count Administrator votes - for (uint256 i=1; i<=numberOfValidators; i++) { - countVotes[administratorVote[validatorAddress[i]]]++; - if (countVotes[administratorVote[validatorAddress[i]]] > _votes) { - _votes = countVotes[administratorVote[validatorAddress[i]]]; - _appointedAdministrator = administratorVote[validatorAddress[i]]; - } - } - // Evaluate: Simple majority of Validators resets administratorAddress - if (_votes > numberOfValidators / 2) { - setAdministrator(_appointedAdministrator); - } - for (uint256 i = 1; i <= numberOfValidators; i++) { - countVotes[administratorVote[validatorAddress[i]]] = 0; - } - return(_appointedAdministrator, _votes); - } -} diff --git a/contracts/MorpherMintingLimiter.sol b/contracts/MorpherMintingLimiter.sol deleted file mode 100644 index 22f5c82..0000000 --- a/contracts/MorpherMintingLimiter.sol +++ /dev/null @@ -1,106 +0,0 @@ -pragma solidity 0.5.16; - -import "./MorpherState.sol"; -import "./MorpherTradeEngine.sol"; -import "./SafeMath.sol"; - -contract MorpherMintingLimiter { - using SafeMath for uint256; - - uint256 public mintingLimitPerUser; - uint256 public mintingLimitDaily; - uint256 public timeLockingPeriod; - - mapping(address => uint256) public escrowedTokens; - mapping(address => uint256) public lockedUntil; - mapping(uint256 => uint256) public dailyMintedTokens; - - address tradeEngineAddress; - MorpherState state; - - event MintingEscrowed(address _user, uint256 _tokenAmount); - event EscrowReleased(address _user, uint256 _tokenAmount); - event MintingDenied(address _user, uint256 _tokenAmount); - event MintingLimitUpdatedPerUser(uint256 _mintingLimitOld, uint256 _mintingLimitNew); - event MintingLimitUpdatedDaily(uint256 _mintingLimitOld, uint256 _mintingLimitNew); - event TimeLockPeriodUpdated(uint256 _timeLockPeriodOld, uint256 _timeLockPeriodNew); - event TradeEngineAddressSet(address _tradeEngineAddress); - event DailyMintedTokensReset(); - - modifier onlyTradeEngine() { - require(msg.sender == tradeEngineAddress, "MorpherMintingLimiter: Only Trade Engine is allowed to call this function"); - _; - } - - modifier onlyAdministrator() { - require(msg.sender == state.getAdministrator(), "MorpherMintingLimiter: Only Administrator can call this function"); - _; - } - - constructor(address _stateAddress, uint256 _mintingLimitPerUser, uint256 _mintingLimitDaily, uint256 _timeLockingPeriodInSeconds) public { - state = MorpherState(_stateAddress); - mintingLimitPerUser = _mintingLimitPerUser; - mintingLimitDaily = _mintingLimitDaily; - timeLockingPeriod = _timeLockingPeriodInSeconds; - } - - function setTradeEngineAddress(address _tradeEngineAddress) public onlyAdministrator { - emit TradeEngineAddressSet(_tradeEngineAddress); - tradeEngineAddress = _tradeEngineAddress; - } - - - function setMintingLimitDaily(uint256 _newMintingLimit) public onlyAdministrator { - emit MintingLimitUpdatedDaily(mintingLimitDaily, _newMintingLimit); - mintingLimitDaily = _newMintingLimit; - } - function setMintingLimitPerUser(uint256 _newMintingLimit) public onlyAdministrator { - emit MintingLimitUpdatedPerUser(mintingLimitDaily, _newMintingLimit); - mintingLimitPerUser = _newMintingLimit; - } - - function setTimeLockingPeriod(uint256 _newTimeLockingPeriodInSeconds) public onlyAdministrator { - emit TimeLockPeriodUpdated(timeLockingPeriod, _newTimeLockingPeriodInSeconds); - timeLockingPeriod = _newTimeLockingPeriodInSeconds; - } - - function mint(address _user, uint256 _tokenAmount) public onlyTradeEngine { - uint256 mintingDay = block.timestamp / 1 days; - if((mintingLimitDaily == 0 || dailyMintedTokens[mintingDay].add(_tokenAmount) <= mintingLimitDaily) && (mintingLimitPerUser == 0 || _tokenAmount <= mintingLimitPerUser )) { - state.mint(_user, _tokenAmount); - dailyMintedTokens[mintingDay] = dailyMintedTokens[mintingDay].add(_tokenAmount); - } else { - escrowedTokens[_user] = escrowedTokens[_user].add(_tokenAmount); - lockedUntil[_user] = block.timestamp + timeLockingPeriod; - emit MintingEscrowed(_user, _tokenAmount); - } - } - - function delayedMint(address _user) public { - require(lockedUntil[_user] <= block.timestamp, "MorpherMintingLimiter: Funds are still time locked"); - uint256 sendAmount = escrowedTokens[_user]; - escrowedTokens[_user] = 0; - state.mint(_user, sendAmount); - emit EscrowReleased(_user, sendAmount); - } - - function adminApprovedMint(address _user, uint256 _tokenAmount) public onlyAdministrator { - escrowedTokens[_user] = escrowedTokens[_user].sub(_tokenAmount); - state.mint(_user, _tokenAmount); - emit EscrowReleased(_user, _tokenAmount); - } - - function adminDisapproveMint(address _user, uint256 _tokenAmount) public onlyAdministrator { - escrowedTokens[_user] = escrowedTokens[_user].sub(_tokenAmount); - emit MintingDenied(_user, _tokenAmount); - } - - function resetDailyMintedTokens() public onlyAdministrator { - dailyMintedTokens[block.timestamp / 1 days] = 0; - emit DailyMintedTokensReset(); - } - - function getDailyMintedTokens() public view returns(uint256) { - return dailyMintedTokens[block.timestamp / 1 days]; - } -} \ No newline at end of file diff --git a/contracts/MorpherOracle.sol b/contracts/MorpherOracle.sol deleted file mode 100644 index 8d8baf2..0000000 --- a/contracts/MorpherOracle.sol +++ /dev/null @@ -1,694 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./MorpherTradeEngine.sol"; -import "./MorpherState.sol"; -import "./SafeMath.sol"; - -// ---------------------------------------------------------------------------------- -// Morpher Oracle contract v 2.0 -// The oracle initates a new trade by calling trade engine and requesting a new orderId. -// An event is fired by the contract notifying the oracle operator to query a price/liquidation unchecked -// for a market/user and return the information via the callback function. Since calling -// the callback function requires gas, the user must send a fixed amount of Ether when -// creating their order. -// ---------------------------------------------------------------------------------- - -contract MorpherOracle is Ownable { - - MorpherTradeEngine tradeEngine; - MorpherState state; // read only, Oracle doesn't need writing access to state - - using SafeMath for uint256; - - bool public paused; - bool public useWhiteList; //always false at the moment - - uint256 public gasForCallback; - - address payable public callBackCollectionAddress; - - mapping(address => bool) public callBackAddress; - mapping(address => bool) public whiteList; - - mapping(bytes32 => uint256) public priceBelow; - mapping(bytes32 => uint256) public priceAbove; - mapping(bytes32 => uint256) public goodFrom; - mapping(bytes32 => uint256) public goodUntil; - - mapping(bytes32 => bool) public orderCancellationRequested; - - mapping(bytes32 => address) public orderIdTradeEngineAddress; - address public previousTradeEngineAddress; - address public skipPreviousTradeEngineAddress; //skips a trade engine address, e.g. typos - address public previousOracleAddress; - -// ---------------------------------------------------------------------------------- -// Events -// ---------------------------------------------------------------------------------- - event OrderCreated( - bytes32 indexed _orderId, - address indexed _address, - bytes32 indexed _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage, - uint256 _onlyIfPriceBelow, - uint256 _onlyIfPriceAbove, - uint256 _goodFrom, - uint256 _goodUntil - ); - - event LiquidationOrderCreated( - bytes32 indexed _orderId, - address _sender, - address indexed _address, - bytes32 indexed _marketId - - ); - - event OrderProcessed( - bytes32 indexed _orderId, - uint256 _price, - uint256 _unadjustedMarketPrice, - uint256 _spread, - uint256 _positionLiquidationTimestamp, - uint256 _timeStamp, - uint256 _newLongShares, - uint256 _newShortShares, - uint256 _newMeanEntry, - uint256 _newMeanSprad, - uint256 _newMeanLeverage, - uint256 _liquidationPrice - ); - - event OrderFailed( - bytes32 indexed _orderId, - address indexed _address, - bytes32 indexed _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage, - uint256 _onlyIfPriceBelow, - uint256 _onlyIfPriceAbove, - uint256 _goodFrom, - uint256 _goodUntil - ); - - event OrderCancelled( - bytes32 indexed _orderId, - address indexed _sender, - address indexed _oracleAddress - ); - - event AdminOrderCancelled( - bytes32 indexed _orderId, - address indexed _sender, - address indexed _oracleAddress - ); - - event OrderCancellationRequestedEvent( - bytes32 indexed _orderId, - address indexed _sender - ); - - event CallbackAddressEnabled( - address indexed _address - ); - - event CallbackAddressDisabled( - address indexed _address - ); - - event OraclePaused( - bool _paused - ); - - event CallBackCollectionAddressChange( - address _address - ); - - event SetGasForCallback( - uint256 _gasForCallback - ); - - event LinkTradeEngine( - address _address - ); - - event LinkMorpherState( - address _address - ); - - event SetUseWhiteList( - bool _useWhiteList - ); - - event AddressWhiteListed( - address _address - ); - - event AddressBlackListed( - address _address - ); - - event AdminLiquidationOrderCreated( - bytes32 indexed _orderId, - address indexed _address, - bytes32 indexed _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage - ); - - /** - * Delisting markets is a function that stops when gas is running low - * if it reached all positions it will emit "DelistMarketComplete" - * otherwise it needs to be re-run. - */ - event DelistMarketIncomplete(bytes32 _marketId, uint256 _processedUntilIndex); - event DelistMarketComplete(bytes32 _marketId); - event LockedPriceForClosingPositions(bytes32 _marketId, uint256 _price); - - - event FallbackOracleUpdated(address _oldFallbackOracle, address _newFallbackOracle); - event FallbackTradeEngineUpdated(address _oldFallbackTradeEngine, address _newFallbackTradeEngine); - event UpdateSkipPreviousTradeEngineAddress(address _oldAddress, address _newAddress); - - modifier onlyOracleOperator { - require(isCallbackAddress(msg.sender), "MorpherOracle: Only the oracle operator can call this function."); - _; - } - - modifier onlyAdministrator { - require(msg.sender == state.getAdministrator(), "Function can only be called by the Administrator."); - _; - } - - modifier notPaused { - require(paused == false, "MorpherOracle: Oracle paused, aborting"); - _; - } - - constructor(address _tradeEngineAddress, address _morpherState, address _callBackAddress, address payable _gasCollectionAddress, uint256 _gasForCallback, address _coldStorageOwnerAddress, address _previousTradeEngineAddress, address _previousOracleAddress) public { - setTradeEngineAddress(_tradeEngineAddress); - setStateAddress(_morpherState); - enableCallbackAddress(_callBackAddress); - setCallbackCollectionAddress(_gasCollectionAddress); - setGasForCallback(_gasForCallback); - transferOwnership(_coldStorageOwnerAddress); - previousTradeEngineAddress = _previousTradeEngineAddress; //that is the address before updating the trade engine. Can set to 0x0000 if a completely new deployment happens. It is only valid when mid-term updating the tradeengine - previousOracleAddress = _previousOracleAddress; //if we are updating the oracle, then this is the previous oracle address. Can be set to 0x00 if a completely new deployment happens. - } - -// ---------------------------------------------------------------------------------- -// Setter/getter functions for trade engine address, oracle operator (callback) address, -// and prepaid gas limit for callback function -// ---------------------------------------------------------------------------------- - function setTradeEngineAddress(address _address) public onlyOwner { - tradeEngine = MorpherTradeEngine(_address); - emit LinkTradeEngine(_address); - } - - function setStateAddress(address _address) public onlyOwner { - state = MorpherState(_address); - emit LinkMorpherState(_address); - } - - function overrideGasForCallback(uint256 _gasForCallback) public onlyOwner { - gasForCallback = _gasForCallback; - emit SetGasForCallback(_gasForCallback); - } - - function setGasForCallback(uint256 _gasForCallback) private { - gasForCallback = _gasForCallback; - emit SetGasForCallback(_gasForCallback); - } - - function enableCallbackAddress(address _address) public onlyOwner { - callBackAddress[_address] = true; - emit CallbackAddressEnabled(_address); - } - - function disableCallbackAddress(address _address) public onlyOwner { - callBackAddress[_address] = false; - emit CallbackAddressDisabled(_address); - } - - function isCallbackAddress(address _address) public view returns (bool _isCallBackAddress) { - return callBackAddress[_address]; - } - - function setCallbackCollectionAddress(address payable _address) public onlyOwner { - callBackCollectionAddress = _address; - emit CallBackCollectionAddressChange(_address); - } - - function getAdministrator() public view returns(address _administrator) { - return state.getAdministrator(); - } - -// ---------------------------------------------------------------------------------- -// Oracle Owner can use a whitelist and authorize individual addresses -// ---------------------------------------------------------------------------------- - function setUseWhiteList(bool _useWhiteList) public onlyOracleOperator { - require(false, "MorpherOracle: Cannot use this functionality in the oracle at the moment"); - useWhiteList = _useWhiteList; - emit SetUseWhiteList(_useWhiteList); - } - - function setWhiteList(address _whiteList) public onlyOracleOperator { - whiteList[_whiteList] = true; - emit AddressWhiteListed(_whiteList); - } - - function setBlackList(address _blackList) public onlyOracleOperator { - whiteList[_blackList] = false; - emit AddressBlackListed(_blackList); - } - - function isWhiteListed(address _address) public view returns (bool _whiteListed) { - if (useWhiteList == false || whiteList[_address] == true) { - _whiteListed = true; - } - return(_whiteListed); - } - -// ---------------------------------------------------------------------------------- -// emitOrderFailed -// Can be called by Oracle Operator to notifiy user of failed order -// ---------------------------------------------------------------------------------- - function emitOrderFailed( - bytes32 _orderId, - address _address, - bytes32 _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage, - uint256 _onlyIfPriceBelow, - uint256 _onlyIfPriceAbove, - uint256 _goodFrom, - uint256 _goodUntil - ) public onlyOracleOperator { - emit OrderFailed( - _orderId, - _address, - _marketId, - _closeSharesAmount, - _openMPHTokenAmount, - _tradeDirection, - _orderLeverage, - _onlyIfPriceBelow, - _onlyIfPriceAbove, - _goodFrom, - _goodUntil); - } - -// ---------------------------------------------------------------------------------- -// createOrder(bytes32 _marketId, bool _tradeAmountGivenInShares, uint256 _tradeAmount, bool _tradeDirection, uint256 _orderLeverage) -// Request a new orderId from trade engine and fires event for price/liquidation check request. -// ---------------------------------------------------------------------------------- - function createOrder( - bytes32 _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage, - uint256 _onlyIfPriceAbove, - uint256 _onlyIfPriceBelow, - uint256 _goodUntil, - uint256 _goodFrom - ) public payable notPaused returns (bytes32 _orderId) { - require(isWhiteListed(msg.sender),"MorpherOracle: Address not eligible to create an order."); - if (gasForCallback > 0) { - require(msg.value >= gasForCallback, "MorpherOracle: Must transfer gas costs for Oracle Callback function."); - callBackCollectionAddress.transfer(msg.value); - } - _orderId = tradeEngine.requestOrderId(msg.sender, _marketId, _closeSharesAmount, _openMPHTokenAmount, _tradeDirection, _orderLeverage); - orderIdTradeEngineAddress[_orderId] = address(tradeEngine); - - //if the market was deactivated, and the trader didn't fail yet, then we got an orderId to close the position with a locked in price - if(state.getMarketActive(_marketId) == false) { - - //price will come from the position where price is stored forever - tradeEngine.processOrder(_orderId, tradeEngine.getDeactivatedMarketPrice(_marketId), 0, 0, now.mul(1000)); - - emit OrderProcessed( - _orderId, - tradeEngine.getDeactivatedMarketPrice(_marketId), - 0, - 0, - 0, - now.mul(1000), - 0, - 0, - 0, - 0, - 0, - 0 - ); - } else { - priceAbove[_orderId] = _onlyIfPriceAbove; - priceBelow[_orderId] = _onlyIfPriceBelow; - goodFrom[_orderId] = _goodFrom; - goodUntil[_orderId] = _goodUntil; - emit OrderCreated( - _orderId, - msg.sender, - _marketId, - _closeSharesAmount, - _openMPHTokenAmount, - _tradeDirection, - _orderLeverage, - _onlyIfPriceBelow, - _onlyIfPriceAbove, - _goodFrom, - _goodUntil - ); - } - - return _orderId; - } - - function getTradeEngineFromOrderId(bytes32 _orderId) public view returns (address) { - //get the current trade engine - if(orderIdTradeEngineAddress[_orderId] != address(0)){ - return orderIdTradeEngineAddress[_orderId]; - } - - if(previousOracleAddress != address(0)) { - MorpherOracle _oracle = MorpherOracle(previousOracleAddress); - address _previousTradeEngine = _oracle.getTradeEngineFromOrderId(_orderId); - if(_previousTradeEngine != skipPreviousTradeEngineAddress) { //fixing a typo - return _previousTradeEngine; - } - } - - //nothing in there, take the previous tradeEngine then. - return previousTradeEngineAddress; - } - - function updateSkipTradeEngineAddress(address _skipTradeEngineAddress) public onlyAdministrator { - emit UpdateSkipPreviousTradeEngineAddress(skipPreviousTradeEngineAddress, _skipTradeEngineAddress); - skipPreviousTradeEngineAddress = _skipTradeEngineAddress; - } - - - function updateFallbackTradeEngineAddress(address _tradeEngineFallbackAddress) public onlyAdministrator { - emit FallbackTradeEngineUpdated(previousTradeEngineAddress, _tradeEngineFallbackAddress); - previousTradeEngineAddress = _tradeEngineFallbackAddress; - } - - - function updateFallbackOracleAddress(address _oracleFallbackAddress) public onlyAdministrator { - emit FallbackOracleUpdated(previousOracleAddress, _oracleFallbackAddress); - previousOracleAddress = _oracleFallbackAddress; - } - - function initiateCancelOrder(bytes32 _orderId) public { - MorpherTradeEngine _tradeEngine = MorpherTradeEngine(getTradeEngineFromOrderId(_orderId)); - require(orderCancellationRequested[_orderId] == false, "MorpherOracle: Order was already canceled."); - (address userId, , , , , , ) = _tradeEngine.getOrder(_orderId); - require(userId == msg.sender, "MorpherOracle: Only the user can request an order cancellation."); - orderCancellationRequested[_orderId] = true; - emit OrderCancellationRequestedEvent(_orderId, msg.sender); - - } - // ---------------------------------------------------------------------------------- - // cancelOrder(bytes32 _orderId) - // User or Administrator can cancel their own orders before the _callback has been executed - // ---------------------------------------------------------------------------------- - function cancelOrder(bytes32 _orderId) public onlyOracleOperator { - require(orderCancellationRequested[_orderId] == true, "MorpherOracle: Order-Cancellation was not requested."); - MorpherTradeEngine _tradeEngine = MorpherTradeEngine(getTradeEngineFromOrderId(_orderId)); - (address userId, , , , , , ) = _tradeEngine.getOrder(_orderId); - _tradeEngine.cancelOrder(_orderId, userId); - clearOrderConditions(_orderId); - emit OrderCancelled( - _orderId, - userId, - msg.sender - ); - } - - // ---------------------------------------------------------------------------------- - // adminCancelOrder(bytes32 _orderId) - // Administrator can cancel before the _callback has been executed to provide an updateOrder functionality - // ---------------------------------------------------------------------------------- - function adminCancelOrder(bytes32 _orderId) public onlyOracleOperator { - MorpherTradeEngine _tradeEngine = MorpherTradeEngine(getTradeEngineFromOrderId(_orderId)); - (address userId, , , , , , ) = _tradeEngine.getOrder(_orderId); - _tradeEngine.cancelOrder(_orderId, userId); - clearOrderConditions(_orderId); - emit AdminOrderCancelled( - _orderId, - userId, - msg.sender - ); - } - - function getGoodUntil(bytes32 _orderId) public view returns(uint) { - if(goodUntil[_orderId] > 0) { - return goodUntil[_orderId]; - } - - //just return the old one - if(previousOracleAddress != address(0)) { - MorpherOracle _oldOracle = MorpherOracle(previousOracleAddress); - return _oldOracle.goodUntil(_orderId); - } - - return 0; - } - function getGoodFrom(bytes32 _orderId) public view returns(uint) { - if(goodFrom[_orderId] > 0) { - return goodFrom[_orderId]; - } - - //just return the old one - if(previousOracleAddress != address(0)) { - MorpherOracle _oldOracle = MorpherOracle(previousOracleAddress); - return _oldOracle.goodFrom(_orderId); - } - return 0; - } - function getPriceAbove(bytes32 _orderId) public view returns(uint) { - if(priceAbove[_orderId] > 0) { - return priceAbove[_orderId]; - } - - //just return the old one - if(previousOracleAddress != address(0)) { - MorpherOracle _oldOracle = MorpherOracle(previousOracleAddress); - return _oldOracle.priceAbove(_orderId); - } - return 0; - } - function getPriceBelow(bytes32 _orderId) public view returns(uint) { - if(priceBelow[_orderId] > 0) { - return priceBelow[_orderId]; - } - - //just return the old one - if(previousOracleAddress != address(0)) { - MorpherOracle _oldOracle = MorpherOracle(previousOracleAddress); - return _oldOracle.priceBelow(_orderId); - } - return 0; - } - -// ------------------------------------------------------------------------ -// checkOrderConditions(bytes32 _orderId, uint256 _price) -// Checks if callback satisfies the order conditions -// ------------------------------------------------------------------------ - function checkOrderConditions(bytes32 _orderId, uint256 _price) public view returns (bool _conditionsMet) { - _conditionsMet = true; - if (now > getGoodUntil(_orderId) && getGoodUntil(_orderId) > 0) { - _conditionsMet = false; - } - if (now < getGoodFrom(_orderId) && getGoodFrom(_orderId) > 0) { - _conditionsMet = false; - } - - if(getPriceAbove(_orderId) > 0 && getPriceBelow(_orderId) > 0) { - if(_price < getPriceAbove(_orderId) && _price > getPriceBelow(_orderId)) { - _conditionsMet = false; - } - } else { - if (_price < getPriceAbove(_orderId) && getPriceAbove(_orderId) > 0) { - _conditionsMet = false; - } - if (_price > getPriceBelow(_orderId) && getPriceBelow(_orderId) > 0) { - _conditionsMet = false; - } - } - - return _conditionsMet; - } - -// ---------------------------------------------------------------------------------- -// Deletes parameters of cancelled or processed orders -// ---------------------------------------------------------------------------------- - function clearOrderConditions(bytes32 _orderId) internal { - priceAbove[_orderId] = 0; - priceBelow[_orderId] = 0; - goodFrom[_orderId] = 0; - goodUntil[_orderId] = 0; - } - -// ---------------------------------------------------------------------------------- -// Pausing/unpausing the Oracle contract -// ---------------------------------------------------------------------------------- - function pauseOracle() public onlyOwner { - paused = true; - emit OraclePaused(true); - } - - function unpauseOracle() public onlyOwner { - paused = false; - emit OraclePaused(false); - } - -// ---------------------------------------------------------------------------------- -// createLiquidationOrder(address _address, bytes32 _marketId) -// Checks if position has been liquidated since last check. Requires gas for callback -// function. Anyone can issue a liquidation order for any other address and market. -// ---------------------------------------------------------------------------------- - function createLiquidationOrder( - address _address, - bytes32 _marketId - ) public notPaused onlyOracleOperator payable returns (bytes32 _orderId) { - if (gasForCallback > 0) { - require(msg.value >= gasForCallback, "MorpherOracle: Must transfer gas costs for Oracle Callback function."); - callBackCollectionAddress.transfer(msg.value); - } - _orderId = tradeEngine.requestOrderId(_address, _marketId, 0, 0, true, 10**8); - orderIdTradeEngineAddress[_orderId] = address(tradeEngine); - emit LiquidationOrderCreated(_orderId, msg.sender, _address, _marketId); - return _orderId; - } - -// ---------------------------------------------------------------------------------- -// __callback(bytes32 _orderId, uint256 _price, uint256 _spread, uint256 _liquidationTimestamp, uint256 _timeStamp) -// Called by the oracle operator. Writes price/spread/liquidiation check to the blockchain. -// Trade engine processes the order and updates the portfolio in state if successful. -// ---------------------------------------------------------------------------------- - function __callback( - bytes32 _orderId, - uint256 _price, - uint256 _unadjustedMarketPrice, - uint256 _spread, - uint256 _liquidationTimestamp, - uint256 _timeStamp, - uint256 _gasForNextCallback - ) public onlyOracleOperator notPaused returns (uint256 _newLongShares, uint256 _newShortShares, uint256 _newMeanEntry, uint256 _newMeanSpread, uint256 _newMeanLeverage, uint256 _liquidationPrice) { - - require(checkOrderConditions(_orderId, _price), 'MorpherOracle Error: Order Conditions are not met'); - - MorpherTradeEngine _tradeEngine = MorpherTradeEngine(getTradeEngineFromOrderId(_orderId)); - ( - _newLongShares, - _newShortShares, - _newMeanEntry, - _newMeanSpread, - _newMeanLeverage, - _liquidationPrice - ) = _tradeEngine.processOrder(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp); - - clearOrderConditions(_orderId); - emit OrderProcessed( - _orderId, - _price, - _unadjustedMarketPrice, - _spread, - _liquidationTimestamp, - _timeStamp, - _newLongShares, - _newShortShares, - _newMeanEntry, - _newMeanSpread, - _newMeanLeverage, - _liquidationPrice - ); - setGasForCallback(_gasForNextCallback); - return (_newLongShares, _newShortShares, _newMeanEntry, _newMeanSpread, _newMeanLeverage, _liquidationPrice); - } - -// ---------------------------------------------------------------------------------- -// delistMarket(bytes32 _marketId) -// Administrator closes out all existing positions on _marketId market at current prices -// ---------------------------------------------------------------------------------- - - uint delistMarketFromIx = 0; - function delistMarket(bytes32 _marketId, bool _startFromScratch) public onlyAdministrator { - require(state.getMarketActive(_marketId) == true, "Market must be active to process position liquidations."); - // If no _fromIx and _toIx specified, do entire _list - if (_startFromScratch) { - delistMarketFromIx = 0; - } - - uint _toIx = state.getMaxMappingIndex(_marketId); - - address _address; - for (uint256 i = delistMarketFromIx; i <= _toIx; i++) { - if(gasleft() < 250000 && i != _toIx) { //stop if there's not enough gas to write the next transaction - delistMarketFromIx = i; - emit DelistMarketIncomplete(_marketId, _toIx); - return; - } - - _address = state.getExposureMappingAddress(_marketId, i); - adminLiquidationOrder(_address, _marketId); - - } - emit DelistMarketComplete(_marketId); - } - - /** - * Course of action would be: - * 1. de-activate market through state - * 2. set the Deactivated Market Price - * 3. let users still close their positions - */ - function setDeactivatedMarketPrice(bytes32 _marketId, uint256 _price) public onlyAdministrator { - //todo updateable tradeEngine - tradeEngine.setDeactivatedMarketPrice(_marketId, _price); - emit LockedPriceForClosingPositions(_marketId, _price); - - } - -// ---------------------------------------------------------------------------------- -// adminLiquidationOrder(address _address, bytes32 _marketId) -// Administrator closes out an existing position of _address on _marketId market at current price -// ---------------------------------------------------------------------------------- - function adminLiquidationOrder( - address _address, - bytes32 _marketId - ) public onlyAdministrator returns (bytes32 _orderId) { - uint256 _positionLongShares = state.getLongShares(_address, _marketId); - uint256 _positionShortShares = state.getShortShares(_address, _marketId); - if (_positionLongShares > 0) { - _orderId = tradeEngine.requestOrderId(_address, _marketId, _positionLongShares, 0, false, 10**8); - emit AdminLiquidationOrderCreated(_orderId, _address, _marketId, _positionLongShares, 0, false, 10**8); - } - if (_positionShortShares > 0) { - _orderId = tradeEngine.requestOrderId(_address, _marketId, _positionShortShares, 0, true, 10**8); - emit AdminLiquidationOrderCreated(_orderId, _address, _marketId, _positionShortShares, 0, true, 10**8); - } - orderIdTradeEngineAddress[_orderId] = address(tradeEngine); - return _orderId; - } - -// ---------------------------------------------------------------------------------- -// Auxiliary function to hash a string market name i.e. -// "CRYPTO_BTC" => 0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9; -// ---------------------------------------------------------------------------------- - function stringToHash(string memory _source) public pure returns (bytes32 _result) { - return keccak256(abi.encodePacked(_source)); - } -} - diff --git a/contracts/MorpherStaking.sol b/contracts/MorpherStaking.sol deleted file mode 100644 index ce95d7c..0000000 --- a/contracts/MorpherStaking.sol +++ /dev/null @@ -1,289 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./IMorpherState.sol"; -import "./MorpherUserBlocking.sol"; - -// ---------------------------------------------------------------------------------- -// Staking Morpher Token generates interest -// The interest is set to 0.015% a day or ~5.475% in the first year -// Stakers will be able to vote on all ProtocolDecisions in MorpherGovernance (soon...) -// There is a lockup after staking or topping up (30 days) and a minimum stake (100k MPH) -// ---------------------------------------------------------------------------------- - -contract MorpherStaking is Ownable { - using SafeMath for uint256; - IMorpherState state; - - MorpherUserBlocking userBlocking; - - uint256 constant PRECISION = 10**8; - uint256 constant INTERVAL = 1 days; - - //mapping(address => uint256) private poolShares; - //mapping(address => uint256) private lockup; - - uint256 public poolShareValue = PRECISION; - uint256 public lastReward; - uint256 public totalShares; - //uint256 public interestRate = 15000; // 0.015% per day initially, diminishing returns over time - struct InterestRate { - uint256 validFrom; - uint256 rate; - } - - mapping(uint256 => InterestRate) public interestRates; - uint256 public numInterestRates; - - uint256 public lockupPeriod = 30 days; // to prevent tactical staking and ensure smooth governance - uint256 public minimumStake = 10**23; // 100k MPH minimum - - address public stakingAdmin; - - address public stakingAddress = 0x2222222222222222222222222222222222222222; - bytes32 public marketIdStakingMPH = 0x9a31fdde7a3b1444b1befb10735dcc3b72cbd9dd604d2ff45144352bf0f359a6; //STAKING_MPH - -// ---------------------------------------------------------------------------- -// Events -// ---------------------------------------------------------------------------- - event SetInterestRate(uint256 newInterestRate); - event InterestRateAdded(uint256 interestRate, uint256 validFromTimestamp); - event InterestRateRateChanged(uint256 interstRateIndex, uint256 oldvalue, uint256 newValue); - event InterestRateValidFromChanged(uint256 interstRateIndex, uint256 oldvalue, uint256 newValue); - event SetLockupPeriod(uint256 newLockupPeriod); - event SetMinimumStake(uint256 newMinimumStake); - event LinkState(address stateAddress); - event SetStakingAdmin(address stakingAdmin); - event SetMorpherUserBlocking(address userBlockingAddress); - - event PoolShareValueUpdated(uint256 indexed lastReward, uint256 poolShareValue); - event StakingRewardsMinted(uint256 indexed lastReward, uint256 delta); - event Staked(address indexed userAddress, uint256 indexed amount, uint256 poolShares, uint256 lockedUntil); - event Unstaked(address indexed userAddress, uint256 indexed amount, uint256 poolShares); - - modifier onlyStakingAdmin { - require(msg.sender == stakingAdmin, "MorpherStaking: can only be called by Staking Administrator."); - _; - } - - modifier userNotBlocked { - require(!userBlocking.userIsBlocked(msg.sender), "MorpherStaking: User is blocked"); - _; - } - - constructor(address _morpherState, address _stakingAdmin, address _userBlockingAddress) public { - setStakingAdmin(_stakingAdmin); - setMorpherStateAddress(_morpherState); - emit SetLockupPeriod(lockupPeriod); - emit SetMinimumStake(minimumStake); - setMorpherUserBlocking(_userBlockingAddress); - addInterestRate(15000,1617094819); //setting the initial interest rate to the trade engine deployed timestamp - lastReward = block.timestamp; - // missing: transferOwnership to Governance once deployed - } - -// ---------------------------------------------------------------------------- -// updatePoolShareValue -// Updates the value of the Pool Shares and returns the new value. -// Staking rewards are linear, there is no compound interest. -// ---------------------------------------------------------------------------- - - function updatePoolShareValue() public returns (uint256 _newPoolShareValue) { - if (now >= lastReward.add(INTERVAL)) { - uint256 _numOfIntervals = now.sub(lastReward).div(INTERVAL); - poolShareValue = poolShareValue.add(_numOfIntervals.mul(interestRate())); - lastReward = lastReward.add(_numOfIntervals.mul(INTERVAL)); - emit PoolShareValueUpdated(lastReward, poolShareValue); - } - mintStakingRewards(); - return poolShareValue; - } - -// ---------------------------------------------------------------------------- -// Staking rewards are minted if necessary -// ---------------------------------------------------------------------------- - - function mintStakingRewards() private { - uint256 _targetBalance = poolShareValue.mul(totalShares); - if (state.balanceOf(stakingAddress) < _targetBalance) { - // If there are not enough token held by the contract, mint them - uint256 _delta = _targetBalance.sub(state.balanceOf(stakingAddress)); - state.mint(stakingAddress, _delta); - emit StakingRewardsMinted(lastReward, _delta); - } - } - -// ---------------------------------------------------------------------------- -// stake(uint256 _amount) -// User specifies an amount they intend to stake. Pool Shares are issued accordingly -// and the _amount is transferred to the staking contract -// ---------------------------------------------------------------------------- - - function stake(uint256 _amount) public userNotBlocked returns (uint256 _poolShares) { - require(state.balanceOf(msg.sender) >= _amount, "MorpherStaking: insufficient MPH token balance"); - updatePoolShareValue(); - _poolShares = _amount.div(poolShareValue); - (uint256 _numOfShares, , , , , ) = state.getPosition(msg.sender, marketIdStakingMPH); - require(minimumStake <= _numOfShares.add(_poolShares).mul(poolShareValue), "MorpherStaking: stake amount lower than minimum stake"); - state.transfer(msg.sender, stakingAddress, _poolShares.mul(poolShareValue)); - totalShares = totalShares.add(_poolShares); - state.setPosition(msg.sender, marketIdStakingMPH, now.add(lockupPeriod), _numOfShares.add(_poolShares), 0, 0, 0, 0, 0); - emit Staked(msg.sender, _amount, _poolShares, now.add(lockupPeriod)); - return _poolShares; - } - -// ---------------------------------------------------------------------------- -// unstake(uint256 _amount) -// User specifies number of Pool Shares they want to unstake. -// Pool Shares get deleted and the user receives their MPH plus interest -// ---------------------------------------------------------------------------- - - function unstake(uint256 _numOfShares) public userNotBlocked returns (uint256 _amount) { - (uint256 _numOfExistingShares, , , , , ) = state.getPosition(msg.sender, marketIdStakingMPH); - require(_numOfShares <= _numOfExistingShares, "MorpherStaking: insufficient pool shares"); - - uint256 lockedInUntil = state.getLastUpdated(msg.sender, marketIdStakingMPH); - require(now >= lockedInUntil, "MorpherStaking: cannot unstake before lockup expiration"); - updatePoolShareValue(); - state.setPosition(msg.sender, marketIdStakingMPH, lockedInUntil, _numOfExistingShares.sub(_numOfShares), 0, 0, 0, 0, 0); - totalShares = totalShares.sub(_numOfShares); - _amount = _numOfShares.mul(poolShareValue); - state.transfer(stakingAddress, msg.sender, _amount); - emit Unstaked(msg.sender, _amount, _numOfShares); - return _amount; - } - -// ---------------------------------------------------------------------------- -// Administrative functions -// ---------------------------------------------------------------------------- - - function setStakingAdmin(address _address) public onlyOwner { - stakingAdmin = _address; - emit SetStakingAdmin(_address); - } - - function setMorpherStateAddress(address _stateAddress) public onlyOwner { - state = IMorpherState(_stateAddress); - emit LinkState(_stateAddress); - } - - function setMorpherUserBlocking(address _userBlockingAddress) public onlyOwner { - userBlocking = MorpherUserBlocking(_userBlockingAddress); - emit SetMorpherUserBlocking(_userBlockingAddress); - } - - - /** - Interest rate - */ - function setInterestRate(uint256 _interestRate) public onlyStakingAdmin { - addInterestRate(_interestRate, block.timestamp); - } - -/** - fallback function in case the old tradeengine asks for the current interest rate - */ - function interestRate() public view returns (uint256) { - //start with the last one, as its most likely the last active one, no need to run through the whole map - for(uint256 i = numInterestRates - 1; i >= 0; i--) { - if(interestRates[i].validFrom <= block.timestamp) { - return interestRates[i].rate; - } - } - } - - function addInterestRate(uint _rate, uint _validFrom) public onlyStakingAdmin { - require(numInterestRates == 0 || interestRates[numInterestRates-1].validFrom < _validFrom, "MorpherStaking: Interest Rate Valid From must be later than last interestRate"); - //omitting rate sanity checks here. It should always be smaller than 100% (100000000) but I'll leave that to the common sense of the admin. - updatePoolShareValue(); - interestRates[numInterestRates].validFrom = _validFrom; - interestRates[numInterestRates].rate = _rate; - numInterestRates++; - emit InterestRateAdded(_rate, _validFrom); - } - - function changeInterestRateValue(uint256 _numInterestRate, uint256 _rate) public onlyStakingAdmin { - emit InterestRateRateChanged(_numInterestRate, interestRates[_numInterestRate].rate, _rate); - updatePoolShareValue(); - interestRates[_numInterestRate].rate = _rate; - } - function changeInterestRateValidFrom(uint256 _numInterestRate, uint256 _validFrom) public onlyStakingAdmin { - emit InterestRateValidFromChanged(_numInterestRate, interestRates[_numInterestRate].validFrom, _validFrom); - require(numInterestRates > _numInterestRate, "MorpherStaking: Interest Rate Does not exist!"); - require( - (_numInterestRate == 0 && numInterestRates-1 > 0 && interestRates[_numInterestRate+1].validFrom > _validFrom) || //we change the first one and there exist more than one - (_numInterestRate > 0 && _numInterestRate == numInterestRates-1 && interestRates[_numInterestRate - 1].validFrom < _validFrom) || //we changed the last one - (_numInterestRate > 0 && _numInterestRate < numInterestRates-1 && interestRates[_numInterestRate - 1].validFrom < _validFrom && interestRates[_numInterestRate + 1].validFrom > _validFrom), - "MorpherStaking: validFrom cannot be smaller than previous Interest Rate or larger than next Interest Rate" - ); - updatePoolShareValue(); - interestRates[_numInterestRate].validFrom = _validFrom; - } - - function getInterestRate(uint256 _positionTimestamp) public view returns(uint256) { - uint256 sumInterestRatesWeighted = 0; - uint256 startingTimestamp = 0; - - for(uint256 i = 0; i < numInterestRates; i++) { - if(i == numInterestRates-1 || interestRates[i+1].validFrom > block.timestamp) { - //reached last interest rate - sumInterestRatesWeighted = sumInterestRatesWeighted.add(interestRates[i].rate.mul(block.timestamp - interestRates[i].validFrom)); - if(startingTimestamp == 0) { - startingTimestamp = interestRates[i].validFrom; - } - break; //in case there are more in the future - } else { - //only take interest rates after the position was created - if(interestRates[i+1].validFrom > _positionTimestamp) { - sumInterestRatesWeighted = sumInterestRatesWeighted.add(interestRates[i].rate.mul(interestRates[i+1].validFrom - interestRates[i].validFrom)); - if(interestRates[i].validFrom <= _positionTimestamp) { - startingTimestamp = interestRates[i].validFrom; - } - } - } - } - return sumInterestRatesWeighted.div(block.timestamp - startingTimestamp); - - } - - function setLockupPeriodRate(uint256 _lockupPeriod) public onlyStakingAdmin { - lockupPeriod = _lockupPeriod; - emit SetLockupPeriod(_lockupPeriod); - } - - function setMinimumStake(uint256 _minimumStake) public onlyStakingAdmin { - minimumStake = _minimumStake; - emit SetMinimumStake(_minimumStake); - } - -// ---------------------------------------------------------------------------- -// Getter functions -// ---------------------------------------------------------------------------- - - function getTotalPooledValue() public view returns (uint256 _totalPooled) { - // Only accurate if poolShareValue is up to date - return poolShareValue.mul(totalShares); - } - - function getStake(address _address) public view returns (uint256 _poolShares) { - (uint256 _numOfShares, , , , , ) = state.getPosition(_address, marketIdStakingMPH); - return _numOfShares; - } - - function getStakeValue(address _address) public view returns(uint256 _value, uint256 _lastUpdate) { - // Only accurate if poolShareValue is up to date - - (uint256 _numOfShares, , , , , ) = state.getPosition(_address, marketIdStakingMPH); - - return (_numOfShares.mul(poolShareValue), lastReward); - } - -// ------------------------------------------------------------------------ -// Don't accept ETH -// ------------------------------------------------------------------------ - - function () external payable { - revert("MorpherStaking: you can't deposit Ether here"); - } -} diff --git a/contracts/MorpherState.sol b/contracts/MorpherState.sol deleted file mode 100644 index dc013c8..0000000 --- a/contracts/MorpherState.sol +++ /dev/null @@ -1,818 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./IMorpherToken.sol"; - -// ---------------------------------------------------------------------------------- -// Data and token balance storage of the Morpher platform -// Writing access is only granted to platform contracts. The contract can be paused -// by an elected platform administrator (see MorpherGovernance) to perform protocol updates. -// ---------------------------------------------------------------------------------- - -contract MorpherState is Ownable { - using SafeMath for uint256; - - bool public mainChain; - uint256 public totalSupply; - uint256 public totalToken; - uint256 public totalInPositions; - uint256 public totalOnOtherChain; - uint256 public maximumLeverage = 10**9; // Leverage precision is 1e8, maximum leverage set to 10 initially - uint256 constant PRECISION = 10**8; - uint256 constant DECIMALS = 18; - uint256 constant REWARDPERIOD = 1 days; - bool public paused = false; - - address public morpherGovernance; - address public morpherRewards; - address public administrator; - address public oracleContract; - address public sideChainOperator; - address public morpherBridge; - address public morpherToken; - - uint256 public rewardBasisPoints; - uint256 public lastRewardTime; - - bytes32 public sideChainMerkleRoot; - uint256 public sideChainMerkleRootWrittenAtTime; - - // Set initial withdraw limit from sidechain to 20m token or 2% of initial supply - uint256 public mainChainWithdrawLimit24 = 2 * 10**25; - - mapping(address => bool) private stateAccess; - mapping(address => bool) private transferAllowed; - - mapping(address => uint256) private balances; - mapping(address => mapping(address => uint256)) private allowed; - - mapping(bytes32 => bool) private marketActive; - - // ---------------------------------------------------------------------------- - // Position struct records virtual futures - // ---------------------------------------------------------------------------- - struct position { - uint256 lastUpdated; - uint256 longShares; - uint256 shortShares; - uint256 meanEntryPrice; - uint256 meanEntrySpread; - uint256 meanEntryLeverage; - uint256 liquidationPrice; - bytes32 positionHash; - } - - // ---------------------------------------------------------------------------- - // A portfolio is an address specific collection of postions - // ---------------------------------------------------------------------------- - mapping(address => mapping(bytes32 => position)) private portfolio; - - // ---------------------------------------------------------------------------- - // Record all addresses that hold a position of a market, needed for clean stock splits - // ---------------------------------------------------------------------------- - struct hasExposure { - uint256 maxMappingIndex; - mapping(address => uint256) index; - mapping(uint256 => address) addy; - } - - mapping(bytes32 => hasExposure) private exposureByMarket; - - // ---------------------------------------------------------------------------- - // Bridge Variables - // ---------------------------------------------------------------------------- - mapping (address => uint256) private tokenClaimedOnThisChain; - mapping (address => uint256) private tokenSentToLinkedChain; - mapping (address => uint256) private tokenSentToLinkedChainTime; - mapping (bytes32 => bool) private positionClaimedOnMainChain; - - uint256 public lastWithdrawLimitReductionTime; - uint256 public last24HoursAmountWithdrawn; - uint256 public withdrawLimit24Hours; - uint256 public inactivityPeriod = 3 days; - uint256 public transferNonce; - bool public fastTransfersEnabled; - - // ---------------------------------------------------------------------------- - // Sidechain spam protection - // ---------------------------------------------------------------------------- - - mapping(address => uint256) private lastRequestBlock; - mapping(address => uint256) private numberOfRequests; - uint256 public numberOfRequestsLimit; - - // ---------------------------------------------------------------------------- - // Events - // ---------------------------------------------------------------------------- - event StateAccessGranted(address indexed whiteList, uint256 indexed blockNumber); - event StateAccessDenied(address indexed blackList, uint256 indexed blockNumber); - - event TransfersEnabled(address indexed whiteList); - event TransfersDisabled(address indexed blackList); - - event Transfer(address indexed sender, address indexed recipient, uint256 amount); - event Mint(address indexed recipient, uint256 amount, uint256 totalToken); - event Burn(address indexed recipient, uint256 amount, uint256 totalToken); - event NewTotalSupply(uint256 newTotalSupply); - event NewTotalOnOtherChain(uint256 newTotalOnOtherChain); - event NewTotalInPositions(uint256 newTotalOnOtherChain); - event OperatingRewardMinted(address indexed recipient, uint256 amount); - - event RewardsChange(address indexed rewardsAddress, uint256 indexed rewardsBasisPoints); - event LastRewardTime(uint256 indexed rewardsTime); - event GovernanceChange(address indexed governanceAddress); - event TokenChange(address indexed tokenAddress); - event AdministratorChange(address indexed administratorAddress); - event OracleChange(address indexed oracleContract); - event MaximumLeverageChange(uint256 maxLeverage); - event MarketActivated(bytes32 indexed activateMarket); - event MarketDeActivated(bytes32 indexed deActivateMarket); - event BridgeChange(address _bridgeAddress); - event SideChainMerkleRootUpdate(bytes32 indexed sideChainMerkleRoot); - event NewSideChainOperator(address indexed sideChainOperator); - event NumberOfRequestsLimitUpdate(uint256 _numberOfRequests); - - event MainChainWithdrawLimitUpdate(uint256 indexed mainChainWithdrawLimit24); - event TokenSentToLinkedChain(address _address, uint256 _token, uint256 _totalTokenSent, bytes32 indexed _tokenSentToLinkedChainHash); - event TransferredTokenClaimed(address _address, uint256 _token); - event LastWithdrawAt(); - event RollingWithdrawnAmountUpdated(uint256 _last24HoursAmountWithdrawn, uint256 _lastWithdrawLimitReductionTime); - event WithdrawLimitUpdated(uint256 _amount); - event InactivityPeriodUpdated(uint256 _periodLength); - event FastWithdrawsDisabled(); - event NewBridgeNonce(uint256 _transferNonce); - event Last24HoursAmountWithdrawnReset(); - - event StatePaused(address administrator, bool _paused); - - event SetAllowance(address indexed sender, address indexed spender, uint256 tokens); - event SetPosition(bytes32 indexed positionHash, - address indexed sender, - bytes32 indexed marketId, - uint256 timeStamp, - uint256 longShares, - uint256 shortShares, - uint256 meanEntryPrice, - uint256 meanEntrySpread, - uint256 meanEntryLeverage, - uint256 liquidationPrice - ); - event SetBalance(address indexed account, uint256 balance, bytes32 indexed balanceHash); - event TokenTransferredToOtherChain(address indexed account, uint256 tokenTransferredToOtherChain, bytes32 indexed transferHash); - - modifier notPaused { - require(paused == false, "MorpherState: Contract paused, aborting"); - _; - } - - modifier onlyPlatform { - require(stateAccess[msg.sender] == true, "MorpherState: Only Platform is allowed to execute operation."); - _; - } - - modifier onlyGovernance { - require(msg.sender == getGovernance(), "MorpherState: Calling contract not the Governance Contract. Aborting."); - _; - } - - modifier onlyAdministrator { - require(msg.sender == getAdministrator(), "MorpherState: Caller is not the Administrator. Aborting."); - _; - } - - modifier onlySideChainOperator { - require(msg.sender == sideChainOperator, "MorpherState: Caller is not the Sidechain Operator. Aborting."); - _; - } - - modifier canTransfer { - require(getCanTransfer(msg.sender), "MorpherState: Caller may not transfer token. Aborting."); - _; - } - - modifier onlyBridge { - require(msg.sender == getMorpherBridge(), "MorpherState: Caller is not the Bridge. Aborting."); - _; - } - - modifier onlyMainChain { - require(mainChain == true, "MorpherState: Can only be called on mainchain."); - _; - } - - modifier onlySideChain { - require(mainChain == false, "MorpherState: Can only be called on mainchain."); - _; - } - - constructor(bool _mainChain, address _sideChainOperator, address _morpherTreasury) public { - // @Deployer: Transfer State Ownership to cold storage address after deploying protocol - mainChain = _mainChain; // true for Ethereum, false for Morpher PoA sidechain - setLastRewardTime(now); - uint256 _sideChainMint = 575000000 * 10**(DECIMALS); - uint256 _mainChainMint = 425000000 * 10**(DECIMALS); - - administrator = owner(); //first set the owner as administrator - morpherGovernance = owner(); //first set the owner as governance - - grantAccess(owner()); - setSideChainOperator(owner()); - if (mainChain == false) { // Create token only on sidechain - balances[owner()] = _sideChainMint; // Create airdrop and team token on sidechain - totalToken = _sideChainMint; - emit Mint(owner(), balanceOf(owner()), _sideChainMint); - setRewardBasisPoints(0); // Reward is minted on mainchain - setRewardAddress(address(0)); - setTotalOnOtherChain(_mainChainMint); - } else { - balances[owner()] = _mainChainMint; // Create treasury and investor token on mainchain - totalToken = _mainChainMint; - emit Mint(owner(), balanceOf(owner()), _mainChainMint); - setRewardBasisPoints(15000); // 15000 / PRECISION = 0.00015 - setRewardAddress(_morpherTreasury); - setTotalOnOtherChain(_sideChainMint); - } - fastTransfersEnabled = true; - setNumberOfRequestsLimit(3); - setMainChainWithdrawLimit(totalSupply / 50); - setSideChainOperator(_sideChainOperator); - denyAccess(owner()); - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for market wise exposure - // ---------------------------------------------------------------------------- - - function getMaxMappingIndex(bytes32 _marketId) public view returns(uint256 _maxMappingIndex) { - return exposureByMarket[_marketId].maxMappingIndex; - } - - function getExposureMappingIndex(bytes32 _marketId, address _address) public view returns(uint256 _mappingIndex) { - return exposureByMarket[_marketId].index[_address]; - } - - function getExposureMappingAddress(bytes32 _marketId, uint256 _mappingIndex) public view returns(address _address) { - return exposureByMarket[_marketId].addy[_mappingIndex]; - } - - function setMaxMappingIndex(bytes32 _marketId, uint256 _maxMappingIndex) public onlyPlatform { - exposureByMarket[_marketId].maxMappingIndex = _maxMappingIndex; - } - - function setExposureMapping(bytes32 _marketId, address _address, uint256 _index) public onlyPlatform { - setExposureMappingIndex(_marketId, _address, _index); - setExposureMappingAddress(_marketId, _address, _index); - } - - function setExposureMappingIndex(bytes32 _marketId, address _address, uint256 _index) public onlyPlatform { - exposureByMarket[_marketId].index[_address] = _index; - } - - function setExposureMappingAddress(bytes32 _marketId, address _address, uint256 _index) public onlyPlatform { - exposureByMarket[_marketId].addy[_index] = _address; - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for bridge variables - // ---------------------------------------------------------------------------- - function setTokenClaimedOnThisChain(address _address, uint256 _token) public onlyBridge { - tokenClaimedOnThisChain[_address] = _token; - emit TransferredTokenClaimed(_address, _token); - } - - function getTokenClaimedOnThisChain(address _address) public view returns (uint256 _token) { - return tokenClaimedOnThisChain[_address]; - } - - function setTokenSentToLinkedChain(address _address, uint256 _token) public onlyBridge { - tokenSentToLinkedChain[_address] = _token; - tokenSentToLinkedChainTime[_address] = now; - emit TokenSentToLinkedChain(_address, _token, tokenSentToLinkedChain[_address], getBalanceHash(_address, tokenSentToLinkedChain[_address])); - } - - function getTokenSentToLinkedChain(address _address) public view returns (uint256 _token) { - return tokenSentToLinkedChain[_address]; - } - - function getTokenSentToLinkedChainTime(address _address) public view returns (uint256 _timeStamp) { - return tokenSentToLinkedChainTime[_address]; - } - - function add24HoursWithdrawn(uint256 _amount) public onlyBridge { - last24HoursAmountWithdrawn = last24HoursAmountWithdrawn.add(_amount); - emit RollingWithdrawnAmountUpdated(last24HoursAmountWithdrawn, lastWithdrawLimitReductionTime); - } - - function update24HoursWithdrawLimit(uint256 _amount) public onlyBridge { - if (last24HoursAmountWithdrawn > _amount) { - last24HoursAmountWithdrawn = last24HoursAmountWithdrawn.sub(_amount); - } else { - last24HoursAmountWithdrawn = 0; - } - lastWithdrawLimitReductionTime = now; - emit RollingWithdrawnAmountUpdated(last24HoursAmountWithdrawn, lastWithdrawLimitReductionTime); - } - - function set24HourWithdrawLimit(uint256 _limit) public onlyBridge { - withdrawLimit24Hours = _limit; - emit WithdrawLimitUpdated(_limit); - } - - function resetLast24HoursAmountWithdrawn() public onlyBridge { - last24HoursAmountWithdrawn = 0; - emit Last24HoursAmountWithdrawnReset(); - } - - function setInactivityPeriod(uint256 _periodLength) public onlyBridge { - inactivityPeriod = _periodLength; - emit InactivityPeriodUpdated(_periodLength); - } - - function getBridgeNonce() public onlyBridge returns (uint256 _nonce) { - transferNonce++; - emit NewBridgeNonce(transferNonce); - return transferNonce; - } - - function disableFastWithdraws() public onlyBridge { - fastTransfersEnabled = false; - emit FastWithdrawsDisabled(); - } - - function setPositionClaimedOnMainChain(bytes32 _positionHash) public onlyBridge { - positionClaimedOnMainChain[_positionHash] = true; - } - - function getPositionClaimedOnMainChain(bytes32 _positionHash) public view returns (bool _alreadyClaimed) { - return positionClaimedOnMainChain[_positionHash]; - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for spam protection - // ---------------------------------------------------------------------------- - - function setLastRequestBlock(address _address) public onlyPlatform { - lastRequestBlock[_address] = block.number; - } - - function getLastRequestBlock(address _address) public view returns(uint256 _lastRequestBlock) { - return lastRequestBlock[_address]; - } - - function setNumberOfRequests(address _address, uint256 _numberOfRequests) public onlyPlatform { - numberOfRequests[_address] = _numberOfRequests; - } - - function increaseNumberOfRequests(address _address) public onlyPlatform{ - numberOfRequests[_address]++; - } - - function getNumberOfRequests(address _address) public view returns(uint256 _numberOfRequests) { - return numberOfRequests[_address]; - } - - function setNumberOfRequestsLimit(uint256 _numberOfRequestsLimit) public onlyPlatform { - numberOfRequestsLimit = _numberOfRequestsLimit; - emit NumberOfRequestsLimitUpdate(_numberOfRequestsLimit); - } - - function getNumberOfRequestsLimit() public view returns (uint256 _numberOfRequestsLimit) { - return numberOfRequestsLimit; - } - - function setMainChainWithdrawLimit(uint256 _mainChainWithdrawLimit24) public onlyGovernance { - mainChainWithdrawLimit24 = _mainChainWithdrawLimit24; - emit MainChainWithdrawLimitUpdate(_mainChainWithdrawLimit24); - } - - function getMainChainWithdrawLimit() public view returns (uint256 _mainChainWithdrawLimit24) { - return mainChainWithdrawLimit24; - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for state access - // ---------------------------------------------------------------------------- - - function grantAccess(address _address) public onlyAdministrator { - stateAccess[_address] = true; - emit StateAccessGranted(_address, block.number); - } - - function denyAccess(address _address) public onlyAdministrator { - stateAccess[_address] = false; - emit StateAccessDenied(_address, block.number); - } - - function getStateAccess(address _address) public view returns(bool _hasAccess) { - return stateAccess[_address]; - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for addresses that can transfer tokens (sidechain only) - // ---------------------------------------------------------------------------- - - function enableTransfers(address _address) public onlyAdministrator { - transferAllowed[_address] = true; - emit TransfersEnabled(_address); - } - - function disableTransfers(address _address) public onlyAdministrator { - transferAllowed[_address] = false; - emit TransfersDisabled(_address); - } - - function getCanTransfer(address _address) public view returns(bool _hasAccess) { - return mainChain || transferAllowed[_address]; - } - - // ---------------------------------------------------------------------------- - // Minting/burning/transfer of token - // ---------------------------------------------------------------------------- - - function transfer(address _from, address _to, uint256 _token) public onlyPlatform notPaused { - require(balances[_from] >= _token, "MorpherState: Not enough token."); - balances[_from] = balances[_from].sub(_token); - balances[_to] = balances[_to].add(_token); - IMorpherToken(morpherToken).emitTransfer(_from, _to, _token); - emit Transfer(_from, _to, _token); - emit SetBalance(_from, balances[_from], getBalanceHash(_from, balances[_from])); - emit SetBalance(_to, balances[_to], getBalanceHash(_to, balances[_to])); - } - - function mint(address _address, uint256 _token) public onlyPlatform notPaused { - balances[_address] = balances[_address].add(_token); - totalToken = totalToken.add(_token); - updateTotalSupply(); - IMorpherToken(morpherToken).emitTransfer(address(0), _address, _token); - emit Mint(_address, _token, totalToken); - emit SetBalance(_address, balances[_address], getBalanceHash(_address, balances[_address])); - } - - function burn(address _address, uint256 _token) public onlyPlatform notPaused { - require(balances[_address] >= _token, "MorpherState: Not enough token."); - balances[_address] = balances[_address].sub(_token); - totalToken = totalToken.sub(_token); - updateTotalSupply(); - IMorpherToken(morpherToken).emitTransfer(_address, address(0), _token); - emit Burn(_address, _token, totalToken); - emit SetBalance(_address, balances[_address], getBalanceHash(_address, balances[_address])); - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for balance and token functions (ERC20) - // ---------------------------------------------------------------------------- - function updateTotalSupply() private { - totalSupply = totalToken.add(totalInPositions).add(totalOnOtherChain); - emit NewTotalSupply(totalSupply); - } - - function setTotalInPositions(uint256 _totalInPositions) public onlyAdministrator { - totalInPositions = _totalInPositions; - updateTotalSupply(); - emit NewTotalInPositions(_totalInPositions); - } - - function setTotalOnOtherChain(uint256 _newTotalOnOtherChain) public onlySideChainOperator { - totalOnOtherChain = _newTotalOnOtherChain; - updateTotalSupply(); - emit NewTotalOnOtherChain(_newTotalOnOtherChain); - } - - function balanceOf(address _tokenOwner) public view returns (uint256 balance) { - return balances[_tokenOwner]; - } - - function setAllowance(address _from, address _spender, uint256 _tokens) public onlyPlatform { - allowed[_from][_spender] = _tokens; - emit SetAllowance(_from, _spender, _tokens); - } - - function getAllowance(address _tokenOwner, address spender) public view returns (uint256 remaining) { - return allowed[_tokenOwner][spender]; - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for platform roles - // ---------------------------------------------------------------------------- - - function setGovernanceContract(address _newGovernanceContractAddress) public onlyGovernance { - morpherGovernance = _newGovernanceContractAddress; - emit GovernanceChange(_newGovernanceContractAddress); - } - - function getGovernance() public view returns (address _governanceContract) { - return morpherGovernance; - } - - function setMorpherBridge(address _newBridge) public onlyGovernance { - morpherBridge = _newBridge; - emit BridgeChange(_newBridge); - } - - function getMorpherBridge() public view returns (address _currentBridge) { - return morpherBridge; - } - - function setOracleContract(address _newOracleContract) public onlyGovernance { - oracleContract = _newOracleContract; - emit OracleChange(_newOracleContract); - } - - function getOracleContract() public view returns(address) { - return oracleContract; - } - - function setTokenContract(address _newTokenContract) public onlyGovernance { - morpherToken = _newTokenContract; - emit TokenChange(_newTokenContract); - } - - function getTokenContract() public view returns(address) { - return morpherToken; - } - - function setAdministrator(address _newAdministrator) public onlyGovernance { - administrator = _newAdministrator; - emit AdministratorChange(_newAdministrator); - } - - function getAdministrator() public view returns(address) { - return administrator; - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for platform operating rewards - // ---------------------------------------------------------------------------- - - function setRewardAddress(address _newRewardsAddress) public onlyGovernance { - morpherRewards = _newRewardsAddress; - emit RewardsChange(_newRewardsAddress, rewardBasisPoints); - } - - function setRewardBasisPoints(uint256 _newRewardBasisPoints) public onlyGovernance { - if (mainChain == true) { - require(_newRewardBasisPoints <= 15000, "MorpherState: Reward basis points need to be less or equal to 15000."); - } else { - require(_newRewardBasisPoints == 0, "MorpherState: Reward basis points can only be set on Ethereum."); - } - rewardBasisPoints = _newRewardBasisPoints; - emit RewardsChange(morpherRewards, _newRewardBasisPoints); - } - - function setLastRewardTime(uint256 _lastRewardTime) private { - lastRewardTime = _lastRewardTime; - emit LastRewardTime(_lastRewardTime); - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for platform administration - // ---------------------------------------------------------------------------- - - function activateMarket(bytes32 _activateMarket) public onlyAdministrator { - marketActive[_activateMarket] = true; - emit MarketActivated(_activateMarket); - } - - function deActivateMarket(bytes32 _deActivateMarket) public onlyAdministrator { - marketActive[_deActivateMarket] = false; - emit MarketDeActivated(_deActivateMarket); - } - - function getMarketActive(bytes32 _marketId) public view returns(bool _active) { - return marketActive[_marketId]; - } - - function setMaximumLeverage(uint256 _newMaximumLeverage) public onlyAdministrator { - require(_newMaximumLeverage > PRECISION, "MorpherState: Leverage precision is 1e8"); - maximumLeverage = _newMaximumLeverage; - emit MaximumLeverageChange(_newMaximumLeverage); - } - - function getMaximumLeverage() public view returns(uint256 _maxLeverage) { - return maximumLeverage; - } - - function pauseState() public onlyAdministrator { - paused = true; - emit StatePaused(msg.sender, true); - } - - function unPauseState() public onlyAdministrator { - paused = false; - emit StatePaused(msg.sender, false); - } - - // ---------------------------------------------------------------------------- - // Setter/Getter for side chain state - // ---------------------------------------------------------------------------- - - function setSideChainMerkleRoot(bytes32 _sideChainMerkleRoot) public onlyBridge { - sideChainMerkleRoot = _sideChainMerkleRoot; - sideChainMerkleRootWrittenAtTime = now; - payOperatingReward(); - emit SideChainMerkleRootUpdate(_sideChainMerkleRoot); - } - - function getSideChainMerkleRoot() public view returns(bytes32 _sideChainMerkleRoot) { - return sideChainMerkleRoot; - } - - function setSideChainOperator(address _address) public onlyAdministrator { - sideChainOperator = _address; - emit NewSideChainOperator(_address); - } - - function getSideChainOperator() public view returns (address _address) { - return sideChainOperator; - } - - function getSideChainMerkleRootWrittenAtTime() public view returns(uint256 _sideChainMerkleRoot) { - return sideChainMerkleRootWrittenAtTime; - } - - // ---------------------------------------------------------------------------- - // Setter/Getter functions for portfolio - // ---------------------------------------------------------------------------- - - function setPosition( - address _address, - bytes32 _marketId, - uint256 _timeStamp, - uint256 _longShares, - uint256 _shortShares, - uint256 _meanEntryPrice, - uint256 _meanEntrySpread, - uint256 _meanEntryLeverage, - uint256 _liquidationPrice - ) public onlyPlatform { - portfolio[_address][_marketId].lastUpdated = _timeStamp; - portfolio[_address][_marketId].longShares = _longShares; - portfolio[_address][_marketId].shortShares = _shortShares; - portfolio[_address][_marketId].meanEntryPrice = _meanEntryPrice; - portfolio[_address][_marketId].meanEntrySpread = _meanEntrySpread; - portfolio[_address][_marketId].meanEntryLeverage = _meanEntryLeverage; - portfolio[_address][_marketId].liquidationPrice = _liquidationPrice; - portfolio[_address][_marketId].positionHash = getPositionHash( - _address, - _marketId, - _timeStamp, - _longShares, - _shortShares, - _meanEntryPrice, - _meanEntrySpread, - _meanEntryLeverage, - _liquidationPrice - ); - if (_longShares > 0 || _shortShares > 0) { - addExposureByMarket(_marketId, _address); - } else { - deleteExposureByMarket(_marketId, _address); - } - emit SetPosition( - portfolio[_address][_marketId].positionHash, - _address, - _marketId, - _timeStamp, - _longShares, - _shortShares, - _meanEntryPrice, - _meanEntrySpread, - _meanEntryLeverage, - _liquidationPrice - ); - } - - function getPosition( - address _address, - bytes32 _marketId - ) public view returns ( - uint256 _longShares, - uint256 _shortShares, - uint256 _meanEntryPrice, - uint256 _meanEntrySpread, - uint256 _meanEntryLeverage, - uint256 _liquidationPrice - ) { - return( - portfolio[_address][_marketId].longShares, - portfolio[_address][_marketId].shortShares, - portfolio[_address][_marketId].meanEntryPrice, - portfolio[_address][_marketId].meanEntrySpread, - portfolio[_address][_marketId].meanEntryLeverage, - portfolio[_address][_marketId].liquidationPrice - ); - } - - function getPositionHash( - address _address, - bytes32 _marketId, - uint256 _timeStamp, - uint256 _longShares, - uint256 _shortShares, - uint256 _meanEntryPrice, - uint256 _meanEntrySpread, - uint256 _meanEntryLeverage, - uint256 _liquidationPrice - ) public pure returns (bytes32 _hash) { - return keccak256( - abi.encodePacked( - _address, - _marketId, - _timeStamp, - _longShares, - _shortShares, - _meanEntryPrice, - _meanEntrySpread, - _meanEntryLeverage, - _liquidationPrice - ) - ); - } - - function getBalanceHash(address _address, uint256 _balance) public pure returns (bytes32 _hash) { - return keccak256(abi.encodePacked(_address, _balance)); - } - - function getLastUpdated(address _address, bytes32 _marketId) public view returns (uint256 _lastUpdated) { - return(portfolio[_address][_marketId].lastUpdated); - } - - function getLongShares(address _address, bytes32 _marketId) public view returns (uint256 _longShares) { - return(portfolio[_address][_marketId].longShares); - } - - function getShortShares(address _address, bytes32 _marketId) public view returns (uint256 _shortShares) { - return(portfolio[_address][_marketId].shortShares); - } - - function getMeanEntryPrice(address _address, bytes32 _marketId) public view returns (uint256 _meanEntryPrice) { - return(portfolio[_address][_marketId].meanEntryPrice); - } - - function getMeanEntrySpread(address _address, bytes32 _marketId) public view returns (uint256 _meanEntrySpread) { - return(portfolio[_address][_marketId].meanEntrySpread); - } - - function getMeanEntryLeverage(address _address, bytes32 _marketId) public view returns (uint256 _meanEntryLeverage) { - return(portfolio[_address][_marketId].meanEntryLeverage); - } - - function getLiquidationPrice(address _address, bytes32 _marketId) public view returns (uint256 _liquidationPrice) { - return(portfolio[_address][_marketId].liquidationPrice); - } - - // ---------------------------------------------------------------------------- - // Record positions by market by address. Needed for exposure aggregations - // and spits and dividends. - // ---------------------------------------------------------------------------- - function addExposureByMarket(bytes32 _symbol, address _address) private { - // Address must not be already recored - uint256 _myExposureIndex = getExposureMappingIndex(_symbol, _address); - if (_myExposureIndex == 0) { - uint256 _maxMappingIndex = getMaxMappingIndex(_symbol).add(1); - setMaxMappingIndex(_symbol, _maxMappingIndex); - setExposureMapping(_symbol, _address, _maxMappingIndex); - } - } - - function deleteExposureByMarket(bytes32 _symbol, address _address) private { - // Get my index in mapping - uint256 _myExposureIndex = getExposureMappingIndex(_symbol, _address); - // Get last element of mapping - uint256 _lastIndex = getMaxMappingIndex(_symbol); - address _lastAddress = getExposureMappingAddress(_symbol, _lastIndex); - // If _myExposureIndex is greater than 0 (i.e. there is an exposure of that address on that market) delete it - if (_myExposureIndex > 0) { - // If _myExposureIndex is less than _lastIndex overwrite element at _myExposureIndex with element at _lastIndex in - // deleted elements position. - if (_myExposureIndex < _lastIndex) { - setExposureMappingAddress(_symbol, _lastAddress, _myExposureIndex); - setExposureMappingIndex(_symbol, _lastAddress, _myExposureIndex); - } - // Delete _lastIndex and _lastAddress element and reduce maxExposureIndex - setExposureMappingAddress(_symbol, address(0), _lastIndex); - setExposureMappingIndex(_symbol, _address, 0); - // Shouldn't happen, but check that not empty - if (_lastIndex > 0) { - setMaxMappingIndex(_symbol, _lastIndex.sub(1)); - } - } - } - - // ---------------------------------------------------------------------------- - // Calculate and send operating reward - // Every 24 hours the protocol mints rewardBasisPoints/(PRECISION) percent of the total - // supply as reward for the protocol operator. The amount can not exceed 0.015% per - // day. - // ---------------------------------------------------------------------------- - - function payOperatingReward() public onlyMainChain { - if (now > lastRewardTime.add(REWARDPERIOD)) { - uint256 _reward = totalSupply.mul(rewardBasisPoints).div(PRECISION); - setLastRewardTime(lastRewardTime.add(REWARDPERIOD)); - mint(morpherRewards, _reward); - emit OperatingRewardMinted(morpherRewards, _reward); - } - } -} diff --git a/contracts/MorpherToken.sol b/contracts/MorpherToken.sol deleted file mode 100644 index 878d440..0000000 --- a/contracts/MorpherToken.sol +++ /dev/null @@ -1,247 +0,0 @@ -pragma solidity 0.5.16; - -import "./IERC20.sol"; -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./MorpherState.sol"; - -/** - * @dev Implementation of the {IERC20} interface. - * - * This implementation is agnostic to the way tokens are created. This means - * that a supply mechanism has to be added in a derived contract using {_mint}. - * For a generic mechanism see {ERC20Mintable}. - * - * TIP: For a detailed writeup see our guide - * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How - * to implement supply mechanisms]. - * - * We have followed general OpenZeppelin guidelines: functions revert instead - * of returning `false` on failure. This behavior is nonetheless conventional - * and does not conflict with the expectations of ERC20 applications. - * - * Additionally, an {Approval} event is emitted on calls to {transferFrom}. - * This allows applications to reconstruct the allowance for all accounts just - * by listening to said events. Other implementations of the EIP may not emit - * these events, as it isn't required by the specification. - * - * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} - * functions have been added to mitigate the well-known issues around setting - * allowances. See {IERC20-approve}. - */ -contract MorpherToken is IERC20, Ownable { - - MorpherState state; - using SafeMath for uint256; - - string public constant name = "Morpher"; - string public constant symbol = "MPH"; - uint8 public constant decimals = 18; - - modifier onlyState { - require(msg.sender == address(state), "ERC20: caller must be MorpherState contract."); - _; - } - - modifier canTransfer { - require(state.getCanTransfer(msg.sender), "ERC20: token transfers disabled on sidechain."); - _; - } - - event LinkState(address _address); - - // ------------------------------------------------------------------------ - // Constructor - // ------------------------------------------------------------------------ - constructor(address _stateAddress, address _coldStorageOwnerAddress) public { - setMorpherState(_stateAddress); - transferOwnership(_coldStorageOwnerAddress); - } - - // ------------------------------------------------------------------------ - // Links Token Contract with State - // ------------------------------------------------------------------------ - function setMorpherState(address _stateAddress) public onlyOwner { - state = MorpherState(_stateAddress); - emit LinkState(_stateAddress); - } - - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view returns (uint256) { - return state.totalSupply(); - } - - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address _account) public view returns (uint256) { - return state.balanceOf(_account); - } - - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - `recipient` cannot be the zero address. - * - the caller must have a balance of at least `amount`. - * - * Emits a {Transfer} event via emitTransfer called by MorpherState - */ - function transfer(address _recipient, uint256 _amount) public returns (bool) { - _transfer(msg.sender, _recipient, _amount); - return true; - } - - /** - * @dev See {IERC20-allowance}. - */ - function allowance(address _owner, address _spender) public view returns (uint256) { - return state.getAllowance(_owner, _spender); - } - - /** - * @dev See {IERC20-approve}. - * - * Requirements: - * - * - `spender` cannot be the zero address. - */ - function approve(address _spender, uint256 _amount) public returns (bool) { - _approve(msg.sender, _spender, _amount); - return true; - } - - /** - * @dev See {IERC20-transferFrom}. - * - * Emits an {Approval} event indicating the updated allowance. This is not - * required by the EIP. See the note at the beginning of {ERC20}; - * - * Requirements: - * - `_sender` and `_recipient` cannot be the zero address. - * - `_sender` must have a balance of at least `amount`. - * - the caller must have allowance for `_sender`'s tokens of at least - * `amount`. - */ - function transferFrom(address _sender, address _recipient, uint256 amount) public returns (bool) { - _transfer(_sender, _recipient, amount); - _approve(_sender, msg.sender, state.getAllowance(_sender, msg.sender).sub(amount, "ERC20: transfer amount exceeds allowance")); - return true; - } - - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `_spender` cannot be the zero address. - */ - function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) { - _approve(msg.sender, _spender, state.getAllowance(msg.sender, _spender).add(_addedValue)); - return true; - } - - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `spender` must have allowance for the caller of at least - * `subtractedValue`. - */ - function decreaseAllowance(address _spender, uint256 _subtractedValue) public returns (bool) { - _approve(msg.sender, _spender, state.getAllowance(msg.sender, _spender).sub(_subtractedValue, "ERC20: decreased allowance below zero")); - return true; - } - - /** - * @dev Caller destroys `_amount` tokens permanently - * - * Emits a {Transfer} event to zero address called by MorpherState via emitTransfer. - * - * Requirements: - * - * - Caller must have token balance of at least `_amount` - * - */ - function burn(uint256 _amount) public returns (bool) { - state.burn(msg.sender, _amount); - return true; - } - - /** - * @dev Emits a {Transfer} event - * - * MorpherState emits a {Transfer} event. - * - * Requirements: - * - * - Caller must be MorpherState - * - */ - function emitTransfer(address _from, address _to, uint256 _amount) public onlyState { - emit Transfer(_from, _to, _amount); - } - - /** - * @dev Moves tokens `_amount` from `sender` to `_recipient`. - * - * This is internal function is equivalent to {transfer}, and can be used to - * e.g. implement automatic token fees, slashing mechanisms, etc. - * - * Emits a {Transfer} event via emitTransfer called by MorpherState - * - * Requirements: - * - * - `_sender` cannot be the zero address. - * - `_recipient` cannot be the zero address. - * - `_sender` must have a balance of at least `_amount`. - */ - function _transfer(address _sender, address _recipient, uint256 _amount) canTransfer internal { - require(_sender != address(0), "ERC20: transfer from the zero address"); - require(_recipient != address(0), "ERC20: transfer to the zero address"); - require(state.balanceOf(_sender) >= _amount, "ERC20: transfer amount exceeds balance"); - state.transfer(_sender, _recipient, _amount); - } - - /** - * @dev Sets `_amount` as the allowance of `spender` over the `owner`s tokens. - * - * This is internal function is equivalent to `approve`, and can be used to - * e.g. set automatic allowances for certain subsystems, etc. - * - * Emits an {Approval} event. - * - * Requirements: - * - * - `owner` cannot be the zero address. - * - `spender` cannot be the zero address. - */ - function _approve(address _owner, address _spender, uint256 _amount) internal { - require(_owner != address(0), "ERC20: approve from the zero address"); - require(_spender != address(0), "ERC20: approve to the zero address"); - state.setAllowance(_owner, _spender, _amount); - emit Approval(_owner, _spender, _amount); - } - - // ------------------------------------------------------------------------ - // Don't accept ETH - // ------------------------------------------------------------------------ - function () external payable { - revert("ERC20: You can't deposit Ether here"); - } -} diff --git a/contracts/MorpherTradeEngine.sol b/contracts/MorpherTradeEngine.sol deleted file mode 100644 index 14e13d4..0000000 --- a/contracts/MorpherTradeEngine.sol +++ /dev/null @@ -1,1042 +0,0 @@ -pragma solidity 0.5.16; - -import "./Ownable.sol"; -import "./SafeMath.sol"; -import "./MorpherState.sol"; -import "./MorpherStaking.sol"; -import "./MorpherMintingLimiter.sol"; -import "./MorpherUserBlocking.sol"; - -// ---------------------------------------------------------------------------------- -// Tradeengine of the Morpher platform -// Creates and processes orders, and computes the state change of portfolio. -// Needs writing/reading access to/from Morpher State. Order objects are stored locally, -// portfolios are stored in state. -// ---------------------------------------------------------------------------------- - -contract MorpherTradeEngine is Ownable { - MorpherState state; - MorpherStaking staking; - MorpherMintingLimiter mintingLimiter; - MorpherUserBlocking userBlocking; - using SafeMath for uint256; - -// ---------------------------------------------------------------------------- -// Precision of prices and leverage -// ---------------------------------------------------------------------------- - uint256 constant PRECISION = 10**8; - uint256 public orderNonce; - bytes32 public lastOrderId; - uint256 public deployedTimeStamp; - - address public escrowOpenOrderAddress = 0x1111111111111111111111111111111111111111; - bool public escrowOpenOrderEnabled; - - - //we're locking positions in for this price at a market marketId; - address public closedMarketPriceLock = 0x0000000000000000000000000000000000000001; - - -// ---------------------------------------------------------------------------- -// Order struct contains all order specific varibles. Variables are completed -// during processing of trade. State changes are saved in the order struct as -// well, since local variables would lead to stack to deep errors *sigh*. -// ---------------------------------------------------------------------------- - struct order { - address userId; - bytes32 marketId; - uint256 closeSharesAmount; - uint256 openMPHTokenAmount; - bool tradeDirection; // true = long, false = short - uint256 liquidationTimestamp; - uint256 marketPrice; - uint256 marketSpread; - uint256 orderLeverage; - uint256 timeStamp; - uint256 longSharesOrder; - uint256 shortSharesOrder; - uint256 balanceDown; - uint256 balanceUp; - uint256 newLongShares; - uint256 newShortShares; - uint256 newMeanEntryPrice; - uint256 newMeanEntrySpread; - uint256 newMeanEntryLeverage; - uint256 newLiquidationPrice; - uint256 orderEscrowAmount; - } - - mapping(bytes32 => order) private orders; - -// ---------------------------------------------------------------------------- -// Events -// Order created/processed events are fired by MorpherOracle. -// ---------------------------------------------------------------------------- - - event PositionLiquidated( - address indexed _address, - bytes32 indexed _marketId, - bool _longPosition, - uint256 _timeStamp, - uint256 _marketPrice, - uint256 _marketSpread - ); - - event OrderCancelled( - bytes32 indexed _orderId, - address indexed _address - ); - - event OrderIdRequested( - bytes32 _orderId, - address indexed _address, - bytes32 indexed _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage - ); - - event OrderProcessed( - bytes32 _orderId, - uint256 _marketPrice, - uint256 _marketSpread, - uint256 _liquidationTimestamp, - uint256 _timeStamp, - uint256 _newLongShares, - uint256 _newShortShares, - uint256 _newAverageEntry, - uint256 _newAverageSpread, - uint256 _newAverageLeverage, - uint256 _liquidationPrice - ); - - event PositionUpdated( - address _userId, - bytes32 _marketId, - uint256 _timeStamp, - uint256 _newLongShares, - uint256 _newShortShares, - uint256 _newMeanEntryPrice, - uint256 _newMeanEntrySpread, - uint256 _newMeanEntryLeverage, - uint256 _newLiquidationPrice, - uint256 _mint, - uint256 _burn - ); - - event LinkState(address _address); - event LinkStaking(address _stakingAddress); - event LinkMintingLimiter(address _mintingLimiterAddress); - event LinkMorpherUserBlocking(address _address); - - - event LockedPriceForClosingPositions(bytes32 _marketId, uint256 _price); - - constructor(address _stateAddress, address _coldStorageOwnerAddress, address payable _stakingContractAddress, bool _escrowOpenOrderEnabled, uint256 _deployedTimestampOverride, address _mintingLimiterAddress, address _userBlockingAddress) public { - setMorpherState(_stateAddress); - setMorpherStaking(_stakingContractAddress); - setMorpherMintingLimiter(_mintingLimiterAddress); - escrowOpenOrderEnabled = _escrowOpenOrderEnabled; - deployedTimeStamp = _deployedTimestampOverride > 0 ? _deployedTimestampOverride : block.timestamp; - setUserBlockingAddress(_userBlockingAddress); - transferOwnership(_coldStorageOwnerAddress); - } - - modifier onlyOracle { - require(msg.sender == state.getOracleContract(), "MorpherTradeEngine: function can only be called by Oracle Contract."); - _; - } - - modifier onlyAdministrator { - require(msg.sender == getAdministrator(), "MorpherTradeEngine: function can only be called by the Administrator."); - _; - } - -// ---------------------------------------------------------------------------- -// Administrative functions -// Set state address, get administrator address -// ---------------------------------------------------------------------------- - - function setMorpherState(address _stateAddress) public onlyOwner { - state = MorpherState(_stateAddress); - emit LinkState(_stateAddress); - } - - function setMorpherStaking(address payable _stakingAddress) public onlyOwner { - staking = MorpherStaking(_stakingAddress); - emit LinkStaking(_stakingAddress); - } - - function setMorpherMintingLimiter(address _mintingLimiterAddress) public onlyOwner { - mintingLimiter = MorpherMintingLimiter(_mintingLimiterAddress); - emit LinkMintingLimiter(_mintingLimiterAddress); - } - function getMorpherMintingLimiter() public view returns(address) { - return address(mintingLimiter); - } - - function setUserBlockingAddress(address _userBlockingAddress) public onlyOwner { - userBlocking = MorpherUserBlocking(_userBlockingAddress); - emit LinkMorpherUserBlocking(_userBlockingAddress); - } - - function getAdministrator() public view returns(address _administrator) { - return state.getAdministrator(); - } - - function setEscrowOpenOrderEnabled(bool _isEnabled) public onlyOwner { - escrowOpenOrderEnabled = _isEnabled; - } - - function paybackEscrow(bytes32 _orderId) private { - //pay back the escrow to the user so he has it back on his balance/** - if(orders[_orderId].orderEscrowAmount > 0) { - //checks effects interaction - uint256 paybackAmount = orders[_orderId].orderEscrowAmount; - orders[_orderId].orderEscrowAmount = 0; - state.transfer(escrowOpenOrderAddress, orders[_orderId].userId, paybackAmount); - } - } - - function buildupEscrow(bytes32 _orderId, uint256 _amountInMPH) private { - if(escrowOpenOrderEnabled && _amountInMPH > 0) { - state.transfer(orders[_orderId].userId, escrowOpenOrderAddress, _amountInMPH); - orders[_orderId].orderEscrowAmount = _amountInMPH; - } - } - - - function validateClosedMarketOrderConditions(address _address, bytes32 _marketId, uint256 _closeSharesAmount, uint256 _openMPHTokenAmount, bool _tradeDirection ) internal view { - //markets active? Still tradeable? - if(_openMPHTokenAmount > 0) { - require(state.getMarketActive(_marketId) == true, "MorpherTradeEngine: market unknown or currently not enabled for trading."); - } else { - //we're just closing a position, but it needs a forever price locked in if market is not active - //the user needs to close his complete position - if(state.getMarketActive(_marketId) == false) { - require(getDeactivatedMarketPrice(_marketId) > 0, "MorpherTradeEngine: Can't close a position, market not active and closing price not locked"); - if(_tradeDirection) { - //long - require(_closeSharesAmount == state.getShortShares(_address, _marketId), "MorpherTradeEngine: Deactivated market order needs all shares to be closed"); - } else { - //short - require(_closeSharesAmount == state.getLongShares(_address, _marketId), "MorpherTradeEngine: Deactivated market order needs all shares to be closed"); - } - } - } - } - - //wrapper for stack too deep errors - function validateClosedMarketOrder(bytes32 _orderId) internal view { - validateClosedMarketOrderConditions(orders[_orderId].userId, orders[_orderId].marketId, orders[_orderId].closeSharesAmount, orders[_orderId].openMPHTokenAmount, orders[_orderId].tradeDirection); - } - -// ---------------------------------------------------------------------------- -// requestOrderId(address _address, bytes32 _marketId, bool _closeSharesAmount, uint256 _openMPHTokenAmount, bool _tradeDirection, uint256 _orderLeverage) -// Creates a new order object with unique orderId and assigns order information. -// Must be called by MorpherOracle contract. -// ---------------------------------------------------------------------------- - - function requestOrderId( - address _address, - bytes32 _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - bool _tradeDirection, - uint256 _orderLeverage - ) public onlyOracle returns (bytes32 _orderId) { - - require(_orderLeverage >= PRECISION, "MorpherTradeEngine: leverage too small. Leverage precision is 1e8"); - require(_orderLeverage <= state.getMaximumLeverage(), "MorpherTradeEngine: leverage exceeds maximum allowed leverage."); - - validateClosedMarketOrderConditions(_address, _marketId, _closeSharesAmount, _openMPHTokenAmount, _tradeDirection); - - //request limits - require(state.getNumberOfRequests(_address) <= state.getNumberOfRequestsLimit() || - state.getLastRequestBlock(_address) < block.number, - "MorpherTradeEngine: request exceeded maximum permitted requests per block." - ); - - /** - * The user can't partially close a position and open another one with MPH - */ - if(_openMPHTokenAmount > 0) { - - if(_tradeDirection) { - //long - require(_closeSharesAmount == state.getShortShares(_address, _marketId), "MorpherTradeEngine: Can't partially close a position and open another one in opposite direction"); - } else { - //short - require(_closeSharesAmount == state.getLongShares(_address, _marketId), "MorpherTradeEngine: Can't partially close a position and open another one in opposite direction"); - } - } - - state.setLastRequestBlock(_address); - state.increaseNumberOfRequests(_address); - orderNonce++; - _orderId = keccak256( - abi.encodePacked( - _address, - block.number, - _marketId, - _closeSharesAmount, - _openMPHTokenAmount, - _tradeDirection, - _orderLeverage, - orderNonce - ) - ); - lastOrderId = _orderId; - orders[_orderId].userId = _address; - orders[_orderId].marketId = _marketId; - orders[_orderId].closeSharesAmount = _closeSharesAmount; - orders[_orderId].openMPHTokenAmount = _openMPHTokenAmount; - orders[_orderId].tradeDirection = _tradeDirection; - orders[_orderId].orderLeverage = _orderLeverage; - emit OrderIdRequested( - _orderId, - _address, - _marketId, - _closeSharesAmount, - _openMPHTokenAmount, - _tradeDirection, - _orderLeverage - ); - - /** - * put the money in escrow here if given MPH to open an order - * - also, can only close positions if in shares, so it will - * definitely trigger a mint there. - * The money must be put in escrow even though we have an existing position - */ - buildupEscrow(_orderId, _openMPHTokenAmount); - - return _orderId; - } - -// ---------------------------------------------------------------------------- -// Getter functions for orders, shares, and positions -// ---------------------------------------------------------------------------- - - function getOrder(bytes32 _orderId) public view returns ( - address _userId, - bytes32 _marketId, - uint256 _closeSharesAmount, - uint256 _openMPHTokenAmount, - uint256 _marketPrice, - uint256 _marketSpread, - uint256 _orderLeverage - ) { - return( - orders[_orderId].userId, - orders[_orderId].marketId, - orders[_orderId].closeSharesAmount, - orders[_orderId].openMPHTokenAmount, - orders[_orderId].marketPrice, - orders[_orderId].marketSpread, - orders[_orderId].orderLeverage - ); - } - - function getPosition(address _address, bytes32 _marketId) public view returns ( - uint256 _positionLongShares, - uint256 _positionShortShares, - uint256 _positionAveragePrice, - uint256 _positionAverageSpread, - uint256 _positionAverageLeverage, - uint256 _liquidationPrice - ) { - return( - state.getLongShares(_address, _marketId), - state.getShortShares(_address, _marketId), - state.getMeanEntryPrice(_address,_marketId), - state.getMeanEntrySpread(_address,_marketId), - state.getMeanEntryLeverage(_address,_marketId), - state.getLiquidationPrice(_address,_marketId) - ); - } - - function setDeactivatedMarketPrice(bytes32 _marketId, uint256 _price) public onlyOracle { - state.setPosition( - closedMarketPriceLock, - _marketId, - now.mul(1000), - 0, - 0, - _price, - 0, - 0, - 0 - ); - - emit LockedPriceForClosingPositions(_marketId, _price); - - } - - function getDeactivatedMarketPrice(bytes32 _marketId) public view returns(uint256) { - ( , , uint positionForeverClosingPrice, , ,) = state.getPosition(closedMarketPriceLock, _marketId); - return positionForeverClosingPrice; - } - -// ---------------------------------------------------------------------------- -// liquidate(bytes32 _orderId) -// Checks for bankruptcy of position between its last update and now -// Time check is necessary to avoid two consecutive / unorderded liquidations -// ---------------------------------------------------------------------------- - - function liquidate(bytes32 _orderId) private { - address _address = orders[_orderId].userId; - bytes32 _marketId = orders[_orderId].marketId; - uint256 _liquidationTimestamp = orders[_orderId].liquidationTimestamp; - if (_liquidationTimestamp > state.getLastUpdated(_address, _marketId)) { - if (state.getLongShares(_address,_marketId) > 0) { - state.setPosition( - _address, - _marketId, - orders[_orderId].timeStamp, - 0, - state.getShortShares(_address, _marketId), - 0, - 0, - PRECISION, - 0); - emit PositionLiquidated( - _address, - _marketId, - true, - orders[_orderId].timeStamp, - orders[_orderId].marketPrice, - orders[_orderId].marketSpread - ); - } - if (state.getShortShares(_address,_marketId) > 0) { - state.setPosition( - _address, - _marketId, - orders[_orderId].timeStamp, - state.getLongShares(_address, _marketId), - 0, - 0, - 0, - PRECISION, - 0 - ); - emit PositionLiquidated( - _address, - _marketId, - false, - orders[_orderId].timeStamp, - orders[_orderId].marketPrice, - orders[_orderId].marketSpread - ); - } - } - } - -// ---------------------------------------------------------------------------- -// processOrder(bytes32 _orderId, uint256 _marketPrice, uint256 _marketSpread, uint256 _liquidationTimestamp, uint256 _timeStamp) -// ProcessOrder receives the price/spread/liqidation information from the Oracle and -// triggers the processing of the order. If successful, processOrder updates the portfolio state. -// Liquidation time check is necessary to avoid two consecutive / unorderded liquidations -// ---------------------------------------------------------------------------- - - function processOrder( - bytes32 _orderId, - uint256 _marketPrice, - uint256 _marketSpread, - uint256 _liquidationTimestamp, - uint256 _timeStampInMS - ) public onlyOracle returns ( - uint256 _newLongShares, - uint256 _newShortShares, - uint256 _newAverageEntry, - uint256 _newAverageSpread, - uint256 _newAverageLeverage, - uint256 _liquidationPrice - ) { - require(orders[_orderId].userId != address(0), "MorpherTradeEngine: unable to process, order has been deleted."); - require(_marketPrice > 0, "MorpherTradeEngine: market priced at zero. Buy order cannot be processed."); - require(_marketPrice >= _marketSpread, "MorpherTradeEngine: market price lower then market spread. Order cannot be processed."); - - orders[_orderId].marketPrice = _marketPrice; - orders[_orderId].marketSpread = _marketSpread; - orders[_orderId].timeStamp = _timeStampInMS; - orders[_orderId].liquidationTimestamp = _liquidationTimestamp; - - /** - * If the market is deactivated, then override the price with the locked in market price - * if the price wasn't locked in: error out. - */ - if(state.getMarketActive(orders[_orderId].marketId) == false) { - validateClosedMarketOrder(_orderId); - orders[_orderId].marketPrice = getDeactivatedMarketPrice(orders[_orderId].marketId); - } - - // Check if previous position on that market was liquidated - if (_liquidationTimestamp > state.getLastUpdated(orders[_orderId].userId, orders[_orderId].marketId)) { - liquidate(_orderId); - } else { - require(!userBlocking.userIsBlocked(orders[_orderId].userId), "MorpherTradeEngine: User is blocked from Trading"); - } - - - paybackEscrow(_orderId); - - if (orders[_orderId].tradeDirection) { - processBuyOrder(_orderId); - } else { - processSellOrder(_orderId); - } - - address _address = orders[_orderId].userId; - bytes32 _marketId = orders[_orderId].marketId; - delete orders[_orderId]; - emit OrderProcessed( - _orderId, - _marketPrice, - _marketSpread, - _liquidationTimestamp, - _timeStampInMS, - _newLongShares, - _newShortShares, - _newAverageEntry, - _newAverageSpread, - _newAverageLeverage, - _liquidationPrice - ); - - return ( - state.getLongShares(_address, _marketId), - state.getShortShares(_address, _marketId), - state.getMeanEntryPrice(_address,_marketId), - state.getMeanEntrySpread(_address,_marketId), - state.getMeanEntryLeverage(_address,_marketId), - state.getLiquidationPrice(_address,_marketId) - ); - } - -// ---------------------------------------------------------------------------- -// function cancelOrder(bytes32 _orderId, address _address) -// Users or Administrator can delete pending orders before the callback went through -// ---------------------------------------------------------------------------- - function cancelOrder(bytes32 _orderId, address _address) public onlyOracle { - require(_address == orders[_orderId].userId || _address == getAdministrator(), "MorpherTradeEngine: only Administrator or user can cancel an order."); - require(orders[_orderId].userId != address(0), "MorpherTradeEngine: unable to process, order does not exist."); - - /** - * Pay back any escrow there - */ - paybackEscrow(_orderId); - - delete orders[_orderId]; - emit OrderCancelled(_orderId, _address); - } - -// ---------------------------------------------------------------------------- -// shortShareValue / longShareValue compute the value of a virtual future -// given current price/spread/leverage of the market and mean price/spread/leverage -// at the beginning of the trade -// ---------------------------------------------------------------------------- - function shortShareValue( - uint256 _positionAveragePrice, - uint256 _positionAverageLeverage, - uint256 _positionTimeStampInMs, - uint256 _marketPrice, - uint256 _marketSpread, - uint256 _orderLeverage, - bool _sell - ) public view returns (uint256 _shareValue) { - - uint256 _averagePrice = _positionAveragePrice; - uint256 _averageLeverage = _positionAverageLeverage; - - if (_positionAverageLeverage < PRECISION) { - // Leverage can never be less than 1. Fail safe for empty positions, i.e. undefined _positionAverageLeverage - _averageLeverage = PRECISION; - } - if (_sell == false) { - // New short position - // It costs marketPrice + marketSpread to build up a new short position - _averagePrice = _marketPrice; - // This is the average Leverage - _averageLeverage = _orderLeverage; - } - if ( - getLiquidationPrice(_averagePrice, _averageLeverage, false, _positionTimeStampInMs) <= _marketPrice - ) { - // Position is worthless - _shareValue = 0; - } else { - // The regular share value is 2x the entry price minus the current price for short positions. - _shareValue = _averagePrice.mul((PRECISION.add(_averageLeverage))).div(PRECISION); - _shareValue = _shareValue.sub(_marketPrice.mul(_averageLeverage).div(PRECISION)); - if (_sell == true) { - // We have to reduce the share value by the average spread (i.e. the average expense to build up the position) - // and reduce the value further by the spread for selling. - _shareValue = _shareValue.sub(_marketSpread.mul(_averageLeverage).div(PRECISION)); - uint256 _marginInterest = calculateMarginInterest(_averagePrice, _averageLeverage, _positionTimeStampInMs); - if (_marginInterest <= _shareValue) { - _shareValue = _shareValue.sub(_marginInterest); - } else { - _shareValue = 0; - } - } else { - // If a new short position is built up each share costs value + spread - _shareValue = _shareValue.add(_marketSpread.mul(_orderLeverage).div(PRECISION)); - } - } - - return _shareValue; - } - - function longShareValue( - uint256 _positionAveragePrice, - uint256 _positionAverageLeverage, - uint256 _positionTimeStampInMs, - uint256 _marketPrice, - uint256 _marketSpread, - uint256 _orderLeverage, - bool _sell - ) public view returns (uint256 _shareValue) { - - uint256 _averagePrice = _positionAveragePrice; - uint256 _averageLeverage = _positionAverageLeverage; - - if (_positionAverageLeverage < PRECISION) { - // Leverage can never be less than 1. Fail safe for empty positions, i.e. undefined _positionAverageLeverage - _averageLeverage = PRECISION; - } - if (_sell == false) { - // New long position - // It costs marketPrice + marketSpread to build up a new long position - _averagePrice = _marketPrice; - // This is the average Leverage - _averageLeverage = _orderLeverage; - } - if ( - _marketPrice <= getLiquidationPrice(_averagePrice, _averageLeverage, true, _positionTimeStampInMs) - ) { - // Position is worthless - _shareValue = 0; - } else { - _shareValue = _averagePrice.mul(_averageLeverage.sub(PRECISION)).div(PRECISION); - // The regular share value is market price times leverage minus entry price times entry leverage minus one. - _shareValue = (_marketPrice.mul(_averageLeverage).div(PRECISION)).sub(_shareValue); - if (_sell == true) { - // We sell a long and have to correct the shareValue with the averageSpread and the currentSpread for selling. - _shareValue = _shareValue.sub(_marketSpread.mul(_averageLeverage).div(PRECISION)); - - uint256 _marginInterest = calculateMarginInterest(_averagePrice, _averageLeverage, _positionTimeStampInMs); - if (_marginInterest <= _shareValue) { - _shareValue = _shareValue.sub(_marginInterest); - } else { - _shareValue = 0; - } - } else { - // We buy a new long position and have to pay the spread - _shareValue = _shareValue.add(_marketSpread.mul(_orderLeverage).div(PRECISION)); - } - } - return _shareValue; - } - -// ---------------------------------------------------------------------------- -// calculateMarginInterest(uint256 _averagePrice, uint256 _averageLeverage, uint256 _positionTimeStamp) -// Calculates the interest for leveraged positions -// ---------------------------------------------------------------------------- - - - function calculateMarginInterest(uint256 _averagePrice, uint256 _averageLeverage, uint256 _positionTimeStampInMs) public view returns (uint256 _marginInterest) { - if (_positionTimeStampInMs.div(1000) < deployedTimeStamp) { - _positionTimeStampInMs = deployedTimeStamp.mul(1000); - } - _marginInterest = _averagePrice.mul(_averageLeverage.sub(PRECISION)); - _marginInterest = _marginInterest.mul((now.sub(_positionTimeStampInMs.div(1000)).div(86400)).add(1)); - _marginInterest = _marginInterest.mul(staking.getInterestRate(_positionTimeStampInMs.div(1000))).div(PRECISION).div(PRECISION); - return _marginInterest; - } - -// ---------------------------------------------------------------------------- -// processBuyOrder(bytes32 _orderId) -// Converts orders specified in virtual shares to orders specified in Morpher token -// and computes the number of short shares that are sold and long shares that are bought. -// long shares are bought only if the order amount exceeds all open short positions -// ---------------------------------------------------------------------------- - - function processBuyOrder(bytes32 _orderId) private { - if (orders[_orderId].closeSharesAmount > 0) { - //calcualte the balanceUp/down first - //then reopen the position with MPH amount - - // Investment was specified in shares - if (orders[_orderId].closeSharesAmount <= state.getShortShares(orders[_orderId].userId, orders[_orderId].marketId)) { - // Partial closing of short position - orders[_orderId].shortSharesOrder = orders[_orderId].closeSharesAmount; - } else { - // Closing of entire short position - orders[_orderId].shortSharesOrder = state.getShortShares(orders[_orderId].userId, orders[_orderId].marketId); - } - } - - //calculate the long shares, but only if the old position is completely closed out (if none exist shortSharesOrder = 0) - if( - orders[_orderId].shortSharesOrder == state.getShortShares(orders[_orderId].userId, orders[_orderId].marketId) && - orders[_orderId].openMPHTokenAmount > 0 - ) { - orders[_orderId].longSharesOrder = orders[_orderId].openMPHTokenAmount.div( - longShareValue( - orders[_orderId].marketPrice, - orders[_orderId].orderLeverage, - now.mul(1000), - orders[_orderId].marketPrice, - orders[_orderId].marketSpread, - orders[_orderId].orderLeverage, - false - )); - } - - // Investment equals number of shares now. - if (orders[_orderId].shortSharesOrder > 0) { - closeShort(_orderId); - } - if (orders[_orderId].longSharesOrder > 0) { - openLong(_orderId); - } - } - -// ---------------------------------------------------------------------------- -// processSellOrder(bytes32 _orderId) -// Converts orders specified in virtual shares to orders specified in Morpher token -// and computes the number of long shares that are sold and short shares that are bought. -// short shares are bought only if the order amount exceeds all open long positions -// ---------------------------------------------------------------------------- - - function processSellOrder(bytes32 _orderId) private { - if (orders[_orderId].closeSharesAmount > 0) { - //calcualte the balanceUp/down first - //then reopen the position with MPH amount - - // Investment was specified in shares - if (orders[_orderId].closeSharesAmount <= state.getLongShares(orders[_orderId].userId, orders[_orderId].marketId)) { - // Partial closing of long position - orders[_orderId].longSharesOrder = orders[_orderId].closeSharesAmount; - } else { - // Closing of entire long position - orders[_orderId].longSharesOrder = state.getLongShares(orders[_orderId].userId, orders[_orderId].marketId); - } - } - - if( - orders[_orderId].longSharesOrder == state.getLongShares(orders[_orderId].userId, orders[_orderId].marketId) && - orders[_orderId].openMPHTokenAmount > 0 - ) { - orders[_orderId].shortSharesOrder = orders[_orderId].openMPHTokenAmount.div( - shortShareValue( - orders[_orderId].marketPrice, - orders[_orderId].orderLeverage, - now.mul(1000), - orders[_orderId].marketPrice, - orders[_orderId].marketSpread, - orders[_orderId].orderLeverage, - false - )); - } - // Investment equals number of shares now. - if (orders[_orderId].longSharesOrder > 0) { - closeLong(_orderId); - } - if (orders[_orderId].shortSharesOrder > 0) { - openShort(_orderId); - } - } - -// ---------------------------------------------------------------------------- -// openLong(bytes32 _orderId) -// Opens a new long position and computes the new resulting average entry price/spread/leverage. -// Computation is broken down to several instructions for readability. -// ---------------------------------------------------------------------------- - function openLong(bytes32 _orderId) private { - address _userId = orders[_orderId].userId; - bytes32 _marketId = orders[_orderId].marketId; - - uint256 _newMeanSpread; - uint256 _newMeanLeverage; - - // Existing position is virtually liquidated and reopened with current marketPrice - // orders[_orderId].newMeanEntryPrice = orders[_orderId].marketPrice; - // _factorLongShares is a factor to adjust the existing longShares via virtual liqudiation and reopening at current market price - - uint256 _factorLongShares = state.getMeanEntryLeverage(_userId, _marketId); - if (_factorLongShares < PRECISION) { - _factorLongShares = PRECISION; - } - _factorLongShares = _factorLongShares.sub(PRECISION); - _factorLongShares = _factorLongShares.mul(state.getMeanEntryPrice(_userId, _marketId)).div(orders[_orderId].marketPrice); - if (state.getMeanEntryLeverage(_userId, _marketId) > _factorLongShares) { - _factorLongShares = state.getMeanEntryLeverage(_userId, _marketId).sub(_factorLongShares); - } else { - _factorLongShares = 0; - } - - uint256 _adjustedLongShares = _factorLongShares.mul(state.getLongShares(_userId, _marketId)).div(PRECISION); - - // _newMeanLeverage is the weighted leverage of the existing position and the new position - _newMeanLeverage = state.getMeanEntryLeverage(_userId, _marketId).mul(_adjustedLongShares); - _newMeanLeverage = _newMeanLeverage.add(orders[_orderId].orderLeverage.mul(orders[_orderId].longSharesOrder)); - _newMeanLeverage = _newMeanLeverage.div(_adjustedLongShares.add(orders[_orderId].longSharesOrder)); - - // _newMeanSpread is the weighted spread of the existing position and the new position - _newMeanSpread = state.getMeanEntrySpread(_userId, _marketId).mul(state.getLongShares(_userId, _marketId)); - _newMeanSpread = _newMeanSpread.add(orders[_orderId].marketSpread.mul(orders[_orderId].longSharesOrder)); - _newMeanSpread = _newMeanSpread.div(_adjustedLongShares.add(orders[_orderId].longSharesOrder)); - - orders[_orderId].balanceDown = orders[_orderId].longSharesOrder.mul(orders[_orderId].marketPrice).add( - orders[_orderId].longSharesOrder.mul(orders[_orderId].marketSpread).mul(orders[_orderId].orderLeverage).div(PRECISION) - ); - orders[_orderId].balanceUp = 0; - orders[_orderId].newLongShares = _adjustedLongShares.add(orders[_orderId].longSharesOrder); - orders[_orderId].newShortShares = state.getShortShares(_userId, _marketId); - orders[_orderId].newMeanEntryPrice = orders[_orderId].marketPrice; - orders[_orderId].newMeanEntrySpread = _newMeanSpread; - orders[_orderId].newMeanEntryLeverage = _newMeanLeverage; - - setPositionInState(_orderId); - } -// ---------------------------------------------------------------------------- -// closeLong(bytes32 _orderId) -// Closes an existing long position. Average entry price/spread/leverage do not change. -// ---------------------------------------------------------------------------- - function closeLong(bytes32 _orderId) private { - address _userId = orders[_orderId].userId; - bytes32 _marketId = orders[_orderId].marketId; - uint256 _newLongShares = state.getLongShares(_userId, _marketId).sub(orders[_orderId].longSharesOrder); - uint256 _balanceUp = calculateBalanceUp(_orderId); - uint256 _newMeanEntry; - uint256 _newMeanSpread; - uint256 _newMeanLeverage; - - if (orders[_orderId].longSharesOrder == state.getLongShares(_userId, _marketId)) { - _newMeanEntry = 0; - _newMeanSpread = 0; - _newMeanLeverage = PRECISION; - } else { - _newMeanEntry = state.getMeanEntryPrice(_userId, _marketId); - _newMeanSpread = state.getMeanEntrySpread(_userId, _marketId); - _newMeanLeverage = state.getMeanEntryLeverage(_userId, _marketId); - resetTimestampInOrderToLastUpdated(_orderId); - } - - orders[_orderId].balanceDown = 0; - orders[_orderId].balanceUp = _balanceUp; - orders[_orderId].newLongShares = _newLongShares; - orders[_orderId].newShortShares = state.getShortShares(_userId, _marketId); - orders[_orderId].newMeanEntryPrice = _newMeanEntry; - orders[_orderId].newMeanEntrySpread = _newMeanSpread; - orders[_orderId].newMeanEntryLeverage = _newMeanLeverage; - - setPositionInState(_orderId); - } - -event ResetTimestampInOrder(bytes32 _orderId, uint oldTimestamp, uint newTimestamp); -function resetTimestampInOrderToLastUpdated(bytes32 _orderId) internal { - address userId = orders[_orderId].userId; - bytes32 marketId = orders[_orderId].marketId; - uint lastUpdated = state.getLastUpdated(userId, marketId); - emit ResetTimestampInOrder(_orderId, orders[_orderId].timeStamp, lastUpdated); - orders[_orderId].timeStamp = lastUpdated; -} - -// ---------------------------------------------------------------------------- -// closeShort(bytes32 _orderId) -// Closes an existing short position. Average entry price/spread/leverage do not change. -// ---------------------------------------------------------------------------- -function calculateBalanceUp(bytes32 _orderId) private view returns (uint256 _balanceUp) { - address _userId = orders[_orderId].userId; - bytes32 _marketId = orders[_orderId].marketId; - uint256 _shareValue; - - if (orders[_orderId].tradeDirection == false) { //we are selling our long shares - _balanceUp = orders[_orderId].longSharesOrder; - _shareValue = longShareValue( - state.getMeanEntryPrice(_userId, _marketId), - state.getMeanEntryLeverage(_userId, _marketId), - state.getLastUpdated(_userId, _marketId), - orders[_orderId].marketPrice, - orders[_orderId].marketSpread, - state.getMeanEntryLeverage(_userId, _marketId), - true - ); - } else { //we are going long, we are selling our short shares - _balanceUp = orders[_orderId].shortSharesOrder; - _shareValue = shortShareValue( - state.getMeanEntryPrice(_userId, _marketId), - state.getMeanEntryLeverage(_userId, _marketId), - state.getLastUpdated(_userId, _marketId), - orders[_orderId].marketPrice, - orders[_orderId].marketSpread, - state.getMeanEntryLeverage(_userId, _marketId), - true - ); - } - return _balanceUp.mul(_shareValue); - } - - function closeShort(bytes32 _orderId) private { - address _userId = orders[_orderId].userId; - bytes32 _marketId = orders[_orderId].marketId; - uint256 _newMeanEntry; - uint256 _newMeanSpread; - uint256 _newMeanLeverage; - uint256 _newShortShares = state.getShortShares(_userId, _marketId).sub(orders[_orderId].shortSharesOrder); - uint256 _balanceUp = calculateBalanceUp(_orderId); - - if (orders[_orderId].shortSharesOrder == state.getShortShares(_userId, _marketId)) { - _newMeanEntry = 0; - _newMeanSpread = 0; - _newMeanLeverage = PRECISION; - } else { - _newMeanEntry = state.getMeanEntryPrice(_userId, _marketId); - _newMeanSpread = state.getMeanEntrySpread(_userId, _marketId); - _newMeanLeverage = state.getMeanEntryLeverage(_userId, _marketId); - - /** - * we need the timestamp of the old order for partial closes, not the new one - */ - resetTimestampInOrderToLastUpdated(_orderId); - } - - orders[_orderId].balanceDown = 0; - orders[_orderId].balanceUp = _balanceUp; - orders[_orderId].newLongShares = state.getLongShares(orders[_orderId].userId, orders[_orderId].marketId); - orders[_orderId].newShortShares = _newShortShares; - orders[_orderId].newMeanEntryPrice = _newMeanEntry; - orders[_orderId].newMeanEntrySpread = _newMeanSpread; - orders[_orderId].newMeanEntryLeverage = _newMeanLeverage; - - setPositionInState(_orderId); - } - -// ---------------------------------------------------------------------------- -// openShort(bytes32 _orderId) -// Opens a new short position and computes the new resulting average entry price/spread/leverage. -// Computation is broken down to several instructions for readability. -// ---------------------------------------------------------------------------- - function openShort(bytes32 _orderId) private { - address _userId = orders[_orderId].userId; - bytes32 _marketId = orders[_orderId].marketId; - - uint256 _newMeanSpread; - uint256 _newMeanLeverage; - // - // Existing position is virtually liquidated and reopened with current marketPrice - // orders[_orderId].newMeanEntryPrice = orders[_orderId].marketPrice; - // _factorShortShares is a factor to adjust the existing shortShares via virtual liqudiation and reopening at current market price - - uint256 _factorShortShares = state.getMeanEntryLeverage(_userId, _marketId); - if (_factorShortShares < PRECISION) { - _factorShortShares = PRECISION; - } - _factorShortShares = _factorShortShares.add(PRECISION); - _factorShortShares = _factorShortShares.mul(state.getMeanEntryPrice(_userId, _marketId)).div(orders[_orderId].marketPrice); - if (state.getMeanEntryLeverage(_userId, _marketId) < _factorShortShares) { - _factorShortShares = _factorShortShares.sub(state.getMeanEntryLeverage(_userId, _marketId)); - } else { - _factorShortShares = 0; - } - - uint256 _adjustedShortShares = _factorShortShares.mul(state.getShortShares(_userId, _marketId)).div(PRECISION); - - // _newMeanLeverage is the weighted leverage of the existing position and the new position - _newMeanLeverage = state.getMeanEntryLeverage(_userId, _marketId).mul(_adjustedShortShares); - _newMeanLeverage = _newMeanLeverage.add(orders[_orderId].orderLeverage.mul(orders[_orderId].shortSharesOrder)); - _newMeanLeverage = _newMeanLeverage.div(_adjustedShortShares.add(orders[_orderId].shortSharesOrder)); - - // _newMeanSpread is the weighted spread of the existing position and the new position - _newMeanSpread = state.getMeanEntrySpread(_userId, _marketId).mul(state.getShortShares(_userId, _marketId)); - _newMeanSpread = _newMeanSpread.add(orders[_orderId].marketSpread.mul(orders[_orderId].shortSharesOrder)); - _newMeanSpread = _newMeanSpread.div(_adjustedShortShares.add(orders[_orderId].shortSharesOrder)); - - orders[_orderId].balanceDown = orders[_orderId].shortSharesOrder.mul(orders[_orderId].marketPrice).add( - orders[_orderId].shortSharesOrder.mul(orders[_orderId].marketSpread).mul(orders[_orderId].orderLeverage).div(PRECISION) - ); - orders[_orderId].balanceUp = 0; - orders[_orderId].newLongShares = state.getLongShares(_userId, _marketId); - orders[_orderId].newShortShares = _adjustedShortShares.add(orders[_orderId].shortSharesOrder); - orders[_orderId].newMeanEntryPrice = orders[_orderId].marketPrice; - orders[_orderId].newMeanEntrySpread = _newMeanSpread; - orders[_orderId].newMeanEntryLeverage = _newMeanLeverage; - - setPositionInState(_orderId); - } - - function computeLiquidationPrice(bytes32 _orderId) public returns(uint256 _liquidationPrice) { - orders[_orderId].newLiquidationPrice = 0; - if (orders[_orderId].newLongShares > 0) { - orders[_orderId].newLiquidationPrice = getLiquidationPrice(orders[_orderId].newMeanEntryPrice, orders[_orderId].newMeanEntryLeverage, true, orders[_orderId].timeStamp); - } - if (orders[_orderId].newShortShares > 0) { - orders[_orderId].newLiquidationPrice = getLiquidationPrice(orders[_orderId].newMeanEntryPrice, orders[_orderId].newMeanEntryLeverage, false, orders[_orderId].timeStamp); - } - return orders[_orderId].newLiquidationPrice; - } - - function getLiquidationPrice(uint256 _newMeanEntryPrice, uint256 _newMeanEntryLeverage, bool _long, uint _positionTimestampInMs) public view returns (uint256 _liquidationPrice) { - if (_long == true) { - _liquidationPrice = _newMeanEntryPrice.mul(_newMeanEntryLeverage.sub(PRECISION)).div(_newMeanEntryLeverage); - _liquidationPrice = _liquidationPrice.add(calculateMarginInterest(_newMeanEntryPrice, _newMeanEntryLeverage, _positionTimestampInMs).mul(PRECISION).div(_newMeanEntryLeverage)); - } else { - _liquidationPrice = _newMeanEntryPrice.mul(_newMeanEntryLeverage.add(PRECISION)).div(_newMeanEntryLeverage); - _liquidationPrice = _liquidationPrice.sub(calculateMarginInterest(_newMeanEntryPrice, _newMeanEntryLeverage, _positionTimestampInMs).mul(PRECISION).div(_newMeanEntryLeverage)); - } - return _liquidationPrice; - } - - -// ---------------------------------------------------------------------------- -// setPositionInState(bytes32 _orderId) -// Updates the portfolio in Morpher State. Called by closeLong/closeShort/openLong/openShort -// ---------------------------------------------------------------------------- - function setPositionInState(bytes32 _orderId) private { - require(state.balanceOf(orders[_orderId].userId).add(orders[_orderId].balanceUp) >= orders[_orderId].balanceDown, "MorpherTradeEngine: insufficient funds."); - computeLiquidationPrice(_orderId); - // Net balanceUp and balanceDown - if (orders[_orderId].balanceUp > orders[_orderId].balanceDown) { - orders[_orderId].balanceUp.sub(orders[_orderId].balanceDown); - orders[_orderId].balanceDown = 0; - } else { - orders[_orderId].balanceDown.sub(orders[_orderId].balanceUp); - orders[_orderId].balanceUp = 0; - } - if (orders[_orderId].balanceUp > 0) { - mintingLimiter.mint(orders[_orderId].userId, orders[_orderId].balanceUp); - } - if (orders[_orderId].balanceDown > 0) { - state.burn(orders[_orderId].userId, orders[_orderId].balanceDown); - } - state.setPosition( - orders[_orderId].userId, - orders[_orderId].marketId, - orders[_orderId].timeStamp, - orders[_orderId].newLongShares, - orders[_orderId].newShortShares, - orders[_orderId].newMeanEntryPrice, - orders[_orderId].newMeanEntrySpread, - orders[_orderId].newMeanEntryLeverage, - orders[_orderId].newLiquidationPrice - ); - emit PositionUpdated( - orders[_orderId].userId, - orders[_orderId].marketId, - orders[_orderId].timeStamp, - orders[_orderId].newLongShares, - orders[_orderId].newShortShares, - orders[_orderId].newMeanEntryPrice, - orders[_orderId].newMeanEntrySpread, - orders[_orderId].newMeanEntryLeverage, - orders[_orderId].newLiquidationPrice, - orders[_orderId].balanceUp, - orders[_orderId].balanceDown - ); - } -} diff --git a/contracts/MorpherUserBlocking.sol b/contracts/MorpherUserBlocking.sol deleted file mode 100644 index 838a1d3..0000000 --- a/contracts/MorpherUserBlocking.sol +++ /dev/null @@ -1,40 +0,0 @@ -pragma solidity 0.5.16; - -import "./MorpherState.sol"; - - -contract MorpherUserBlocking { - mapping(address => bool) public userIsBlocked; - MorpherState state; - - address public allowedToAddBlockedUsersAddress; - - event ChangeUserBlocked(address _user, bool _oldIsBlocked, bool _newIsBlocked); - event ChangedAddressAllowedToAddBlockedUsersAddress(address _oldAddress, address _newAddress); - - constructor(address _state, address _allowedToAddBlockedUsersAddress) public { - state = MorpherState(_state); - emit ChangedAddressAllowedToAddBlockedUsersAddress(address(0), _allowedToAddBlockedUsersAddress); - allowedToAddBlockedUsersAddress = _allowedToAddBlockedUsersAddress; - } - - modifier onlyAdministrator() { - require(msg.sender == state.getAdministrator(), "UserBlocking: Only Administrator can call this function"); - _; - } - - modifier onlyAllowedUsers() { - require(msg.sender == state.getAdministrator() || msg.sender == allowedToAddBlockedUsersAddress, "UserBlocking: Only White-Listed Users can call this function"); - _; - } - - function setAllowedToAddBlockedUsersAddress(address _newAddress) public onlyAdministrator { - emit ChangedAddressAllowedToAddBlockedUsersAddress(allowedToAddBlockedUsersAddress, _newAddress); - allowedToAddBlockedUsersAddress = _newAddress; - } - - function setUserBlocked(address _user, bool _isBlocked) public onlyAllowedUsers { - emit ChangeUserBlocked(_user, userIsBlocked[_user], _isBlocked); - userIsBlocked[_user] = _isBlocked; - } -} \ No newline at end of file diff --git a/contracts/Ownable.sol b/contracts/Ownable.sol deleted file mode 100644 index 540d6a6..0000000 --- a/contracts/Ownable.sol +++ /dev/null @@ -1,81 +0,0 @@ -pragma solidity 0.5.16; - -import "./IERC20.sol"; - -/** - * @title Ownable - * @dev The Ownable contract has an owner address, and provides basic authorization control - * functions, this simplifies the implementation of "user permissions". - */ -contract Ownable { - address public _owner; - - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); - - /** - * @dev The Ownable constructor sets the original `owner` of the contract to the sender - * account. - */ - constructor () internal { - _owner = msg.sender; - emit OwnershipTransferred(address(0), _owner); - } - - /** - * @return the address of the owner. - */ - function owner() public view returns (address) { - return _owner; - } - - /** - * @dev Throws if called by any account other than the owner. - */ - modifier onlyOwner() { - require(isOwner(), "Ownable: caller should be owner."); - _; - } - - /** - * @return true if `msg.sender` is the owner of the contract. - */ - function isOwner() public view returns (bool) { - return msg.sender == _owner; - } - - /** - * @dev Allows the current owner to relinquish control of the contract. - * It will not be possible to call the functions with the `onlyOwner` - * modifier anymore. - * @notice Renouncing ownership will leave the contract without an owner, - * thereby removing any functionality that is only available to the owner. - */ - function renounceOwnership() public onlyOwner { - emit OwnershipTransferred(_owner, address(0)); - _owner = address(0); - } - - /** - * @dev Allows the current owner to transfer control of the contract to a newOwner. - * @param newOwner The address to transfer ownership to. - */ - function transferOwnership(address newOwner) public onlyOwner { - _transferOwnership(newOwner); - } - - /** - * @dev Transfers control of the contract to a newOwner. - * @param newOwner The address to transfer ownership to. - */ - function _transferOwnership(address newOwner) internal { - require(newOwner != address(0), "Ownable: use renounce ownership instead."); - emit OwnershipTransferred(_owner, newOwner); - _owner = newOwner; - } - // ------------------------------------------------------------------------ - // Owner can transfer out any accidentally sent ERC20 tokens - // ------------------------------------------------------------------------ - function transferAnyERC20Token(address _tokenAddress, uint256 _tokens) public onlyOwner returns (bool _success) { - return IERC20(_tokenAddress).transfer(owner(), _tokens); - } -} diff --git a/contracts/SafeMath.sol b/contracts/SafeMath.sol deleted file mode 100644 index 9e472de..0000000 --- a/contracts/SafeMath.sol +++ /dev/null @@ -1,156 +0,0 @@ -pragma solidity 0.5.16; - -/** - * @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; - } -} diff --git a/docs/2_deploy_contracts.js.bpk b/docs/2_deploy_contracts.js.bpk deleted file mode 100644 index 408bb36..0000000 --- a/docs/2_deploy_contracts.js.bpk +++ /dev/null @@ -1,166 +0,0 @@ -/* THIS FILE IS THE OLD MIGRATIONS FILE BEFORE RE-WRITING IT FOR BACKUP PURPOSES */ -/* IT'S AN ALL-IN-ONE-MIGRATIONS FILE */ - -const Web3 = require('web3'); -const EthereumTx = require('ethereumjs-tx'); - -const MorpherState = artifacts.require("MorpherState"); -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); -const MorpherBridge = artifacts.require("MorpherBridge"); -const MorpherGovernance = artifacts.require("MorpherGovernance"); -const MorpherAirdrop = artifacts.require("MorpherAirdrop"); -const MorpherEscrow = artifacts.require("MorpherEscrow"); -const MorpherOracle = artifacts.require("MorpherOracle"); - -const CRYPTO_BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9'; -const CRYPTO_ETH = '0x5376ff169a3705b2003892fe730060ee74ec83e5701da29318221aa782271779'; - -module.exports = async function(deployer, network, accounts) { - let web3; - - const deployerAddress = process.env.MORPHER_DEPLOYER; - const deployerKey = Buffer.from(process.env.MORPHER_DEPLOYER_KEY, 'hex'); - - let ownerAddress = process.env.MORPHER_OWNER; - let treasuryAddress = process.env.MORPHER_TREASURY; - let administratorAddress = process.env.MORPHER_ADMINISTRATOR; - let callbackAddress = process.env.CALLBACK_ADDRESS; - let gasCollectionAddress = process.env.GAS_COLLECTION; - let sidechainOperatorAddress = process.env.SIDECHAIN_OPERATOR; - let airdropAdminAddress = process.env.AIRDROP_ADMIN; - - if(network === 'local'){ - web3 = new Web3('http://127.0.0.1:7545'); - ownerAddress = deployerAddress; - treasuryAddress = deployerAddress; - administratorAddress = deployerAddress; - callbackAddress = deployerAddress; - gasCollectionAddress = deployerAddress; - sidechainOperatorAddress = deployerAddress; - airdropAdminAddress = deployerAddress - } - - await deployer.deploy(MorpherState, true, sidechainOperatorAddress, deployerAddress); // deployer is changed to owner later - await deployer.deploy(MorpherToken, MorpherState.address, deployerAddress); // deployer is changed to owner later - await deployer.deploy(MorpherTradeEngine, MorpherState.address, ownerAddress); - await deployer.deploy(MorpherBridge, MorpherState.address, ownerAddress); - await deployer.deploy(MorpherGovernance, MorpherState.address, ownerAddress); - await deployer.deploy(MorpherAirdrop, airdropAdminAddress, MorpherToken.address, ownerAddress); - await deployer.deploy(MorpherEscrow, treasuryAddress, MorpherToken.address, ownerAddress); - await deployer.deploy(MorpherOracle, MorpherTradeEngine.address, callbackAddress, gasCollectionAddress, 0, deployerAddress); // deployer is changed to owner later - - if(network === 'local'){ - let data; - - const morpherState = new web3.eth.Contract(MorpherState.abi, MorpherState.address); - - // ------ MorpherStateBeta ------ - // grantAccess(morpherTokenAddress) - // grantAccess(morpherTradeEngineAddress) - // grantAccess(morpherBridgeAddress) - // grantAccess(morpherGovernanceAddress) - // setSideChainOperator(sideChainOperatorAddress) - data = await morpherState.methods.grantAccess(MorpherToken.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Access granted to MorpherToken.'); - - data = await morpherState.methods.grantAccess(MorpherTradeEngine.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Access granted to MorpherTradeEngine.'); - - data = await morpherState.methods.grantAccess(MorpherBridge.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Access granted to MorpherBridge.'); - - data = await morpherState.methods.grantAccess(MorpherGovernance.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Access granted to MorpherGovernance.'); - - data = await morpherState.methods.grantAccess(deployerAddress); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Access granted to MorpherGovernance.'); - - data = await morpherState.methods.setSideChainOperator(deployerAddress); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('SidechainOperator set.'); - - // ------ Only relevant on sidechain ------ - // enableTransfers(addressOfDeployer) - // enableTransfers(morpherAirdropAddress) - data = await morpherState.methods.enableTransfers(deployerAddress); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Transfers enabled for deployerAddress.'); - - data = await morpherState.methods.enableTransfers(MorpherAirdrop.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Transfers enabled for MorpherAirdrop.'); - - // ------ To have an Administrator and Oracle until there is a vote in the governance contract ------ - // setGovernanceContract(addressOfDeployer) - // setAdministrator(addressOfDeployer) - data = await morpherState.methods.setGovernanceContract(deployerAddress); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('GovernanceContract set.'); - - data = await morpherState.methods.setAdministrator(deployerAddress); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Administrator set.'); - - // ------ Set protocol contracts in state ------ - // setTokenContract(morpherTokenAddress) - // setMorpherBridge(bridgeAddress) - // setOracleContract(oracleAddress) - data = await morpherState.methods.setTokenContract(MorpherToken.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('MorpherToken set.'); - - data = await morpherState.methods.setMorpherBridge(MorpherBridge.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('MorpherBridge set.'); - - data = await morpherState.methods.setOracleContract(MorpherOracle.address); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('MorpherOracle set.'); - - // ------ Enable "CRYPTO_BTC" and "CRYPTO_ETH" as markets for testing purposes ------ - data = await morpherState.methods.activateMarket(CRYPTO_BTC); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('CRYPTO_BTC market enabled.'); - - data = await morpherState.methods.activateMarket(CRYPTO_ETH); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('CRYPTO_ETH market enabled.'); - - // ------ MorpherState: set Governance properly ------ - // ONLY MAIN CHAIN: setGovernanceContract(morpherGovernanceAddress) - // transferOwnership(ownerAddress) - data = await morpherState.methods.transferOwnership(ownerAddress); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Ownership transferred to ownerAddress.'); - } -}; - - -// Helper function to parse and send transactions to the blockchain. -async function sendTransactionFrom(account, data, privateKey, contractAddress) { - const nonce = await web3.eth.getTransactionCount(account); - - const transactionData = { - nonce, - from: account, - to: contractAddress, - gasLimit: 8000000, - gasPrice: 1000000000, - data: data.encodeABI() - }; - - const tx = new EthereumTx(transactionData); - - /** - * Signing the transaction - * */ - tx.sign(privateKey); - const raw = '0x' + tx.serialize().toString('hex'); - return await web3.eth.sendSignedTransaction(raw); -} diff --git a/docs/addressesAndRoles.json b/docs/addressesAndRoles.json index 77e8169..32fcef9 100644 --- a/docs/addressesAndRoles.json +++ b/docs/addressesAndRoles.json @@ -9,7 +9,7 @@ "address": "0xC44628734a9432a3DAA302E11AfbdFa8361424A5" }, "MorpherBridge": { - "address": "0x43e3f95d110ad4aF1e24AAffA81351BD41C0A652", + "address": "0xd4399F4f73A9e84c0A788D582B89F3702b4dA781", "oldAddresses": [ { "address": "0x43e3f95d110ad4aF1e24AAffA81351BD41C0A652", diff --git a/docs/markets.js b/docs/markets.js deleted file mode 100644 index 233f83f..0000000 --- a/docs/markets.js +++ /dev/null @@ -1,654 +0,0 @@ -module.exports = { - '0x368684ce6dd192a072abcb1db67abc080c39fbddfcb1ee8921388ee2bc680392': 'STOCK_CRM', - '0xe5f524899a2e636f420bff5a187075fa7af15927f86bb4b499066237f80c7e7a': 'STOCK_ITW', - '0x03049bc4bfafb2ea1a76ba0b9e544d5fb7d269982dbdfb7e10d718e5dfed936b': 'STOCK_COTY', - '0x734b29078d0e6278d65efa2d4e14d24fe4a33693339bb183dd83576e6d12a98b': 'CRYPTO_TRX', - '0xce76ca29bf3f759b8a18ee52f0ff971b0883f863d995c6c7de0a2a30f4634c43': 'STOCK_AAP', - '0xf7eef97f15758a6bfddc965ede764783706305f61007ccb377b410d61c0b8297': 'STOCK_ULTA', - '0x6d22fc6f7155823a61e2e51d5115923333551a01604baa6bbfa37284734362a9': 'STOCK_ATVI', - '0x977b331c540cf130de411765864d0c198cc56b8d8775fad62914da4352697843': 'STOCK_UDR', - '0xa9df2cd33d9d7e27c1890ac68aa37c3b3d0fad09802a53133f6fc7e394f9c215': 'FOREX_USD_SEK', - '0x28084e753ce304f1ae88317f7f588ff07be4f0700f65fa37f13bc0b05dfa1c93': 'STOCK_SGEN', - '0x0974e938b22d72eb9869967a639f0cdd281692b318d2663f2017d63d0ade3a72': 'STOCK_PAYX', - '0x03a1ed54014662b0d820fcc2c3f4a3ac6bf2daaac5519bb3515bd6b5a5a004f4': 'STOCK_GE', - '0x33064f0b016383fbe039a40502964985ef8b3333546cbc6571289e1091257dee': 'STOCK_NOW', - '0xb078d49c4f8d3ca8b8f8ed20e9b6b01dd43e32cff04b55722e2ad14d6a8970ad': 'STOCK_ZION', - '0x5373ddeefccbbf2257a639060e84a0286a3c1ad181d43c471a894fe74ea6146b': 'STOCK_AMT', - '0x6d2bec48e243f6c9a0a429ee1662c5b36144685c222d14eb89fb53bfbffef010': 'STOCK_L', - '0x09203003ce01180ddb4ea48720ebb971d2f407ed7cf4871e1f525e7a8f60eaff': 'STOCK_WELL', - '0x1624f636824e40f74ebd18ba5264ca377101b71c083a583e92df328bcbafa209': 'FOREX_GBP_USD', - '0x11ec3da1b2081482b3820a3d37ad2b78027fe97d75fb4c651bb9ce3845ed3d28': 'STOCK_FTV', - '0x0dd0dd557abbe5637d936943efe51b858311cf085c399adff94af527eefa877e': 'FOREX_USD_ZAR', - '0x0f113ebdcafd2aa7275df93e10e4fa4258db1066fa3eb60fadfb656ad69ea2f7': 'STOCK_HOG', - '0x4263d89054d0219c59dacc49e9dc3756a78290c3fc0c0cf4169214fe2bb3e4ce': 'STOCK_ABC', - '0xa6b06f187ee9b8d9af35191297d14b12deec2d7c0d453275ddcda137d05578fe': 'STOCK_CRSP', - '0x4c689a0d4fee23711b0021c4b817414343657f191d7ec9c4251cad8947e99e5a': 'STOCK_ED', - '0xe3dce946e6d0f779d6b94ec02ca93ae6d4da4957a9fddc9ce07284385b70536a': 'STOCK_ORLY', - '0x5803c592f0ad6aaf964bba8e1a8027bbd28e2bcf59c006f7cddd8dd615190dd6': 'STOCK_HIG', - '0xb09a3017f449763544ddedd26a3efa0e35f3bad0ceec901215689211eda5f529': 'STOCK_KR', - '0x3808b6d61633d335dfbb80cb1e1e43e2a9e4a839834d998ccc38399686c1d3a7': 'STOCK_EOG', - '0x01bf1260847fa4c292ea8442b7bc650e5228575f5878776ba42f9f7ed11378cf': 'STOCK_SJM', - '0x89365cef9fcb3898f6ed1196384f1ba082a0c310814afcb089afc5ba1741cf3d': 'STOCK_COO', - '0xac4a30b780dbbcc59fb2af819d70cc08ed280829114e10ffee3604da2d060574': 'STOCK_INTC', - '0x02149a56a425b87aedc0f1fb306b19e232ac40d13f308e5c8bde067c2d374515': 'STOCK_NI', - '0x5a2f38fae862c17fde0daa9a98264a8db379d9e688e01a52b98f2eeef1bad61f': 'STOCK_SPOT', - '0x29a528cdfac9b909dbeb9e7bc0c71a7ca4a6b215996ac196cdef4d829860ea7d': 'STOCK_AAPL', - '0xd50aa9a5bb27ee871168cf2af440fecf8e2fe28d1fc9e8218855497c58afbd7d': 'STOCK_CME', - '0x99dd3e35bfe3a0f819e9fb38001a0c74c40d4a315433640415ba153b899a3178': 'STOCK_DD', - '0x9b2e1cb5941605667246beb5fe041273706ceae603af073a63f0dbbb41e4380f': 'STOCK_SNPS', - '0x3de64e3e7b395e2ce94b64d66cfe601698bb907345d51902e3a2087c6862f561': 'STOCK_PVH', - '0x28a59c2b2e1737cca00dddd6bda4e82e3761a9332fabfe3404d495a29696976c': 'STOCK_VZ', - '0x82d106943f154487ef985db6bbf7ccac79b534b2222c9d167a513b6b1edb8f2c': 'STOCK_GL', - '0xb6e56c6f699e32535f9dcec751e13114469e76b0539279d0e6cc067a04e3be30': 'STOCK_SPG', - '0x367322e64682f88c8f96fa08b16aa4b789286d6b353222bec748741388ac019b': 'STOCK_GLW', - '0x821d56bb1554075f0332c3c765f8b5e47d64ee731efa39ebefd577a399b1949f': 'STOCK_AJG', - '0x2cd0e79b08960941a7f1b4f89d21f3a12393c9643e77245502b78f9d1b60140e': 'STOCK_ROKU', - '0x4ca7df2e8565a3db9b668e992c093dfb7e002390eeff1b55513d092611cd350a': 'STOCK_AWK', - '0xac347724357018ece9b115ceb5381e3e265e900e5185ef757d5d10df3515b638': 'STOCK_XEL', - '0x94df19d997e009bf62d2912b68d43d8bdebbe4919e138559c2d99eeeda3545b2': 'STOCK_DISCA', - '0x283004a9f580215ab404e83fca11c4f5be7980f768014fe66c48712ab2fd0e28': 'STOCK_GIS', - '0xefde72987b32a47657b9595879c94b1c7966f001bcb19ca030126cf14b85fd7c': 'STOCK_HP', - '0x357f9a4249c29f9baec4dbf2c45e2e0181fed44944b892d263af2e1f875c588e': 'STOCK_LH', - '0x39a872d5c3e45de395d0982809d4f29fef6218389029a24d2dc99748849055f6': 'STOCK_SEE', - '0x7f99942ffdd031eb2d6b52958fe0639296eef5c3ce1a5356368c4757462a179f': 'STOCK_ADSK', - '0xfc50c4690aec3abcacc02b39ac8c6af6a85e0b023bdd6ae668eeb94d18b34478': 'STOCK_CCI', - '0x0f87eaa0b681b227e206706330477fa9a50a380612cc72297a831c130737d31a': 'STOCK_INFO', - '0x265f05039f8419c5046c20c0396f8f7a7126765dc3a34646571d3a6aedcb12de': 'STOCK_JD', - '0x1e4546b2c3feb5b10b70e9ff4654e3d2faf6d8210878425c5be760e65696de2a': 'FOREX_USD_DKK', - '0xe57bbda620d9b6da925236efb9f528791c0170aafc45dd3cf6e5f12f8a17738a': 'STOCK_PH', - '0x86524e67a46b7d60f13e3b6a507202f093792af784421647d92cb7d097e414f9': 'STOCK_APH', - '0x0e9895c2e0ac41329e11e434dec3b09af9ac9f9ad6b8665b5eae97eae0e234ae': 'STOCK_KMX', - '0x620b0ad552380e14459b91cb9fd6904ffd59599124b700d8bbdae5b058459a2d': 'STOCK_AIV', - '0x07835d621e2f4c41a4d982f014cb70f78a41751c0ff91082659060234fe0bbc5': 'CRYPTO_ONT', - '0x2ad8e64711c2a6dad611d74be71a8cce6b7d137c5da72b4adeffa4433c30c3ac': 'STOCK_TJX', - '0xce6c3fa8fac34cb684e061ac9eebb73e09d28f0049e02e5f6a882824ff743c70': 'STOCK_IRM', - '0xb5161f349461b1e88f1d5b690a06d26dacda22033c0e9800624296ed76efa3c9': 'STOCK_MRK', - '0x84b8402833178f62e1ee382f6994f86fc83edb183a54525299129f371ecf6335': 'STOCK_HAL', - '0xf71b73098be6ad1b8955eefa3fd7f007778ef7e9a89325bc54d5dc63c8d54248': 'STOCK_DHI', - '0xb090c7e3b9754ee2dfd822ec6b057cef8b68dfdca99a66072574fe9b4f3ff445': 'STOCK_KO', - '0x6c6f2426a7e7e3b4bb90a3f796101c1690f1171b475d7c84b7f151b782031b60': 'STOCK_CGC', - '0xa00c06d8051d4a94104cbbe6fc551cf856ed75f026ec3635889296376e69e4ea': 'STOCK_KLAC', - '0x48666d40a989240c4bec92d7afc65174693dd5c5ffc2a0ea19105e95cf83431c': 'STOCK_MCD', - '0xf5b7dfb8d3327d32c3b73357b4e1cb0613ab5fa33e8978eed195edf98639eefe': 'STOCK_HPQ', - '0x4816efdbf74aae24f7bccd8281110c1a734c2e45aa4187cda10540fb27d017a2': 'STOCK_SIVB', - '0x5cd1ba2014ecd3879a1f842017a3ef3fa31a29bc177d6c6b77d8c1f9d2b5b6f8': 'STOCK_TWTR', - '0x51fbc53d1ae2cbfa27dc5afd61a9f763aeaac8c0c4bf1b0a3728468954b39638': 'STOCK_LB', - '0x9952262f8e0f5660b4d754fa5f16b4912c0ad645a35763c9aa2e9a729d05c441': 'STOCK_ISRG', - '0x543c66324f64ca40f204030653f671d22923170348f0a9c10e2e3df34fa3677d': 'STOCK_QCOM', - '0x5efe9d1c9395ef25d010fd002461a8f3331d1f96cc40d1392d28231aac21aac5': 'STOCK_VFC', - '0x5ef8136af318b7b93b5967d8dd8bb98252b7d2f076220b366d9299302d1043ce': 'STOCK_SYY', - '0xf444ccebb4080250dbfd94816fc911369c138e0cccd76776762b3388af494299': 'STOCK_GRPN', - '0x7d9e2e41404a976f57ebba282083ad84fcac183580054688597e32563255c588': 'STOCK_LDOS', - '0x66251add70d38fd017a83db8f828c04db184c7b7f7ae0544687db68d930781ea': 'STOCK_STE', - '0xa57c37c11d452d47becfb2417a1578995e0a2922b644a7a241e8ccc354f381f3': 'STOCK_LEG', - '0x05feaa05ea1c6b85287b466ca97723bf9c9dafd8ee1a102f16a0e005675e2a97': 'STOCK_FRT', - '0x980b0a24520ad7c37894a741c24fa4daa6d239b37f221803b16f6e7d9c5a8118': 'STOCK_DIS', - '0x024b724ad83897444d0c019db21dad0ffa5900d73b2b7eee73a0cb7342a8bb04': 'STOCK_WDAY', - '0x680d5cc861db561e1bacbad85543cdf2c296978fb480f26037e7cfc0c5a71b86': 'STOCK_JPM', - '0xe9ddda51df5c4c7403f2ea80ec6fa0c909d46c4b80bf67a3fa93d790d14c2075': 'STOCK_VRSK', - '0xb1b28c998b9fee137b7daaa758120758956857c2953ca27380b2388d5fd8baa6': 'STOCK_MCK', - '0x5f550cb8da4d75cb5a0fb9421ca11b7f461671b30bd311be3bf4a9eda1a6b271': 'STOCK_LBTYK', - '0x3f4ea1d941086bb5f8ea91476da8f487480b6c9d883f17a43a69fd09bed6a5d5': 'STOCK_CB', - '0x86083c8264caa43245ae69abc0287bb36c45c3a7e84b6c132f795e664ef2be6e': 'STOCK_NFLX', - '0xad8dcc7599aa6ff585376026e065037780db99cac3316ebaaafbbd2ca1ab8b9c': 'STOCK_AIG', - '0x8aec4092caf9c3ae47d29258e255ac51e78382e46222d2a13cd5ec150efb052c': 'STOCK_IVZ', - '0x21e3e851a72ca5f583825c09761b070a1197f4934d19db349adf8571957f030f': 'STOCK_RCL', - '0x9c0deb5aed24f3ce00e24e8f430f4c41c6d3529b8cdf6b1a35a6730272b1cc70': 'STOCK_PWR', - '0x4607aaae615f56cf8c7a638a2678dc86a7dfee0d762d6c496274ce6ebe18efca': 'STOCK_AMP', - '0xbba61973823d660156654df07b3038577cd9d6061cbe5fab288eba5f9e739585': 'STOCK_VAR', - '0x8d0b838ac6872cb4b6d9d55df187c6f5c43114ee9418d22865513e27aa248a78': 'STOCK_BR', - '0x043175b8dcdd47fab744cec14db6da4fe4b545fad6ac85fa85c771eccb31791c': 'STOCK_AOS', - '0x25034e7a2ddec6820e14b20940470684beb6189761520907abbcda58a5f6b26d': 'STOCK_RL', - '0xa3e7cb51d84239e7f4ad5f1d6739db61e5be89f9cb71af29f1b148ce69cdd913': 'STOCK_ADP', - '0x70d173cb7333c689ffa75d566f130f7211c2e91187fe527466917cb82ee1ed61': 'STOCK_PPL', - '0x40b038dc70f6dcdfd02cfc0cbcd9104f1e15cddd6b46c6cdc642e187c1af88c0': 'STOCK_PBCT', - '0x568a33bdd21c6de5ff036950658938bdf00875414d43d9c9d4d67234bab88af8': 'STOCK_AMAT', - '0xd59d8fb784cfa63970db26c1e879616de8f2c9c432878f5367dc0908906e4828': 'STOCK_PGR', - '0x4ce6c813ec1604b76b629227e81ff51dd002b857c5af7a8f3b126b963fc085d2': 'STOCK_EW', - '0x6caa04866ab17fa38a1d0999e23160f1504cf3acded0fa6dfc1796d5127991fe': 'STOCK_NEE', - '0x7a07642ee0abcd8b706af5ee17c8868317ace9aef96f8f5208c4d6a1e789d651': 'STOCK_DVA', - '0x5aeac0294402f109f55b70409d34c1a9b0c298c8be022568240552d3daddb6f7': 'STOCK_FANG', - '0xafb15c46ad20647a90ff64be2c093cacd059cd2a7057872b5c65f9a79ca60abf': 'STOCK_VNO', - '0xf1acdfc3a98499b51d6290f2e7049659e1e21ab557584f9b20fab19c8d3c1cf8': 'STOCK_TSN', - '0x283cb38e6f264949822a7969b1463540efc2b67082c088ec08c87e556bc8c43f': 'STOCK_JNJ', - '0xbaf5a88f8f877563ecbf728a639db5e0bb1ddf05d7f1d2d36cfec68cd276509c': 'STOCK_NVR', - '0x87a3a987a1eeaca20dec44f0ed48468b112e387ee3da10dfc15b4d222816a4b4': 'STOCK_STT', - '0x3e48e9b350134785fbbedc27695b8c4933f5cbcb30958003c26e8da0b067b833': 'STOCK_CSCO', - '0xe376f89e05fc26a9e7d31cba62cc0ba8f1350a659ab089243b38ddac32e2fd08': 'STOCK_NVDA', - '0x3d5aae92b240db385f17ef27f76f5e61eab89d73656b3ba6989a134854ca9551': 'STOCK_MMC', - '0x4f68ac2531d402252815fe1860cb574682d0a626fc55ad06947b338f01dd6a91': 'STOCK_UNP', - '0x161d888f6b2e06ed70e27de40d19e6362527a8b909cbdb706b24e15cb043ce6e': 'STOCK_ABMD', - '0x924d9a63682fb8960906e052dce66e079bd9eaa8184c7151c106b8ed571c6885': 'STOCK_ALGN', - '0x3c55cc404bed995dcad63490c45693baa51df0924bcaf0a3dcc71d604842943f': 'STOCK_SYF', - '0x2cf3f1228af92902b1e2144c21805c6a6f6641a90ad4c23991e99bdf710ee23d': 'STOCK_RF', - '0x3ed1f8c109143d92bdd90b2bbbb7eeeaa3fc7be4f1fbd78fb60e41c8d3e8580c': 'STOCK_TSCO', - '0xe5de1f7f59edd989b486b80c2ee73e3de749296c3c05c1d22bb7ed5e3b641aa2': 'STOCK_GOOGL', - '0x081a443b30ba4b692bb056fa672dd2b8a063017dd2b413ee9e6cf8bbf1eeee2d': 'STOCK_AXP', - '0x0cf922b4380e03d27273c096a82bed6b144c7363960e07901cf71275494b94c5': 'STOCK_HBAN', - '0x15a0133b3bd3139a0a56dae604a5ad396425771480f8cddd04bb76f3fd7eea53': 'STOCK_A', - '0x81374c8f14c460282e2a5beda91edeebcc8dd6ed2b232872dc8498cf4410a46b': 'STOCK_NWL', - '0xb3f5a4a1086503a9e8f64bf47d4b8aad1c3a3f7bbc3e852216582cd890dba753': 'STOCK_BMRN', - '0x953e1303cb9b7badc48c69e61138b712700578404dbaca2c0df5445b43c77907': 'STOCK_BAC', - '0xd2fd34d616a367143e1215ddea19b7224fc0471d783b602fe3c350d8687fef52': 'STOCK_EIX', - '0x5b7d2a66eb7f4f4cbf3594e77d1230fc849f6a0192e689c2ab543cf01a284324': 'CRYPTO_ADA', - '0x5f70d45b74d2c67c67c3493350cba021d6a0609e139f72352721387909cac960': 'STOCK_OXY', - '0x12bb588cd3f21424ce56d5f000885de08f2ccbf4904c803dcb3819cc30a47e4f': 'STOCK_UNM', - '0xfcea4490d5c43900059a1b86af91382b43e808819b87a478df006d58a193b7f7': 'STOCK_MCHP', - '0xccb8e534a9329550aaa764415ff44442a14b3d89ce7caa1a1e2c376a64bf7906': 'STOCK_CL', - '0x4d7b627cfaeb9d27ecd5ac4a5f5d8aaed4c5848c53f9397f249c82377817bc7f': 'STOCK_KSS', - '0xc55a3cafb995d914c54fa3713ba7714f6b9c2a07cb679e722d1123ae759abd05': 'STOCK_EMN', - '0x4c88484ddd6f27c7fa20c30a95121ce1da020303dd7cc4ba2025f19cb8cb0628': 'STOCK_SPLK', - '0xb0b4175d666f592faa6a51bafe1a79f360cbd4ec87dd1cafd6ee96b102aeb3fe': 'STOCK_FLT', - '0xdf9a264a648b7c7294e1b6b1e65e7803e126cc19d6e914e1cad21fa86aa40705': 'STOCK_PHM', - '0xc1f7d1d258119f2144b90520bd9b5496c944d8688277ffc1386211a851398984': 'STOCK_FISV', - '0x4e0b1c42df215b86bf2e1cd235b36a650f4fff6f08fba01e2774fbbe883b7d45': 'STOCK_D', - '0xc96086f0ba81f452dcbfd402e4d22b4dd6c8f9d33d5c59c14e3c0babc52ea3e8': 'STOCK_ANSS', - '0xc02130453fe3fc1c5e134a64b7d389a10d05b7a954b3f1c91d66b913b990f8c2': 'STOCK_DRI', - '0x50ff10ccc2a72a8dd58e34a6140458c562dfc96812f7b535599638b96eb54685': 'STOCK_UBER', - '0x51faabc4d2445482c0568f3af60fcf0e8103cfa91ecff03a612cbb8ef199c874': 'STOCK_KHC', - '0x59b3853f0200b628e431f845a2718230173b832863c69bea6f08d834d886b910': 'CRYPTO_XRP', - '0x881348b4ff669131e8bcec6042f38905a41ab4538b3ca72d911faf0b2671443f': 'STOCK_TTWO', - '0x2727d8ebb39402443a5a664f11ace815c9464ff9b5249c4acef7a164eb471071': 'STOCK_GWW', - '0xb2e91cd8da83f6cc10590a849aa3994d0b4b08cc56c793ee665d4ab5a1a27d40': 'STOCK_KEYS', - '0xa1b81ddda993fb851cacf1e728083cae43c673692809973b423a026f866e8545': 'STOCK_AFL', - '0x7a55d72ed12798183a6567a9971d05ef1c1b7fc0a6d1bde8856334ba3963991a': 'STOCK_CHD', - '0x7412e1c1cf7719998f0297827780febaf6a723c750557d526c649461a25351c4': 'STOCK_VRTX', - '0xf5b4e618cd23819f4f61d94779e229be0b180c6b811ebb654490e13bd03436ef': 'STOCK_JKHY', - '0x772c98b1cc53197dc3e252e02eda23ff428665c0812c30d931532ecd1f09b275': 'STOCK_XRAY', - '0x0c77d169d5aa1aa527931d93749de56720584d65f5de08e3d32c46e3a1e0de55': 'STOCK_CAT', - '0xc65fb86e66c867df6dbeeba1a73fd9bc9b5dfe5c181840a91303c61c40910dce': 'STOCK_BABA', - '0xebaa1a3dc8bc79489828042b7ea333f4d0e65aaa3bbed07f8ef7736c4558672e': 'STOCK_MO', - '0x5376ff169a3705b2003892fe730060ee74ec83e5701da29318221aa782271779': 'CRYPTO_ETH', - '0x1d7649822075b1de8989c763c1356b3b91528aebfb30693c507eb5862382c2b3': 'STOCK_CMI', - '0x94d3d23b5e8c94397fae6ac413e41a23825dd493345a3adf1a89187d6eb6e8fa': 'STOCK_V', - '0xe6ff25f44d08e40ceec6c887d98cf8220ab9876d28ca4655cbafb2894148b370': 'STOCK_ZNGA', - '0x2e956d666adc91281b85e6b7b80d9dcd1a87a1ccf2280fc764bd1802c3922302': 'FOREX_USD_JPY', - '0x277d814c5c6742eb73b1182abebf00ed15974d3cf81b1e744885fb680a9e4a48': 'STOCK_UNH', - '0x0ecec49b13d7e3f3fffa5c24e501bdd43f1edfc1a191b33fd0ca3948eba63d91': 'STOCK_UHS', - '0x08f85501ba58055098910decb86a43dc73a0ceff3ec063f810cff8257c0acda0': 'STOCK_ECL', - '0xb82a77cddae1783c9922c465308ce0426b1d7abf760569b1602761d5f372b6e1': 'STOCK_BKNG', - '0xbf592a0ded4485385cca30fa3011d9534c23801c9dceee2054fc443c6dd8b2ee': 'STOCK_KMB', - '0x9b68b7a34d8a694ddaae4370a1f31b56d59452d368cf0e3e8cf20b41cbe9b8a7': 'STOCK_DISH', - '0x05b7155070d30f6ea4d492d5075b240f5d30c6de1c8c64abcaa7d4e15cf44c7c': 'STOCK_LYFT', - '0x2a19a217fd7ab53aa18b512e6f7cc6b4a3b34dd8e2f9f1715732a24914ffb040': 'CRYPTO_BNB', - '0xf1e5a9b0e1a5e408c6913f472e69fdc565b20592128fb19d771f13cb807571b6': 'STOCK_COST', - '0xf26f12e39c150f1a1933c8a777c5d81c12148dd0afba5c64396e661e8be1885c': 'STOCK_CNC', - '0x1dd5332f355d75b9f4066d2a562c5b0d24704923c505e24e6d24a091c70444c8': 'STOCK_CDNS', - '0xc0ac79bdaa14d24d6b069d99bd269faa3bba9fd807557db98f4f980f0e2c61e1': 'STOCK_WLTW', - '0x92c7bd21dda0b0b6350b3337f640fe4af96944335d3d02478232df1b1227a55f': 'FOREX_USD_MXN', - '0x18e169759d3a5087673362daba2cdab6bf4efbae982855f3ea16625bdf4a788b': 'STOCK_FRC', - '0x06b3d3daa421db08066c4e350609aad2ae5092ca69b803e4a31ad06cbfd5b357': 'STOCK_CPB', - '0xbe7d65f819339b28ec01cb4ee854baf77ea59d5c5f5f3a07ce8af4d8d6bdaa30': 'CRYPTO_MIOTA', - '0xd198495a6b460d9f0eae73fbccd4158aef7480880f51aaf0ccb9f959d96cebb0': 'STOCK_VTR', - '0x3958c472913c3b52ce0d9238836df2c41f305cd0f15caa5edb49cde09b940444': 'STOCK_LYB', - '0x1626aff68a39e30614cc4e8cc61b293ab52247130ce8b57f38d22a110acff7e4': 'STOCK_IP', - '0xff8ca92b6455fed384efe496b4dd4c9298b917d6a79d3261e507fa559e4b8e8d': 'STOCK_VMC', - '0x1e7fc9f08c379fc3cad0b49184985e55f6fdf8ecffd88e2992cc360dcd9efac7': 'STOCK_LYV', - '0xe446515196c578b005ca8c45b283ad129ad7f8263cd767889d3a26e0039ffc8f': 'STOCK_IT', - '0xe1e04f112438afea96ff0e8fc83affa75c0de9799e16d2b84dccce1f47a6ae88': 'STOCK_EVRG', - '0x4e196cbf3da7ce0890fc7d2849991d72899728f90033dfa1eb7eef438f641849': 'STOCK_TXT', - '0x12ea1472e43abc00b3998728d6b064572dac64a22c6518b02b4061f421437dae': 'STOCK_ADM', - '0xa1e7312f5621811726580b6a82751096700217c32de6714f8ebabc79701d1137': 'STOCK_MMM', - '0xfa3664b445f60e2021befb6657f7a3a504b48f619b1318472d067fd5ab0a6086': 'CRYPTO_XLM', - '0x7ed78fdb5427f23cd20ed86e5a55d93a75386517ce001ae146e15249cf40faf6': 'STOCK_TMUS', - '0x1386da212c7d01a0e4fe7509dd7523e798d239a99811422c8149c7906d1314bf': 'STOCK_EMR', - '0x625e54ae68ec3f78f3d8427aa948be639f9e62f8fdea4cc9bc9c9e917dd826af': 'STOCK_NTAP', - '0x42e31fddb219102bda30061650f4ff2b42fc582c85d28fbbfd96b4362f2fb16e': 'STOCK_IPGP', - '0xb72f4082098c7da115e15b5267f74df24cf79897806f956dab5f5565df2f6a93': 'STOCK_WDC', - '0x8319ed79bbc2530ddf25a83caa59a5590416aa0f21fed74586def4be71dc9f9e': 'STOCK_PXD', - '0xaa51934096f8a857a3cb10bcc9c425e331908103ea2437eaf0e2e2be304d9be3': 'STOCK_OMC', - '0xdc18a6624aa1e51562df908782b6bff841bbdd4f815abc9b29978970e7bc091d': 'STOCK_PPG', - '0x982859ffa6cdef849d67f8978bd8f6cbaab91c7a04c0e089048c457ffb369d3e': 'STOCK_ABT', - '0x2d157a678db58810502619e82b217226fab27a0e95724240c367618969439a44': 'STOCK_GPC', - '0x5ba227ec54870e7ece236281f55b822f43dd41113dd897392081aed5dbd7ce9a': 'STOCK_MNST', - '0xc0f9d6cbf13fba395088bd0d4f708e3da2d8ac90a6c2844519d888a5552063c8': 'STOCK_LKQ', - '0x2e0ea17bc5cce866bdb4a5f7780f22a874d8f46645d3df468d0f4ad3108a2440': 'STOCK_FCX', - '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9': 'CRYPTO_BTC', - '0x652d9132081b721b802e17757045a971a3a432ed391d941d3786fbe0e37c5302': 'STOCK_DOW', - '0x447bdb5e021ab8fc34dfe435d0e4bccf082c1556808b16d77b5df8b22afe272f': 'STOCK_FE', - '0x41c18d8b46f6df510f68a5bbe5ad41fb003635c03593ee5390d049afc97cf1fa': 'STOCK_ILMN', - '0xb4c6708589101e4738e8ce74a0cdbb044c9f184bdabf8d0b0a4427c14ef4b761': 'STOCK_MDT', - '0x56ff2d09587efb0c63eb5de18b95a2a055f5da4b4c6de6551a06d935661c33b7': 'STOCK_CMCSA', - '0x43e467e6efb8735714d94b8f99fc4605a550ad6835d8a27db110c195ad25f698': 'STOCK_SIRI', - '0x8213223412733f6c8a1a20049384217685e10564847de16a0b35b4f20f9047b8': 'STOCK_WEC', - '0xa2c833a45c5f656abab167ed95cc896672bc62740566e1748c0364b0e5373bb5': 'STOCK_CVX', - '0x7e14f89b7d5debbe9546f1a39c858b79319ebd9d8d5f79abd32d018fd0005c93': 'STOCK_KSU', - '0x9a31050cd8beaf06a568d5fa000a96b0c53686751970a79b2fd081651156ae9d': 'STOCK_APD', - '0xb8a5638057e662ec81402845d5de1828752437ce64559571ec831036adc452c7': 'CRYPTO_LTC', - '0xb4db70dd76db06f88c6c3ea4706bd57e6c16c776a742cacaf788e98ff3db3bc0': 'STOCK_NUE', - '0xc93fc270e2e17cd0d08e90670befb041718d7bdbbf50f1a0973d4554ed60719a': 'STOCK_CMG', - '0xfaf3a303b5803ad1ab6f500d7d89ab51683f4ef2c8c1bd4598f59afb961efa18': 'STOCK_NDAQ', - '0xa7fb8e57bf9b7637a852656fdaa8683991121116bc05e57d63de3040c2ab6642': 'STOCK_HD', - '0x8edb8897dc3cd6c16fc33cb83c7a98709afec37499d4303c87c1ef580d22e900': 'STOCK_MAA', - '0x1d564f1577fe86fabca2a6cd95ce8cff4ea6579ab6935c8e32cddf52e2c8f15e': 'STOCK_PFG', - '0x7116332115b445c798d879980bfa380276e012947edd2da54a3b1b21beb80186': 'STOCK_CMS', - '0xcd5456d21b2e22e6a3dc986dff19d14af929972844f76a75c57874196ef1562f': 'STOCK_BLK', - '0xadb8cb0b86382642041f596e78e66882378730e04845ed6d73f3e2f4e1e41c22': 'STOCK_WY', - '0x1776e2a1a2d61ad5f8ae4bdccb07ad93ab7deb5cdac34457b4adc55ea4a0de88': 'STOCK_UPS', - '0x104183fac21e41ff5990fda7fdf25a231197b7b61bb7a0b120deb20b4f5abab4': 'STOCK_CTXS', - '0x7bd66fc9029431177aa936e3b1dcb5a8b847c5d6eb32ec7f601114558cbfdb26': 'STOCK_NTRS', - '0xe3a9b73737149f62b1fca52d0dd2fbf65267cbfd7aa7f7cedc5c49e2847d8b7c': 'STOCK_ES', - '0x65180baa72ec949c62bb341fa1e6fab24330d263d7370a594f2a7f8968b345f6': 'STOCK_FMC', - '0x3447136b2bfe4168bd5111fcaea4accbac8e2b338225be27897846c8dc98a162': 'STOCK_DTE', - '0x6ebc0a93529978441a1cf3c09db9d8f1c3d75ab0fd6ce76b843643d5e1b6cc12': 'STOCK_LW', - '0x77fe7b0bd2fbb46ab859d2df571cad283ba0da81502e56070a32121bf38bc2f8': 'STOCK_M', - '0x3810bba093293ac05856546e91333987bfa4dd27287853535cc15012576f1c26': 'STOCK_MA', - '0xa1834553e21dc9662e7b266e68a91c3e0dd2c5b88ca03238f1cf293d06ec5b0e': 'CRYPTO_XTZ', - '0xdbff450b984d0710a47506e4cf6f0374fd3b9fcbdef108a91599ad2821ded88c': 'STOCK_ZTS', - '0x76c5a65ee2c74821ce7831f2f02c0e160393df3fcb6b8249f0730a34c9782a76': 'STOCK_ADI', - '0xc9e13b9ecba665c13177e38cbeb5e2ac40861f2429e2b9b218a89e77a85f8b82': 'STOCK_GPS', - '0xc133127f930ed15335cf55eaa959e109b42ffd32c1e0af169fce586a00f1baf3': 'STOCK_BF_B', - '0x16b0327bc5e546f8ce8281e8ffa641e4dcb0d77bf27013767880dc749c1209e5': 'STOCK_CF', - '0x8c5aea601199650f6512df06feba33149c1752ad750de4deaa2d94d1580a38ca': 'STOCK_APHA', - '0x5aa155234d55d0763e66f266ba6a3d7fdb20feb9eefcaf07b8e98ae6438b135c': 'STOCK_ALB', - '0xb309b0364b4c0c7b700ed7392ade587158c196ff7732c8741a6de800fcac7778': 'STOCK_XOM', - '0xf4eab380ef31d261b4162fbb4a188886e3b0d9924be0198dae29b5ea17def516': 'STOCK_JWN', - '0x393312c690adf734153fd1eb928d2d7646075f93a31f5febdab7d4d5450a7996': 'STOCK_PNR', - '0xb18a5f2f7f7227c474ff222661e829001d4b50a9d67ae76aeda33a02dd343e92': 'STOCK_ARE', - '0x8598d7d5399efc3cb3a32d57030671b943202b2d26f93afbafa6a7219f4cda1b': 'STOCK_CAG', - '0x81270339c9de23ec3c3f0e58572d54f3a330f18fd8bf68c26fe3a89d28cb9a1b': 'STOCK_SCHW', - '0xf9b84b4f35b23212e9bb6bb4db91af3309537bcbcb1d9eb87e9beeb309d65ea6': 'FOREX_USD_RUB', - '0xb11486377b8cc2661443eee328d2915e92cc7e9e6c79d1117b15cca98e4925d0': 'STOCK_PEG', - '0xdcf215c7941c073a08737943c2f0a2d6ece205438730547488c3aab0fb448321': 'STOCK_EXPD', - '0x950c557e67e803ec3acd2a9b7417eff9a7f4447456d41e1f14253952561ba56c': 'FOREX_EUR_USD', - '0xde71668ca10b2c9cd56dc612a4ebbab400088f6fc512de9b6b3b8b3ff80ea165': 'STOCK_COP', - '0xf1b20e0a06db1f673d1a94c0ab008ce2a8ffec6e6f1c1edfd63bbd11bd8142a3': 'STOCK_EA', - '0xa7985b2eabedaf80dbd162693ba4414d741d8beeeb5e69f5c5cef2e901444c7b': 'CRYPTO_EOS', - '0x82d90f7ad95452c6ca0285abef3f8eea0511c84fb7ff889b95ce66e0b13a9a1b': 'STOCK_BDX', - '0xe154b39db4307cd41acee15260bb133d030030778eb7a029bab2061f469e551e': 'STOCK_TAP', - '0xabee67ad90205c1b3a7c9c0fc0bffa2a08c5d25fd8db9e67bd96a98910209c73': 'STOCK_NLSN', - '0xac4529fd18e52d2f66c1314c1b7567bd14b09ea4428e519318884349a195acb8': 'CRYPTO_ZEC', - '0xb59ea33cbf30b5b5e2315ab34a027b46002d489e5d9adf8471aaf11e55ea3a5b': 'STOCK_GD', - '0x2c7d6a193fa54d4fd2b02d2d987b867bf9d7acaa44b310bfd5b1b9bf66b0aa99': 'STOCK_LUV', - '0x16de0b733ff4cb77053f60f7e5c6d76f7b7b57e7ddcddbf42275d0891e891941': 'STOCK_SPGI', - '0x0e78df9d9138b220040400912b6c095e090c2d26518fcc46708a00939783b282': 'STOCK_COF', - '0x697c834543584df6e4047401dfb62ceb9a8277864e1b1ec504fa7a429763fef1': 'CRYPTO_XMR', - '0x2f8866909b867729e887875cd532c4de9af1290f9194c2f2481738c111ddf187': 'STOCK_AEP', - '0x3381783a66e17a5fae01cc755541586a38a7297e180822931da2674b6275d15a': 'STOCK_NXPI', - '0xc85572c9aa92a0ab256a15fdb03e23105b86d9499280976d8d56e151ec9ccc9b': 'CRYPTO_WAVES', - '0x4f9bbd9e3252d57c8202ca20e5dfa7aba93b02047e0fdcd21f70ed4dec747959': 'STOCK_BEN', - '0x018b7cab7a90dc06ed6b4304541de3e7c91079e5f83ebe296bb1cda1e6521ccf': 'STOCK_STX', - '0x0df67b2759f7b66d9a1c60127dacbeccd2f5663c11c5e324a81636122d0eaa40': 'STOCK_AMD', - '0xcb9e184158c2323f6d8689ff43d31ddbf7cb3cc03c971c1fe6e181d8369b1a61': 'STOCK_ETN', - '0xf3c0a9e4fc837e07ad5e034a2d7ecde00af054c40305c54583c1737d5ee9a919': 'STOCK_BXP', - '0x76c08d1bc34e0411f4aa126df4fe34c1bf8b25a55cf27ecd7cbdc80497c06d36': 'STOCK_BMY', - '0x5c14429556a093d1f301c6092586667cc2f30a6ba98bc7cf575676c2b1222b3d': 'STOCK_FB', - '0x87c9f9d514d26c5730872a5c252402d35c0bee072ef893062155e3e7d16c16d4': 'STOCK_HUM', - '0xed77b12e7588c66fd672a92607d91727658ad6c7a89ebb6edc93e479653eecca': 'STOCK_LBTYA', - '0x18f918f95cdf922a2c913d4ed3112f9b6269b6925793e3f3db557875a4ac2b12': 'STOCK_VRSN', - '0x9d15042a754467fa023b3b3e70a7336f2941a9d28be1eeec8b5bf677b8d38810': 'STOCK_FTI', - '0xc1b050e1034cb06a3d216004df79e48cd5e5cc5ed4a04a9eb13090ed4bbf45a1': 'STOCK_CTVA', - '0x67ea86f77e95c36042d583dbce4953a143363ec8094acbaecb7974a8312e0906': 'STOCK_RMD', - '0xe7fc957aa8d9a3b3b52bf271113591788d8b3bcf1d53b46f5c4952e396dbd1d3': 'STOCK_DLTR', - '0x1df52ce04cf220e2291b8189f05077ad1b1597a8da8e20a363b5bf10753c417e': 'STOCK_ROP', - '0x9471b39995a1abee7008b5029142ffad2392c1719dbc3bd05a09ee5c48bb3847': 'STOCK_ROST', - '0xc6fe3d7a90d401a72256d5bf9a5e9e61d84ff754370377f6be28143a5d7281f8': 'STOCK_CCL', - '0x0ba36016a6b9cb5203e6bdafc78e6db495db02edf4b71eab08bc21959b8915c0': 'STOCK_TDG', - '0x2686ff836d9b3a06fcc23e8b646e9dc869f4402846b69f2ce2d073b220b0bbd2': 'STOCK_MDLZ', - '0x1414a1cf6c6560512c85ec12fe1efb04922fbeb35a009b083cca254c349ab520': 'STOCK_TXN', - '0xeb078eed28a950b353d70c253fb5394392e6006fdb43e4548f35acd1c8d79192': 'STOCK_CMA', - '0x3c9c298fad3c79b2d0862ad6413a85be214995c1914ab85e1005426ce4d0d5a4': 'STOCK_AZO', - '0x2ff302b6d3683b651cf718508adc3a0545ca2c7caf2814959c035d6253924d04': 'STOCK_IQV', - '0x16c23e7345a3a413a5e0341615e4ab0d85418e7a565b0f5c51ed26ba2944ae8b': 'STOCK_FAST', - '0xc3e8eb3c4c3ed090ec90f37e28ea7e7ca74bdfe8c144fd9b43a8a12ad68f4edf': 'CRYPTO_REP', - '0x94776c40e3eca7279a22bee997bf991f62b8be95e750ccabc1154c8a9fd825ca': 'STOCK_REG', - '0xd0e08aa5f971cec68b4258f43c88edf6a076c81720ba923fe16cf37944fa4d9d': 'STOCK_HRB', - '0x746187c8b1b7d950cebaa679780ede28bb38bfd8ab2fd215197931f96e8c2fa6': 'STOCK_MOS', - '0x0368e93d6fa5c98f9a1096cff847b68c3a06cec4d7a7d495f1035c106a97576b': 'STOCK_AVB', - '0x6f8302b64a560d2345012545985207801e7185e2ab99f04041d7ca075a27cf6e': 'STOCK_AON', - '0xd218445dc0ddc70577692e45b5878b73d3f08d5d57e317e0eda678f4c2c77d13': 'STOCK_MSI', - '0x8dea2c788eed0520123ae26f635f4176f68e69ce95f6712558a604c91d0c1454': 'STOCK_IR', - '0x2c035b2192318f4707f8cc1ac7e97668fabcd20a74b2043e3a80da8255321b8a': 'STOCK_MS', - '0x4c2bddbbeafc3f120c3eee5bce84cb54d04ae4ffb5549c72aa1d953f83cd213e': 'STOCK_LVS', - '0xf373603112c6e6810951a447aec418bffe3ddd0673324a77ebd1c0e82ff40864': 'STOCK_CVS', - '0x531b8be82b5ae31f03b8da49c5ffbebded0f32193d60778d9bb716be1007e13c': 'STOCK_LRCX', - '0xaa85e602b69244bbf8f36c93f355e19f1c6ad28f7ee2c17ca455caa711262948': 'STOCK_PFE', - '0x66bc2cf58b83746a31aed95074807b2599752bd6935c39188f38d507330e2975': 'STOCK_EFX', - '0x2bbd57f959ca7960b6fb9156049dd170317355380851f045fa58dfadfe84c6f9': 'COMMODITY_XAG', - '0x25528c1a8d60b535a9734ab556d8fd27923afaf606f46ebfd691d74f52a9fec0': 'STOCK_WMT', - '0x84ed2a7d638b43caae7abcca42e3dad0351acdf80e25921d4355140f502218e3': 'STOCK_SRE', - '0x9e48a466f4e041a2f5adceaedd9143cb90d685bd6bc16560a45ef81745b02a41': 'STOCK_MCO', - '0x95b4848ef44313ea9e2540d3dcc13ef190951579716871ded6d3931bfc7d537e': 'STOCK_GPN', - '0x44eeae7728df0a465d16ef0d8a3c317448e6903e8ede567a4ec37feb038cc499': 'STOCK_FDX', - '0x1df2df18701af4dcd14581591a83927c5790bdb00540c803f7071f8d78ff56c8': 'STOCK_ALLE', - '0xbede381dee9992c1e4fd6ba5799619f0258c07f75793d7d0e98f697205298b21': 'STOCK_SHOP', - '0xdd5588842a23d22c3ee8ee0f65df5d4f301b1598a22cc5d7f628907e3397e33d': 'STOCK_PKG', - '0x151f25412be0583429a504bb9c1510458435b9e2f7b96deb6a6952411485ea8d': 'STOCK_ADBE', - '0x1abb9a8075f0d7da44303dc937ce990389ec5fa34a94c8d9adc06590666c6046': 'STOCK_APA', - '0x30f94352f71a06b57521d5d8dfc38893314811ded7131efa2d332e5f76aba044': 'STOCK_CRON', - '0x5092068ee8ecc01360fac78a6659677e5c35eb60c677554bfce7e555ab24d67f': 'STOCK_EXR', - '0xf31380d7250c78811a53c1a39914c28143c67b270fb2ab88dc91251cadafd950': 'STOCK_NEM', - '0x5ee4b8c9c4c679e5b86785210c2835788f76d256c40640148cc4b4b0e049f0c9': 'STOCK_ANTM', - '0xe61c82469e3bc3b65184bcc168e78d3ba4f87d7730d8faa027f4b0bfb3fa30dd': 'FOREX_USD_NOK', - '0xe2d7b0496b4040fb000e1c1c3a9361aec98bbf4acdb7cfc900c9ceba4711928e': 'STOCK_BSX', - '0x4a8669e3e5d9cea6104a3549b05916dfb71b79fb4cf5bb08434e42e38c87ded7': 'CRYPTO_BAT', - '0xec038d27c6be4ce9f9e570aaa4de65a3af017319acde9c94941b6a430b075221': 'STOCK_CINF', - '0x3193870ff000568700a984050500b6c22657cf41496596066150b01f1161c824': 'STOCK_STZ', - '0x2a4d8deaeb8676e0e36c6c90c62eeac7b70bab6c0fd10c9cd2d9ef8f87c71b97': 'STOCK_HLT', - '0x18830e4f18a88c231b46684090976f59cb23f6c3dbd2e39da16bd00c492aadc3': 'FOREX_NZD_USD', - '0xb58c2036e7f5e65abb09b9ebd9169a6bdda17ce7b26226c34126c1adbba895ca': 'STOCK_PYPL', - '0xfa728b61bf47e1dc0f4bf427e6dc096655e5cbe8c5ec8a1f089f407aa046adff': 'STOCK_FIS', - '0x7e0e312ce1954d8e2f41efd9e6faf10291832a313b464efd4cc2e139d5ff7a2b': 'STOCK_ROL', - '0xe987196eadf747f69f11b843ac90d898784e4110fef2ea3c3647bcc38bf54e3f': 'STOCK_ALL', - '0x02dfd3bf4abc6e79c19f04324df88a30c638c9045c42d3f1687847cb27e9b91b': 'STOCK_GRMN', - '0x6f0cb50b3e1bbe4eb1aea3ab994be8df927bbf4af19910d2b03df7decfe16f46': 'STOCK_NOV', - '0xbd72d55faf7958084d224db0aa8577279bd52667b797748b843b846f46e9bddf': 'STOCK_MPC', - '0xa6f8ba35d97a5a81578ccf0885ea6bbbbc30f040d37a92eaf79ee76d3a1e86c6': 'STOCK_CI', - '0x58b2493454f15887eb672bf726dec448ce012c6fdd2e3d70fd8ad0637fb0512c': 'STOCK_SO', - '0xca0a93fb2fb122dca66b4acca397406b998f6dc72c0fd83325f4a31a0fb3a4c5': 'STOCK_MKTX', - '0xbad92d81b7093b4e038b15b856922706d97d77c115e41fe072db67efc3bb1257': 'STOCK_GS', - '0xd4088e591ab0f05b694af352d7522df6a17ef0e9189c9a7b227f2c1e9e9569fc': 'STOCK_TRIP', - '0x404ca12dd2a067b2b85e00173a60462f18fcc81b8ca94f31f7a80a7d2fad7e81': 'STOCK_SNA', - '0x17b9f3307f2fe1e81554ede6e00a729139e68b6d5fb6ebd3aeabe6bce9b1aa6d': 'STOCK_CBRE', - '0x096d0e50bc9f12314a006e3e0ec1e113008f8a755611038d4c17e866d3981186': 'STOCK_BWA', - '0xe40c9b6fe6407881a5264839d2c8263213530cd4bb7ebc321b9ef4d9442cf2d7': 'STOCK_MXIM', - '0x8ad7e6218e0fbf39808c7a9972531a701bea0906ed8bd2d08f9c97ef685d17dd': 'STOCK_DVN', - '0xbe1cee2439d741cee5cb9240ca3d7f36e738ba2098f83b9b5a06bd31d6231f82': 'STOCK_ODFL', - '0xba809ef869ecaeecc890c1e5fa09429b64c1eba6681b39d1c013bce254712cc4': 'STOCK_ETR', - '0x87f5a4f98a2981a599a40f8c5fa39a8fcd7ae74dde23184b18c7919a0e6ca5a1': 'STOCK_HSY', - '0x04897152a52a2a9f8f1af624c2e35618345848c183980e3d00b54a801944e0a5': 'STOCK_MLM', - '0xb51e15f11a93c5642764e78e0b94324a9d60fd466150fd2a009273492a6649d5': 'STOCK_ESS', - '0x036401ec111240a4978485c2a472177729f68d12589168e1b76658f47469aed5': 'STOCK_ACB', - '0x365156fc8646607982d80c0337d87430a27f2e5ce1cda4928cc488c5c4087e18': 'STOCK_WYNN', - '0x81b40365a69677d2e94fcea518cc1e7ff5dfb86d6cf6051e7cbaea3113dff09e': 'STOCK_DRE', - '0xcbc23d009ba62a4fb7fda3647d82d716a4292f73d4b17cbdd5e7fac85d88a427': 'STOCK_HRL', - '0xa015670926d778b67dbdf78fba4370e9509f5e81f9c3b26fa26bba3c484ec0a1': 'STOCK_CTSH', - '0x8b3f818476103aff56fef3baf587c8c0abd9d5e27df8b6c09bef1dce868d65cd': 'STOCK_PNC', - '0xbf02a1068ff8347f6e312279dd38e51c6353e7fee6c551f69b2ead4df0a51423': 'STOCK_PLD', - '0x947faf2b22d78cd8d113d29562c8e7baf3849d94ccb9dbc03c2f789a7ae8dd60': 'STOCK_MGM', - '0x0f499adf0fbbef0cdc3abe35ec7e44ede3512b78f2c4f3bcb4a11b2ace24434a': 'STOCK_TWLO', - '0x14fc67757fbdc49b93bb9a974ce02874a854ae8a442761c646c895f0bb7ec778': 'STOCK_BRK_B', - '0x9dc8b26f2f7c41fe60cf5c3b5d038517c76ffa8a33c01ced7d1e2c46b72e4af2': 'STOCK_UAA', - '0xde9092f0054f59d375fc1d37b6104181a26d8bf5be5b6c3081fc9b763822b1ce': 'STOCK_FIT', - '0x7ea56341befd1dee4a5535a4ea36c08d4027be891c78d7f83137718c53ab220a': 'STOCK_HON', - '0x7d3c02e3d4242ad9e9cb74f5e92eb0cf4e6830a3e98420679af7af5e4b5b850a': 'STOCK_TEL', - '0x7dcaf215ba117ec04a9b5a5d48dccf2f576ea5de83f36b4ed3e0faa935a47f70': 'STOCK_DOV', - '0xa24dde276c58e049634bdabf6777b53cd2f6347ff535909cc25a3d5608910056': 'STOCK_ATO', - '0x6edeaea7fa7c4a9a8f0597d9446494239e784760117e12516e2c6640ba6a9090': 'STOCK_AYI', - '0x9e51db41eee8c80892d275c974c0fd88d14eb67ae810823972dfc6f36e10cce8': 'STOCK_CPRT', - '0x4234f6e3b5da08255776a93691c6fd3318ecc2ab8e305b114197df8d4a7bd1ac': 'STOCK_SBAC', - '0xfba21684c64651245d31033ca5914432b3819c4d2b2b6f2ab0497443900fb1db': 'STOCK_FLS', - '0x932c6091bc028388d96457810c1cc084f9de161afbfe3e87c77e3f859f32f327': 'STOCK_EQIX', - '0x031af5fc7cf579a29a9e8c52a0404ce89181a2abda9d5dfb727732440f7e87a9': 'STOCK_MET', - '0xa896815781eb1b86d20d56105b29230a596b9935f8b888f9b1f96c89311b9e1d': 'STOCK_CDW', - '0x1a47af556c008adb3b5e557af7bc6a733d4a512536230eff3812ddef30f7370c': 'STOCK_PG', - '0xa265d8d9a958f801af817b4622835d0bf94b94e8af8d9d931c8ce48aebc98ac7': 'STOCK_PKI', - '0x1b9dc1ee3248eb88e03f5a8b3d23667911594966520caeb24e6848fd6716d6d5': 'STOCK_RHI', - '0xa430793049ebb2c69b48c5dc6a9e4b408803729094650df47bbb0d8803b6d23b': 'STOCK_KMI', - '0xe0bffb3dff7439e776232adf098d7c5af9158defbcd2baecf3ce4384832fe8a5': 'STOCK_CERN', - '0x65bf2a7259d1886775977774e1f846c63df76ec755a936186d45fc7c9ea7a63a': 'STOCK_BBY', - '0x78fb604ec2c6441a435ff8a9cbb942266f26ff9c603b59af5ae32ffb58ab1a39': 'STOCK_RSG', - '0x01af7aeee06cd232108da83748b474655a7df9d15ead60a8dffd4ac325aaa648': 'STOCK_CHRW', - '0x43ec4ee8859e160e978564d5d0d75ebf57520f239ef48eda284daf00ec16e975': 'STOCK_AVY', - '0xeff65d0fefa40f8da1f83d4df787a7efed3efce03b24e0a1de6b726008292ccd': 'CRYPTO_OMG', - '0x3efd76ec8d1176738662ef76650e0ddef5ec1c0ee2e2ed6ac87ec1021b20e548': 'STOCK_XLNX', - '0xaab78ca29f05dcb85a719c32da2fb823873521a0d851b2f624b5d9786fe7ad3b': 'STOCK_JBHT', - '0x2e0a6d6d68a1222463ff1bf6700829cd5aca8b1f14d86823cdd326f3c28a6de6': 'STOCK_DAL', - '0x477b71221b12ad5ee947f1dd0b2be0129fb5e2a2d190ef162decb0fc40dea7ca': 'STOCK_PSA', - '0x7a760ae695e4f73dc0c946246ec225317c2cc066d9e40cf80753b613f67ab590': 'STOCK_MRO', - '0x37ee5a30a44a09eb2ab6cfec2a30ffd53092b2f4ebd365ccea237d8cd1112b7e': 'STOCK_WAT', - '0x1ff92a44b037c87e6e3768df4b45a0dd7aa7d7362bcca7c31982268cae9e206e': 'STOCK_NOC', - '0xf24b38ccf14b60cd5a0d36b98d01eaeb48897a8c83ff3127b869c94f02b64dd1': 'STOCK_BYND', - '0x6321778879a264666b14db70158d438be7cff452f067bcaa5e7d84a0c420d6d0': 'STOCK_HFC', - '0x6e8449c9ce66cf37e07f4e42b7167bd871e8453fc4fb897a36aa91ab6b7861e1': 'CRYPTO_QTUM', - '0x7754581771af6b69808a95da236c9faf5b80ff2820e0675db43d747770398c64': 'STOCK_BLL', - '0xa28b0faf01c6edc09de90519ccad66868a4ce0f58ee7499ae61eae7a5298da35': 'STOCK_ALXN', - '0x785f38f3cd80feb7601ec1f6b6f0bdcda4217227501f18d236d182f5cb8ed692': 'CRYPTO_XEM', - '0x5e6b1a1d862ce1c7ed26b5c6e2aa225e5799d59ec545352b1d6f6b2910ea34df': 'STOCK_HCA', - '0x1a6381d578ff99c250b4c6dc3959f5a8bbc27cd86dde6d2e11a1090bd0aaa2e9': 'STOCK_MSFT', - '0x779fe48c2ca7856a1d2b782c04c02e2484fb7dd981c78e79200c2e1797adff70': 'STOCK_JCI', - '0x89aa0dcec14afefe7cb555952e67025d5a22f1ff2035e23a3b2601480ef52b3b': 'STOCK_BAX', - '0x0de2e44aa5e9310441aa75a8626fea9d1559d235370aaf1887d2f7298657a15f': 'STOCK_TROW', - '0x6d5624a6099d4152d21f6d6ba8da0a9d3b054540eafdbc191926dbee0638a4a9': 'STOCK_LOW', - '0x417b1ce44be023e5ab33239d2ae1694984abf67115bbb0bcdd7bd358a599a1de': 'STOCK_SYK', - '0xc06e80fdf6e2ae56c7a2cb87519fbcb531f6efea02c34d2cae15aca3e1b84757': 'STOCK_BK', - '0xeeee683eb883e52287f270b813042d6b8c37f40c27bbbf412ce4e0f6efc36feb': 'STOCK_EBAY', - '0xd2ba3b574340f86dce5a375f56d917884f2d08ea5d0aaada0749632a497a0481': 'STOCK_CPRI', - '0xc5872b5029fb4659ec2c74badb5e5d9c146bb8e19b85223d8326bd1b1bba3206': 'STOCK_IFF', - '0x1e88a1498b6d456b74d8db5b58e49aacb374fcfb4d64ec375552b68d6e720aaf': 'FOREX_USD_SGD', - '0x7db6799195037a7cebb3b72253bbe5113ab0548c5c8b80488f7b616f444bb78f': 'STOCK_LNT', - '0xde24d032e0d43238a0670749324bc57784bafadde4a2aa7474b7c81008ec4e3c': 'STOCK_HBI', - '0xbf45e835c89cc053d5f213aba81be79c4fc45dc1806f10c098faec9dad48a689': 'FOREX_AUD_USD', - '0x5c559118a2f196c0fa3c87c7fa2138b0ddca2570edfce600a3d22f8c7081e810': 'STOCK_PRU', - '0x5506d56b52d2a3d3575ff45fcc8d2696803acc8811f5e2b25403b5346f34bc35': 'STOCK_TMO', - '0xa70dbaf0485d25ce1cf5fbf874bbe8e91ac28987c5c7367ad49fe3e80091be12': 'STOCK_ARNC', - '0xcece8b8c0d034b878ee7f10dd9095a3649765f3fdcbe1ed205f972e1ad2a81e2': 'STOCK_NRG', - '0x86bc2aeb42120a4d51484d9dbf0891621a83d89534ec526f03f492151b96d34a': 'STOCK_KIM', - '0xc6ca490de75f45ed480e692b813387a4b7176fee3f6140e31a3f9297839f66fd': 'STOCK_MELI', - '0x48de7712a9e3438cfde701f39e917b0669d8e5349bee70dcfdae91de0f7061d8': 'STOCK_MAS', - '0xd82addb1175f4c92dd30a91116051d7fa08a375a9c1c3ebbbb4d0f9d52b46318': 'STOCK_PAYC', - '0x856b20f1c49fa5004b3d1cbf97d239ed5253acbde777237dac72f06060f6c34e': 'STOCK_MTB', - '0x92559f5455396df5b109b60e2939b3c7016c94c2760aa47066626fad25c75cea': 'STOCK_ICE', - '0x7ce00de713ac74a20f25812561b41dff73ec1b8d283102b00f6eb5336a3d9617': 'CRYPTO_NEO', - '0x7310fb88dbda67443ae7451e5f912103a2654cc9ce71ffdd49712a01257cd42e': 'STOCK_FBHS', - '0x9cae7dcef6626557302c4717fc19d04cd154ff20077d26676319f08fac9c971d': 'STOCK_HST', - '0xbbe7be032b363cb71f79f44ab53cb7e0520df3e81c22a99160ec9d11db4ca931': 'STOCK_TIF', - '0xb6fc2307dfd7101f3ef6e1140728c0f29e5eb71764d98bfeff433737c883a701': 'STOCK_K', - '0xc9a3b074f317b2dfaf4f27c2ef8187522ea27e5a053b12d8c265d7c2425f4ce6': 'STOCK_ANET', - '0xae641a9b12f66ef8c7f9f73573141b74cdf3c1f7b910373fa45f2b66f23666f3': 'STOCK_AAL', - '0xb76d6028ef38fbe38810a81c2acb46efbcaa4b19121ed2293dedd5e373d2dbfb': 'STOCK_CE', - '0x952a7ad6d4f32850c5f3f6c31d08b6586514ef400684606470bdd0c42803ca0f': 'STOCK_WRB', - '0x2b9c2341d3c700bb6ca1a2fb9ddb20187bd39b1e1a963672db1a3694a72d8fd7': 'STOCK_CHKP', - '0xbc429cae1e500dbc586ddd01fdb13f8b8bea85608c62d6cfc1f548d342886825': 'STOCK_WMB', - '0xd0050efe44c20f57bf77ee620c51e66becdcea6344574d08737c9cb69c90db7f': 'FOREX_USD_HKD', - '0xc86ca0f064ad3bfe38cac5234a54e48b7e8cf6e516b83727a19937ca5528c6d1': 'STOCK_LEN', - '0x7faa04866c4e046b85a674c76534b3acf06c93e44b2365bbe54d2a9cb8344398': 'STOCK_INTU', - '0x3ecb04e9ff082e50915d7b45a1631bf0412cdf3da14c1436bb8f0d33ca64b466': 'STOCK_AKAM', - '0x9ac133a500e9818ad59d13785d81877dcfc2b91d2730e97baaad130c01c37dfe': 'STOCK_USB', - '0x0e4e26206c050620bd359310ec97bb285b170629b2b3ff5b37e502e648a53c7b': 'STOCK_LMT', - '0x86234bce25b4160a2781a289190f78db12a31bf0b0259b87dcef4423ff9c7165': 'STOCK_WU', - '0x635ace95790f978144e86da0ee098d902c31e4e22b7e6a46dc765939ad462227': 'STOCK_CXO', - '0x62ba49e46553f9dae160b244fe9df3c655fb19a4aa6dfa4a0f38d3c620d378f7': 'STOCK_SLB', - '0x383db945be4ac5976a7a1401aa1828ab0c6fd02bba4ecfb195fd042412150c80': 'STOCK_IPG', - '0x62eaa0278b0c971b12401b8a8de81acceaafb860bf1357252c1c828b9a73b59e': 'STOCK_WAB', - '0xe4d508e8e0fe6bac3fa6d1ea9101d8fb58880afdc10d3afc3e0ad01880f84ca6': 'STOCK_DLR', - '0xa98b49de786c61809c21d6a8d44a240cf2903e576dce8348ff4ec3bd834ded02': 'STOCK_MSCI', - '0xdc499b36d004cb050275e74cea31cd717a9c162c7646519d653f2b37c6539f15': 'STOCK_EXC', - '0x7d04bf6b3347ad1e4096ae85efa903b6bb75e38f28dfbbd97e22bd53875ae30c': 'STOCK_NWSA', - '0x7f7acac44f6da95d9ab7980a876a3c73a13d1f739218cea1e008aba8d30880b4': 'STOCK_EQR', - '0x6862095e3ba4a7a49e7257c459d01358afa80293d54d1263204c19c723570e8c': 'STOCK_IBM', - '0xd90f6044909a671d6453c3885a4314860cb57cf7ccd66cc3adfae1df9cd017fb': 'STOCK_TLRY', - '0xbab8f99310b6edd4a3796e3ca9f94a3237113b2f141757d4568dbce26dc88d52': 'STOCK_WM', - '0xa9c48f3e25c408f0f52b2334e5c5f95ac447e68e8747190cb56cc6bdbda04e4e': 'STOCK_WFC', - '0xd1a22a6bddbc85a88520e495177e51252ea2e3cee11df813d6ab5cd90f8792c9': 'STOCK_LULU', - '0x4dcd2645540bbff4adcdc5a0b55d011290908b44cab5d11915be42e173dde755': 'STOCK_MHK', - '0x5979fbdb637ba3aefcc146de48db46626a853ab79e7ea8abfaa8c1ae32ebf172': 'STOCK_PNW', - '0x84b02a60556eff8451eb7d2baec452d037fdd151e19692cd56abae6925a15e0d': 'STOCK_APTV', - '0x4a7afccefe29fc045bb24c51af7a3267337e55db15fa4d569daae29198a9d6bc': 'STOCK_LLY', - '0x30303545ff289efd75321055592f38380dbc58d34701d96d7360d8d1811d53c7': 'STOCK_INCY', - '0x7abf6d79d07285b0b291607e6e672f44459da50dc54ce40d0a951a729ee93648': 'STOCK_RJF', - '0x91116c733588387e442a1639bcf3b2a70bac289372219e3f9c18660b60c54358': 'STOCK_ZBRA', - '0x9d66192f15fefccf1e7614248cda119c46ef796fdbb86bf5460229049858d6f3': 'STOCK_AVGO', - '0x25a4f6d64e563c3217b0d595436b8824fb9a6f9c9d689edad9cde59ab4d58a85': 'STOCK_NCLH', - '0xb282150880f3548c0c1d4c8e2adc6a4b62f4d82023c00cc3f2f05a9201becc7e': 'STOCK_AES', - '0xf5c4c2b62a43af0718d16ff7da73723773c4cbcc7e2215fbe31d1aa164fd7e6d': 'STOCK_RE', - '0x84674c34bab5ddadc2b22c7e26d2bfd60d68cc563c571477ddefa7a2b0a12d7a': 'STOCK_FITB', - '0x1f821f5e74907f2dff59206d706b86448c18db932c7a760e01cdb51a89e1114e': 'STOCK_OKE', - '0x550eff8f0dab708efe56d2ccfb7b564920de3812da61f4f815fa2dfcfc746cb2': 'STOCK_IDXX', - '0x5e1ae665a50d34cc99d3816ffad1c4f7b146dabe3888f9c319d9f0a77a3871e1': 'STOCK_NTES', - '0xec8c22612f34bb5e14ae6f9c64d8cf6f407ebab33b1b488c80e688325806a09f': 'STOCK_FOXA', - '0x25a39fd8f903e054acd8f4821fecf8a2dd53e01fa5e5012dbfa2f79eb14ba72b': 'CRYPTO_BSV', - '0x0874455665d0e7e47de092d49d9728dcf753d10b42d4ae7bfe292bee6c282ebe': 'STOCK_TPR', - '0x3ec17d3254a0980a66f2aa52e9f0d902326a928f158d58b9ad1a573e0eb44aaf': 'STOCK_PEP', - '0xcd8b1798501b99214f1ddec8584dad36702c9aefcb1a4d81d30e3f2005aa68ac': 'STOCK_SLG', - '0x21e2f03645f46cea1e48e10960d9ddcb87acb4aa79d302802fc0fb803d2f73cc': 'STOCK_ACN', - '0x75c90454f4c734b9933e292e4effe66cbac11dea315743842c0db17179a46b1d': 'STOCK_DXC', - '0xd632edcb212b9aba939f806af845057234019149df8195289978e82daeee29c1': 'STOCK_CAH', - '0xae898cc0899d9f40fc1db6f69b5bd65447407120f48df27671cd6773c8d9080c': 'STOCK_WBA', - '0x26754f857a29d332cd4d6d3bab2cb6a9003eef4dc672863ed2f66e156739c894': 'COMMODITY_XAU', - '0x1b7ec9c8b670df6c1786a137d832d65aaeaaa5aa8e11b4e616eac6434a451255': 'STOCK_WHR', - '0xa95986e919f4e530868b9f655890cf616f5f4d5a2b5cb978c66ab7058d4bc9d3': 'STOCK_GPRO', - '0x47e0d8bb2434e2ef3c33931163d0c5f223f7cdea93e7d0e764b6726cb069677b': 'STOCK_DFS', - '0x6acd3e112314021dac7f5bd6fd2d8029c0f9221f3790ba71c933743b91013a32': 'STOCK_SWKS', - '0x7c85ef97f1fc14891be08f8f1fc2af6175a3ebd3189e6406a90704a6ec7d2d23': 'STOCK_CSGP', - '0x7967996f7d90be6d8cab58fe83043778e11f530a455a8ec35e9d180f1c472635': 'STOCK_PCAR', - '0x6b60223d1b211279d85fbd079c30b2f20d59e4ed6c48141fa2790862231dcec2': 'STOCK_CNP', - '0x5cfccdeeae6d8c170e31c707d08269499340e4a0fba0577b42a70481f51e5cd6': 'STOCK_CHTR', - '0x6a8ef72afaabc3de794dd2f3721a7fd3b061255329d8beb23243f3210ba1aa3d': 'STOCK_GILD', - '0xdd57d60501f52d6002b6a30602c0fe097bbca87a78e65e4e065ce583dfe7d4e8': 'STOCK_VLO', - '0xf2ac6b1381c65b43c96601b31c0e2cd97d89b6ffb66c0a2eb83647a57cede3d2': 'STOCK_MTD', - '0x7bea7b2b1880b5ca5cf35ef1dbe9c083a9ed0580f75bc5b52349279c12018577': 'STOCK_SHW', - '0x04b1ef8d108ff57be450c2782318ea1595989f75ad265fdeb58c5c78a2217069': 'STOCK_AMCR', - '0xf58b2c4893322fb18d58457bcfa3660dbd46983db25ef8b769f07b3384a4be05': 'STOCK_DGX', - '0x26c64bdfbea62b20fd6281a04db37e6e64b1dcb0f68142d651c6ba68490537a5': 'STOCK_MAR', - '0x19fee264eba35deea6b7a8acad0fd9997e8b827bcff8797660bcd781d58f4f1f': 'STOCK_EL', - '0xa40d17cb226766280c73c950c37aebda3179c36e3db89f58dce945109777c89f': 'CRYPTO_BCH', - '0xeaca2b76481561cf301a934abaafd82f480f3d126c761dbe3a77ab5dfab0864c': 'STOCK_AIZ', - '0x1f3679ecf7403055da0904bd02ce16f1c4b7897331d6cee8b394e0e3b3dd72e4': 'STOCK_ALK', - '0xcb74917f820520097b9a52af9806b1e26f82b9e6bf25ea0f665fd81711df6c6d': 'STOCK_COG', - '0xd56e28b6582b81ef902f797c1e1005942d707fa0a4f2ddc39f4a62d68166b87e': 'STOCK_URI', - '0x45d3374da185a4109504f62aa1e20441945b7db959de448367db7d7447bbc555': 'STOCK_AME', - '0xe974297bac589b2dd412d1cf82afa90ef971b977c644c11fec553e7978748516': 'STOCK_AMZN', - '0xd3b53692c4ec9fc1a11f8562d63d84cb2dc495faec9dd80362d0b8387806d145': 'STOCK_DG', - '0xadbb406a2fa9de4be7d799730120f16ea92cdf3022134fe39ffaf4472e43942d': 'STOCK_T', - '0xc2cbd7899523e699a3fbcb75ec3140c6ba5e6e8a56a95cb15c46b644df8ba1fd': 'STOCK_ABBV', - '0x07e9706fee09b8c2093e71dba9f9a43c11f4eb08a47ac5232227dba5f7da8472': 'STOCK_MKC', - '0xcde45df7d95208aa26d8bdb0729e81a672696a43aeb5e1d00b9ffd16357105af': 'STOCK_KEY', - '0xa88e66d2eaed9e1deb6dee95be1512e191533db52922c80b63c78e4d981742d5': 'STOCK_XRX', - '0xb0f7d335959339c31eafc1de352c0fae3d59b1f76e1db393fe0729a026e7f0f9': 'STOCK_FTNT', - '0x0b61a052595f7e9e2ff94bd6f91af31ab5164caeb9f4e542724b400b66353a06': 'STOCK_TGT', - '0xac6588ca800da1ae912916434b7282095720f0fe141a4bb660c403b557f0d2e0': 'STOCK_HOLX', - '0x76f2f2391fbc1c07afa1a6dc7eac9d659a39a81795e52eecac82152a7f542e02': 'STOCK_SBUX', - '0xcc508e6360a7f8b827e88270f0e4af6298aa3fa76c5141528c7382e969146ef6': 'STOCK_PSX', - '0x8bf1bfaac7f16376ecfb607a8eb06227a7b04fb254b7817e1c993e53217c0fde': 'STOCK_DUK', - '0xb0dce1ba4988dd52cd59f5d12e8799a31b707de4b30c996e01d8b9c7457fbf65': 'STOCK_ROK', - '0xc4638ad25c13161751a92d2f2dbc7db93e0e931e01eaffcecffb2d410f5854a3': 'STOCK_SNAP', - '0x5a1cbe3d35969ca8ff52a53db7909fd9ee742e8ca53ab2ea495ff12b2c336d31': 'STOCK_ASML', - '0x7e45b814a4028425b1a454b1e0f5f93c9bd64024cc400762a2b31f926be2ddbd': 'STOCK_HAS', - '0x434397b341e94446783a0941e02d30e917ed91d2311183f902aff6b065b0783d': 'STOCK_C', - '0xe617aae88f9e51bf87fd8e159812b7c6d27cc703b0468f380c43829724698039': 'STOCK_ADS', - '0xb542d8b0be4bc64c0dc4508ed6dcf1f6f8d2edb8a2bbf2052a7bf682e2afa25b': 'STOCK_EXPE', - '0xa1206ea7ca62bde591ba6191be83208aa1ed744eedac891b71540a9665e5d653': 'STOCK_QRVO', - '0xc392489841efcaf7e1fa43eecaeecc48d7ffc515c507ae51b715e1bc47aab347': 'STOCK_XYL', - '0x639a692a5097491d6a2b2bff7e6a58bf0e15a5700e744f1c13834acb69a2e1a2': 'STOCK_REGN', - '0x45b8666ccce28bb327c37ffecd49a53653ecdcc9d13d7bc99893b52d234cdae4': 'STOCK_NKE', - '0xaafc1fb4d34a173623424bf202ac87cfe743cb49e757c40f06dd963351596345': 'STOCK_BA', - '0xa1979648187693c1675e8fba288db6d2e8e7f209e98e9760ec85e6198cae8ee1': 'STOCK_AEE', - '0x4999f8fca509ad1854369ba7c25e041fa14bb332708a62bff3825e3d8e822d73': 'STOCK_TSLA', - '0x44b8321fdefa52cfc281ec71749ff44e375268c942cc87909befdddd4fcd88a9': 'STOCK_O', - '0xdc56278f42e831b1fc60ff867c78db07ca0b67d2734c7d5e02ac5c908868f772': 'STOCK_HSIC', - '0x6d3f7638e53a4448bd9b633d23690b4a1289beba0ba8375b169b22e83116bfb4': 'STOCK_CBOE', - '0x6d571c2610d48780c1bc838176d970653c76e75c5b239bab8cf9602eb8c79f95': 'CRYPTO_DASH', - '0x03808985ad77199aaedf7321afcbaf87960a966a44562eccf5cdc034cce9f6b9': 'STOCK_YUM', - '0x31e64fd4d3a8a115ce2b1c541de47e66d1003355ef04cc407a910f428d925fef': 'STOCK_BIIB', - '0xa7b9c191b263ca30eccfb75c64e3dc59b237649704af58285822a0dc25d3a742': 'STOCK_HPE', - '0xd217b7cdf0b2da3714f1b3741f5f53f1df69665effa864d98477515633f50cc5': 'STOCK_ORCL', - '0x4207aa3bf940aa29d757526fdf4837e579d37fd09a049a89ec9f75ab120a2f10': 'STOCK_CTAS', - '0xb17b4b4bbd95e56770b553a53c3cc6489181ba99de18e05adf1b90e8f4b84147': 'STOCK_DE', - '0xba039c5ddcaa4789ef1a73200c55c413dde04929b5eb7d24cb267d2da737a6df': 'STOCK_IEX', - '0x3a440c57f0ee3205031c4813498e912ff7e1c3b6812401740fc9c0ea104fed4d': 'STOCK_DHR', - '0x4ca42f76eef42501efd3e1adbf7750e1415206d9fe4127b305b21e56dbc5649f': 'STOCK_LNC', - '0xb627ca8844c5c04d60af4116dc3a18560cd0256dae7cc40f806933159784a0bf': 'STOCK_F', - '0x054156b0e146db9b810c6c09e085178e171d6354855a36950cf4880e3db29d21': 'STOCK_PM', - '0x956554a7242c0fd38f8f636e03996eab46c4a61257ee44abae9a45d65e875a7e': 'STOCK_GM', - '0xcdd01ac97e1475614a38a15dbc0e6c2a1367295b8d9b880ea7cad801b0a3697d': 'STOCK_JNPR', - '0x4f4c7b94258a047673a8efb76fd677d42491844e79e6ba42358aa51b3d7cd503': 'STOCK_SFIX', - '0xfcaf4826dbe62bc988af0a622d515e2a153c696749319448fdfa0a516f59d542': 'FOREX_USD_PLN', - '0xdd4d2b2b6758eea427f062707d3c4ea5045c31086fe887234609c39859bb48a2': 'STOCK_TRV', - '0xd028a24f4a6651541cbb284350147dd3fff2071dd8f8f5ce1a17dc4ed9f6e704': 'STOCK_HES', - '0x67f01982bfde57d91fd500704ae0e5bebd68877aef8e61e3a6dff0789b06842b': 'STOCK_CFG', - '0xa09479450d8ad01d318e2cca457101a60999d2afef21ab56e23ee9e469cf61e1': 'STOCK_WRK', - '0x820ff2197656c676b44835eef43bd0ea1948d9393ba6d9a25dbf73060aa0053d': 'STOCK_TFX', - '0x9e7ba4912f10cdd4b8aecc6297e0372e558dbdc771ffbdbe28a454c70c040600': 'STOCK_CLX', - '0x241a868a84c4fc87bbe9da262f4e96a1ffa92f6e264720532c4f4b645f138cc6': 'STOCK_NSC', - '0xb5cb403e5db087fff238a6ef328a51519c58aaa1b4f3be58378a9c13e7ebc693': 'STOCK_BIDU', - '0xfb96f3a9c75b8feb4446a3d262225ce99889a4dd6be0d30586ab6dbed9188b33': 'STOCK_AMGN', - '0x816909d4991c4cb98e85dd8099d275fce26208251a3df66cbb27eff93dfd3ba3': 'STOCK_CSX', - '0x74885147f77a0f83438fd9edfc6d5bb495e145d03619d46900637776dfbd2e40': 'STOCK_FFIV', - '0xc6a034e4e8c1eff1bca1b9f5939c98663381daebc717ae4605cd866b11e05f8e': 'STOCK_ZBH', - '0x9ffff5edf25bd91105f02dceb5b579e0bc13ece929b607c747afc2be29a0ab3b': 'STOCK_FLIR', - '0xfa8ae8cd2ab9c5c5950baeb9871848e085e24fb301b698e19650c4052fcb8fb8': 'STOCK_MU', - '0x9958d72d0765471fd2e258e345b3e62e6fda7e9d318ee755b07f9cfbb8626999': 'STOCK_LHX', - '0xe48706e737dcb8a92bf8d5aacd8205cf519e53dd9f95b795dabeee9af8039906': 'STOCK_PRGO', - '0x47a5ef2de21a2c20aa926eb91b8935f3b1fa6916fb93081754e428ca512c9206': 'STOCK_UAL', - '0xd9ab965e9500ab80f91c6c6c75017ff50adb39a7d362d75d77ef5992bd698bb4': 'STOCK_SWK', - '0x639c6d6ebc444bfc0f517551c855372840f1f89aca696b5108c07aae60de7090': 'STOCK_HII', - '0xa05c41c753ba377899f8afe055d6f1c32d6de5e9e6c0f24a87c53731ff13235a': 'STOCK_PINS', - '0x85669fd712e759ec146b2f5e703ac592c99d2985ef2924f505431cb84811bc16': 'INDEX_INDU', - '0x51f5ddaa2beaeb6968324d0db3e4f63ff4b0e9ab3be8ef204ffab0067dbbb44e': 'INDEX_SPX', - '0x7c0d1dbe8f6ca0d83e4904fb23dcbb81474d7ce2c35613aeb5c8df86d83864c3': 'INDEX_NDX', - '0x152ac73df05b5d6c0f9b57e1897f441f60ed35705161fe8ca7fae233f70c82bf': 'CRYPTO_ATOM', - '0xec61c835b3c93bd5c7f1d6ca0976a6e5bcf1173c59c50e96c4dded475cc72b9c': 'CRYPTO_DOT', - '0x9a56eca3ef788917d00224967115c083119d078aa0553df66cb175813941d82c': 'CRYPTO_THETA', - '0x996c6ae24d74e2a790d11b5ec26ac3972e618f5c670afb061c81c8413e2fd01a': 'CRYPTO_VET', - '0xe59f22b039bca8ea76c8d602fd5db11e8c38bf73923421734ffcaf32057fbac3': 'CRYPTO_LINK', - '0x3f8b0194c9221ffd1af7ec6328ddf0174a4dc252109b3f0b0c31c7e37fd34b84': 'CRYPTO_CRO', - '0x450cbad9f67690c9b6408922730b601956b8d75058193e7d070e22347546bd0c': 'CRYPTO_FIL', - '0x1cd2475ebd899d8988c9b3c608f2ff019ec2781eb79c5c541020bfa96fa03b51': 'CRYPTO_UNI', - '0xce190796dbfe26c2a956c943021460e21ced86d1e195628b65cf29f98221f2ce': 'STOCK_VTI', - '0x2eec9ce5f030c98da23b89d651dcbc7c2dfcf90fd08e709651e189cb91daed4a': 'STOCK_DKNG', - '0x849e5baf6fb603f20f22e5e6f52b3e269025ae8a7b5469e39dc41ead51a44492': 'STOCK_INO', - '0x0cce38a115b63a44389d395f37b4725ccf3654823883c854ba0f6a0a80e35b47': 'STOCK_JBLU', - '0x699875c32048d6bfeac613cb2bc69dd8402ed4fd8d2abe551283f463c401ae44': 'STOCK_MRNA', - '0x4a5e6cc58e55c233c5d66af2ee9f87aecd6bba949f8b85044b394e25fe0d40ca': 'STOCK_NKLA', - '0x970d007615681ee052fefa3cc22def8ccc764d6f65e92d594fb021eda72497cd': 'STOCK_PLTR', - '0xa531624355e21f801d81fda707b5748702e1031a3189a277eb48bdcdefc69c6c': 'STOCK_PTON', - '0x7b75f949827a656e175b43a82f3edc3820c43daa70ba4f14fd393c8e4d333ea8': 'STOCK_RKT', - '0x2b0938c1262a48a28c56f600b8007288d5e1916d6a0c54b014daa066ac8d9f68': 'STOCK_SAVE', - '0x5c95cc8a596e5be67edd4e2ea238e26eb921ab2d8c7e0c9013fc590c671eceb5': 'STOCK_SPCE', - '0x3d59afb6e522af4f3b6ca3d39e95e62fea02a214c1f6901bdb1197ad48a8beea': 'STOCK_SQ', - '0x99fc2948b21f246a5dd363418f6f1eeb7258755ae062d6895ca7bf29705d449d': 'STOCK_SRNE', - '0x35759bbd2fbe7d22e9c39246d3dd431b80c2b3ea965e0b88704a57275d6f40c7': 'STOCK_WORK', - '0xf80ff772996a3bd2323c0546f6912e32e5e5887f2e28100f1c32cc27d94430d7': 'STOCK_ZM', - '0xd592d2c53ca12c179607e0efd2b174be5a1a6982211e7581974cdce7f7a8469f': 'STOCK_SNOW', - '0x229b71ea944d163ba1b03dcf9ef29e3b1c9be293aa8c8bf801bb0faf00a75a2e': 'STOCK_RTX', - '0xe45c8a8c374501ef3d6dc5d9d985906f29b73066e952e52d0ab7bedec0b253f3': 'FOREX_CAD_USD', - '0xda14ab5255638a2d6899d93c3059c6c3eb8096ceb66ceee4d678a9c437514e29': 'FOREX_CHF_USD', - '0x3ff830fdaab999b4f778a27287ffa63ed078297d33ea89839b56a7ee0faa42fc': 'STOCK_AMC', - '0xcdcc2cf3cb7438522a0b86044f6d969834dcc039822100eebf0c5c74b734c801': 'STOCK_BB', - '0x6fb0f1cc0d4e2ffefb4959376fb8fa9714e20c45589e3edd17d5a1b17e511327': 'STOCK_BBBY', - '0x0ea2879c7e95a1b5afe16344f9023ae7aae8534903a1e16ea234faca0ee34036': 'STOCK_GME', - '0x566ad5918a253413d5538646009cab625c3f0d1a35f1329cdb477cb863f5e43e': 'STOCK_TR', - '0xd552906e7f82ec0606b9a54a2faa6073c76023f2f5b84a9565b74eeadfeb0044': 'CRYPTO_1INCH', - '0x0d6669d472b3287531af2829e0e48580a313ee7e74b22f7fa6c7483d1f3b1f2c': 'CRYPTO_AAVE', - '0xc47939f3c91266f4761c6ec7032f26d978240325575f0251c46d7ced39b172ac': 'CRYPTO_AVAX', - '0x55f803b868da741821a5a89ffe21e135ed31536de1bcb7c0d8c829452b966b50': 'CRYPTO_CAKE', - '0xe6d5d8520b6bf5d30fe65063530f9c9a10f82bdf52bf5e89d87a2b8590d16a39': 'CRYPTO_COMP', - '0xcd95869b7bd41015d530741dec9f19d72f546aa2efb47d7e31a5e75f0ee9cf45': 'CRYPTO_CRV', - '0xf561d76927c3f92b3bf56057886404015c5b5c8a42c4456240c2808ed6e1e97a': 'CRYPTO_DOGE', - '0x9e58396bcc5f5929d69d20467654756b7de7ccb7c504322e182930117fed21be': 'CRYPTO_EGLD', - '0xbc6593f3a65b63ad03a9dd1899dd65ecb684d35ec8f74d59f5a56fa223a8ac8b': 'CRYPTO_FTM', - '0x3638721edc942ba1c940a1c8b75ffa69f60fa9be5d07d9a84351ea0ff34a879c': 'CRYPTO_GRT', - '0x50bf9dbd0110f92efd3096afed6b88e2add080e10ce2e04246f37249407e15eb': 'CRYPTO_HBAR', - '0x1b73e75c6184ef73718e7990ffaa1b32ac7d3de314600f463ac188e53f71260a': 'CRYPTO_ICX', - '0x1d03f09069f84353f56e015480353250f8508e2d2ae711002877bb927f1f26cd': 'CRYPTO_IOST', - '0xdec4706fe9841185443bd652c6389b15a97f0bf105aa71cc0aa972206932c0cb': 'CRYPTO_KSM', - '0xfb038d0a143a5a2ccee6d8884d2ba09aa2baafa07a2df24403d33baabdb0b6c9': 'CRYPTO_LUNA', - '0x8f621d04b3e6b8f1808c4ca8027dbe18a3225f422ffec7842977a634656d2534': 'CRYPTO_MATIC', - '0x1a0c430e65dbef587baa1c7076aa03b22bd44eb20daac0acd43ea1165b41906b': 'CRYPTO_MKR', - '0x7e8ecbeff15206df569eeec2dde7671059c3434228539fafe4a928790a38fcc5': 'CRYPTO_NEAR', - '0xb8ac3d317cc8b687194154523a2445e0afb41ff24d413963ff39b5bff699b4d2': 'CRYPTO_REN', - '0xf2e6b1deeada9c8a817cb3b07fd95ee60741c206fc4cd7f4c32348d53855c63b': 'CRYPTO_RVN', - '0xb4ab2dfac2573e12b21859aff90180ce3ceecc6e5e45412ab31a818f1a73e49e': 'CRYPTO_SNX', - '0xbc80939fd678de0476c2e9522861c66f86d1dce96772455e054f86a886e6f4e6': 'CRYPTO_SOL', - '0x41a955c5ca0a4c22d4b65d206ee40f0a03ba6970c6219a67c1e9983f1395d73b': 'CRYPTO_SUSHI', - '0xf2270ff10b25cb2ce0d5e07c3b109953ad597860601af947495b293d837918f9': 'CRYPTO_ZEN', - '0xecc44f4db41b6cdd3b6d039f53217f23c528ed36ccee60420d1b00a47d7a1dca': 'CRYPTO_YFI', - '0x20018b24fc1a4cdfba90371f3290adaa8bf2b23f514f8b82ef4c1cc1314b2878': 'CRYPTO_ZIL', - '0x4a32590e1a2511f46866e1fb8ba384057a4e19aaf90f4c2fc6f841e9857e9419': 'CRYPTO_ZRX', - '0x5c386e087c5f8602ca063d154d8fd13a0b5cbe82440cdc71477debcdea65666e': 'STOCK_ABNB', - '0x31a0ea32edb862242c5efa56cdbed2cd58f19b9867c8547e768fe136dc0c1b68': 'STOCK_DASH', - '0x315196beb37a8b860906b5bd83996fc39095ce1604233003078eeaea22672ac6': 'STOCK_UPST', - '0xcdee8d9ed02e35b4230203d4ee8a73c786151b95697d02dc5b6ac22990518ba4': 'STOCK_POSH', - '0x71cfb7c2117dcad94aae9183e67a35e1bec4c1e8d3434f42b3faa70ce137e7fc': 'STOCK_AFRM', - '0xd4ab8ab5628704ea3e439b7c8c54a1cbd708187ec8c4b6aca6a3d15f2950b860': 'STOCK_AI', - '0x3b49f18a35bc3af849425f5ea1e018a6a058009a57d11af9bb53275652f02bfe': 'STOCK_WISH', - '0x52a80fb6612cbd5e438dda2a82a892b6f9e538b1877e339270aad3f127a7a70c': 'STOCK_WOOF', - '0x95668731fc1001fb63bb043847f148e190ea496c1eb5f2ff6ee2298deda3f170': 'STOCK_BMBL', - '0xb04d11c1a0d680a5d8ebd07f0e2c0364acf265a59feb906a57c1bc0aad0c192d': 'STOCK_QS', - '0x4da7aa32875461ecabc402eacb555643d5758d1927c9d30048fb81262c2e4a79': 'STOCK_ETSY', - '0xfb5f8db2de41ef3981ffaae16a7711d327df43f4f10000d0f102a4eb1e3a6001': 'STOCK_NVAX', - '0xb64eea93a5c1bf8686ef0461c1a53c4ab99846c0922f537a6c293a753969940b': 'STOCK_OPEN', - '0xd8db2114c6d873ee1e4615fe67676c9f90562c09fcbc83ee42c4e49a35657095': 'STOCK_SHLS', - '0x7d8aab61ed8e60f3ef047f4ee82918a930bdfb8ee7434c44cb3e1ae9f3378f11': 'STOCK_WKHS', - '0x1446415e4b90a09dfb6b0af271ed214d73d04669f3b80cd0c7a407d8969a2af1': 'STOCK_XM', - '0x8b33f041e58f1c8d716ba1c9045e4c615d47dc51a5295490a375c3b8e7f3f9c8': 'STOCK_AMRS', - '0x73840e5dcf9b8efb8762fe80d63d5fbce630530edd07ea349cc958fd34b92ee8': 'STOCK_HIMS', - '0x039d93dc2928e206ddfde97c9bba94748168fc0eea84d0046c599148ade5d9c5': 'STOCK_SNDL', - '0xbd4cfcf7a45c143c92fd9142ba05fd388b66c8ec6f6e7724b6368c68c348bdcb': 'STOCK_STPK', - '0x120f2957fde8a373226423fbd14fe1534f300fb7aa42132a29473e8492ae31b9': 'STOCK_ZOM', - '0x07ecbb8b7be35e53173ad71b8dfe0ee60fac6fb0b4c8939afec70ece32fc5d2f': 'STOCK_IIPR', - '0x4e44fe1a2785b820c0f9053c2cd9512edfcb515e6ea88a37beba79b52a66546a': 'STOCK_CPNG', - '0xb41eebc2452311c8da2851575ed5d09ebf0a4909f765b53c18f9c42ac7b71956': 'STOCK_RBLX', - '0x47eae69db7b96783137246d2a25ada5cb7e2d05fe352f74f0930d90dea0af07b': 'STOCK_PLUG' -}; \ No newline at end of file diff --git a/helpers/activateMarket.js b/helpers/activateMarket.js deleted file mode 100644 index be4fb4a..0000000 --- a/helpers/activateMarket.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * how to use this? - * - * truffle console --network YOURNETWORK - * - * let activateMarket = require("./helpers/activateMarket"); - * activateMarket(MorpherAdministratorProxy); - * - * that will fetch the markets from docs/markets.js and start bulk activating them, given the private key is in the .env file etc... - */ -module.exports = async function (MorpherAdministratorProxy) { - const marketHashObject = require('../docs/markets') - - const marketHashes = Object.keys(marketHashObject) - const adminOverrideProxy = await MorpherAdministratorProxy.deployed() - let marketsToAdd = [] - for (let i = 0; i < marketHashes.length; i++) { - marketsToAdd.push(marketHashes[i]) - if (marketsToAdd.length == 20) { - await adminOverrideProxy.bulkActivateMarkets(marketsToAdd, { - from: accounts[0], - }) - marketsToAdd = [] - console.log('Added 20 Markets') - } - } - if (marketsToAdd.length > 0) { - await adminOverrideProxy.bulkActivateMarkets(marketsToAdd, { - from: accounts[0], - }) - console.log('Added', marketsToAdd.length, 'Markets') - marketsToAdd = [] - } -} diff --git a/helpers/deployNewStaking.js b/helpers/deployNewStaking.js deleted file mode 100644 index c4edd75..0000000 --- a/helpers/deployNewStaking.js +++ /dev/null @@ -1,64 +0,0 @@ -const MorpherState = artifacts.require('MorpherState') -const MorpherOracle = artifacts.require('MorpherOracle') -const MorpherTradeEngine = artifacts.require('MorpherTradeEngine') -const MorpherStaking = artifacts.require('MorpherStaking') - -module.exports = async function (callback) { - const accounts = await web3.eth.getAccounts() - const [deployer, adminAccount] = accounts - const ownerAddress = '0x720B9742632566b76B53B60Eee8d5FDC20aC74bE' //must be the same as the account that deploys the contract! - console.log(deployer, adminAccount) - const stateAddress = '0x52F74D95185f11a9A4885bFbDA77072Ff3CaaDCF' - const morpherOracleAddress = '0xEBd036277a37034D77457042c473f9304fAa37fc' - const oldStakingAddress = '0x2Ec8c0eB62f7191A27658C4E101061cE2a4F1447' - let morpherStakingNew = await MorpherStaking.new(stateAddress, ownerAddress, { - from: deployer, - }) - console.log('New Morpher Staking', morpherStakingNew.address) - - let oldTradeEngineAddress = '0xa8C7039Db427549d3B4CB29cD1E23622dAb99a15' - let oldTradeEngine = await MorpherTradeEngine.at(oldTradeEngineAddress) - await oldTradeEngine.setMorpherStaking(morpherStakingNew.address) - console.log('✅ Set the new Staking in old Trade Engine') - - const morpherMintingLimiterAddress = - '0x52F74D95185f11a9A4885bFbDA77072Ff3CaaDCF' //set to state on dev - - let deployedTimestamp = 1613399217 - let tradeEngine = await MorpherTradeEngine.new( - stateAddress, - ownerAddress, - morpherStakingNew.address, - true, - deployedTimestamp, - morpherMintingLimiterAddress, - ) - console.log('New Trade Engine', tradeEngine.address) - - // await morpherMintingLimiter.setTradeEngineAddress(tradeEngine.address); //on dev not necessary - - let morpherState = await MorpherState.at(stateAddress) - await morpherState.grantAccess(tradeEngine.address, { from: adminAccount }) - console.log('✅ Granted access for new Trade Engine') - await morpherState.enableTransfers(tradeEngine.address, { - from: adminAccount, - }) - console.log('✅ Granted Transfers for new Trade Engine') - let morpherOracle = await MorpherOracle.at(morpherOracleAddress) - await morpherOracle.setTradeEngineAddress(tradeEngine.address) - console.log('✅ Set new Trade Engine in Oracle') - - await morpherState.grantAccess(morpherStakingNew.address, { - from: adminAccount, - }) - await morpherState.enableTransfers(morpherStakingNew.address, { - from: adminAccount, - }) - console.log('✅ Granted access for new Staking contract') - //revoke the old staking contract access - await morpherState.denyAccess(oldStakingAddress, { from: adminAccount }) - console.log('✅ Denied old staking access in stake') - await morpherStakingNew.setMinimumStake(0) - console.log('✅ Set minimum stake to 0') - callback() -} diff --git a/helpers/deployNewTradeEngine.js b/helpers/deployNewTradeEngine.js deleted file mode 100644 index 6d98409..0000000 --- a/helpers/deployNewTradeEngine.js +++ /dev/null @@ -1,128 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherOracle = artifacts.require("MorpherOracle"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); -const MorpherMintingLimiter = artifacts.require("MorpherMintingLimiter"); - -module.exports = async function (callback) { - const addressesAndRoles = require("../docs/addressesAndRoles.json"); - console.log("Deploying a new TradeEngine with Truffle Dashboard..."); - const chain = "sidechain"; - - const [, contracts, roles] = Object.values(addressesAndRoles[chain]); - - /** - * Owner Actions: - * 1. Deploy new Trade Engine - * 2. Set new Trade Engine in Oracle - * 3. Set Minting Limiter on new Trade Engine - */ - - //select deployer account - await waitForAccount(roles.owner); - - const deployedTimestamp = 1613399217; - const newTradeEngine = await MorpherTradeEngine.new( - contracts.MorpherState.address, - roles.owner, - contracts.MorpherStaking.address, - true, - deployedTimestamp, - contracts.MorpherState.address, //set minting limiter to state first - contracts.MorpherUserBlocking.address - ); - console.log("New Trade Engine", newTradeEngine.address); - - const morpherState = await MorpherState.at(contracts.MorpherState.address); - await morpherState.grantAccess(newTradeEngine.address, { - from: roles.owner, - }); - console.log("✅ Granted access for new Trade Engine"); - await morpherState.enableTransfers(newTradeEngine.address, { - from: roles.owner, - }); - console.log("✅ Granted Transfers for new Trade Engine"); - - - const morpherOracle = await MorpherOracle.at(contracts.MorpherOracle.address); - await morpherOracle.setTradeEngineAddress(newTradeEngine.address); - console.log("✅ Set new Trade Engine in Oracle"); - - if (chain != "sidechainDev") { - // const morpherMintingLimiter = await MorpherMintingLimiter.at( - // contracts.MorpherMintingLimiter.address - // ); - // await morpherMintingLimiter.setTradeEngineAddress(newTradeEngine.address); //on dev not necessary - await newTradeEngine.setMorpherMintingLimiter( - contracts.MorpherMintingLimiter.address - ); - - console.log("✅ Removing minting limiter from old tradeEngine"); - const oldTradeEngine = await MorpherTradeEngine.at( - contracts.MorpherTradeEngine.address - ); - await oldTradeEngine.setMorpherMintingLimiter( - contracts.MorpherState.address - ); - } - - /** - * Administrative Actions: - * 1. Remove Minting Limiter from Old Trade Engine - * 2. Set Minting Limiter on New Trade Engine - * 3. Grant Access - * 4. Grant Transfers for new Trade Engine - */ - await waitForAccount(roles.administrator); - - if (chain != "sidechainDev") { - const morpherMintingLimiter = await MorpherMintingLimiter.at(contracts.MorpherMintingLimiter.address); - await morpherMintingLimiter.setTradeEngineAddress(newTradeEngine.address); //on dev not necessary - // await newTradeEngine.setMorpherMintingLimiter( - // contract.MorpherMintingLimiter.address - // ); - } - - - if ( - addressesAndRoles[chain].contracts.MorpherTradeEngine.oldAddresses == - undefined - ) { - addressesAndRoles[chain].contracts.MorpherTradeEngine.oldAddresses = []; - } - addressesAndRoles[chain].contracts.MorpherTradeEngine.oldAddresses.push({ - address: contracts.MorpherTradeEngine.address, - replacedOn: Date.now(), - }); - addressesAndRoles[chain].contracts.MorpherTradeEngine.address = - newTradeEngine.address; - //print the new addressesAndRoles object - console.log(JSON.stringify(addressesAndRoles, undefined, 2)); - return callback(); -}; - -const keypress = async () => { - process.stdin.setRawMode(true); - return new Promise((resolve) => - process.stdin.once("data", () => { - process.stdin.setRawMode(false); - resolve(); - }) - ); -}; - -const waitForAccount = async (account) => { - let [currentAccount] = await web3.eth.getAccounts(); - if (account != currentAccount) { - console.log( - "Please select account " + - account + - "! Current Account: " + - currentAccount - ); - await keypress(); - [currentAccount] = await web3.eth.getAccounts(); - if (account != currentAccount) { - await waitForAccount(account); - } - } -}; diff --git a/helpers/utils.js b/helpers/utils.js deleted file mode 100644 index e281760..0000000 --- a/helpers/utils.js +++ /dev/null @@ -1,41 +0,0 @@ -advanceTimeAndBlock = async (time) => { - await advanceTime(time); - await advanceBlock(); - - return Promise.resolve(web3.eth.getBlock('latest')); -}; - -advanceTime = (time) => { - return new Promise((resolve, reject) => { - web3.currentProvider.send({ - jsonrpc: "2.0", - method: "evm_increaseTime", - params: [time], - id: new Date().getTime() - }, (err, result) => { - if (err) { return reject(err); } - return resolve(result); - }); - }); -}; - -advanceBlock = () => { - return new Promise((resolve, reject) => { - web3.currentProvider.send({ - jsonrpc: "2.0", - method: "evm_mine", - id: new Date().getTime() - }, (err, result) => { - if (err) { return reject(err); } - const newBlockHash = web3.eth.getBlock('latest').hash; - - return resolve(newBlockHash) - }); - }); -}; - -module.exports = { - advanceTime, - advanceBlock, - advanceTimeAndBlock -}; \ No newline at end of file diff --git a/migrations/10_deploy_escrow.js b/migrations/10_deploy_escrow.js deleted file mode 100644 index be0fbb9..0000000 --- a/migrations/10_deploy_escrow.js +++ /dev/null @@ -1,10 +0,0 @@ -const MorpherEscrow = artifacts.require("MorpherEscrow"); -const MorpherToken = artifacts.require("MorpherToken"); - -module.exports = async function (deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - const treasuryAddress = process.env.MORPHER_TREASURY || accounts[0]; - - const morpherToken = await MorpherToken.deployed(); - await deployer.deploy(MorpherEscrow, treasuryAddress, morpherToken.address, ownerAddress); -}; diff --git a/migrations/11_deploy_governance.js b/migrations/11_deploy_governance.js deleted file mode 100644 index 50163c1..0000000 --- a/migrations/11_deploy_governance.js +++ /dev/null @@ -1,40 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherGovernance = artifacts.require("MorpherGovernance"); - -module.exports = async function (deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - - const morpherState = await MorpherState.deployed(); - - /** - * Only setting the Governance on Mainchain - */ - if (network === "mainchain") { - - await deployer.deploy( - MorpherGovernance, - morpherState.address, - ownerAddress - ); - - /** - * Grant the Token access - */ - await morpherState.grantAccess(MorpherGovernance.address); - - /** - * Set it to the State - */ - await morpherState.setGovernanceContract(MorpherGovernance.address); - - - // // transferOwnership(ownerAddress) - // data = await morpherState.methods.transferOwnership(ownerAddress); - - } else { - /** - * Override access for sidechain - */ - await morpherState.setGovernanceContract(ownerAddress); - } -}; diff --git a/migrations/12_deploy_faucet.js b/migrations/12_deploy_faucet.js deleted file mode 100644 index b2ea7ca..0000000 --- a/migrations/12_deploy_faucet.js +++ /dev/null @@ -1,36 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherFaucet = artifacts.require("MorpherFaucet"); -const MorpherToken = artifacts.require("MorpherToken"); - -module.exports = async function (deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - - const morpherState = await MorpherState.deployed(); - - /** - * Only setting the Governance on Mainchain - */ - if (network === 'kovan' || network === 'test' || network === 'local') { - - await deployer.deploy( - MorpherFaucet, - MorpherToken.address, - ownerAddress, - web3.utils.toWei('100','ether') - ); - - /** - * Grant the Token access - */ - await morpherState.grantAccess(MorpherFaucet.address); - - await morpherState.enableTransfers(MorpherFaucet.address); - - /** - * fund the contract with 1 million MPH - */ - const morpherToken = await MorpherToken.deployed(); - await morpherToken.transfer(MorpherFaucet.address, web3.utils.toWei('1000000','ether')); - - } -}; diff --git a/migrations/13_deploy_adminOverride.js b/migrations/13_deploy_adminOverride.js deleted file mode 100644 index b6dd6e9..0000000 --- a/migrations/13_deploy_adminOverride.js +++ /dev/null @@ -1,38 +0,0 @@ -const AdminOverrideProxy = artifacts.require('MorpherAdministratorProxy.sol') -const MorpherState = artifacts.require('MorpherState') - -module.exports = async function (deployer, network, accounts) { - const administratorAddress = process.env.MORPHER_ADMINISTRATOR || accounts[0] - - const morpherState = await MorpherState.deployed(); - if (network !== 'mainchain') { - await deployer.deploy( - AdminOverrideProxy, - accounts[0], - morpherState.address, - ) - - await morpherState.setAdministrator(AdminOverrideProxy.address) - - const marketHashObject = require('../docs/markets') - - const marketHashes = Object.keys(marketHashObject) - const adminOverrideProxy = await AdminOverrideProxy.deployed() - let marketsToAdd = [] - for (let i = 0; i < marketHashes.length; i++) { - marketsToAdd.push(marketHashes[i]) - if (marketsToAdd.length == 20) { - await adminOverrideProxy.bulkActivateMarkets(marketsToAdd) - marketsToAdd = [] - console.log('Added 20 Markets') - } - } - if (marketsToAdd.length > 0) { - await adminOverrideProxy.bulkActivateMarkets(marketsToAdd) - console.log('Added', marketsToAdd.length, 'Markets') - marketsToAdd = [] - } - - await morpherState.setAdministrator(administratorAddress); - } -} diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js deleted file mode 100644 index 03e87bd..0000000 --- a/migrations/1_initial_migration.js +++ /dev/null @@ -1,5 +0,0 @@ -const Migrations = artifacts.require("Migrations"); - -module.exports = function(deployer) { - deployer.deploy(Migrations); -}; diff --git a/migrations/2_deploy_state.js b/migrations/2_deploy_state.js deleted file mode 100644 index c04e6ec..0000000 --- a/migrations/2_deploy_state.js +++ /dev/null @@ -1,31 +0,0 @@ -const MorpherState = artifacts.require('MorpherState') - -module.exports = async function (deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0] - const treasuryAddress = process.env.MORPHER_TREASURY || accounts[0] - const sidechainOperatorAddress = process.env.SIDECHAIN_OPERATOR || accounts[0] - - let isMainChain = false - if (network === 'mainchain' || network === 'kovan' || network == 'test' || network == 'local' || network == "develop") { - isMainChain = true - } - - await deployer.deploy( - MorpherState, - isMainChain, - sidechainOperatorAddress, - treasuryAddress, - ) // deployer is changed to owner later - - const morpherState = await MorpherState.deployed() - - /** - * Sidechain relevant settings - */ - if (!isMainChain) { - await morpherState.enableTransfers(ownerAddress) - await morpherState.grantAccess(ownerAddress) - } - - await morpherState.setSideChainOperator(ownerAddress) -} diff --git a/migrations/3_deploy_userblocking.js b/migrations/3_deploy_userblocking.js deleted file mode 100644 index 769e8b1..0000000 --- a/migrations/3_deploy_userblocking.js +++ /dev/null @@ -1,12 +0,0 @@ -const MorpherUserBlocking = artifacts.require('MorpherUserBlocking') -const MorpherState = artifacts.require('MorpherState') - -module.exports = async function (deployer, network, accounts) { - let accountUserBlocking = process.env.ACCOUNT_USER_BLOCKING || accounts[0]; - const morpherState = await MorpherState.deployed() - await deployer.deploy( - MorpherUserBlocking, - morpherState.address, - accountUserBlocking - ) -} diff --git a/migrations/4_deploy_token.js b/migrations/4_deploy_token.js deleted file mode 100644 index 5f6d4a1..0000000 --- a/migrations/4_deploy_token.js +++ /dev/null @@ -1,22 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherToken = artifacts.require("MorpherToken"); - -module.exports = async function(deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - - const morpherState = await MorpherState.deployed(); - await deployer.deploy(MorpherToken, morpherState.address, ownerAddress); // deployer is changed to owner later - - /** - * Grant the Token access - */ - await morpherState.grantAccess(MorpherToken.address); - - /** - * configure State - */ - await morpherState.setTokenContract(MorpherToken.address); - - -}; - diff --git a/migrations/5_deploy_staking.js b/migrations/5_deploy_staking.js deleted file mode 100644 index 7cba3a5..0000000 --- a/migrations/5_deploy_staking.js +++ /dev/null @@ -1,21 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherStaking = artifacts.require("MorpherStaking"); -const MorpherUserBlocking = artifacts.require("MorpherUserBlocking"); - -module.exports = async function(deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - - const morpherState = await MorpherState.deployed(); - const morpherUserBlocking = await MorpherUserBlocking.deployed(); - - await deployer.deploy(MorpherStaking, morpherState.address, ownerAddress, morpherUserBlocking.address); - - /** - * Grant the Token access - */ - await morpherState.grantAccess(MorpherStaking.address); - await morpherState.grantAccess(ownerAddress); - - -}; - diff --git a/migrations/6_deploy_tradeEngine.js b/migrations/6_deploy_tradeEngine.js deleted file mode 100644 index 0501168..0000000 --- a/migrations/6_deploy_tradeEngine.js +++ /dev/null @@ -1,36 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); -const MorpherStaking = artifacts.require("MorpherStaking"); -const MorpherMintingLimiter = artifacts.require("MorpherMintingLimiter"); -const MorpherUserBlocking = artifacts.require("MorpherUserBlocking"); - -module.exports = async function(deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - const mintLimitPerUser = process.env.MINTING_LIMIT_PER_USER || 0; - const mintLimitDaily = process.env.MINTING_LIMIT_DAILY || 0; - const timelockPeriodMinting = process.env.MINTING_TIME_LOCK_PERIOD || 0; - - const morpherState = await MorpherState.deployed(); - const morpherUserBlocking = await MorpherUserBlocking.deployed(); - let deployedTimestamp = 1613399217; - if(network == "local" || network == "test") { - deployedTimestamp = Math.round(Date.now() / 1000) - (60*60*24*30*5); //settings this for testing 5 months back - } - await deployer.deploy(MorpherMintingLimiter, morpherState.address, mintLimitPerUser, mintLimitDaily, timelockPeriodMinting); - const morpherMintingLimiter = await MorpherMintingLimiter.deployed(); - await deployer.deploy(MorpherTradeEngine, morpherState.address, ownerAddress, MorpherStaking.address, true, deployedTimestamp, morpherMintingLimiter.address, morpherUserBlocking.address); - - const tradeEngine = await MorpherTradeEngine.deployed(); - await morpherMintingLimiter.setTradeEngineAddress(tradeEngine.address); - - /** - * Grant the Token access - */ - await morpherState.grantAccess(tradeEngine.address); - await morpherState.grantAccess(morpherMintingLimiter.address); - await morpherState.enableTransfers(tradeEngine.address); - await morpherState.enableTransfers(morpherMintingLimiter.address); - - -}; - diff --git a/migrations/7_deploy_bridge.js b/migrations/7_deploy_bridge.js deleted file mode 100644 index f29597b..0000000 --- a/migrations/7_deploy_bridge.js +++ /dev/null @@ -1,23 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherBridge = artifacts.require("MorpherBridge"); -const MorpherUserBlocking = artifacts.require("MorpherUserBlocking"); - -module.exports = async function(deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - - const morpherState = await MorpherState.deployed(); - const morpherUserBlocking = await MorpherUserBlocking.deployed(); - await deployer.deploy(MorpherBridge, morpherState.address, ownerAddress, '0x0000000000000000000000000000000000000000', morpherUserBlocking.address); - - /** - * Grant the access - */ - await morpherState.grantAccess(MorpherBridge.address); - - /** - * Set it to the State - */ - await morpherState.setMorpherBridge(MorpherBridge.address); - - -}; diff --git a/migrations/8_deploy_oracle.js b/migrations/8_deploy_oracle.js deleted file mode 100644 index 5439660..0000000 --- a/migrations/8_deploy_oracle.js +++ /dev/null @@ -1,66 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherOracle = artifacts.require("MorpherOracle"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); - -const CRYPTO_BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9'; -const CRYPTO_ETH = '0x5376ff169a3705b2003892fe730060ee74ec83e5701da29318221aa782271779'; - -module.exports = async function (deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - const callbackAddress1 = process.env.CALLBACK_ADDRESS_1 || accounts[0]; - const callbackAddress2 = process.env.CALLBACK_ADDRESS_2; - const callbackAddress3 = process.env.CALLBACK_ADDRESS_3; - const coldStorageOwnerAddress = process.env.COLDSTORAGE_OWNER_ADDRESS || accounts[0]; - const gasCollectionAddress = process.env.GAS_COLLECTION || accounts[0]; - - - const morpherState = await MorpherState.deployed(); - /** - * override governance first - */ - await morpherState.setGovernanceContract(ownerAddress); - - - let isMainChain = false; - // console.log("NETWORK", network); - if (network === "mainchain" || network === 'kovan' || network == "develop") { - isMainChain = true; - } - - - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - - await deployer.deploy(MorpherOracle, morpherTradeEngine.address, morpherState.address, callbackAddress1, gasCollectionAddress, 0, coldStorageOwnerAddress, '0x0000000000000000000000000000000000000000', '0x0000000000000000000000000000000000000000'); // deployer is changed to owner later - - await morpherState.setGovernanceContract(ownerAddress); //will be set on 9_deploy_governance again - await morpherState.setOracleContract(MorpherOracle.address); - - let morpherOracle = await MorpherOracle.deployed(); - - if(callbackAddress2 !== undefined) { - await morpherOracle.enableCallbackAddress(callbackAddress2); - } - if(callbackAddress3 !== undefined) { - await morpherOracle.enableCallbackAddress(callbackAddress3); - } - - if (!isMainChain) { - await morpherState.setAdministrator(ownerAddress); - await morpherState.activateMarket(CRYPTO_BTC); - await morpherState.activateMarket(CRYPTO_ETH); - } - - /* - if(network === 'local'){ - let data; - - // ------ To have an Administrator and Oracle until there is a vote in the governance contract ------ - // setAdministrator(addressOfDeployer) - - data = await morpherState.methods.setAdministrator(deployerAddress); - await sendTransactionFrom(deployerAddress, data, deployerKey, MorpherState.address); - console.log('Administrator set.'); - - } - */ -}; diff --git a/migrations/9_deploy_airdrop.js b/migrations/9_deploy_airdrop.js deleted file mode 100644 index a80ea87..0000000 --- a/migrations/9_deploy_airdrop.js +++ /dev/null @@ -1,20 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); -const MorpherAirdrop = artifacts.require("MorpherAirdrop"); -const MorpherToken = artifacts.require("MorpherToken"); - -module.exports = async function (deployer, network, accounts) { - const ownerAddress = process.env.MORPHER_OWNER || accounts[0]; - const airdropAdminAddress = process.env.AIRDROP_ADMIN || accounts[0]; - - /** - * only on sidechain - */ - if (network !== "mainchain" && network !== 'kovan') { - const morpherToken = await MorpherToken.deployed(); - const morpherState = await MorpherState.deployed(); - - await deployer.deploy(MorpherAirdrop, airdropAdminAddress, morpherToken.address, ownerAddress); - - await morpherState.enableTransfers(MorpherAirdrop.address); - } -}; diff --git a/package.json b/package.json deleted file mode 100644 index 09ea7f5..0000000 --- a/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "truffle-deploy", - "version": "1.0.0", - "description": "", - "main": "", - "scripts": { - "start": "" - }, - "dependencies": { - "@truffle/hdwallet-provider": "^1.7.0", - "bignumber.js": "^9.0.1", - "dotenv": "^8.6.0", - "ethereumjs-tx": "^1.3.7", - "merkletreejs": "^0.2.24", - "truffle": "^5.4.21", - "truffle-assertions": "^0.9.2", - "web3": "^1.6.1" - }, - "author": "Morpher Team", - "license": "ISC" -} diff --git a/test/helpers/tradeFunctions.js b/test/helpers/tradeFunctions.js deleted file mode 100644 index 9ec7f1f..0000000 --- a/test/helpers/tradeFunctions.js +++ /dev/null @@ -1,10 +0,0 @@ -const BN = require('bn.js'); -function getLeverage(leverage) { - return (new BN(leverage)).mul(new BN(100000000)).toString(); -} - -function roundToInteger(price) { - return Math.round(price * Math.pow(10, 8)); -} - -module.exports = { getLeverage, roundToInteger }; \ No newline at end of file diff --git a/test/morpherAdminProxy.test.js b/test/morpherAdminProxy.test.js deleted file mode 100644 index a7b344a..0000000 --- a/test/morpherAdminProxy.test.js +++ /dev/null @@ -1,71 +0,0 @@ -const MorpherAdministratorProxy = artifacts.require("MorpherAdministratorProxy"); -const MorpherState = artifacts.require("MorpherState"); - -const truffleAssert = require('truffle-assertions'); - -let marketsToEnable = ["STOCK_AFRM", "STOCK_AAPL", "STOCK_MSFT", "CRYPTO_YFI", "STOCK_ETSY", "STOCK_DASH", "STOCK_UPST", "STOCK_POSH", "STOCK_AFRM", "STOCK_PLUG", "STOCK_SPCE", "STOCK_TR", "STOCK_ZM", "STOCK_SRNE", "STOCK_SAVE", "STOCK_PTON", "STOCK_PLTR", "CRYPTO_FIL", "CRYPTO_CRO", "CRYPTO_LINK", ]; - -contract('Admin Proxy', (accounts) => { - it('test admin bulk markets enable', async () => { - - const morpherAdminProxy = await MorpherAdministratorProxy.deployed(); - const morpherState = await MorpherState.deployed(); - - let adminAddressFromState = await morpherState.getAdministrator(); - if(adminAddressFromState != morpherAdminProxy.address) { - await morpherState.setAdministrator(morpherAdminProxy.address); - } - - - let marketHashes = []; - - for(let i = 0; i < marketsToEnable.length; i++) { - marketHashes.push(web3.utils.sha3(marketsToEnable[i])); - } - - let txResult = await morpherAdminProxy.bulkActivateMarkets(marketHashes); - console.log("Activating", marketsToEnable.length, "Markets cost", txResult.receipt.gasUsed, "Gas"); - - for(let i = 0; i < marketHashes.length; i++) { - let isActive = await morpherState.getMarketActive(marketHashes[i]); - assert.equal(isActive, true); - } - - - if(adminAddressFromState != morpherAdminProxy.address) { - await morpherState.setAdministrator(adminAddressFromState); - } - - }); - - - it('test normal markets enable', async () => { - - const morpherAdminProxy = await MorpherAdministratorProxy.deployed(); - const morpherState = await MorpherState.deployed(); - const morpherStateThroughAdminProxy = await MorpherState.at(morpherAdminProxy.address); - - let adminAddressFromState = await morpherState.getAdministrator(); - if(adminAddressFromState != morpherAdminProxy.address) { - await morpherState.setAdministrator(morpherAdminProxy.address); - } - //deactivate everything first - for(let i = 0; i < marketsToEnable.length; i++) { - await morpherStateThroughAdminProxy.deActivateMarket(web3.utils.sha3(marketsToEnable[i])); - } - - let gasUsedSum = 0; - - for(let i = 0; i < marketsToEnable.length; i++) { - let txResult = await morpherStateThroughAdminProxy.activateMarket(web3.utils.sha3(marketsToEnable[i])); - gasUsedSum += txResult.receipt.gasUsed; - } - - console.log("Activating", marketsToEnable.length, "Markets cost", gasUsedSum, "Gas"); - - if(adminAddressFromState != morpherAdminProxy.address) { - await morpherState.setAdministrator(adminAddressFromState); - } - - }); -}); \ No newline at end of file diff --git a/test/morpherAirdrop.test.js b/test/morpherAirdrop.test.js deleted file mode 100644 index d938290..0000000 --- a/test/morpherAirdrop.test.js +++ /dev/null @@ -1,78 +0,0 @@ -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherAirdrop = artifacts.require("MorpherAirdrop"); - -const truffleAssert = require('truffle-assertions'); - -contract('MorpherAirdrop', (accounts) => { - it('test airdrop authorizations', async () => { - const deployerAddress = accounts[0]; const testAddress1 = accounts[1]; const testAddress2 = accounts[2]; - - const morpherAirdrop = await MorpherAirdrop.deployed(); - const morpherToken = await MorpherToken.deployed(); - - await morpherToken.transfer(morpherAirdrop.address, '20000000', { from: deployerAddress }); - - let totalAirdropAuthorized = await morpherAirdrop.totalAirdropAuthorized({ from: testAddress1 }); - let totalAirdropClaimed = await morpherAirdrop.totalAirdropClaimed({ from: testAddress2 }); - - assert.equal(totalAirdropAuthorized, '0'); - assert.equal(totalAirdropClaimed, '0'); - - // Test setting airdrop admins and numbers. - await truffleAssert.reverts(morpherAirdrop.setAirdropAdmin(testAddress1, { from: testAddress1 })); // fails - await truffleAssert.reverts(morpherAirdrop.setAirdropAuthorized(testAddress1, '10000', { from: testAddress1 })); // fails - - await morpherAirdrop.setAirdropAuthorized(testAddress1, '10000', { from: deployerAddress }); - await morpherAirdrop.setAirdropAuthorized(testAddress2, '450000', { from: deployerAddress }); - - totalAirdropAuthorized = await morpherAirdrop.totalAirdropAuthorized({ from: testAddress1 }); - totalAirdropClaimed = await morpherAirdrop.totalAirdropClaimed({ from: testAddress1 }); - assert.equal(totalAirdropAuthorized, '460000'); - assert.equal(totalAirdropClaimed, '0'); - - let airdropAuthorized1 = await morpherAirdrop.getAirdropAuthorized(testAddress1, { from: testAddress2 }); - let airdropAuthorized2 = await morpherAirdrop.getAirdropAuthorized(testAddress2, { from: testAddress1 }); - assert.equal(airdropAuthorized1, '10000'); - assert.equal(airdropAuthorized2, '450000'); - - // Test airdrop claim. - await morpherAirdrop.claimSomeAirdrop('150000', { from: testAddress2 }); - let airdropClaimed2 = await morpherAirdrop.getAirdropClaimed(testAddress2, { from: testAddress2 }); - airdropAuthorized2 = await morpherAirdrop.getAirdropAuthorized(testAddress2, { from: testAddress1 }); - totalAirdropClaimed = await morpherAirdrop.totalAirdropClaimed({ from: testAddress1 }); - assert.equal(airdropClaimed2, '150000'); - assert.equal(airdropAuthorized2, '450000'); - assert.equal(totalAirdropClaimed, '150000'); - - await morpherAirdrop.setAirdropAuthorized(testAddress2, '400000', { from: deployerAddress }); - totalAirdropClaimed = await morpherAirdrop.totalAirdropClaimed({ from: testAddress1 }); - assert.equal(totalAirdropClaimed, '150000'); - - totalAirdropAuthorized = await morpherAirdrop.totalAirdropAuthorized({ from: testAddress1 }); - airdropAuthorized2 = await morpherAirdrop.getAirdropAuthorized(testAddress2, { from: testAddress2 }); - assert.equal(totalAirdropAuthorized, '410000'); - assert.equal(airdropAuthorized2, '400000'); - - await truffleAssert.reverts(morpherAirdrop.adminSendSomeAirdrop(testAddress2, '400000', { from: deployerAddress })); // fails - await morpherAirdrop.adminSendSomeAirdrop(testAddress2, '100000', { from: deployerAddress }); - totalAirdropClaimed = await morpherAirdrop.totalAirdropClaimed({ from: testAddress1 }); - assert.equal(totalAirdropClaimed, '250000'); - - await morpherAirdrop.setAirdropAuthorized(testAddress2, '420000', { from: deployerAddress }); - await morpherAirdrop.adminSendAirdrop(testAddress2, { from: deployerAddress }); - totalAirdropClaimed = await morpherAirdrop.totalAirdropClaimed({ from: testAddress1 }); - assert.equal(totalAirdropClaimed, '420000'); - - airdropClaimed2 = await morpherAirdrop.getAirdropClaimed(testAddress2, { from: testAddress2 }); - airdropAuthorized2 = await morpherAirdrop.getAirdropAuthorized(testAddress2, { from: testAddress2 }); - assert.equal(airdropClaimed2, '420000'); - assert.equal(airdropAuthorized2, '420000'); - - await morpherAirdrop.claimAirdrop({ from: testAddress1 }); - - airdropClaimed1 = await morpherAirdrop.getAirdropClaimed(testAddress1, { from: testAddress1 }); - airdropAuthorized1 = await morpherAirdrop.getAirdropAuthorized(testAddress1, { from: testAddress1 }); - assert.equal(airdropClaimed1, '10000'); - assert.equal(airdropAuthorized1, '10000'); - }); -}); \ No newline at end of file diff --git a/test/morpherBridge.test.js b/test/morpherBridge.test.js deleted file mode 100644 index 5473954..0000000 --- a/test/morpherBridge.test.js +++ /dev/null @@ -1,519 +0,0 @@ -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherBridge = artifacts.require("MorpherBridge"); - -const truffleAssert = require('truffle-assertions'); - -const { MerkleTree } = require('merkletreejs') - -const { keccak256 } = require('ethereumjs-util'); -const { BN } = require("bn.js") - - -contract('MorpherBridge: change Limits tests', (accounts) => { - - it('is possible to change 24hours limits', async() => { - const morpherBridge = await MorpherBridge.deployed(); - let result = await morpherBridge.updateWithdrawLimitDaily(web3.utils.toWei('1', 'ether')); - await truffleAssert.eventEmitted(result, 'WithdrawLimitDailyChanged'); - let currentLimit = await morpherBridge.withdrawalLimitDaily(); - assert.equal(currentLimit.toString(), web3.utils.toWei('1','ether')); - - //set it back - await morpherBridge.updateWithdrawLimitDaily(web3.utils.toWei('200000', 'ether')); - }); - - it('is possible to change 30 days limits', async() => { - const morpherBridge = await MorpherBridge.deployed(); - let result = await morpherBridge.updateWithdrawLimitMonthly(web3.utils.toWei('1', 'ether')); - await truffleAssert.eventEmitted(result, 'WithdrawLimitMonthlyChanged'); - let currentLimit = await morpherBridge.withdrawalLimitMonthly(); - assert.equal(currentLimit.toString(), web3.utils.toWei('1','ether')); - - //set it back - await morpherBridge.updateWithdrawLimitMonthly(web3.utils.toWei('1000000', 'ether')); - }); - - it('is possible to change 365 days limits', async() => { - const morpherBridge = await MorpherBridge.deployed(); - let result = await morpherBridge.updateWithdrawLimitYearly(web3.utils.toWei('1', 'ether')); - await truffleAssert.eventEmitted(result, 'WithdrawLimitYearlyChanged'); - let currentLimit = await morpherBridge.withdrawalLimitYearly(); - assert.equal(currentLimit.toString(), web3.utils.toWei('1','ether')); - - //set it back - await morpherBridge.updateWithdrawLimitYearly(web3.utils.toWei('5000000', 'ether')); - }); -}); - -contract('MorpherBridge: transferToSidechain tests', (accounts) => { - - /** - * tests first if up to 24h withdrawal limit works - * - * the tests if going beyond the withdrawal limit fails - */ - it('test withdrawal transferToSideChain daily limit', async () => { - const [deployer, addr1] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - await morpherBridge.updateWithdrawLimitDaily(web3.utils.toWei("20", "ether")); - await morpherToken.transfer(addr1, web3.utils.toWei("20","ether"), { from: deployer }); - - const addrBalanceBeforeBurn = await morpherToken.balanceOf(addr1); - assert.equal(addrBalanceBeforeBurn.toString(), web3.utils.toWei('20', 'ether')); - - - let result = await morpherBridge.transferToSideChain(web3.utils.toWei("20", "ether"), { from: addr1 }); - await truffleAssert.eventEmitted(result, "TransferToLinkedChain"); - const addr1BalanceAfterBurn = await morpherToken.balanceOf(addr1); - assert.equal(addr1BalanceAfterBurn.toString(), web3.utils.toWei('0', 'ether')); - - //now lets go beyond the 24h withdrawal limit - await morpherToken.transfer(addr1, web3.utils.toWei("1","ether"), { from: deployer }); - - const addrBalanceBeforeBurn1 = await morpherToken.balanceOf(addr1); - assert.equal(addrBalanceBeforeBurn1.toString(), web3.utils.toWei('1', 'ether')); - - await truffleAssert.fails( - morpherBridge.transferToSideChain(web3.utils.toWei("1", "ether"), { from: addr1 }), - truffleAssert.ErrorType.REVERT, - "MorpherBridge: Withdrawal Amount exceeds daily limit" - ); - - const addr1BalanceAfterBurn1 = await morpherToken.balanceOf(addr1); - - //nothing changed, the burn didn't happen, send the 1mph back because we need a clean account - assert.equal(addr1BalanceAfterBurn1.toString(), web3.utils.toWei('1', 'ether')); - await morpherToken.transfer(deployer, web3.utils.toWei("1","ether"), { from: addr1 }); - - }); - - - /** - * tests first if up to 30 days withdrawal limit works - * - * the tests if going beyond the withdrawal limit fails - */ - it('test withdrawal transferToSideChain 30 days limit', async () => { - const [deployer, addr1] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - await morpherBridge.updateWithdrawLimitDaily(web3.utils.toWei("2000000", "ether")); - await morpherBridge.updateWithdrawLimitMonthly(web3.utils.toWei("40", "ether")); //double it from the daily limit test above - await morpherToken.transfer(addr1, web3.utils.toWei("20","ether"), { from: deployer }); - - const addrBalanceBeforeBurn = await morpherToken.balanceOf(addr1); - assert.equal(addrBalanceBeforeBurn.toString(), web3.utils.toWei('20', 'ether')); - - - let result = await morpherBridge.transferToSideChain(web3.utils.toWei("20", "ether"), { from: addr1 }); - await truffleAssert.eventEmitted(result, "TransferToLinkedChain"); - const addr1BalanceAfterBurn = await morpherToken.balanceOf(addr1); - assert.equal(addr1BalanceAfterBurn.toString(), web3.utils.toWei('0', 'ether')); - - //now lets go beyond the 24h withdrawal limit - await morpherToken.transfer(addr1, web3.utils.toWei("1","ether"), { from: deployer }); - - const addrBalanceBeforeBurn1 = await morpherToken.balanceOf(addr1); - assert.equal(addrBalanceBeforeBurn1.toString(), web3.utils.toWei('1', 'ether')); - - await truffleAssert.fails( - morpherBridge.transferToSideChain(web3.utils.toWei("1", "ether"), { from: addr1 }), - truffleAssert.ErrorType.REVERT, - "MorpherBridge: Withdrawal Amount exceeds monthly limit" - ); - - const addr1BalanceAfterBurn1 = await morpherToken.balanceOf(addr1); - - //nothing changed, the burn didn't happen, send the 1mph back because we need a clean account - assert.equal(addr1BalanceAfterBurn1.toString(), web3.utils.toWei('1', 'ether')); - await morpherToken.transfer(deployer, web3.utils.toWei("1","ether"), { from: addr1 }); - - }); - - /** - * tests if up to 365 days withdrawal limit works - * - * the tests if going beyond the withdrawal limit fails - */ - it('test withdrawal transferToSideChain 365 days limit', async () => { - const [deployer, addr1] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - await morpherBridge.updateWithdrawLimitDaily(web3.utils.toWei("2000000", "ether")); - await morpherBridge.updateWithdrawLimitMonthly(web3.utils.toWei("2000000", "ether")); //double it from the daily limit test above - await morpherBridge.updateWithdrawLimitYearly(web3.utils.toWei("60", "ether")); //double it from the daily limit test above - await morpherToken.transfer(addr1, web3.utils.toWei("20","ether"), { from: deployer }); - - const addrBalanceBeforeBurn = await morpherToken.balanceOf(addr1); - assert.equal(addrBalanceBeforeBurn.toString(), web3.utils.toWei('20', 'ether')); - - - let result = await morpherBridge.transferToSideChain(web3.utils.toWei("20", "ether"), { from: addr1 }); - await truffleAssert.eventEmitted(result, "TransferToLinkedChain"); - const addr1BalanceAfterBurn = await morpherToken.balanceOf(addr1); - assert.equal(addr1BalanceAfterBurn.toString(), web3.utils.toWei('0', 'ether')); - - //now lets go beyond the withdrawal limit - await morpherToken.transfer(addr1, web3.utils.toWei("1","ether"), { from: deployer }); - const addrBalanceBeforeBurn1 = await morpherToken.balanceOf(addr1); - assert.equal(addrBalanceBeforeBurn1.toString(), web3.utils.toWei('1', 'ether')); - - await truffleAssert.fails( - morpherBridge.transferToSideChain(web3.utils.toWei("1", "ether"), { from: addr1 }), - truffleAssert.ErrorType.REVERT, - "MorpherBridge: Withdrawal Amount exceeds yearly limit" - ); - - const addr1BalanceAfterBurn1 = await morpherToken.balanceOf(addr1); - - //nothing changed, the burn didn't happen, send the 1mph back because we need a clean account - assert.equal(addr1BalanceAfterBurn1.toString(), web3.utils.toWei('1', 'ether')); - await morpherToken.transfer(deployer, web3.utils.toWei("1","ether"), { from: addr1 }); - - }); -}); - -contract('MorpherBridge: trustlessTransferFromLinkedChain tests', (accounts) => { - - - it('test withdrawal trustlessTransferFromLinkedChain', async () => { - const [deployer, addr1, addr2, addr3, addr4] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - const leaves = []; - leaves.push(web3.utils.soliditySha3(addr1, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr2, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr3, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr4, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - - leaves.sort(); - - const nearestPowerOf2 = Math.ceil(Math.log(leaves.length) / Math.log(2)); - - const zeroHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; - - // Create empty array to input the necessary amount of leaves. - const leavesOut = []; - - // Fill new array with existing leaves and trivial leaves if needed. - for (let i = 0; i < leaves.length; i++) { - leavesOut[i] = leaves[i]; - } - - for (let k = leaves.length; k < 2 ** nearestPowerOf2; k++) { - leavesOut.push(zeroHash); - } - - - // Initiate helper MerkleTree class for calculations. - const merkleTree = new MerkleTree(leavesOut, keccak256, { sortPairs: true }) - const newMerkleTreeRoot = '0x' + merkleTree.getRoot().toString('hex'); - - await morpherBridge.updateSideChainMerkleRoot(newMerkleTreeRoot); - await morpherBridge.set24HourWithdrawLimit(web3.utils.toWei("20", "ether")); - - const addrBalanceBeforeClaim = await morpherToken.balanceOf(addr1); - assert.equal(addrBalanceBeforeClaim.toString(), "0"); - - const proofForAddr1 = merkleTree.getHexProof(web3.utils.soliditySha3(addr1, web3.utils.toWei("20", "ether"))); - - let result = await morpherBridge.trustlessTransferFromLinkedChain(web3.utils.toWei("20", "ether"), web3.utils.toWei("20", "ether"), proofForAddr1, { from: addr1 }); - await truffleAssert.eventEmitted(result, "TrustlessWithdrawFromSideChain"); - - const addr1BalanceAfterClaim = await morpherToken.balanceOf(addr1); - - assert.equal(addr1BalanceAfterClaim.toString(), web3.utils.toWei('20', 'ether')); - - }); - - - - - it('test withdrawal limit global for all users', async () => { - const [deployer, addr1, addr2, addr3, addr4] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - const leaves = []; - leaves.push(web3.utils.soliditySha3(addr1, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr2, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr3, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr4, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - - leaves.sort(); - - const nearestPowerOf2 = Math.ceil(Math.log(leaves.length) / Math.log(2)); - - const zeroHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; - - // Create empty array to input the necessary amount of leaves. - const leavesOut = []; - - // Fill new array with existing leaves and trivial leaves if needed. - for (let i = 0; i < leaves.length; i++) { - leavesOut[i] = leaves[i]; - } - - for (let k = leaves.length; k < 2 ** nearestPowerOf2; k++) { - leavesOut.push(zeroHash); - } - - - // Initiate helper MerkleTree class for calculations. - const merkleTree = new MerkleTree(leavesOut, keccak256, { sortPairs: true }) - const newMerkleTreeRoot = '0x' + merkleTree.getRoot().toString('hex'); - - await morpherBridge.updateSideChainMerkleRoot(newMerkleTreeRoot); - await morpherBridge.set24HourWithdrawLimit(web3.utils.toWei("1", "ether")); - - const addrBalanceBeforeClaim = await morpherToken.balanceOf(addr2); - assert.equal(addrBalanceBeforeClaim.toString(), "0"); - - const proofForAddr2 = merkleTree.getHexProof(web3.utils.soliditySha3(addr2, web3.utils.toWei("20", "ether"))); - - await truffleAssert.fails( - morpherBridge.trustlessTransferFromLinkedChain(web3.utils.toWei("20", "ether"), web3.utils.toWei("20", "ether"), proofForAddr2, { from: addr2 }), - truffleAssert.ErrorType.REVERT, - "MorpherBridge: Withdraw amount exceeds permitted 24 hour limit. Please try again in a few hours." - ); - - const addr1BalanceAfterClaim = await morpherToken.balanceOf(addr2); - - assert.equal(addr1BalanceAfterClaim.toString(), '0'); - - }); - - - - it('test withdrawal limit 24 hours per user', async () => { - const [deployer, addr1, addr2, addr3, addr4] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - const leaves = []; - let current24hLimit = new BN(await morpherBridge.withdrawalLimitDaily()); - - await morpherBridge.set24HourWithdrawLimit(web3.utils.toWei("1000000000", "ether")); - - leaves.push(web3.utils.soliditySha3(addr2, current24hLimit.add(new BN(1)))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr3, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - - leaves.sort(); - - const nearestPowerOf2 = Math.ceil(Math.log(leaves.length) / Math.log(2)); - - const zeroHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; - - // Create empty array to input the necessary amount of leaves. - const leavesOut = []; - - // Fill new array with existing leaves and trivial leaves if needed. - for (let i = 0; i < leaves.length; i++) { - leavesOut[i] = leaves[i]; - } - - for (let k = leaves.length; k < 2 ** nearestPowerOf2; k++) { - leavesOut.push(zeroHash); - } - - - // Initiate helper MerkleTree class for calculations. - const merkleTree = new MerkleTree(leavesOut, keccak256, { sortPairs: true }) - const newMerkleTreeRoot = '0x' + merkleTree.getRoot().toString('hex'); - - await morpherBridge.updateSideChainMerkleRoot(newMerkleTreeRoot); - - const addrBalanceBeforeClaim = await morpherToken.balanceOf(addr2); - assert.equal(addrBalanceBeforeClaim.toString(), "0"); - - const proofForAddr2 = merkleTree.getHexProof(web3.utils.soliditySha3(addr2, current24hLimit.add(new BN(1)))); - - let result = await morpherBridge.trustlessTransferFromLinkedChain(current24hLimit, current24hLimit.add(new BN(1)), proofForAddr2, { from: addr2 }); //transfer the limit - await truffleAssert.eventEmitted(result, "TrustlessWithdrawFromSideChain"); - - - //we transferred the maximum limit for 24 hours, another token would trigger an exception - await truffleAssert.fails( - morpherBridge.trustlessTransferFromLinkedChain("1", current24hLimit.add(new BN(1)), proofForAddr2, { from: addr2 }), - truffleAssert.ErrorType.REVERT, - "MorpherBridge: Withdrawal Amount exceeds daily limit" - ); - - const addr1BalanceAfterClaim = await morpherToken.balanceOf(addr2); - - assert.equal(addr1BalanceAfterClaim.toString(), current24hLimit.toString()); - - //another user still can transfer - - - const proofForAddr3 = merkleTree.getHexProof(web3.utils.soliditySha3(addr3, web3.utils.toWei('20', 'ether'))); - result = await morpherBridge.trustlessTransferFromLinkedChain("1", web3.utils.toWei('20', 'ether'), proofForAddr3, { from: addr3 }); //transfer the limit - await truffleAssert.eventEmitted(result, "TrustlessWithdrawFromSideChain"); - - - }); - - - - it('test withdrawal limit 30 days per user', async () => { - const [deployer, addr1, addr2, addr3, addr4, addr5] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - const leaves = []; - let settingsLimits24Hours = await morpherBridge.updateWithdrawLimitDaily(web3.utils.toWei('1000001', 'ether')); - await truffleAssert.eventEmitted(settingsLimits24Hours, 'WithdrawLimitDailyChanged'); - let currentDailyLimit = await morpherBridge.withdrawalLimitDaily(); - assert.equal(currentDailyLimit.toString(), web3.utils.toWei('1000001', 'ether')); - let currentMonthlyLimit = new BN(await morpherBridge.withdrawalLimitMonthly()); - - leaves.push(web3.utils.soliditySha3(addr4, currentMonthlyLimit.add(new BN(1)))); //packaging 20 MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr5, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - - leaves.sort(); - - const nearestPowerOf2 = Math.ceil(Math.log(leaves.length) / Math.log(2)); - - const zeroHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; - - // Create empty array to input the necessary amount of leaves. - const leavesOut = []; - - // Fill new array with existing leaves and trivial leaves if needed. - for (let i = 0; i < leaves.length; i++) { - leavesOut[i] = leaves[i]; - } - - for (let k = leaves.length; k < 2 ** nearestPowerOf2; k++) { - leavesOut.push(zeroHash); - } - - - // Initiate helper MerkleTree class for calculations. - const merkleTree = new MerkleTree(leavesOut, keccak256, { sortPairs: true }) - const newMerkleTreeRoot = '0x' + merkleTree.getRoot().toString('hex'); - - await morpherBridge.updateSideChainMerkleRoot(newMerkleTreeRoot); - - const addrBalanceBeforeClaim = await morpherToken.balanceOf(addr4); - assert.equal(addrBalanceBeforeClaim.toString(), "0"); - - const proofForAddr4 = merkleTree.getHexProof(web3.utils.soliditySha3(addr4, currentMonthlyLimit.add(new BN(1)))); - - let result = await morpherBridge.trustlessTransferFromLinkedChain(currentMonthlyLimit, currentMonthlyLimit.add(new BN(1)), proofForAddr4, { from: addr4 }); //transfer the limit - await truffleAssert.eventEmitted(result, "TrustlessWithdrawFromSideChain"); - - - //we transferred the maximum limit for 24 hours, another token would trigger an exception - await truffleAssert.fails( - morpherBridge.trustlessTransferFromLinkedChain("1", currentMonthlyLimit.add(new BN(1)), proofForAddr4, { from: addr4 }), - truffleAssert.ErrorType.REVERT, - "MorpherBridge: Withdrawal Amount exceeds monthly limit" - ); - - const addrBalanceAfterClaim = await morpherToken.balanceOf(addr4); - - assert.equal(addrBalanceAfterClaim.toString(), currentMonthlyLimit.toString()); - - //another user still can transfer - const proofForAddr5 = merkleTree.getHexProof(web3.utils.soliditySha3(addr5, web3.utils.toWei('20', 'ether'))); - result = await morpherBridge.trustlessTransferFromLinkedChain("1", web3.utils.toWei('20', 'ether'), proofForAddr5, { from: addr5 }); //transfer the limit - await truffleAssert.eventEmitted(result, "TrustlessWithdrawFromSideChain"); - - }); - - - it('test withdrawal limit 365 days per user', async () => { - const [deployer, addr1, addr2, addr3, addr4, addr5, addr6, addr7] = accounts; - const morpherBridge = await MorpherBridge.deployed(); - const morpherToken = await MorpherToken.deployed(); - - //setup a merkle tree to test withdrawal - - const leaves = []; - let settingsLimits24Hours = await morpherBridge.updateWithdrawLimitDaily(web3.utils.toWei('5000001', 'ether')); - await truffleAssert.eventEmitted(settingsLimits24Hours, 'WithdrawLimitDailyChanged'); - let currentDailyLimit = await morpherBridge.withdrawalLimitDaily(); - assert.equal(currentDailyLimit.toString(), web3.utils.toWei('5000001', 'ether')); - let settingsLimits30Days = await morpherBridge.updateWithdrawLimitMonthly(web3.utils.toWei('5000001', 'ether')); - await truffleAssert.eventEmitted(settingsLimits30Days, 'WithdrawLimitMonthlyChanged'); - let currentMonthlyLimit = await morpherBridge.withdrawalLimitMonthly(); - assert.equal(currentMonthlyLimit.toString(), web3.utils.toWei('5000001', 'ether')); - let currentYearlyLimit = new BN(await morpherBridge.withdrawalLimitYearly()); - - leaves.push(web3.utils.soliditySha3(addr6, currentYearlyLimit.add(new BN(1)))); //packaging more than yearly limit MPH into a leaf - leaves.push(web3.utils.soliditySha3(addr7, web3.utils.toWei("20", "ether"))); //packaging 20 MPH into a leaf - - leaves.sort(); - - const nearestPowerOf2 = Math.ceil(Math.log(leaves.length) / Math.log(2)); - - const zeroHash = '0x0000000000000000000000000000000000000000000000000000000000000000'; - - // Create empty array to input the necessary amount of leaves. - const leavesOut = []; - - // Fill new array with existing leaves and trivial leaves if needed. - for (let i = 0; i < leaves.length; i++) { - leavesOut[i] = leaves[i]; - } - - for (let k = leaves.length; k < 2 ** nearestPowerOf2; k++) { - leavesOut.push(zeroHash); - } - - - // Initiate helper MerkleTree class for calculations. - const merkleTree = new MerkleTree(leavesOut, keccak256, { sortPairs: true }) - const newMerkleTreeRoot = '0x' + merkleTree.getRoot().toString('hex'); - - await morpherBridge.updateSideChainMerkleRoot(newMerkleTreeRoot); - - const addrBalanceBeforeClaim = await morpherToken.balanceOf(addr6); - assert.equal(addrBalanceBeforeClaim.toString(), "0"); - - const proofForAddr6 = merkleTree.getHexProof(web3.utils.soliditySha3(addr6, currentYearlyLimit.add(new BN(1)))); - - let result = await morpherBridge.trustlessTransferFromLinkedChain(currentYearlyLimit, currentYearlyLimit.add(new BN(1)), proofForAddr6, { from: addr6 }); //transfer the limit - await truffleAssert.eventEmitted(result, "TrustlessWithdrawFromSideChain"); - - - //we transferred the maximum limit for 365 days, another token would trigger an exception - await truffleAssert.fails( - morpherBridge.trustlessTransferFromLinkedChain("1", currentYearlyLimit.add(new BN(1)), proofForAddr6, { from: addr6 }), - truffleAssert.ErrorType.REVERT, - "MorpherBridge: Withdrawal Amount exceeds yearly limit" - ); - - const addrBalanceAfterClaim = await morpherToken.balanceOf(addr6); - - assert.equal(addrBalanceAfterClaim.toString(), currentYearlyLimit.toString()); - - //another user still can transfer - const proofForAddr7 = merkleTree.getHexProof(web3.utils.soliditySha3(addr7, web3.utils.toWei('20', 'ether'))); - result = await morpherBridge.trustlessTransferFromLinkedChain("1", web3.utils.toWei('20', 'ether'), proofForAddr7, { from: addr7 }); //transfer the limit - await truffleAssert.eventEmitted(result, "TrustlessWithdrawFromSideChain"); - - }); - - -}); \ No newline at end of file diff --git a/test/morpherDelistMarket.test.js b/test/morpherDelistMarket.test.js deleted file mode 100644 index d14b9b9..0000000 --- a/test/morpherDelistMarket.test.js +++ /dev/null @@ -1,134 +0,0 @@ -const MorpherOracle = artifacts.require("MorpherOracle"); -const MorpherState = artifacts.require("MorpherState"); -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); - -const truffleAssert = require('truffle-assertions'); - -const { roundToInteger, getLeverage } = require('./helpers/tradeFunctions'); - -let BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9'; - -const BN = require("bn.js"); - -contract('MorpherOracle delist Market', (accounts) => { - it('test MorpherOracle delistMarket emits the correct events', async () => { - const deployerAddress = accounts[0]; const testAddress1 = accounts[1]; const testAddress2 = accounts[2]; - const oracle = await MorpherOracle.deployed(); - let result = await oracle.delistMarket(web3.utils.sha3('CRYPTO_BTC'), true); - truffleAssert.eventEmitted(result, "DelistMarketComplete"); - }); - - it('test MorpherOracle delistMarket emits the correct events', async () => { - const [deployerAddress, addr1, addr2, addr3, addr4, addr5, addr6, addr7] = accounts; - const morpherState = await MorpherState.deployed(); - await morpherState.setPosition(addr1, BTC, 0, 100, 0, 100000000, 0, 100000000, 0, { from: deployerAddress }); - await morpherState.setPosition(addr2, BTC, 0, 100, 0, 100000000, 0, 100000000, 0, { from: deployerAddress }); - await morpherState.setPosition(addr3, BTC, 0, 100, 0, 100000000, 0, 100000000, 0, { from: deployerAddress }); - await morpherState.setPosition(addr4, BTC, 0, 100, 0, 100000000, 0, 100000000, 0, { from: deployerAddress }); - await morpherState.setPosition(addr5, BTC, 0, 100, 0, 100000000, 0, 100000000, 0, { from: deployerAddress }); - const oracle = await MorpherOracle.deployed(); - let result = await oracle.delistMarket(BTC, true); - truffleAssert.eventEmitted(result, "DelistMarketComplete"); - }); - - it('test MorpherOracle delistMarket emits the correct events', async () => { - const [deployerAddress] = accounts; - const morpherState = await MorpherState.deployed(); - for (let i = 1; i <= 30; i++) { - await morpherState.setPosition("0x" + pad_with_zeroes(i, 40), BTC, 0, 100, 0, 100000000, 0, 100000000, 0, { from: deployerAddress }); - } - const oracle = await MorpherOracle.deployed(); - let result = await oracle.delistMarket(BTC, true, { gas: 300000 }); - truffleAssert.eventEmitted(result, "DelistMarketIncomplete"); - - result = await oracle.delistMarket(BTC, false); - truffleAssert.eventEmitted(result, "DelistMarketComplete"); - }); -}); - - -contract('MorpherOracle lock in price testing', (accounts) => { - it('user cant trade an inactive market', async () => { - const [deployerAddress, testAddress1] = accounts; - const oracle = await MorpherOracle.deployed(); - const state = await MorpherState.deployed(); - const morpherToken = await MorpherToken.deployed(); - await morpherToken.transfer(testAddress1, web3.utils.toWei("1", "ether")); - - let userBalance = await morpherToken.balanceOf(testAddress1); - assert.equal(userBalance.toString(), web3.utils.toWei("1", "ether")); - /** - * Create a position - */ - let orderId = (await oracle.createOrder(BTC, 0, roundToInteger(500), true, getLeverage(1), 0, 0, 0, 0, { from: testAddress1 })).logs[0].args._orderId; - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await oracle.__callback(orderId, roundToInteger(100), roundToInteger(100), 0, 0, 0, 0, { from: deployerAddress }); - - userBalance = await morpherToken.balanceOf(testAddress1); - assert.equal(userBalance.toString(), web3.utils.toWei(new BN(1), "ether").sub(new BN(roundToInteger(500))).toString()); - - await state.deActivateMarket(BTC); - - await truffleAssert.fails(oracle.createOrder(BTC, 0, roundToInteger(500), true, getLeverage(1), 0, 0, 0, 0, { from: testAddress1 }), truffleAssert.ErrorType.REVERT); - }); - - - it('user cant close an inactive position if market locked in price was not set', async () => { - const [deployerAddress, testAddress1] = accounts; - const oracle = await MorpherOracle.deployed(); - const state = await MorpherState.deployed(); - - - //try to close the position from above - await truffleAssert.fails(oracle.createOrder(BTC, 5, 0, false, getLeverage(1), 0, 0, 0, 0, { from: testAddress1 }), truffleAssert.ErrorType.REVERT, "MorpherTradeEngine: Can't close a position, market not active and closing price not locked"); - - }); - - it('A forever price can be set to close a market which is not active anymore', async () => { - - const [deployerAddress, testAddress1] = accounts; - const oracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - let result = await oracle.setDeactivatedMarketPrice(BTC, roundToInteger(100)); - await truffleAssert.eventEmitted(result, 'LockedPriceForClosingPositions'); - - let price = await morpherTradeEngine.getDeactivatedMarketPrice(BTC); - assert.equal(price.toString(), roundToInteger(100)); - }); - - - it('user cant partially close an inactive position if market locked in price was set', async () => { - const [deployerAddress, testAddress1] = accounts; - const oracle = await MorpherOracle.deployed(); - - //try to close the position from above - await truffleAssert.fails(oracle.createOrder(BTC, 4, 0, false, getLeverage(1), 0, 0, 0, 0, { from: testAddress1 }), truffleAssert.ErrorType.REVERT, "MorpherTradeEngine: Deactivated market order needs all shares to be closed"); - - }); - - it('user can fully close an inactive position if market locked in price was set', async () => { - const [deployerAddress, testAddress1] = accounts; - const oracle = await MorpherOracle.deployed(); - - const morpherToken = await MorpherToken.deployed(); - - //try to close the position from above - let result = await oracle.createOrder(BTC, 5, 0, false, getLeverage(1), 0, 0, 0, 0, { from: testAddress1 }); - await truffleAssert.eventEmitted(result, 'OrderProcessed'); - let userBalance = await morpherToken.balanceOf(testAddress1); - assert.equal(userBalance.toString(), web3.utils.toWei("1", "ether")); - }); - -}); - -function pad_with_zeroes(number, length) { - - var my_string = '' + number; - while (my_string.length < length) { - my_string = '0' + my_string; - } - - return my_string; - -} \ No newline at end of file diff --git a/test/morpherEscrow.test.js b/test/morpherEscrow.test.js deleted file mode 100644 index e43ac4a..0000000 --- a/test/morpherEscrow.test.js +++ /dev/null @@ -1,70 +0,0 @@ -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherState = artifacts.require("MorpherState"); -const MorpherEscrow = artifacts.require("MorpherEscrow"); - -const { advanceTimeAndBlock } = require('../helpers/utils'); - -contract('MorpherEscrow', (accounts) => { - it('test MorpherEscrow fund releases', async () => { - const deployerAddress = accounts[0]; const testAddress1 = accounts[1]; const testAddress2 = accounts[2]; - - const morpherToken = await MorpherToken.deployed(); - const morpherEscrow = await MorpherEscrow.deployed(); - let morpherState = await MorpherState.deployed(); - - // Grant access and enable transfers for the accounts. - await morpherState.grantAccess(deployerAddress); - await morpherState.grantAccess(morpherEscrow.address); - await morpherState.enableTransfers(deployerAddress); - await morpherState.enableTransfers(morpherEscrow.address); - - // Transfer morpher token to the escrow address - await morpherToken.transfer(morpherEscrow.address, '21000000000000000000000000', { from: deployerAddress }); - await morpherEscrow.setRecipientAddress(testAddress1, { from: deployerAddress }); - - let morpherEscrowBalance = await morpherToken.balanceOf(morpherEscrow.address, { from: testAddress2 }); - assert.equal(morpherEscrowBalance.toString(), '21000000000000000000000000'); - - await morpherEscrow.releaseFromEscrow({ from: testAddress2 }); - - morpherEscrowBalance = await morpherToken.balanceOf(morpherEscrow.address, { from: testAddress2 }); - assert.equal(morpherEscrowBalance.toString(), '21000000000000000000000000'); - - // Simulate one month wait in blockchain (3600s * 24 hours * 31 days as a threshold). - await advanceTimeAndBlock(3600 * 24 * 31); - - await morpherEscrow.releaseFromEscrow({ from: testAddress2 }); - - morpherEscrowBalance = await morpherToken.balanceOf(morpherEscrow.address, { from: testAddress2 }); - assert.equal(morpherEscrowBalance.toString(), '11000000000000000000000000'); - - await morpherEscrow.releaseFromEscrow({ from: testAddress2 }); - - morpherEscrowBalance = await morpherToken.balanceOf(morpherEscrow.address, { from: testAddress2 }); - assert.equal(morpherEscrowBalance.toString(), '11000000000000000000000000'); - - // Simulate one month wait in blockchain (3600s * 24 hours * 31 days as a threshold). - await advanceTimeAndBlock(3600 * 24 * 31); - - await morpherEscrow.releaseFromEscrow({ from: testAddress2 }); - - morpherEscrowBalance = await morpherToken.balanceOf(morpherEscrow.address, { from: testAddress2 }); - assert.equal(morpherEscrowBalance.toString(), '1000000000000000000000000'); - - await morpherEscrow.releaseFromEscrow({ from: testAddress2 }); - - morpherEscrowBalance = await morpherToken.balanceOf(morpherEscrow.address, { from: testAddress2 }); - assert.equal(morpherEscrowBalance.toString(), '1000000000000000000000000'); - - // Simulate one month wait in blockchain (3600s * 24 hours * 31 days as a threshold). - await advanceTimeAndBlock(3600 * 24 * 31); - - await morpherEscrow.releaseFromEscrow({ from: testAddress2 }); - - morpherEscrowBalance = await morpherToken.balanceOf(morpherEscrow.address, { from: testAddress2 }); - assert.equal(morpherEscrowBalance.toString(), '0'); - - testAddress1Balance = await morpherToken.balanceOf(testAddress1, { from: testAddress2 }); - assert.equal(testAddress1Balance.toString(), '21000000000000000000000000'); - }); -}); \ No newline at end of file diff --git a/test/morpherFaucet.test.js b/test/morpherFaucet.test.js deleted file mode 100644 index 6d5453b..0000000 --- a/test/morpherFaucet.test.js +++ /dev/null @@ -1,48 +0,0 @@ -const BN = require('bn.js'); - -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherFaucet = artifacts.require("MorpherFaucet"); - - -const truffleAssert = require('truffle-assertions'); - -contract('MorpherFaucet', (accounts) => { - - const [deployerAddress, testAddress1, testAddress2] = accounts; - - it('can top up with MorpherFaucet', async () => { - - const morpherToken = await MorpherToken.deployed(); - const morpherFaucet = await MorpherFaucet.deployed(); - - const startingBalance = await morpherToken.balanceOf(testAddress1); - - const topUpAmount = await morpherFaucet.fillUpAmount(); - assert.equal(topUpAmount.toString(), web3.utils.toWei('100','ether')); - - await morpherFaucet.topUpToken({ from: testAddress1 }); - - const endingBalance = await morpherToken.balanceOf(testAddress1) - - assert.equal(startingBalance.toString(), "0"); - assert.equal(endingBalance.toString(), web3.utils.toWei('100','ether')); - }); - - it('topup again will fail', async () => { - - const morpherToken = await MorpherToken.deployed(); - const morpherFaucet = await MorpherFaucet.deployed(); - - const startingBalance = await morpherToken.balanceOf(testAddress1); - - - - await truffleAssert.fails(morpherFaucet.topUpToken({ from: testAddress1 }), truffleAssert.ErrorType.REVERT); - - const endingBalance = await morpherToken.balanceOf(testAddress1); - - - assert.equal(startingBalance.toString(), endingBalance.toString()); - }); - -}); \ No newline at end of file diff --git a/test/morpherMintLimiter.test.js b/test/morpherMintLimiter.test.js deleted file mode 100644 index e9e2670..0000000 --- a/test/morpherMintLimiter.test.js +++ /dev/null @@ -1,698 +0,0 @@ -const truffleAssert = require('truffle-assertions') - -const MorpherToken = artifacts.require('MorpherToken') -const MorpherTradeEngine = artifacts.require('MorpherTradeEngine') -const MorpherState = artifacts.require('MorpherState') -const MorpherOracle = artifacts.require('MorpherOracle') -const MorpherMintingLimiter = artifacts.require('MorpherMintingLimiter') - -let BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9' - -function roundToInteger(price) { - return Math.round(price * Math.pow(10, 8)) -} - -const BN = require('bn.js') - -contract( - 'MorpherMintingLimiter: Trades with and without minting limiter', - (accounts) => { - const [callbackAccount, tradeAccount] = accounts - - it('No Minting Limit Set - No Escrow happens', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherToken = await MorpherToken.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - let morpherMintingLimiter = await MorpherMintingLimiter.deployed() - - const startingBalance = web3.utils.toWei(new BN(1), 'ether') - assert.equal( - (await morpherMintingLimiter.mintingLimitDaily()).toString(), - '0', - 'Morpher Minting Limit is not 0', - ) - assert.equal( - (await morpherMintingLimiter.mintingLimitPerUser()).toString(), - '0', - 'Morpher Minting Limit is not 0', - ) - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(tradeAccount, startingBalance) - - //open an order and make it super successful - let orderId = ( - await morpherOracle.createOrder( - BTC, - 0, - roundToInteger(300), - true, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - await morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - let position = await morpherState.getPosition(tradeAccount, BTC) - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = - position._longShares.toNumber() * - ( - await morpherTradeEngine.longShareValue( - position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), - 0, - roundToInteger(150), - 0, - 100000000, - true, - ) - ).toNumber() - - let userBalance = (await morpherState.balanceOf(tradeAccount)).toString() - - assert.equal(positionValue, roundToInteger(300)) - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(150)) - assert.equal(position._longShares.toNumber(), 2) - assert.equal(position._shortShares.toNumber(), 0) - assert.equal( - userBalance, - startingBalance.sub(new BN(roundToInteger(300))).toString(), - ) - - orderId = ( - await morpherOracle.createOrder( - BTC, - 2, - 0, - false, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - let txResult = await morpherOracle.__callback( - orderId, - roundToInteger(15000), - roundToInteger(15000), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - await truffleAssert.eventNotEmitted(txResult, 'MintingEscrowed') - - userBalance = (await morpherState.balanceOf(tradeAccount)).toString() - assert(userBalance > startingBalance.toString()) - let escrowedAmount = await morpherMintingLimiter.escrowedTokens( - tradeAccount, - ) - assert.equal(escrowedAmount.toString(), '0') - }) - it('Minting Limit Per User Set, self delayed minting', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherToken = await MorpherToken.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - let morpherMintingLimiter = await MorpherMintingLimiter.deployed() - - const startingBalance = await morpherState.balanceOf(tradeAccount) - - //set minting limit to 1 MPH - assert.equal( - (await morpherMintingLimiter.mintingLimitPerUser()).toString(), - '0', - 'Morpher Minting Limit is not 0', - ) - await morpherMintingLimiter.setMintingLimitPerUser( - web3.utils.toWei('1', 'ether'), - ) - assert.equal( - (await morpherMintingLimiter.mintingLimitPerUser()).toString(), - web3.utils.toWei('1', 'ether'), - 'Morpher Minting Limit is not 1 MPH', - ) - - await morpherMintingLimiter.setTimeLockingPeriod(3) //set the time lock period to 3 seconds - - //open an order and make it super successful - let orderId = ( - await morpherOracle.createOrder( - BTC, - 0, - web3.utils.toWei('1', 'ether'), - true, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - await morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - let position = await morpherState.getPosition(tradeAccount, BTC) - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = - position._longShares.toNumber() * - ( - await morpherTradeEngine.longShareValue( - position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), - 0, - roundToInteger(150), - 0, - 100000000, - true, - ) - ).toNumber() - - let userBalanceBeforeCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - - assert.equal(positionValue, '999999990000000000') - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(150)) - assert.equal(position._longShares.toNumber(), 66666666) - assert.equal(position._shortShares.toNumber(), 0) - assert.equal( - userBalanceBeforeCallback, - startingBalance.sub(new BN('999999990000000000')).toString(), - ) - - orderId = ( - await morpherOracle.createOrder( - BTC, - 66666666, - 0, - false, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - let txResult = await morpherOracle.__callback( - orderId, - roundToInteger(152), - roundToInteger(152), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - - let event = txResult.receipt.rawLogs.some((l) => { - return ( - l.topics[0] == web3.utils.sha3('MintingEscrowed(address,uint256)') - ) - }) - assert.ok(event, 'MintingEscrowed event not emitted') - - let userBalanceAfterCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - assert( - userBalanceBeforeCallback == userBalanceAfterCallback, - 'amount was not escrowed', - ) - let escrowedAmount = await morpherMintingLimiter.escrowedTokens( - tradeAccount, - ) - assert.equal( - escrowedAmount.toString(), - '1013333323200000000', - 'The minted amount should be in escrow', - ) - - await truffleAssert.fails( - morpherMintingLimiter.delayedMint(tradeAccount, { from: tradeAccount }), - ) - - await new Promise((resolve) => setTimeout(resolve, 4000)) - let txMint = await morpherMintingLimiter.delayedMint(tradeAccount, { - from: tradeAccount, - }) - await truffleAssert.eventEmitted(txMint, 'EscrowReleased') - userBalance = (await morpherState.balanceOf(tradeAccount)).toString() - assert( - userBalance > startingBalance.toString(), - 'amount was not escrowed', - ) - escrowedAmount = await morpherMintingLimiter.escrowedTokens(tradeAccount) - assert.equal(escrowedAmount.toString(), '0', '0 MPH should be in escrow') - }) - - it('Minting Limit Set, administrator minting', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - let morpherMintingLimiter = await MorpherMintingLimiter.deployed() - - const startingBalance = await morpherState.balanceOf(tradeAccount) - - //open an order and make it super successful - let orderId = ( - await morpherOracle.createOrder( - BTC, - 0, - web3.utils.toWei('1', 'ether'), - true, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - await morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - let position = await morpherState.getPosition(tradeAccount, BTC) - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = - position._longShares.toNumber() * - ( - await morpherTradeEngine.longShareValue( - position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), - 0, - roundToInteger(150), - 0, - 100000000, - true, - ) - ).toNumber() - - let userBalanceBeforeCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - - assert.equal(positionValue, '999999990000000000') - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(150)) - assert.equal(position._longShares.toNumber(), 66666666) - assert.equal(position._shortShares.toNumber(), 0) - assert.equal( - userBalanceBeforeCallback, - startingBalance.sub(new BN('999999990000000000')).toString(), - ) - - orderId = ( - await morpherOracle.createOrder( - BTC, - 66666666, - 0, - false, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - let txResult = await morpherOracle.__callback( - orderId, - roundToInteger(152), - roundToInteger(152), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - - let event = txResult.receipt.rawLogs.some((l) => { - return ( - l.topics[0] == web3.utils.sha3('MintingEscrowed(address,uint256)') - ) - }) - assert.ok(event, 'MintingEscrowed event not emitted') - - let userBalanceAfterCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - assert( - userBalanceBeforeCallback == userBalanceAfterCallback, - 'amount was not escrowed', - ) - let escrowedAmount = await morpherMintingLimiter.escrowedTokens( - tradeAccount, - ) - assert.equal( - escrowedAmount.toString(), - '1013333323200000000', - 'The minted amount should be in escrow', - ) - - let txMint = await morpherMintingLimiter.adminApprovedMint( - tradeAccount, - escrowedAmount.toString(), - { from: callbackAccount }, - ) - await truffleAssert.eventEmitted(txMint, 'EscrowReleased') - escrowedAmount = await morpherMintingLimiter.escrowedTokens(tradeAccount) - assert.equal(escrowedAmount.toString(), '0', '0 MPH should be in escrow') - }) - - it('Minting Limit Set, administrator adminDisapproveMint', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - let morpherMintingLimiter = await MorpherMintingLimiter.deployed() - - const startingBalance = await morpherState.balanceOf(tradeAccount) - - //open an order and make it super successful - let orderId = ( - await morpherOracle.createOrder( - BTC, - 0, - web3.utils.toWei('1', 'ether'), - true, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - await morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - let position = await morpherState.getPosition(tradeAccount, BTC) - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = - position._longShares.toNumber() * - ( - await morpherTradeEngine.longShareValue( - position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), - 0, - roundToInteger(150), - 0, - 100000000, - true, - ) - ).toNumber() - - let userBalanceBeforeCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - - assert.equal(positionValue, '999999990000000000') - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(150)) - assert.equal(position._longShares.toNumber(), 66666666) - assert.equal(position._shortShares.toNumber(), 0) - assert.equal( - userBalanceBeforeCallback, - startingBalance.sub(new BN('999999990000000000')).toString(), - ) - - orderId = ( - await morpherOracle.createOrder( - BTC, - 66666666, - 0, - false, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - let txResult = await morpherOracle.__callback( - orderId, - roundToInteger(152), - roundToInteger(152), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - - let event = txResult.receipt.rawLogs.some((l) => { - return ( - l.topics[0] == web3.utils.sha3('MintingEscrowed(address,uint256)') - ) - }) - assert.ok(event, 'MintingEscrowed event not emitted') - - let userBalanceAfterCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - assert( - userBalanceBeforeCallback == userBalanceAfterCallback, - 'amount was not escrowed', - ) - let escrowedAmount = await morpherMintingLimiter.escrowedTokens( - tradeAccount, - ) - assert.equal( - escrowedAmount.toString(), - '1013333323200000000', - 'The minted amount should be in escrow', - ) - - let txMint = await morpherMintingLimiter.adminDisapproveMint( - tradeAccount, - escrowedAmount.toString(), - { from: callbackAccount }, - ) - await truffleAssert.eventEmitted(txMint, 'MintingDenied') - escrowedAmount = await morpherMintingLimiter.escrowedTokens(tradeAccount) - assert.equal(escrowedAmount.toString(), '0', '0 MPH should be in escrow') - - assert.equal( - userBalanceBeforeCallback, - (await morpherState.balanceOf(tradeAccount)).toString(), - 'The user got Tokens although we denied tokens', - ) - }) - - it('Minting Limit Daily Set, administrator approved', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - let morpherToken = await MorpherToken.deployed() - let morpherMintingLimiter = await MorpherMintingLimiter.deployed() - - //set minting limit daily to 1 MPH - assert.equal( - (await morpherMintingLimiter.mintingLimitDaily()).toString(), - '0', - 'Morpher Minting Limit is not 0', - ) - await morpherMintingLimiter.setMintingLimitPerUser( - web3.utils.toWei('100', 'ether'), - ) - assert.equal( - (await morpherMintingLimiter.mintingLimitPerUser()).toString(), - web3.utils.toWei('100', 'ether'), - 'Morpher Minting Limit is not 100 MPH', - ) - //set the daily limit - await morpherMintingLimiter.setMintingLimitDaily( - web3.utils.toWei('1', 'ether'), - ) - assert.equal( - (await morpherMintingLimiter.mintingLimitDaily()).toString(), - web3.utils.toWei('1', 'ether'), - 'Morpher Minting Limit is not 1 MPH', - ) - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(tradeAccount, web3.utils.toWei('1', 'ether')) - - const startingBalance = await morpherState.balanceOf(tradeAccount) - - //open an order and make it super successful - let orderId = ( - await morpherOracle.createOrder( - BTC, - 0, - web3.utils.toWei('1', 'ether'), - true, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - await morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - let position = await morpherState.getPosition(tradeAccount, BTC) - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = - position._longShares.toNumber() * - ( - await morpherTradeEngine.longShareValue( - position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), - 0, - roundToInteger(150), - 0, - 100000000, - true, - ) - ).toNumber() - - let userBalanceBeforeCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - - assert.equal(positionValue, '999999990000000000') - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(150)) - assert.equal(position._longShares.toNumber(), 66666666) - assert.equal(position._shortShares.toNumber(), 0) - assert.equal( - userBalanceBeforeCallback, - startingBalance.sub(new BN('999999990000000000')).toString(), - ) - - orderId = ( - await morpherOracle.createOrder( - BTC, - 66666666, - 0, - false, - 100000000, - 0, - 0, - 0, - 0, - { from: tradeAccount }, - ) - ).logs[0].args._orderId - let txResult = await morpherOracle.__callback( - orderId, - roundToInteger(152), - roundToInteger(152), - 0, - 0, - 0, - 0, - { from: callbackAccount }, - ) - - let event = txResult.receipt.rawLogs.some((l) => { - return ( - l.topics[0] == web3.utils.sha3('MintingEscrowed(address,uint256)') - ) - }) - assert.ok(event, 'MintingEscrowed event not emitted') - - let userBalanceAfterCallback = ( - await morpherState.balanceOf(tradeAccount) - ).toString() - assert( - userBalanceBeforeCallback == userBalanceAfterCallback, - 'amount was not escrowed', - ) - let escrowedAmount = await morpherMintingLimiter.escrowedTokens( - tradeAccount, - ) - assert.equal( - escrowedAmount.toString(), - '1013333323200000000', - 'The minted amount should be in escrow', - ) - - let txMint = await morpherMintingLimiter.adminApprovedMint( - tradeAccount, - escrowedAmount.toString(), - { from: callbackAccount }, - ) - await truffleAssert.eventEmitted(txMint, 'EscrowReleased') - escrowedAmount = await morpherMintingLimiter.escrowedTokens(tradeAccount) - assert.equal(escrowedAmount.toString(), '0', '0 MPH should be in escrow') - }) - - it('Minting Limit Daily Reset, administrator approved', async () => { - - let morpherMintingLimiter = await MorpherMintingLimiter.deployed() - - //set minting limit daily to 1 MPH - assert.equal( - (await morpherMintingLimiter.mintingLimitDaily()).toString(), - web3.utils.toWei('1', 'ether'), - 'Morpher Minting Limit is not 1 MPH', - ) - - let mintedTokensToday = await morpherMintingLimiter.getDailyMintedTokens(); - assert(mintedTokensToday.toNumber() > 0); - - let txResult = await morpherMintingLimiter.resetDailyMintedTokens(); - truffleAssert.eventEmitted(txResult, "DailyMintedTokensReset"); - - let mintedTokensTodayAfterReset = await morpherMintingLimiter.getDailyMintedTokens(); - assert(mintedTokensTodayAfterReset.toNumber() == 0, "getDailyMintedTokens should be back to 0"); - - }) - }, -) diff --git a/test/morpherOracle.test.js b/test/morpherOracle.test.js deleted file mode 100644 index 2fef652..0000000 --- a/test/morpherOracle.test.js +++ /dev/null @@ -1,639 +0,0 @@ -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); -const MorpherOracle = artifacts.require("MorpherOracle"); -const MorpherState = artifacts.require("MorpherState"); -const MorpherStaking = artifacts.require("MorpherStaking"); -const MintingLimiter = artifacts.require("MorpherMintingLimiter"); - -const truffleAssert = require('truffle-assertions'); -const BN = require("bn.js"); - -const { getLeverage } = require('./helpers/tradeFunctions'); - -const MARKET = 'CRYPTO_BTC'; -const MARKET2 = 'CRYPTO_ETH'; -const gasPriceInGwei = 200; //gwei gas price for callback funding - -const historicalGasConsumptionFromOracle = []; //an array that holds the gas - -const average = arr => arr.reduce((sume, el) => sume + el, 0) / arr.length; - -contract('MorpherOracle', (accounts) => { - - const [ - deployerAddress, - testUserAddress, - oracleCallbackAddress - ] = accounts; - - - it('Deployer can enable and disable oracle callback addresses', async () => { - const morpherOracle = await MorpherOracle.deployed(); - - let isAllowed = await morpherOracle.callBackAddress(oracleCallbackAddress); - assert.equal(isAllowed, false); - - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - isAllowed = await morpherOracle.callBackAddress(oracleCallbackAddress); - assert.equal(isAllowed, true); - }); - - it('Oracle can be paused', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - const orderId1 = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, 100000000, 0, 0, 0, 0, { from: testUserAddress })).logs[0].args._orderId; - // Asserts - assert.notEqual(orderId1, null); - // Test order failure if oracle is paused. - await morpherOracle.pauseOracle({ from: deployerAddress }); - await truffleAssert.reverts(morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 200, true, 100000000, 0, 0, 0, 0, { from: testUserAddress }), "Oracle paused"); - await truffleAssert.reverts(morpherOracle.__callback(orderId1, 100000000, 100000000, 1000000, 0, 1234, 0, { from: oracleCallbackAddress }), "Oracle paused"); - - // Test last created order is orderId1 not orderId2 because Oracle was paused. - const lastOrderId = await morpherTradeEngine.lastOrderId(); - assert.equal(lastOrderId, orderId1); - // Test order creation after unpausing oracle. - await morpherOracle.unpauseOracle({ from: deployerAddress }); - - await morpherOracle.__callback(orderId1, 100000000, 100000000, 1000000, 0, 1234, 0, { from: oracleCallbackAddress }); - - // orderID1 should have '0' values because we successfully called the callback. - const order = await morpherTradeEngine.getOrder(orderId1); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - it('TradeEngine can be updated', async () => { - const morpherOracle = await MorpherOracle.deployed(); - - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherState = await MorpherState.deployed(); - const morpherStaking = await MorpherStaking.deployed(); - const mintingLimiter = await MintingLimiter.deployed(); - - const morpherToken = await MorpherToken.deployed(); - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - const orderId1 = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, 100000000, 0, 0, 0, 0, { from: testUserAddress })).logs[0].args._orderId; - // Asserts - assert.notEqual(orderId1, null); - - //deploy new trade engine - const morpherTradeEngine2 = await MorpherTradeEngine.new(morpherState.address, deployerAddress, morpherStaking.address, true, 0, mintingLimiter.address) - await morpherState.grantAccess(morpherTradeEngine2.address); - await morpherOracle.setTradeEngineAddress(morpherTradeEngine2.address); - - - await morpherOracle.__callback(orderId1, 100000000, 100000000, 1000000, 0, 1234, 0, { from: oracleCallbackAddress }); - // orderID1 should have '0' values because we successfully called the callback. - const order = await morpherTradeEngine.getOrder(orderId1); - - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - const orderId2 = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, 100000000, 0, 0, 0, 0, { from: testUserAddress })).logs[0].args._orderId; - // orderID1 should have '0' values because we successfully called the callback. - const order2 = await morpherTradeEngine.getOrder(orderId2); - assert.equal(order2._openMPHTokenAmount, '0'); // not created in the original tradeEngine - const order2_newTradeEngine = await morpherTradeEngine2.getOrder(orderId2); - assert.equal(order2_newTradeEngine._openMPHTokenAmount.toString(), '10'); // not created in the original tradeEngine - await morpherOracle.__callback(orderId2, 100000000, 100000000, 1000000, 0, 1234, 0, { from: oracleCallbackAddress }); - - //set it back. - await morpherOracle.setTradeEngineAddress(morpherTradeEngine.address); - - - }); - - it('Oracle can be updated', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherToken = await MorpherToken.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherState = await MorpherState.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const goodFrom = Math.round((Date.now() / 1000)) + 10; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, 0, goodFrom, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_goodFrom'], goodFrom); - - //deploy a new oracle - const morpherOracle2 = await MorpherOracle.new(morpherTradeEngine.address, morpherState.address, oracleCallbackAddress, deployerAddress, 0, deployerAddress, morpherTradeEngine.address, morpherOracle.address); - await morpherState.setOracleContract(morpherOracle2.address); - - await truffleAssert.fails( - morpherOracle2.__callback(txReceipt.logs[0].args['_orderId'], 100000000, 100000000, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }), - truffleAssert.ErrorType.REVERT, - "Error: Order Conditions are not met" - ); - - await morpherState.setOracleContract(morpherOracle.address); - }); - - it('Oracle and then TradeEngine can be updated', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherToken = await MorpherToken.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherState = await MorpherState.deployed(); - const morpherStaking = await MorpherStaking.deployed(); - const morpherMintingLimiter = await MintingLimiter.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const goodFrom = Math.round((Date.now() / 1000)) + 10; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, 0, goodFrom, { from: testUserAddress }); - //create a second order to test working orders - const txReceipt2 = await morpherOracle.createOrder(web3.utils.sha3(MARKET2), 0, 10, true, getLeverage(1), 0, 0, 0, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_goodFrom'], goodFrom); - - //deploy a new oracle - const morpherOracle2 = await MorpherOracle.new(morpherTradeEngine.address, morpherState.address, oracleCallbackAddress, deployerAddress, 0, deployerAddress, morpherTradeEngine.address, morpherOracle.address); - await morpherState.setOracleContract(morpherOracle2.address); - - await truffleAssert.fails( - morpherOracle2.__callback(txReceipt.logs[0].args['_orderId'], 100000000, 100000000, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }), - truffleAssert.ErrorType.REVERT, - "Error: Order Conditions are not met" - ); - - //deploy new trade engine - const morpherTradeEngine2 = await MorpherTradeEngine.new(morpherState.address, deployerAddress, morpherStaking.address, true, 0, morpherMintingLimiter.address) - await morpherState.grantAccess(morpherTradeEngine2.address); - await morpherOracle2.setTradeEngineAddress(morpherTradeEngine2.address); - - //then callback the second order, it should go through - await morpherOracle2.__callback(txReceipt2.logs[0].args['_orderId'], 100000000, 100000000, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - //then create a new order - const txReceipt3 = await morpherOracle2.createOrder(web3.utils.sha3(MARKET2), 0, 10, true, getLeverage(1), 0, 0, 0, 0, { from: testUserAddress }); - //this order should be in the new tradeEngine - const order = await morpherTradeEngine2.getOrder(txReceipt3.logs[0].args['_orderId']); - assert.equal(order._userId, testUserAddress); - await morpherState.setOracleContract(morpherOracle.address); - }); - - - it('Orders can be canceled if requested', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test new order creation and cancellation. - const orderId = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 200, true, 100000000, 0, 0, 0, 0, { from: testUserAddress })).logs[0].args._orderId; - await truffleAssert.fails(morpherOracle.cancelOrder(orderId, { from: testUserAddress }), truffleAssert.REVERT, "MorpherOracle: Only the oracle operator can call this function."); - - let cancellationResult = await morpherOracle.initiateCancelOrder(orderId, { from: testUserAddress }); - - await truffleAssert.eventEmitted(cancellationResult, "OrderCancellationRequestedEvent", { _orderId: orderId, _sender: testUserAddress }); - - const order = await morpherTradeEngine.getOrder(orderId); - assert.equal(order._openMPHTokenAmount, '200'); // order not canceled yet - - let oracleCancelCallback = await morpherOracle.cancelOrder(orderId, { from: oracleCallbackAddress }); - - await truffleAssert.eventEmitted(oracleCancelCallback, "OrderCancelled", { _orderId: orderId, _sender: testUserAddress, _oracleAddress: oracleCallbackAddress }); - const orderAfterCancel = await morpherTradeEngine.getOrder(orderId); - assert.equal(orderAfterCancel._openMPHTokenAmount, '0'); // order canceled - - }); - - it('Orders can not be canceled if not requested by user', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test new order creation and cancellation. - const orderId = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 200, true, 100000000, 0, 0, 0, 0, { from: testUserAddress })).logs[0].args._orderId; - await truffleAssert.fails(morpherOracle.cancelOrder(orderId, { from: oracleCallbackAddress }), truffleAssert.REVERT, "MorpherOracle: Order-Cancellation was not requested."); - - const order = await morpherTradeEngine.getOrder(orderId); - assert.equal(order._openMPHTokenAmount, '200'); // order not canceled yet - - }); - - it('Updating Orders can trigger an order cancellation', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test new order creation and cancellation. - const orderId = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 200, true, 100000000, 0, 0, 0, 0, { from: testUserAddress })).logs[0].args._orderId; - await truffleAssert.fails(morpherOracle.cancelOrder(orderId, { from: oracleCallbackAddress }), truffleAssert.REVERT, "MorpherOracle: Order-Cancellation was not requested."); - - let result = await morpherOracle.adminCancelOrder(orderId, { from: oracleCallbackAddress }); - await truffleAssert.eventEmitted(result, 'AdminOrderCancelled'); - const order = await morpherTradeEngine.getOrder(orderId); - assert.equal(order._openMPHTokenAmount, '0'); // order canceled - - }); - - it('goodUntil fails if in the past', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const goodUntil = Math.round((Date.now() / 1000)) - 10; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, goodUntil, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_goodUntil'], goodUntil); - - await truffleAssert.fails( - morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 100000000, 100000000, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }), - truffleAssert.ErrorType.REVERT, - "Error: Order Conditions are not met" - ); - }); - - it('goodUntil works if in the future', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const goodUntil = Math.round((Date.now() / 1000)) + 120; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, goodUntil, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_goodUntil'], goodUntil); - - await morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 100000000, 100000000, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - it('goodFrom fails if in the future', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const goodFrom = Math.round((Date.now() / 1000)) + 10; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, 0, goodFrom, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_goodFrom'], goodFrom); - - await truffleAssert.fails( - morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 100000000, 100000000, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }), - truffleAssert.ErrorType.REVERT, - "Error: Order Conditions are not met" - ); - }); - - it('goodFrom works if in the past', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const goodFrom = Math.round((Date.now() / 1000)) - 10; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, 0, goodFrom, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_goodFrom'], goodFrom); - - await morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 100000000, 100000000, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - - it('onlyIfPriceAbove fails if smaller', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const priceAbove = 12; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), priceAbove, 0, 0, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_onlyIfPriceAbove'], priceAbove); - - await truffleAssert.fails( - morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 11, 11, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }), - truffleAssert.ErrorType.REVERT, - "Error: Order Conditions are not met" - ); - }); - - it('onlyIfPriceAbove works if price larger than current price', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const priceAbove = 10; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), priceAbove, 0, 0, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_onlyIfPriceAbove'], priceAbove); - - await morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 11, 11, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - - it('onlyIfPriceBelow fails if smaller', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const priceBelow = 9; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, priceBelow, 0, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_onlyIfPriceBelow'], priceBelow); - - await truffleAssert.fails( - morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 11, 11, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }), - truffleAssert.ErrorType.REVERT, - "Error: Order Conditions are not met" - ); - }); - - it('onlyIfPriceBelow works if price larger than current price', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const priceBelow = 12; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, priceBelow, 0, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_onlyIfPriceBelow'], priceBelow); - - await morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 11, 11, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - it('price < onlyIfPriceBelow works in conjunction with onlyIfPriceAbove', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const priceBelow = 12; - //or - const priceAbove = 14; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), priceAbove, priceBelow, 0, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_onlyIfPriceBelow'], priceBelow); - - await morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 11, 11, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - it('price > onlyIfPriceAbove works in conjunction with onlyIfPriceBelow', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const priceBelow = 12; - //or - const priceAbove = 14; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), priceAbove, priceBelow, 0, 0, { from: testUserAddress }); - - // Asserts - assert.equal(txReceipt.logs[0].args['_onlyIfPriceBelow'], priceBelow); - - await morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 15, 15, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - it('price < onlyIfPriceAbove FAILS in conjunction with price > onlyIfPriceBelow', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - - const priceBelow = 12; - //or - const priceAbove = 14; - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), priceAbove, priceBelow, 0, 0, { from: testUserAddress }); - await truffleAssert.fails( - morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 13, 13, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }), - truffleAssert.ErrorType.REVERT, - "Error: Order Conditions are not met" - ); - }); - - it('Oracle can do gasCallbacks correctly', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - const setGasForCallbackValue = web3.utils.toWei("0.001", "ether"); - await morpherOracle.overrideGasForCallback(setGasForCallbackValue); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - const gasForCallback = await morpherOracle.gasForCallback(); - assert.equal(gasForCallback.toString(), setGasForCallbackValue); - - const orderId1 = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, 100000000, 0, 0, 0, 0, { from: testUserAddress, value: gasForCallback })).logs[0].args._orderId; - - // Asserts - assert.notEqual(orderId1, null); - - await morpherOracle.__callback(orderId1, 100000000, 100000000, 1000000, 0, 1234, setGasForCallbackValue, { from: oracleCallbackAddress }); - - // orderID1 should have '0' values because we successfully called the callback. - const order = await morpherTradeEngine.getOrder(orderId1); - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - it('Gas Escrow does not drain Oracle Wallet', async () => { - const morpherOracle = await MorpherOracle.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - await morpherToken.transfer(oracleCallbackAddress, web3.utils.toWei("1", "ether"), { from: deployerAddress }); - - // Test successful state variables change and order creation. - const setGasForCallbackValue = web3.utils.toWei("0.001", "ether"); - await morpherOracle.overrideGasForCallback(setGasForCallbackValue); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - await morpherOracle.setCallbackCollectionAddress(oracleCallbackAddress); - - let nextOrderGasEscrowInEther = await morpherOracle.gasForCallback(); - assert.equal(nextOrderGasEscrowInEther.toString(), setGasForCallbackValue); - - - // let web3Contract = new web3.eth.Contract(morpherOracle.abi, morpherOracle.address); - const oracleStartingBalance = await web3.eth.getBalance(oracleCallbackAddress); - // console.log("Round;Average Gas last Transactions;Gas Estimated;Gas Used;Balance Oracle"); - - for (let i = 0; i < 10; i++) { - - let orderId = (await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, 100000000, 0, 0, 0, 0, { from: testUserAddress, value: nextOrderGasEscrowInEther })).logs[0].args._orderId; - - // Asserts - assert.notEqual(orderId, null); - - /** - * this is not the same as the gas cost for the transaction - */ - const transactionRequiresGasToFinish = await morpherOracle.__callback.estimateGas(orderId, 100000000, 100000000, 1000000, 0, 1234, nextOrderGasEscrowInEther, { from: oracleCallbackAddress }); - - if (historicalGasConsumptionFromOracle.length == 0) { - historicalGasConsumptionFromOracle.push(transactionRequiresGasToFinish); // we don't have anything yet, we need to start with something - } - - const gasRequiredOnAverage = Math.round(average(historicalGasConsumptionFromOracle)); - - // let gasEstimateWeb3 = await web3Contract.methods.__callback(orderId, 100000000, 1000000, 0, 1234, nextOrderGas).estimateGas({ from: oracleCallbackAddress }); - // console.log(gasEstimateWeb3); - - - nextOrderGasEscrowInEther = web3.utils.toWei((gasRequiredOnAverage * gasPriceInGwei).toString(), "gwei"); - - let balanceBefore = await web3.eth.getBalance(oracleCallbackAddress); - - /** - * we provide more gas and get the rest refunded - * But the user needs to pay our oracle on average the amount back we paid. So our oracle never runs out of money - */ - let receipt = await morpherOracle.__callback(orderId, 100000000, 100000000, 1000000, 0, 1234, nextOrderGasEscrowInEther, { from: oracleCallbackAddress, gas: web3.utils.toHex(transactionRequiresGasToFinish + 100000), gasPrice: web3.utils.toWei(gasPriceInGwei.toString(), 'gwei') }); - - // let balanceAfter = await web3.eth.getBalance(oracleCallbackAddress); - // console.log(i + ";" + gasRequiredOnAverage + ";" + transactionRequiresGasToFinish + ";" + receipt.receipt.gasUsed + ";" + web3.utils.fromWei(balanceAfter, 'ether')); - historicalGasConsumptionFromOracle.push(receipt.receipt.gasUsed); - if (historicalGasConsumptionFromOracle.length > 10) { - historicalGasConsumptionFromOracle.shift(); - } - - // assert.isTrue(gasEstimateForCallback >= receipt.receipt.gasUsed, "Gas used was more than what we estimated"); - - } - - const oracleBalanceAfterOrders = new BN(await web3.eth.getBalance(oracleCallbackAddress)); - assert.isTrue(oracleBalanceAfterOrders.gte(new BN(oracleStartingBalance)), "We're loosing money at the callback, it should not happen normally " + oracleBalanceAfterOrders + " vs " + oracleStartingBalance); - //console.log(oracleBalanceAfterOrders, oracleStartingBalance); - }); - -}); \ No newline at end of file diff --git a/test/morpherStaking.test.js b/test/morpherStaking.test.js deleted file mode 100644 index da7cf43..0000000 --- a/test/morpherStaking.test.js +++ /dev/null @@ -1,200 +0,0 @@ -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherStaking = artifacts.require("MorpherStaking"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); - -const truffleAssert = require('truffle-assertions'); -const BN = require("bn.js"); - - -contract('MorpherStaking: increase/decrease staked amount', (accounts) => { - const [deployer, account1] = accounts; - - it('staking is possible', async () => { - - - const token = await MorpherToken.deployed(); - const staking = await MorpherStaking.deployed(); - await token.transfer(account1, web3.utils.toWei('1000000', 'ether'), { from: deployer }); //fill up some tokens - let result = await staking.stake(web3.utils.toWei('100000', 'ether'), { from: account1 }); - await truffleAssert.eventEmitted(result, 'Staked', (ev) => { - return ev.userAddress === account1 && ev.amount.toString() === '100000000000000000000000'; //poolshares cannot be easily defined, as they change with the rounding errors of the days * interestRate passed - }); - - }); - - it('stake should be accurate', async () => { - const staking = await MorpherStaking.deployed(); - let stake = await staking.getStake(account1); - assert.equal(stake.toString(), '1000000000000000', 'Stake is not accurate'); - }); - - it('decreasing is impossible because of lock in period', async () => { - const staking = await MorpherStaking.deployed(); - await truffleAssert.fails(staking.unstake(1, { from: account1 }), truffleAssert.ErrorType.REVERT, 'MorpherStaking: cannot unstake before lockup expiration'); - }); - - it('decreasing is impossible if lockup changed', async () => { - const staking = await MorpherStaking.deployed(); - await staking.setLockupPeriodRate(0); - await truffleAssert.fails(staking.unstake(1, { from: account1 }), truffleAssert.ErrorType.REVERT, 'MorpherStaking: cannot unstake before lockup expiration'); - }); - - it('decreasing is possible if lockup changed and re-staked', async () => { - const staking = await MorpherStaking.deployed(); - const totalAmount = await staking.totalShares(); - await staking.setLockupPeriodRate(0); - let result = await staking.stake('100000000', { from: account1 }); //buy exactly one share - await truffleAssert.eventEmitted(result, 'Staked', (ev) => { - return ev.lockedUntil <= Math.round(Date.now() / 1000); - }); - result = await staking.unstake(1, { from: account1 }); - await truffleAssert.eventEmitted(result, 'Unstaked', (ev) => { - return ev.userAddress === account1 && ev.amount.toString() === '100000000' && ev.poolShares.toString() === '1'; - }); - - const totalAmountAfterUnstake = await staking.totalShares(); - assert.equal(totalAmount.toString(), totalAmountAfterUnstake.toString()); - }); - - - - it('minimumStake is accounted for', async () => { - const staking = await MorpherStaking.deployed(); - - await staking.unstake(await staking.getStake(account1), { from: account1 }); //unstake everything - - assert.equal('0', (await staking.getStake(account1)).toString()); - let result = await staking.setMinimumStake(100); - await truffleAssert.eventEmitted(result, 'SetMinimumStake'); - - await truffleAssert.fails(staking.stake(95, { from: account1 }), truffleAssert.ErrorType.REVERT, 'MorpherStaking: stake amount lower than minimum stake'); - }); - -}); - -contract('MorpherStaking: Administrative Actions', (accounts) => { - const [deployer, account1, account2] = accounts; - it('moving staking admin to another address', async () => { - - const staking = await MorpherStaking.deployed(); - const stakingAdmin = await staking.stakingAdmin(); - await staking.setStakingAdmin(account1); - let result = await staking.setMinimumStake(web3.utils.toWei('1','ether'), { from: account1 }); - await truffleAssert.eventEmitted(result, 'SetMinimumStake'); - await truffleAssert.fails(staking.setInterestRate(web3.utils.toWei('100','ether'), { from: account2 }), truffleAssert.ErrorType.REVERT, 'MorpherStaking: can only be called by Staking Administrator.'); - }); - - it('moving staking admin can only be done by owner', async () => { - const staking = await MorpherStaking.deployed(); - await truffleAssert.fails(staking.setStakingAdmin(deployer, { from: account1 }), truffleAssert.ErrorType.REVERT, 'Ownable: caller should be owner.'); - }); - - it('morpherStateAddress can only be set by owner', async () => { - const staking = await MorpherStaking.deployed(); - await truffleAssert.fails(staking.setMorpherStateAddress(deployer, { from: account1 }), truffleAssert.ErrorType.REVERT, 'Ownable: caller should be owner.'); - }); - - it('setLockupPeriodRate can only be called by staking admin', async () => { - const staking = await MorpherStaking.deployed(); - - let result = await staking.setLockupPeriodRate((60 * 60 * 24), { from: account1 }); - await truffleAssert.eventEmitted(result, 'SetLockupPeriod'); - let lockUpPeriod = await staking.lockupPeriod(); - assert.equal(lockUpPeriod.toString(), (60 * 60 * 24).toString()); - }); - - it('setMinimumStake can only be called by staking admin', async () => { - const staking = await MorpherStaking.deployed(); - - let result = await staking.setMinimumStake(100, { from: account1 }); - await truffleAssert.eventEmitted(result, 'SetMinimumStake'); - let minimumStake = await staking.minimumStake(); - assert.equal(minimumStake.toString(), '100'); - }); - -}); - - -contract('MorpherStaking: Interest Rate Actions', (accounts) => { - const [deployer, account1, account2] = accounts; - it('has a default interest rate', async() => { - const staking = await MorpherStaking.deployed(); - const interestRate = await staking.interestRate(); - assert.equal(interestRate.toString(), '15000'); - }) - it('add Interest Rate', async () => { - - const staking = await MorpherStaking.deployed(); - const result = await staking.addInterestRate('30000', Math.round((Date.now() / 1000) + (60*60*24))); - await truffleAssert.eventEmitted(result, "InterestRateAdded") - }); - - it('add Interest Rate with a past validFrom rate fails', async () => { - const staking = await MorpherStaking.deployed(); - await truffleAssert.fails(staking.addInterestRate('50000', Math.round(Date.now() / 1000)), truffleAssert.ErrorType.REVERT, 'MorpherStaking: Interest Rate Valid From must be later than last interestRate'); - - }); - - it('change interest rate', async () => { - const staking = await MorpherStaking.deployed(); - const result = await staking.changeInterestRateValue(0, 20000); - await truffleAssert.eventEmitted(result, "InterestRateRateChanged"); - const interestRateAfterChange = await staking.interestRate(); - assert.equal(interestRateAfterChange.toString(), '20000'); - await staking.changeInterestRateValue(0, 15000); - - }); - - it('is possible to change the valid From date of interest rates', async() =>{ - const staking = await MorpherStaking.deployed(); - - const firstInterestRate = await staking.interestRates(0); - const result = await staking.changeInterestRateValidFrom(1, Math.round((Date.now()/1000) - ((Date.now() / 1000) - firstInterestRate.validFrom.toNumber())/2)); //set back the valid from to halfway in the past, so the average right now should be (15000+30000)/2=22500 - await truffleAssert.eventEmitted(result, "InterestRateValidFromChanged"); - }) - - it('position interest rate for past position', async () => { - const staking = await MorpherStaking.deployed(); - const firstInterestRate = await staking.interestRates(0); - const result = await staking.getInterestRate(firstInterestRate.validFrom); //get the interest rate from a position opened at the creation date of the first interest rate - //it should be a weighted average of 15000 and 30000, which in this case is 50:50 with a little rounding error - if(result.toString() == '22499') { - assert.equal(result.toString(), '22499'); - } else { - assert.equal(result.toString(), '22500'); - } - }); - -}); - -contract('MorpherStaking: Interest Rate calculations', (accounts) => { - it('margin calculation works correctly - single interest rate', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let createdTimestamp = Date.now() - 2592000000; //today - 30 days - //30 days should yield interest = price * (leverage - 1) * (days + 1) * 0.000015 percent - //30000000000 * (200000000 - 100000000) * ( (2592000 / 86400) + 1) * (15000 / 100000000) / 100000000 percent = 13950000 is the interest on the exsting position - - assert('13950000', (await morpherTradeEngine.calculateMarginInterest(roundToInteger(300), 200000000, createdTimestamp)).toString(), 'Margin interest calculation doesnt work'); - }) - - it('margin calculation works correctly - multiple interest rates', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let createdTimestamp = Date.now() - 2592000000; //today - 30 days - const staking = await MorpherStaking.deployed(); - const result = await staking.addInterestRate('30000', Math.round(createdTimestamp/1000) + 24*60*60); //adding another interest rate after the positionTimestamp should average the position interest rate - let interestRate = await staking.getInterestRate(createdTimestamp); - assert.isBelow(15000, interestRate.toNumber()); - let expectedResult = roundToInteger(300) * (200000000 - 100000000) * (Math.round(2592000000/86400) + 1) * interestRate / 100000000 / 100000000; - //30 days should yield interest = price * (leverage - 1) * (days + 1) * 0.000015 percent - //30000000000 * (200000000 - 100000000) * ( (2592000 / 86400) + 1) * (15000 / 100000000) / 100000000 percent = 13950000 is the interest on the exsting position - - assert(expectedResult, (await morpherTradeEngine.calculateMarginInterest(roundToInteger(300), 200000000, createdTimestamp)).toString(), 'Margin interest calculation doesnt work'); - }) - -}); - - - -function roundToInteger(price) { - return Math.round(price * Math.pow(10, 8)); -} diff --git a/test/morpherState.test.js b/test/morpherState.test.js deleted file mode 100644 index 1959129..0000000 --- a/test/morpherState.test.js +++ /dev/null @@ -1,114 +0,0 @@ -const MorpherState = artifacts.require("MorpherState"); - -const truffleAssert = require('truffle-assertions'); - -const CRYPTO_BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9'; -const CRYPTO_ETH = '0x5376ff169a3705b2003892fe730060ee74ec83e5701da29318221aa782271779'; - -contract('MorpherState', (accounts) => { - it('test state changes and state function calls', async () => { - const deployerAddress = accounts[0]; const addressAdministrator = accounts[1]; const testAddress2 = accounts[2]; - - let morpherState = await MorpherState.deployed(); - - // Grant state access to deployer and set testAddress1 as admin. - await morpherState.grantAccess(deployerAddress, { from: deployerAddress }); - await morpherState.grantAccess(addressAdministrator, { from: deployerAddress }); - await morpherState.setGovernanceContract(deployerAddress, { from: deployerAddress }); - await morpherState.setAdministrator(addressAdministrator, { from: deployerAddress }); - - // Activate the markets and test if function calls were successful. - await morpherState.activateMarket(CRYPTO_BTC, { from: addressAdministrator }); - await morpherState.activateMarket(CRYPTO_ETH, { from: addressAdministrator }); - await morpherState.deActivateMarket(CRYPTO_ETH, { from: addressAdministrator }); - - const isETHActive = await morpherState.getMarketActive(CRYPTO_ETH, { from: testAddress2 }); - const isBTCActive = await morpherState.getMarketActive(CRYPTO_BTC, { from: testAddress2 }); - - assert.equal(isETHActive, false); - assert.equal(isBTCActive, true); - - // Test max leverage change. - await morpherState.setMaximumLeverage('500000000', { from: addressAdministrator }); - const maximumLeverage = (await morpherState.getMaximumLeverage({ from: testAddress2 })).toString(); - assert.equal(maximumLeverage, '500000000'); - - // Test correct change of administrator. - const administrator = await morpherState.getAdministrator({ from: testAddress2 }); - assert.equal(administrator, addressAdministrator); - - // Test MorpherToken minting. - await morpherState.mint(testAddress2, '2000000', { from: addressAdministrator }); - let testAddress2Balance = (await morpherState.balanceOf(testAddress2, { from: testAddress2 })).toString(); - assert.equal(testAddress2Balance, '2000000'); - - // Only state operators are allowed to call the Mint function. - await truffleAssert.reverts(morpherState.mint(testAddress2, '2000000', { from: testAddress2 }), "Only Platform"); // fails - await morpherState.mint(testAddress2, '3000000', { from: addressAdministrator }); // successful - - // Test state pause interaction. - await truffleAssert.reverts(morpherState.pauseState({ from: testAddress2 }), "Caller is not the Administrator"); // fails - await morpherState.pauseState({ from: addressAdministrator }); - - await truffleAssert.reverts(morpherState.mint(testAddress2, '1000000', { from: addressAdministrator }), "Contract paused, aborting"); // fails - await truffleAssert.reverts(morpherState.unPauseState({ from: testAddress2 }), "Caller is not the Administrator"); // fails - - await morpherState.unPauseState({ from: addressAdministrator }); - - // Burn MorpherToken and assert the balances. - await truffleAssert.reverts(morpherState.burn(testAddress2, '2000000', { from: testAddress2 }), "Only Platform"); // fails - - await morpherState.burn(testAddress2, '1000000', { from: addressAdministrator }); - - testAddress2Balance = (await morpherState.balanceOf(testAddress2, { from: testAddress2 })).toString(); - assert.equal(testAddress2Balance, '4000000'); - - // Test total cash supply functions. - await truffleAssert.reverts(morpherState.setTotalInPositions('1000000000', { from: testAddress2 }), "Caller is not the Administrator"); // fails - await morpherState.setTotalInPositions('2000000000', { from: addressAdministrator }); - - let totalInPositions = (await morpherState.totalInPositions({ from: addressAdministrator })).toString(); - assert.equal(totalInPositions, '2000000000'); - - await truffleAssert.reverts(morpherState.setTotalInPositions('3000000000', { from: testAddress2 }), "Caller is not the Administrator"); // fails - await morpherState.setTotalInPositions('4000000000', { from: addressAdministrator }); - - totalInPositions = (await morpherState.totalInPositions({ from: addressAdministrator })).toString(); - assert.equal(totalInPositions, '4000000000'); - - // Test reward addresses functions. - await truffleAssert.reverts(morpherState.setRewardAddress(testAddress2, { from: addressAdministrator })); // fails - - await morpherState.setRewardAddress(testAddress2, { from: deployerAddress }); - - const rewardsAddress = await morpherState.morpherRewards({ from: testAddress2 }); - assert.equal(rewardsAddress, testAddress2); - - await truffleAssert.reverts(morpherState.setRewardBasisPoints(1000, { from: testAddress2 })); // fails - await truffleAssert.reverts(morpherState.setRewardBasisPoints(65000, { from: deployerAddress })); // fails - await truffleAssert.reverts(morpherState.setRewardBasisPoints(14000, { from: addressAdministrator })); // fails - - // Test set morpher bridge functions. - await truffleAssert.reverts(morpherState.setMorpherBridge(deployerAddress, { from: addressAdministrator })); // fails - await morpherState.setMorpherBridge(testAddress2, { from: deployerAddress }); - - const morpherBridge = await morpherState.morpherBridge(); - assert.equal(morpherBridge, testAddress2); - - // Test sidechain merkel root test. - await truffleAssert.reverts(morpherState.setSideChainMerkleRoot(CRYPTO_BTC, { from: addressAdministrator }), "Caller is not the Bridge"); // fails - await morpherState.setSideChainMerkleRoot(CRYPTO_BTC, { from: testAddress2 }); - - const sideChainMerkleRoot = await morpherState.getSideChainMerkleRoot(); - assert.equal(sideChainMerkleRoot, CRYPTO_BTC); - - // Test set position. - await truffleAssert.reverts(morpherState.setPosition(addressAdministrator, CRYPTO_BTC, 12345, 1000, 0, 100, 1, 100000000, 90, { from: testAddress2 }), "Only Platform"); // fails - await morpherState.setPosition(addressAdministrator, CRYPTO_BTC, 12345, 2000, 0, 200, 1, 100000000, 190, { from: addressAdministrator }); - - const position = await morpherState.getPosition(addressAdministrator, CRYPTO_BTC, { from: testAddress2 }); - assert.equal(position._longShares.toString(), '2000'); - assert.equal(position._meanEntryPrice.toString(), '200'); - assert.equal(position._liquidationPrice.toString(), '190'); - }); -}); \ No newline at end of file diff --git a/test/morpherToken.test.js b/test/morpherToken.test.js deleted file mode 100644 index 50a1531..0000000 --- a/test/morpherToken.test.js +++ /dev/null @@ -1,116 +0,0 @@ -const BN = require('bn.js'); - -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherState = artifacts.require("MorpherState"); - - -const truffleAssert = require('truffle-assertions'); - -contract('MorpherToken', (accounts) => { - - const [deployerAddress, testAddress1, testAddress2] = accounts; - - it('Can transfer/approve/transferFrom Tokens', async () => { - - const morpherToken = await MorpherToken.deployed(); - const morpherState = await MorpherState.deployed(); - - // Grant access and enable transfers for test accounts. - await morpherState.grantAccess(testAddress1); - await morpherState.grantAccess(testAddress2); - await morpherState.enableTransfers(testAddress1); - await morpherState.enableTransfers(testAddress2); - - const deployerStartingTokenBalance = await morpherState.balanceOf(deployerAddress); - - const toTestAddress1 = web3.utils.toWei(new BN(1), 'ether'); //1 MPH - const toTestAddress2 = web3.utils.toWei(new BN(0.1), 'ether'); //0.1 MPH - const toTestAddress2FromTestAddress1 = web3.utils.toWei(new BN(0.01), 'ether'); //0.01 MPH - const toTestAddress2FromTestAddress1Approve = web3.utils.toWei(new BN(0.001), 'ether'); //0.001 MPH - - await morpherToken.transfer(testAddress1, toTestAddress1, { from: deployerAddress }); - await morpherToken.transfer(testAddress2, toTestAddress2, { from: deployerAddress }); - await morpherToken.transfer(testAddress2, toTestAddress2FromTestAddress1, { from: testAddress1 }); - await morpherToken.approve(testAddress2, toTestAddress2FromTestAddress1Approve, { from: testAddress1 }); - await morpherToken.transferFrom(testAddress1, testAddress2, toTestAddress2FromTestAddress1Approve, { from: testAddress2 }); - - // ASSERTS: - const deployerBalance = await morpherState.balanceOf(deployerAddress); - const testAddress1Balance = await morpherState.balanceOf(testAddress1) - const testAddress2Balance = await morpherState.balanceOf(testAddress2); - const allowance = await morpherState.getAllowance(testAddress1, testAddress2); - - assert.equal(deployerBalance.toString(), (deployerStartingTokenBalance.sub(toTestAddress1).sub(toTestAddress2).sub(toTestAddress2FromTestAddress1Approve)).toString()); // 4.249×10^26 - assert.equal(testAddress1Balance.toString(), toTestAddress1.sub(toTestAddress2FromTestAddress1).sub(toTestAddress2FromTestAddress1Approve).toString()); // 9.09x10^22 - assert.equal(testAddress2Balance.toString(), toTestAddress2.add(toTestAddress2FromTestAddress1).add(toTestAddress2FromTestAddress1Approve).toString()); // 9.1x10^21 - assert.equal(allowance.toString(), '0'); // 9x10^20 - }); - - it("Transferring too many tokens will fail", async () => { - - const morpherToken = await MorpherToken.deployed(); - const morpherState = await MorpherState.deployed(); - - // Grant access and enable transfers for test accounts. - await morpherState.grantAccess(testAddress1); - await morpherState.grantAccess(testAddress2); - await morpherState.enableTransfers(testAddress1); - await morpherState.enableTransfers(testAddress2); - - const deployerStartingTokenBalance = await morpherState.balanceOf(deployerAddress); - - const toTestAddress1 = deployerStartingTokenBalance.add(new BN(1)); //1 MPH more than I own - - truffleAssert.fails(morpherToken.transfer(testAddress1, toTestAddress1, { from: deployerAddress }), truffleAssert.ErrorType.REVERT, 'ERC20: transfer amount exceeds balance'); - - // ASSERTS: token balance must have stayed the same, no transfer happened - const deployerBalance = await morpherState.balanceOf(deployerAddress); - assert.equal(deployerBalance.toString(), deployerStartingTokenBalance.toString()); - }); - - it("transferFrom too many tokens will fail", async () => { - - const morpherToken = await MorpherToken.deployed(); - const morpherState = await MorpherState.deployed(); - - // Grant access and enable transfers for test accounts. - await morpherState.grantAccess(testAddress1); - await morpherState.grantAccess(testAddress2); - await morpherState.enableTransfers(testAddress1); - await morpherState.enableTransfers(testAddress2); - - const deployerStartingTokenBalance = await morpherState.balanceOf(deployerAddress); - - const toTestAddress1 = deployerStartingTokenBalance.add(new BN(1)); //1 MPH more than I own - morpherToken.approve(testAddress1, toTestAddress1, { from: deployerAddress }); - truffleAssert.fails(morpherToken.transferFrom(deployerAddress, testAddress1, toTestAddress1, {from: deployerAddress}), truffleAssert.ErrorType.REVERT, 'ERC20: transfer amount exceeds balance'); - - // ASSERTS: token balance must have stayed the same, no transfer happened - const deployerBalance = await morpherState.balanceOf(deployerAddress); - assert.equal(deployerBalance.toString(), deployerStartingTokenBalance.toString()); - }); - - it("transferFrom without approval will fail", async () => { - - const morpherToken = await MorpherToken.deployed(); - const morpherState = await MorpherState.deployed(); - - // Grant access and enable transfers for test accounts. - await morpherState.grantAccess(testAddress1); - await morpherState.grantAccess(testAddress2); - await morpherState.enableTransfers(testAddress1); - await morpherState.enableTransfers(testAddress2); - - const deployerStartingTokenBalance = await morpherState.balanceOf(deployerAddress); - - const approvalAmount = '0'; - const sendingAmount = '1'; - - morpherToken.approve(testAddress1, approvalAmount, { from: deployerAddress }); - truffleAssert.fails(morpherToken.transferFrom(deployerAddress, testAddress1, sendingAmount, {from: deployerAddress}), truffleAssert.ErrorType.REVERT, 'ERC20: transfer amount exceeds allowance'); - - // ASSERTS: token balance must have stayed the same, no transfer happened - const deployerBalance = await morpherState.balanceOf(deployerAddress); - assert.equal(deployerBalance.toString(), deployerStartingTokenBalance.toString()); - }); -}); \ No newline at end of file diff --git a/test/morpherTradeEngine.basicTests.test.js b/test/morpherTradeEngine.basicTests.test.js deleted file mode 100644 index 3117ada..0000000 --- a/test/morpherTradeEngine.basicTests.test.js +++ /dev/null @@ -1,691 +0,0 @@ -const truffleAssertions = require("truffle-assertions"); - -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); -const MorpherState = artifacts.require("MorpherState"); -const MorpherOracle = artifacts.require("MorpherOracle"); - -let BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9'; - -function roundToInteger(price) { - return Math.round(price * Math.pow(10, 8)); -} - -const BN = require("bn.js"); - - -const startingBalance = web3.utils.toWei(new BN(1),'ether'); - -contract('MorpherTradeEngine: Trade long/short with MPH', (accounts) => { - - // ---- TEST 1 ----- - // userBalance = 1000000000000000000000; - // position.averagePrice = 0; - // position.longShares = 0; - // position.shortShares = 0; - // - // market.price = 15000000000; - // market.spread = 0; - // - // trade.amount = 30000000000; - // trade.amountGivenInShares = false; - // trade.direction = long; //true - // - // ---- RESULT 1 ----- - // - // position.value = 30000000000; - // position.averagePrice = 15000000000; - // position.longShares = 2; - // position.shortShares = 0; - // - // userBalance = 999999999970000000000‬; - it('test case 1: open long, 300 MPH, each share 150MPH = 2 Shares', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, startingBalance); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 0, roundToInteger(300), true, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - // console.log(orderId); - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(150), roundToInteger(150), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._longShares.toNumber() * - (await morpherTradeEngine.longShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(150), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(300)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(150)); - assert.equal(position._longShares.toNumber(), 2); - assert.equal(position._shortShares.toNumber(), 0); - - assert.equal(userBalance, startingBalance.sub(new BN(roundToInteger(300))).toString()); - - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - orderId = (await morpherOracle.createOrder(BTC, 2, 0, false, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - await morpherOracle.__callback(orderId, roundToInteger(150), roundToInteger(150), 0, 0, 0, 0, { from: account0 }); - - userBalance = (await morpherState.balanceOf(account1)).toString(); - assert.equal(userBalance, startingBalance.toString()); - - }); - - // ---- TEST 2 ----- - // userBalance = 100000000000000000000; - // position.averagePrice = 0; - // position.longShares = 0; - // position.shortShares = 0; - // - // market.price = 1000000000; - // market.spread = 0; - // - // trade.amount = 2000000000; - // trade.amountGivenInShares = false; - // trade.direction = short; //false - // - // ---- RESULT 2 ----- - // - // position.value = 2000000000; - // position.averagePrice = 1000000000; - // position.longShares = 0; - // position.shortShares = 2; - // - // userBalance = 99999999998000000000; - it('test case 2: Open Short, 200 MPH, each share 100MPH = 2 shares', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 0, roundToInteger(20), false, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - //(_orderId, _price, _originalPrice, _spread, _liquidationTimestamp, _timeStamp, _callbackGasNextOrder) - await morpherOracle.__callback(orderId, roundToInteger(10), roundToInteger(10), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // shortShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(10), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(20)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(10)); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 2); - - assert.equal(userBalance, startingBalance.sub(new BN(roundToInteger(20))).toString()); - }); -}); - -contract('MorpherTradeEngine cannot close with MPH', (accounts) => { - //test closing with MPH will fail, - // 1. positions are not deleted, - // 2. Token amount stays the same, - // 3. no escrow is taken - it('test case 3: close long 2 BTC@50 with short 160MPH should fail, can only trade in shares.', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, '500000000000000000000'); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(50), 100000000, true, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 2, 0, roundToInteger(50), 0, 100000000, liquidationPrice, { from: account0 }); - - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - truffleAssertions.fails( - morpherOracle.createOrder(BTC, 0, roundToInteger(160), false, 100000000, 0, 0, 0, 0, { from: account1 }), - truffleAssertions.ErrorType.REVERT, - "MorpherTradeEngine: Can't partially close a position and open another one in opposite direction" - ); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // shortShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(80), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, 0); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(50)); - assert.equal(position._longShares.toNumber(), 2); - assert.equal(position._shortShares.toNumber(), 0); - - assert.equal(userBalance, '500000000000000000000'); - }); - - it('test case 4: close short with MPH fails (cannot close with MPH)', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(40), 100000000, false, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 0, 5, roundToInteger(40), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - await truffleAssertions.fails( - morpherOracle.createOrder(BTC, 0, roundToInteger(150), true, 100000000, 0 ,0 ,0 ,0, { from: account1 }), - truffleAssertions.ErrorType.REVERT, - "MorpherTradeEngine: Can't partially close a position and open another one in opposite direction" - ); - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // shortShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(40), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(40 * 5)); //5 shares each 40MPH - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(40)); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 5); - - assert.equal(userBalance, '500000000000000000000'); - }); -}); - -contract('MorpherTradeEngine: Double down tests long/short', (accounts) => { - - - // ---- TEST 5 ----- - // userBalance = 500000000000000000000; - // position.averagePrice = 5000000000; - // position.longShares = 5; - // position.shortShares = 0; - // - // market.price = 10000000000; - // market.spread = 0; - // - // trade.amount = 50000000000; - // trade.amountGivenInShares = false; - // trade.direction = long; // true - // - // ---- RESULT 5 ----- - // - // position.value = 100000000000; - // position.averagePrice = 10000000000; - // position.longShares = 10; - // position.shortShares = 0; - // - // userBalance = 499999999950000000000‬; - it('test case 5: double down on long position', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, startingBalance); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(50), 100000000, true, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 5, 0, roundToInteger(50), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 0, roundToInteger(500), true, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(100), roundToInteger(100), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._longShares.toNumber() * - (await morpherTradeEngine.longShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(100), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(1000)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(100)); - assert.equal(position._longShares.toNumber(), 10); - assert.equal(position._shortShares.toNumber(), 0); - - assert.equal(userBalance, startingBalance.sub(new BN(roundToInteger(500))).toString()); - - orderId = (await morpherOracle.createOrder(BTC, 10, 0, false, 100000000, 0, 0, 0, 0, {from: account1})).logs[0].args._orderId; - //sell at 0 - await morpherOracle.__callback(orderId, roundToInteger(50), roundToInteger(50), 0, 0, 0, 0, { from: account0 }); //back to 500 - - position = await morpherState.getPosition(account1, BTC); - - assert.equal(position._meanEntryPrice.toNumber(), 0); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 0); - - userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(userBalance, startingBalance.toString()); //nothing changed, all sold - }); - - // ---- TEST 6 ----- - // userBalance = 1000000000000000000000; - // position.averagePrice = 10000000000; - // position.longShares = 0; - // position.shortShares = 50; - // - // market.price = 8000000000; - // market.spread = 0; - // - // trade.amount = 400000000000; - // trade.amountGivenInShares = false; - // trade.direction = short; // false - // - // ---- RESULT 6 ----- - // - // position.value = 1000000000000‬; - // position.averagePrice = 8000000000; - // position.longShares = 0; - // position.shortShares = 125; - // - // userBalance = 999999999600000000000‬; - it('test case 6: double down on short position', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(100), 100000000, false, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 0, 50, roundToInteger(100), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 0, roundToInteger(4000), false, 100000000, 0 ,0 ,0 ,0, { from: account1, value: 301000000000000 })).logs[0].args._orderId; - - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(80), roundToInteger(80), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // shortShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(80), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(10000)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(80)); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 125); - - assert.equal(userBalance, startingBalance.sub(new BN(roundToInteger(4000))).toString()); - - orderId = (await morpherOracle.createOrder(BTC, 125, 0, true, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; //sell everything - await morpherOracle.__callback(orderId, roundToInteger(128), roundToInteger(48), 0, 0, 0, 0, { from: account0 }); //4000 / 125 shares = 32 - userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(userBalance, startingBalance.toString()); //everything is back to start - - }); -}); - -contract('MorpherTradeEngine: rollover tests', (accounts) => { - // ---- TEST 7 ----- - // userBalance = 500000000000000000000; - // position.averagePrice = 5000000000; - // position.longShares = 100; - // position.shortShares = 0; - // - // market.price = 4000000000; - // market.spread = 0; - // - // trade.amount = 600000000000; - // trade.amountGivenInShares = false; - // trade.direction = short; // false - // - // ---- RESULT 7 ----- - // - // position.value = 200000000000‬; - // position.averagePrice = 4000000000; - // position.longShares = 0; - // position.shortShares = 50; - // - // userBalance = 500000000200000000000‬; - it('test case 7: rollover long to short with payout', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, startingBalance); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(50), 100000000, true, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 100, 0, roundToInteger(50), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 100, roundToInteger(6000), false, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(40), roundToInteger(40), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // shortShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(40), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(6000)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(40)); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 150); - - assert.equal(userBalance, startingBalance.sub(new BN(roundToInteger(6000))).add(new BN(roundToInteger(40 * 100)))); - - orderId = (await morpherOracle.createOrder(BTC, 150, 0, true, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; //sell everything - await morpherOracle.__callback(orderId, roundToInteger(66.66666667), 13333333, 0, 0, 0, 0, { from: account0 }); - userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(userBalance, startingBalance.sub(new BN(50)).toString()); //can't calculate the exact results cause of floating point precision error - }); - - // ---- TEST 8 ----- - // userBalance = 400000000000000000000; - // position.averagePrice = 10000000000; - // position.longShares = 0; - // position.shortShares = 20; - // - // market.price = 8000000000; - // market.spread = 0; - // - // trade.amount = 256000000000‬; - // trade.amountGivenInShares = false; - // trade.direction = long; // true - // - // ---- RESULT 8 ----- - // - // position.value = 16000000000; - // position.averagePrice = 8000000000; - // position.longShares = 2; - // position.shortShares = 0; - // - // userBalance = 400000000224000000000; - it('test case 8: rollover short to long with payout', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, '50'); //fill up the account to be at startingBalance - assert.equal((await morpherState.balanceOf(account1)).toString(), startingBalance.toString(), "balance is the same again"); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(100), 100000000, false, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 0, 20, roundToInteger(100), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 20, roundToInteger(2560), true, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(80), roundToInteger(80), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._longShares.toNumber() * - (await morpherTradeEngine.longShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(80), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(80 * 32)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(80)); - assert.equal(position._longShares.toNumber(), 32); - assert.equal(position._shortShares.toNumber(), 0); - - assert.equal(userBalance, startingBalance.add(new BN(roundToInteger(120*20))).sub(new BN(roundToInteger(2560))).toString()); - }); -}); - - -contract('MorpherTradeEngine: partial closing tests', (accounts) => { - // ---- TEST 7 ----- - // userBalance = 500000000000000000000; - // position.averagePrice = 5000000000; - // position.longShares = 100; - // position.shortShares = 0; - // - // market.price = 4000000000; - // market.spread = 0; - // - // trade.amount = 600000000000; - // trade.amountGivenInShares = false; - // trade.direction = short; // false - // - // ---- RESULT 7 ----- - // - // position.value = 200000000000‬; - // position.averagePrice = 4000000000; - // position.longShares = 0; - // position.shortShares = 50; - // - // userBalance = 500000000200000000000‬; - it('test case 7: partial closing long with payout', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, startingBalance); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(50), 100000000, true, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 100, 0, roundToInteger(50), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 50, 0, false, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(40), roundToInteger(40), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // shortShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._longShares.toNumber() * - (await morpherTradeEngine.longShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(40), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(40*50)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(50)); - assert.equal(position._longShares.toNumber(), 50); - assert.equal(position._shortShares.toNumber(), 0); - - assert.equal(userBalance, startingBalance.add(new BN(roundToInteger(40 * 50)))); - - await morpherState.enableTransfers(account1); - await morpherToken.transfer(account0, roundToInteger(40*50), {from: account1}); //reset to startingBalance - - }); - - // ---- TEST 8 ----- - // userBalance = 400000000000000000000; - // position.averagePrice = 10000000000; - // position.longShares = 0; - // position.shortShares = 20; - // - // market.price = 8000000000; - // market.spread = 0; - // - // trade.amount = 256000000000‬; - // trade.amountGivenInShares = false; - // trade.direction = long; // true - // - // ---- RESULT 8 ----- - // - // position.value = 16000000000; - // position.averagePrice = 8000000000; - // position.longShares = 2; - // position.shortShares = 0; - // - // userBalance = 400000000224000000000; - it('test case 8: partial close short with payout', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - assert.equal((await morpherState.balanceOf(account1)).toString(), startingBalance.toString(), "balance is the same again"); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(100), 100000000, false, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 0, 0, 20, roundToInteger(100), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 10, 0, true, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(80), roundToInteger(80), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(80), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(120 * 10)); //10 shares * (averagePrice * 2 - marketPrice) = 10 * 120 - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(100)); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 10); - - assert.equal(userBalance, startingBalance.add(new BN(roundToInteger(120*10))).toString()); - }); - - - it('test case 9: partial close timestamp reset', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(100), 100000000, false, Math.round(Date.now() / 1000))).toNumber(); - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, 123, 0, 20, roundToInteger(100), 0, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 10, 0, true, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback(orderId, roundToInteger(80), roundToInteger(80), 0, 0, 234, 0, { from: account0 }); - - let lastUpdated = await morpherState.getLastUpdated(account1, BTC); - assert.equal(lastUpdated, 123); - }); -}); diff --git a/test/morpherTradeEngine.escrow.test.js b/test/morpherTradeEngine.escrow.test.js deleted file mode 100644 index ef82d6e..0000000 --- a/test/morpherTradeEngine.escrow.test.js +++ /dev/null @@ -1,98 +0,0 @@ -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); -const MorpherOracle = artifacts.require("MorpherOracle"); - -const truffleAssert = require('truffle-assertions'); -const BN = require("bn.js"); - -const { getLeverage } = require('./helpers/tradeFunctions'); - -const MARKET = 'CRYPTO_BTC'; -const gasPriceInGwei = 200; //gwei gas price for callback funding - -const historicalGasConsumptionFromOracle = []; //an array that holds the gas - -const average = arr => arr.reduce((sume, el) => sume + el, 0) / arr.length; - -const escrowAddress = "0x1111111111111111111111111111111111111111"; - -contract('MorpherOracle', (accounts) => { - - const [ - deployerAddress, - testUserAddress, - oracleCallbackAddress - ] = accounts; - - - it('Escrow 100 MPH with 100 MPH open position with 100 MPH price will have 1 share and User has 0 tokens left', async () => { - - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, 10, { from: deployerAddress }); //10 tokens to address - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, 0, 0, { from: testUserAddress }); //10 tokens open order - - const balanceAfterOpenOrder = await morpherToken.balanceOf(escrowAddress); - assert.equal(balanceAfterOpenOrder.toString(), 10, "Balance in escrow should be 10"); - const balanceAfterOpenOrderTestUser = await morpherToken.balanceOf(testUserAddress); - assert.equal(balanceAfterOpenOrderTestUser.toString(), 0, "Balance of User should be 0"); - - await morpherOracle.__callback(txReceipt.logs[0].args['_orderId'], 10, 10, 0, 0, Date.now(), 0, { from: oracleCallbackAddress }); - - - const balanceAfterProcessOrder = await morpherToken.balanceOf(escrowAddress); - assert.equal(balanceAfterProcessOrder.toString(), 0, "Balance in escrow should be 0"); - - const balanceAfterProcessOrderTestUser = await morpherToken.balanceOf(testUserAddress); - assert.equal(balanceAfterProcessOrderTestUser.toString(), 0, "Balance of User should be 0"); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._closeSharesAmount, '0'); // callback was called successfully - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - - - it('Canceling an order will pay back the escrow', async () => { - - const morpherOracle = await MorpherOracle.deployed(); - const morpherTradeEngine = await MorpherTradeEngine.deployed(); - const morpherToken = await MorpherToken.deployed(); - - // Topup test accounts with MorpherToken. - await morpherToken.transfer(testUserAddress, 10, { from: deployerAddress }); //10 tokens to address - - // Test successful state variables change and order creation. - await morpherOracle.overrideGasForCallback(0); - await morpherOracle.enableCallbackAddress(oracleCallbackAddress); - - const txReceipt = await morpherOracle.createOrder(web3.utils.sha3(MARKET), 0, 10, true, getLeverage(1), 0, 0, 0, 0, { from: testUserAddress }); //10 tokens open order - - const balanceAfterOpenOrder = await morpherToken.balanceOf(escrowAddress); - assert.equal(balanceAfterOpenOrder.toString(), 10, "Balance in escrow should be 10"); - const balanceAfterOpenOrderTestUser = await morpherToken.balanceOf(testUserAddress); - assert.equal(balanceAfterOpenOrderTestUser.toString(), 0, "Balance of User should be 0"); - - await morpherOracle.initiateCancelOrder(txReceipt.logs[0].args['_orderId'], { from: testUserAddress }); - await morpherOracle.cancelOrder(txReceipt.logs[0].args['_orderId'], { from: oracleCallbackAddress }); - - - const balanceAfterProcessOrder = await morpherToken.balanceOf(escrowAddress); - assert(balanceAfterProcessOrder.toString(), 10, "Balance in escrow should be 0"); - - const balanceAfterProcessOrderTestUser = await morpherToken.balanceOf(testUserAddress); - assert(balanceAfterProcessOrderTestUser.toString(), 10, "Balance of User should be 10"); - - const order = await morpherTradeEngine.getOrder(txReceipt.logs[0].args['_orderId']); - assert.equal(order._closeSharesAmount, '0'); // callback was called successfully - assert.equal(order._openMPHTokenAmount, '0'); // callback was called successfully - }); - -}); \ No newline at end of file diff --git a/test/morpherTradeEngine.leverage.spread.liquidation.test.js b/test/morpherTradeEngine.leverage.spread.liquidation.test.js deleted file mode 100644 index e97d315..0000000 --- a/test/morpherTradeEngine.leverage.spread.liquidation.test.js +++ /dev/null @@ -1,306 +0,0 @@ -const MorpherToken = artifacts.require("MorpherToken"); -const MorpherTradeEngine = artifacts.require("MorpherTradeEngine"); -const MorpherState = artifacts.require("MorpherState"); -const MorpherOracle = artifacts.require("MorpherOracle"); - -let BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9'; - -function roundToInteger(price) { - return Math.round(price * Math.pow(10, 8)); -} - -contract('MorpherTradeEngine', (accounts) => { - - it('margin calculation works correctly', async () => { - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let createdTimestamp = Date.now() - 2592000000; //today - 30 days - //30 days should yield interest = price * (leverage - 1) * (days + 1) * 0.000015 percent - //30000000000 * (200000000 - 100000000) * ( (2592000 / 86400) + 1) * (15000 / 100000000) / 100000000 percent = 13950000 is the interest on the exsting position - - assert.equal('135000000', (await morpherTradeEngine.calculateMarginInterest(roundToInteger(300), 200000000, createdTimestamp)).toString(), 'Margin interest calculation doesnt work'); - }) - - // ---- TEST 1 ----- - // userBalance = 1000000000000000000000; - // position.averagePrice = 30000000000; - // position.longShares = 10; - // position.shortShares = 0; - // position.averageSpread = 2000000; - // position.averageLeverage = 200000000; - - // market.price = 20000000000; - // market.spread = 1000000; - // - // trade.amount = 220000000000; - // trade.amountGivenInShares = false; - // trade.orderLeverage = 500000000; - // trade.direction = short; //false - // - // ---- RESULT 11 ----- - // - // position.value = 99975000000; - // position.averagePrice = 20000000000; - // position.longShares = 0; - // position.shortShares = 5; - // position.averageSpread = 1000000; - // position.averageLeverage = 500000000; - // - // userBalance = 999999999999955000000; - it('test case 1: rollover 10 longshares to 5 short shares with spread 5x', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, '1000000000000000000000'); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(300), 200000000, true, Math.round(Date.now() / 1000))).toNumber(); - - let createdTimestamp = Date.now() - 2592000000 + 100000; //today - 30 days + 100 seconds buffer for rollover from 1 day to the other - //30 days should yield interest = price * (leverage - 1) * (days + 1) * 0.000015 percent - //30000000000 * (200000000 - 100000000) * ( (2592000 / 86400) + 1) * (15000 / 100000000) / 100000000 percent = 13950000 is the interest on the exsting position - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, createdTimestamp, 10, 0, roundToInteger(300), 2000000, 200000000, liquidationPrice); - - // (address _address, bytes32 _marketId) - let position1 = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValueOldPosition = position1._longShares.toNumber() * - (await morpherTradeEngine.longShareValue(position1._meanEntryPrice.toString(), - position1._meanEntryLeverage.toString(), createdTimestamp, - roundToInteger(200), 1000000, 0, true)).toNumber(); - - assert.equal(positionValueOldPosition, 98630000000); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 10, roundToInteger(1000), false, 500000000, 0, 0, 0, 0, { from: account1 })).logs[0].args._orderId; - - - //(_orderId, _price, _unadjustedPrice, _spread, _liquidationTimestamp, _timeStamp, gasForNextCallback) - const oracleTimestampForPosition = Date.now() - 60000; //1 minute delay - await morpherOracle.__callback(orderId, roundToInteger(200), roundToInteger(200), 1000000, 0, oracleTimestampForPosition, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toString(), - position._meanEntryLeverage.toString(), oracleTimestampForPosition, - roundToInteger(200), 1000000, 0, true)).toNumber(); - - assert.equal(positionValue, 79932000000); - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(200)); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 4); - assert.equal(position._meanEntrySpread.toNumber(), 1000000); - assert.equal(position._meanEntryLeverage.toNumber(), 500000000); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - assert.equal(userBalance, '1000000000018610000000'); - }); -}); - -contract('MorpherTradeEngine', (accounts) => { - // ---- TEST 2 ----- - // userBalance = 1000000000000000000000; - // position.averagePrice = 30000000000; - // position.longShares = 0; - // position.shortShares = 100; - // position.averageSpread = 2000000; - // position.averageLeverage = 300000000; - - // market.price = 20000000000; 20,005,000,000‬ - // market.spread = 1000000; - // - // trade.amount = 10000000000000; - // trade.amountGivenInShares = false; - // trade.orderLeverage = 500000000; - // trade.direction = true; //false - // - // ---- RESULT 12 ----- - // - // position.value = 3979005000000; - // position.averagePrice = 20000000000; - // position.longShares = 199; - // position.shortShares = 0; - // position.averageSpread = 1000000; - // position.averageLeverage = 500000000; - // - // userBalance = 1000000002018705000000‬; - it('test case 2: rollover 100 short shares to long shares, payout rest', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, '1000000000000000000000'); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(300), 300000000, false, Math.round(Date.now() / 1000))).toNumber(); - - - let createdTimestamp = Date.now() - 2592000000 + 100000; //today - 30 days + 100 seconds buffer for rollover from 1 day to the other - - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, createdTimestamp, 0, 100, roundToInteger(300), 2000000, 300000000, liquidationPrice); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 100, roundToInteger(100000), true, 500000000, 0, 0, 0, 0, { from: account1, value: 301000000000000 })).logs[0].args._orderId; - - // console.log(orderId); - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - const oracleTimestampForPosition = Date.now() - 60000; //1 minute delay - await morpherOracle.__callback(orderId, roundToInteger(200), roundToInteger(200), 1000000, 0, oracleTimestampForPosition, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._longShares.toNumber() * - (await morpherTradeEngine.longShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), oracleTimestampForPosition, - roundToInteger(200), 1000000, 500000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, 9971517000000); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(200)); - assert.equal(position._longShares.toNumber(), 499); - assert.equal(position._shortShares.toNumber(), 0); - assert.equal(position._meanEntrySpread.toNumber(), 1000000); - assert.equal(position._meanEntryLeverage.toNumber(), 500000000); - - assert.equal(userBalance, '999999995990205000000'); - }); -}); - - -contract('MorpherTradeEngine', (accounts) => { - // ---- TEST 9 ----- - // userBalance = 80000000000000000000; - // position.averagePrice = 9000000000; - // position.longShares = 0; - // position.shortShares = 1; - // position.averageSpread = 500000; - // - // market.price = 20000000000; - // market.spread = 200000; - // - // trade.amount = 1; - // trade.amountGivenInShares = true; - // trade.direction = long; //true - // - // ---- RESULT 9 ----- - // - // position.value = 0; - // position.averagePrice = 0; - // position.longShares = 0; - // position.shortShares = 0; - // position.averageSpread = 0; - // - // userBalance = 80000000000000000000; - it('test case 9: liquidate a short position, price + spread calculation', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, '80000000000000000000'); - - //(_newMeanEntryPrice, _newMeanEntryLeverage, _long) - let liquidationPrice = (await morpherTradeEngine.getLiquidationPrice(roundToInteger(90), 100000000, false, Math.round(Date.now() / 1000))).toNumber(); - - let createdTimestamp = Math.round(Date.now() / 1000) - 2592000 + 100; //today - 30 days + 100 seconds buffer for rollover from 1 day to the other - //(_address, _marketId, _timeStamp, _longShares, _shortShares, _meanEntryPrice, _meanEntrySpread, _meanEntryLeverage, _liquidationPrice) - await morpherState.setPosition(account1, BTC, createdTimestamp, 0, 1, roundToInteger(90), 500000, 100000000, liquidationPrice, { from: account0 }); - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 1, 0, true, 100000000, 0, 0, 0, 0, { from: account1, value: 301000000000000 })).logs[0].args._orderId; - - // console.log(orderId); - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - const oracleTimestampForPosition = Math.round(Date.now() / 1000) - 60; //1 minute delay - await morpherOracle.__callback(orderId, roundToInteger(200), 0, 200000, 0, oracleTimestampForPosition, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._longShares.toNumber() * - (await morpherTradeEngine.longShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), oracleTimestampForPosition * 1000, - roundToInteger(200), 200000, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, 0); - - assert.equal(position._meanEntryPrice.toNumber(), 0); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 0); - assert.equal(position._meanEntrySpread.toNumber(), 0); - - assert.equal(userBalance, '80000000000000000000'); - }); -}); - -contract('MorpherTradeEngine', (accounts) => { - - it('position opening is possible with leverage 10 and old deployed timestamp', async () => { - let account0 = accounts[0]; let account1 = accounts[1]; - - let morpherTradeEngine = await MorpherTradeEngine.deployed(); - let morpherToken = await MorpherToken.deployed(); - let morpherState = await MorpherState.deployed(); - let morpherOracle = await MorpherOracle.deployed(); - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, web3.utils.toWei('1','ether')); - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 0, web3.utils.toWei('1','ether'), true, 1000000000, 0, 0, 0, 0, { from: account1 })).logs[0].args._orderId; - - // console.log(orderId); - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - const oracleTimestampForPosition = Math.round(Date.now() / 1000) - 60; //1 minute delay - await morpherOracle.__callback(orderId, roundToInteger(200), 0, 0, 0, oracleTimestampForPosition, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = - (await morpherTradeEngine.longShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), oracleTimestampForPosition * 1000, - roundToInteger(200), 200000, 100000000, true)).toNumber(); - - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, 19971000000); - - assert.equal(position._meanEntryPrice.toNumber(), 20000000000); - assert.equal(position._longShares.toNumber(), 50000000); - assert.equal(position._shortShares.toNumber(), 0); - assert.equal(position._meanEntrySpread.toNumber(), 0); - - assert.equal(userBalance, '0'); - }); -}); diff --git a/test/morpherUserBlocking.test.js b/test/morpherUserBlocking.test.js deleted file mode 100644 index 38a68de..0000000 --- a/test/morpherUserBlocking.test.js +++ /dev/null @@ -1,233 +0,0 @@ -const truffleAssertions = require('truffle-assertions') - -const MorpherToken = artifacts.require('MorpherToken') -const MorpherTradeEngine = artifacts.require('MorpherTradeEngine') -const MorpherState = artifacts.require('MorpherState') -const MorpherOracle = artifacts.require('MorpherOracle') -const MorpherStaking = artifacts.require('MorpherStaking') -const MorpherUserBlocking = artifacts.require('MorpherUserBlocking') - -let BTC = '0x0bc89e95f9fdaab7e8a11719155f2fd638cb0f665623f3d12aab71d1a125daf9' - -function roundToInteger(price) { - return Math.round(price * Math.pow(10, 8)) -} - -const BN = require('bn.js') - -const startingBalance = web3.utils.toWei(new BN(1), 'ether') - -contract('MorpherTradeEngine: Trade long/short with MPH', (accounts) => { - it('test case 1: unblocked users can trade normally', async () => { - let account0 = accounts[0] - let account1 = accounts[1] - - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherToken = await MorpherToken.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - - // Set balance of testing account. - //(address to, uint256 tokens) - await morpherToken.transfer(account1, startingBalance) - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = ( - await morpherOracle.createOrder( - BTC, - 0, - roundToInteger(300), - true, - 100000000, - 0, - 0, - 0, - 0, - { from: account1 }, - ) - ).logs[0].args._orderId - - // console.log(orderId); - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - await morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: account0 }, - ) - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC) - - // longShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = - position._longShares.toNumber() * - ( - await morpherTradeEngine.longShareValue( - position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), - 0, - roundToInteger(150), - 0, - 100000000, - true, - ) - ).toNumber() - - let userBalance = (await morpherState.balanceOf(account1)).toString() - - assert.equal(positionValue, roundToInteger(300)) - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(150)) - assert.equal(position._longShares.toNumber(), 2) - assert.equal(position._shortShares.toNumber(), 0) - - assert.equal( - userBalance, - startingBalance.sub(new BN(roundToInteger(300))).toString(), - ) - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - orderId = ( - await morpherOracle.createOrder(BTC, 2, 0, false, 100000000, 0, 0, 0, 0, { - from: account1, - }) - ).logs[0].args._orderId - - await morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: account0 }, - ) - - userBalance = (await morpherState.balanceOf(account1)).toString() - assert.equal(userBalance, startingBalance.toString()) - }) - - it('test case 2: Block User from Opening Positions', async () => { - let account0 = accounts[0] - let account1 = accounts[1] - - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherToken = await MorpherToken.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - let morpherUserBlocking = await MorpherUserBlocking.deployed() - truffleAssertions.eventEmitted( - await morpherUserBlocking.setUserBlocked(account1, true), - 'ChangeUserBlocked', - ) - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 0, roundToInteger(20), false, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - // console.log(orderId); - //(_orderId, _price, _spread, _liquidationTimestamp, _timeStamp) - truffleAssertions.fails( - morpherOracle.__callback( - orderId, - roundToInteger(150), - roundToInteger(150), - 0, - 0, - 0, - 0, - { from: account0 }, - ), - truffleAssertions.ErrorType.REVERT, - 'MorpherTradeEngine: User is blocked from Trading', - ) - }) - - it('test case 3: Unblocking works for trading', async () => { - let account0 = accounts[0] - let account1 = accounts[1] - - let morpherTradeEngine = await MorpherTradeEngine.deployed() - let morpherToken = await MorpherToken.deployed() - let morpherState = await MorpherState.deployed() - let morpherOracle = await MorpherOracle.deployed() - let morpherUserBlocking = await MorpherUserBlocking.deployed() - truffleAssertions.eventEmitted( - await morpherUserBlocking.setUserBlocked(account1, false), - 'ChangeUserBlocked', - ) - - //(_marketId, _closeSharesAmount, _openMPHAmount, _tradeDirection, _orderLeverage, _onlyIfPriceAbove, _onlyIfPriceBelow, _goodUntil, _goodFrom) - let orderId = (await morpherOracle.createOrder(BTC, 0, roundToInteger(20), false, 100000000, 0 ,0 ,0 ,0, { from: account1 })).logs[0].args._orderId; - - await morpherOracle.__callback(orderId, roundToInteger(10), roundToInteger(10), 0, 0, 0, 0, { from: account0 }); - - // (address _address, bytes32 _marketId) - let position = await morpherState.getPosition(account1, BTC); - - // shortShareValue( _positionAveragePrice, _positionAverageLeverage, _liquidationPrice, _marketPrice, _marketSpread, _orderLeverage, _sell) - let positionValue = position._shortShares.toNumber() * - (await morpherTradeEngine.shortShareValue(position._meanEntryPrice.toNumber(), - position._meanEntryLeverage.toNumber(), 0, - roundToInteger(10), 0, 100000000, true)).toNumber(); - - let userBalance = (await morpherState.balanceOf(account1)).toString(); - - assert.equal(positionValue, roundToInteger(20)); - - assert.equal(position._meanEntryPrice.toNumber(), roundToInteger(10)); - assert.equal(position._longShares.toNumber(), 0); - assert.equal(position._shortShares.toNumber(), 2); - - assert.equal(userBalance, startingBalance.sub(new BN(roundToInteger(40))).toString()); - }) -}) - - - -contract('MorpherStaking: increase/decrease staked amount', (accounts) => { - const [deployer, account1] = accounts; - - it('unblocked user: staking is possible', async () => { - - - const token = await MorpherToken.deployed(); - const staking = await MorpherStaking.deployed(); - await staking.setLockupPeriodRate(0); - await token.transfer(account1, web3.utils.toWei('10000000', 'ether'), { from: deployer }); //fill up some tokens - let result = await staking.stake(web3.utils.toWei('1000000', 'ether'), { from: account1 }); - await truffleAssertions.eventEmitted(result, 'Staked'); - - }); - it('blocked user: staking/unstaking is impossible', async () => { - let morpherUserBlocking = await MorpherUserBlocking.deployed() - truffleAssertions.eventEmitted( - await morpherUserBlocking.setUserBlocked(account1, true), - 'ChangeUserBlocked', - ) - const staking = await MorpherStaking.deployed(); - - await truffleAssertions.fails(staking.stake(web3.utils.toWei('100000', 'ether'), { from: account1 }), truffleAssertions.ErrorType.REVERT, "MorpherStaking: User is blocked"); - - await truffleAssertions.fails(staking.unstake(1, {from: account1}), truffleAssertions.ErrorType.REVERT, "MorpherStaking: User is blocked"); - }); - - it('unblocked user: unstaking is impossible', async () => { - let morpherUserBlocking = await MorpherUserBlocking.deployed() - truffleAssertions.eventEmitted( - await morpherUserBlocking.setUserBlocked(account1, false), - 'ChangeUserBlocked', - ) - const staking = await MorpherStaking.deployed(); - - result = await staking.unstake(1, { from: account1 }); - await truffleAssertions.eventEmitted(result, 'Unstaked', (ev) => { - return ev.userAddress === account1 && ev.amount.toString() === '100000000' && ev.poolShares.toString() === '1'; - }); - }); -}); \ No newline at end of file diff --git a/truffle-config.js b/truffle-config.js deleted file mode 100644 index 88a8207..0000000 --- a/truffle-config.js +++ /dev/null @@ -1,73 +0,0 @@ -require("dotenv").config(); -let HDWalletProvider = require("@truffle/hdwallet-provider"); - -module.exports = { - networks: { - local: { - host: "127.0.0.1", - port: 8545, - network_id: "*", - }, - morpher: { - provider: () => - new HDWalletProvider( - process.env.MORPHER_DEPLOYER_PK, - "https://sidechain.morpher.com" - ), - network_id: "21", - timeoutBlocks: 200, - }, - morphertest: { - provider: () => - new HDWalletProvider( - [process.env.MORPHER_DEPLOYER_PK, process.env.MORPHER_ADMINISTRATOR_KEY], - "wss://sidechain-test-ws.morpher.com:8546" - ), - network_id: "21", - chainId: 21, - //timeoutBlocks: 200, - }, - ropsten: { - provider: () => - new HDWalletProvider( - process.env.MORPHER_DEPLOYER_KEY, - "wss://ropsten.infura.io/ws/v3/" + process.env.INFURA_PROJECT_ID - ), - network_id: '3', - gasPrice: 15000000000, - }, - mainnet: { - provider: () => - new HDWalletProvider( - process.env.MORPHER_DEPLOYER_PK, - "wss://mainnet.infura.io/ws/v3/" + process.env.INFURA_PROJECT_ID - ), - network_id: '1', - gasPrice: 150000000000, - }, - kovan: { - provider: () => - new HDWalletProvider( - process.env.MORPHER_DEPLOYER_PK, - "wss://kovan.infura.io/ws/v3/" + process.env.INFURA_PROJECT_ID - ), - network_id: '*', - gasPrice: 10000000000, - }, - }, - compilers: { - solc: { - version: "0.5.16", - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - }, - mocha: { - enableTimeouts: false, - before_timeout: 120000 // Here is 2min but can be whatever timeout is suitable for you. - } -};