From d30766330f0adf5d9ea6a9c19692496d9957195d Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 14 May 2021 12:25:50 +0530 Subject: [PATCH 01/33] Added deployed address in Readme --- README.md | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3b30fdc8..1b4dcba0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Build Status](https://travis-ci.org/plotx/smart-contracts-L2.svg?branch=03-2021_audit)](https://travis-ci.org/plotx/smart-contracts-L2) +[![Build Status](https://travis-ci.org/plotx/smart-contracts-L2.svg?branch=merge-finalDeployment)](https://travis-ci.org/plotx/smart-contracts-L2) -[![Coverage Status](https://coveralls.io/repos/github/plotx/smart-contracts-L2/badge.svg?branch=03-2021_audit)](https://coveralls.io/github/plotx/smart-contracts-L2) +[![Coverage Status](https://coveralls.io/repos/github/plotx/smart-contracts-L2/badge.svg?branch=merge-finalDeployment)](https://coveralls.io/github/plotx/smart-contracts-L2)

PlotX SMART CONTRACTS

Smart contracts for PlotX - Curated prediction markets for crypto traders . https://plotx.io/.

@@ -39,20 +39,10 @@ npm run coverage ``` ### Contract Addresses -- PLOT Token: 0x72F020f8f3E8fd9382705723Cd26380f8D0c66Bb -- Master: 0x03c41c5Aff6D541EF7D4c51c8B2E32a5d4427275 -- MarketRegistry: 0xE210330d6768030e816d223836335079C7A0c851 -- AllMarkets: 0xb9448E3a0d95cFF578F9508084A0ed92D724c29A -- MarketCreationRewards: 0x22376814188De44e8B6f482daa98B050ac190B46 -- MarketUtility: 0x2330058D49fA61D5C5405fA8B17fcD823c59F7Bb -- Governance: 0x16763F192d529B420F33B699dC72F39f16620717 -- ProposalCategory: 0x2D90743ef134b35cE415855F1c38ca47d65b314C -- MemberRoles: 0xda06bcd22a68Fa40B63867277aA0eB34702fd80D -- TokenController: 0x12d7053Efc680Ba6671F8Cb96d1421D906ce3dE2 -- bPLOT Token: 0x82cB6Cd09Bf80fB4014935871fF553b027255D36 -- Vesting: 0x5172C83B5316b86861802d29746d8435f4cB67e6 - -Market Implementation Addresses -- ETH/USD: 0x25cf9d73b711bff4d3445a0f7f2e63ade5133e67 -- BTC/USD: 0x5d24cf40ead0601893c212ff3af4895dc42a760b +- PLOT Token: 0xe82808eaA78339b06a691fd92E1Be79671cAd8D3 +- Master: 0x5aac88D5A607b69b7a90D7e0519A6fb5265Aa60a +- AllPlotMarkets: 0xcc424cfff84B0FcdDf6F3A7163cc6fFB415c0844 +- CyclicMarkets: 0x7EA095c78eE700D36E203A04E40eb26752f6F7A8 +- bPLOT Token: 0x4A7e335B8653F2DF6216e7d266cC643D09d83bdD + From 2817debb6b3ab7bb67a229ccb6f3bb5a75625605 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Tue, 25 May 2021 00:20:53 +0530 Subject: [PATCH 02/33] Added function to predict on behalf of --- contracts/AllPlotMarkets_2.sol | 66 ++++++++++++++++++++++++++++ contracts/interfaces/IAllMarkets.sol | 2 + 2 files changed, 68 insertions(+) create mode 100644 contracts/AllPlotMarkets_2.sol diff --git a/contracts/AllPlotMarkets_2.sol b/contracts/AllPlotMarkets_2.sol new file mode 100644 index 00000000..8f8a30ea --- /dev/null +++ b/contracts/AllPlotMarkets_2.sol @@ -0,0 +1,66 @@ +/* Copyright (C) 2021 PlotX.io + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ */ + +pragma solidity 0.5.7; + +import "./AllPlotMarkets.sol"; + +contract AllPlotMarkets_2 is AllPlotMarkets { + + mapping(address => bool) public authToProxyPrediction; + + /** + * @dev Function to deposit prediction token for participation in markets + * @param _amount Amount of prediction token to deposit + */ + function _depositFor(uint _amount, address _msgSenderAddress, address _depositForAddress) internal { + _transferTokenFrom(predictionToken, _msgSenderAddress, address(this), _amount); + UserData storage _userData = userData[_depositForAddress]; + _userData.unusedBalance = _userData.unusedBalance.add(_amount); + emit Deposited(_depositForAddress, _amount, now); + } + + /** + * @dev Deposit and Place prediction on the available options of the market with both PLOT and BPLOT. + * @param _marketId Index of the market + * @param _tokenDeposit prediction token amount to deposit + * @param _asset The asset used by user during prediction whether it is prediction token address or in Bonus token. + * @param _prediction The option on which user placed prediction. + * @param _plotPredictionAmount The PLOT amount staked by user at the time of prediction. + * @param _bPLOTPredictionAmount The BPLOT amount staked by user at the time of prediction. + * _tokenDeposit should be passed with 18 decimals + * _plotPredictionAmount and _bPLOTPredictionAmount should be passed with 8 decimals, reduced it to 8 decimals to reduce the storage space of prediction data + */ + function depositAndPredictFor(address _predictFor, uint _tokenDeposit, uint _marketId, address _asset, uint256 _prediction, uint64 _plotPredictionAmount, uint64 _bPLOTPredictionAmount) external { + address payable _msgSenderAddress = _msgSender(); + require(authToProxyPrediction[_msgSenderAddress]); + uint64 _predictionStake = _plotPredictionAmount.add(_bPLOTPredictionAmount); + //Can deposit only if prediction stake amount contains plot + if(_plotPredictionAmount > 0 && _tokenDeposit > 0) { + _depositFor(_tokenDeposit, _msgSenderAddress, _predictFor); + } + if(_bPLOTPredictionAmount > 0) { + UserData storage _userData = userData[_predictFor]; + require(!_userData.userMarketData[_marketId].predictedWithBlot); + _userData.userMarketData[_marketId].predictedWithBlot = true; + uint256 _amount = (10**predictionDecimalMultiplier).mul(_bPLOTPredictionAmount); + bPLOTInstance.convertToPLOT(_predictFor, address(this), _amount); + _userData.unusedBalance = _userData.unusedBalance.add(_amount); + } + require(_asset == plotToken); + _placePrediction(_marketId, _predictFor, _asset, _predictionStake, _prediction); + } + +} diff --git a/contracts/interfaces/IAllMarkets.sol b/contracts/interfaces/IAllMarkets.sol index f5cf2061..315136f2 100644 --- a/contracts/interfaces/IAllMarkets.sol +++ b/contracts/interfaces/IAllMarkets.sol @@ -46,4 +46,6 @@ contract IAllMarkets { function getTotalOptions(uint256 _marketId) external view returns(uint); function getTotalStakedByUser(address _user) external view returns(uint); + + function depositAndPredictFor(address _predictFor, uint _tokenDeposit, uint _marketId, address _asset, uint256 _prediction, uint64 _plotPredictionAmount, uint64 _bPLOTPredictionAmount) external; } From 66b309954731eb595c787e1a3d561f9026ec53af Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Tue, 25 May 2021 00:21:33 +0530 Subject: [PATCH 03/33] Added an external contract to swap any token and predict with plot --- contracts/ProxyPlotPrediction.sol | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 contracts/ProxyPlotPrediction.sol diff --git a/contracts/ProxyPlotPrediction.sol b/contracts/ProxyPlotPrediction.sol new file mode 100644 index 00000000..9a11fe50 --- /dev/null +++ b/contracts/ProxyPlotPrediction.sol @@ -0,0 +1,34 @@ +/* Copyright (C) 2021 PlotX.io + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ */ + +pragma solidity 0.5.7; + +import "./external/openzeppelin-solidity/math/SafeMath.sol"; +import "./external/NativeMetaTransaction.sol"; +import "./interfaces/IAllMarkets.sol"; +import "./interfaces/IToken.sol"; + +contract ProxyPlotPrediction is NativeMetaTransaction { + + using SafeMath for uint; + + IAllMarkets allPlotMarkets; + IToken plotToken; + function swapAndPlacePrediction(address[] calldata _path, uint _inputAmount, address _predictFor, uint _marketId, uint _prediction, uint64 _bPLOTPredictionAmount) external { + address payable _msgSenderAddress = _msgSender(); + uint _tokenDeposit = _inputAmount; // Process input amount + allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(plotToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); + } +} \ No newline at end of file From 548e76cc245328abf728d68d3b00939d962366c2 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Tue, 25 May 2021 18:23:19 +0530 Subject: [PATCH 04/33] Updated swap functionality --- contracts/ProxyPlotPrediction.sol | 44 +++++++++++-- contracts/interfaces/ISwapRouter.sol | 95 ++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 contracts/interfaces/ISwapRouter.sol diff --git a/contracts/ProxyPlotPrediction.sol b/contracts/ProxyPlotPrediction.sol index 9a11fe50..3442dc97 100644 --- a/contracts/ProxyPlotPrediction.sol +++ b/contracts/ProxyPlotPrediction.sol @@ -19,6 +19,7 @@ import "./external/openzeppelin-solidity/math/SafeMath.sol"; import "./external/NativeMetaTransaction.sol"; import "./interfaces/IAllMarkets.sol"; import "./interfaces/IToken.sol"; +import "./interfaces/ISwapRouter.sol"; contract ProxyPlotPrediction is NativeMetaTransaction { @@ -26,9 +27,44 @@ contract ProxyPlotPrediction is NativeMetaTransaction { IAllMarkets allPlotMarkets; IToken plotToken; - function swapAndPlacePrediction(address[] calldata _path, uint _inputAmount, address _predictFor, uint _marketId, uint _prediction, uint64 _bPLOTPredictionAmount) external { - address payable _msgSenderAddress = _msgSender(); - uint _tokenDeposit = _inputAmount; // Process input amount + IUniswapV2Router router; + + address public maticAddress; + + function initiate(address _allPlotMarkets, address _plotToken, address _router, address _maticAddress) external { + allPlotMarkets = IAllMarkets(_allPlotMarkets); + plotToken = IToken(_plotToken); + router = IUniswapV2Router(_router); + maticAddress = _maticAddress; + } + + function swapAndPlacePrediction(address[] calldata _path, uint _inputAmount, address _predictFor, uint _marketId, uint _prediction, uint64 _bPLOTPredictionAmount) external payable { + uint deadline = now*2; + uint amountOutMin = 1; + require(_path[_path.length-1] == address(plotToken)); + uint[] memory _output; + if(_path[0] == maticAddress && msg.value >0) { + _output = router.swapExactETHForTokens( + amountOutMin, + _path, + address(this), + deadline + ); + } else { + require(msg.value == 0); + address payable _msgSenderAddress = _msgSender(); + IToken(_path[0]).transferFrom(_msgSenderAddress, address(this), _inputAmount); + IToken(_path[0]).approve(address(router), _inputAmount); + _output = router.swapExactTokensForTokens( + _inputAmount, + amountOutMin, + _path, + address(this), + deadline + ); + } + uint _tokenDeposit = _output[1]; + plotToken.approve(address(allPlotMarkets), _inputAmount); allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(plotToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); } -} \ No newline at end of file +} diff --git a/contracts/interfaces/ISwapRouter.sol b/contracts/interfaces/ISwapRouter.sol new file mode 100644 index 00000000..531a5ec3 --- /dev/null +++ b/contracts/interfaces/ISwapRouter.sol @@ -0,0 +1,95 @@ +pragma solidity 0.5.7; + +interface IUniswapV2Router { + function factory() external pure returns (address); + function WETH() external pure returns (address); + + function addLiquidity( + address tokenA, + address tokenB, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint amountA, uint amountB, uint liquidity); + function addLiquidityETH( + address token, + uint amountTokenDesired, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external payable returns (uint amountToken, uint amountETH, uint liquidity); + function removeLiquidity( + address tokenA, + address tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns (uint amountA, uint amountB); + function removeLiquidityETH( + address token, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external returns (uint amountToken, uint amountETH); + function removeLiquidityWithPermit( + address tokenA, + address tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountA, uint amountB); + function removeLiquidityETHWithPermit( + address token, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline, + bool approveMax, uint8 v, bytes32 r, bytes32 s + ) external returns (uint amountToken, uint amountETH); + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + address[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); + function swapTokensForExactTokens( + uint amountOut, + uint amountInMax, + address[] calldata path, + address to, + uint deadline + ) external returns (uint[] memory amounts); + function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) + external + payable + returns (uint[] memory amounts); + function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) + external + returns (uint[] memory amounts); + function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) + external + returns (uint[] memory amounts); + function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) + external + payable + returns (uint[] memory amounts); + + function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); + function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); + function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); + function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); + function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); +} \ No newline at end of file From 706d66e9946582e05ea4b0322cd2e06612117fb3 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Tue, 25 May 2021 21:17:54 +0530 Subject: [PATCH 05/33] Fixed swap eth to token --- contracts/ProxyPlotPrediction.sol | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/contracts/ProxyPlotPrediction.sol b/contracts/ProxyPlotPrediction.sol index 3442dc97..ff0481f2 100644 --- a/contracts/ProxyPlotPrediction.sol +++ b/contracts/ProxyPlotPrediction.sol @@ -42,9 +42,12 @@ contract ProxyPlotPrediction is NativeMetaTransaction { uint deadline = now*2; uint amountOutMin = 1; require(_path[_path.length-1] == address(plotToken)); + bool _isNativeToken = (_path[0] == maticAddress && msg.value >0); + // uint _initialFromTokenBalance = getTokenBalance(_path[0], _isNativeToken); + // uint _initialToTokenBalance = getTokenBalance(_path[_path.length-1], false); uint[] memory _output; - if(_path[0] == maticAddress && msg.value >0) { - _output = router.swapExactETHForTokens( + if(_isNativeToken) { + _output = router.swapExactETHForTokens.value(msg.value)( amountOutMin, _path, address(this), @@ -66,5 +69,18 @@ contract ProxyPlotPrediction is NativeMetaTransaction { uint _tokenDeposit = _output[1]; plotToken.approve(address(allPlotMarkets), _inputAmount); allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(plotToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); + // require(_initialFromTokenBalance == getTokenBalance(_path[0], _isNativeToken)); + // require(_initialToTokenBalance == getTokenBalance(_path[_path.length-1], false)); + } + + function getTokenBalance(address _token, bool _isNativeCurrency) public returns(uint) { + if(_isNativeCurrency) { + return ((address(this)).balance); + } + return IToken(_token).balanceOf(address(this)); + } + + function transferLeftOverTokens(address _token) external { + IToken(_token).transfer(msg.sender, getTokenBalance(_token, false)); } } From 62a58028c341649a90a7985418a9a83ac64af65d Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Wed, 26 May 2021 11:04:04 +0530 Subject: [PATCH 06/33] Added auth checks --- contracts/AllPlotMarkets_2.sol | 4 +++ ...diction.sol => SwapAndPredictWithPlot.sol} | 33 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) rename contracts/{ProxyPlotPrediction.sol => SwapAndPredictWithPlot.sol} (66%) diff --git a/contracts/AllPlotMarkets_2.sol b/contracts/AllPlotMarkets_2.sol index 8f8a30ea..21c4b355 100644 --- a/contracts/AllPlotMarkets_2.sol +++ b/contracts/AllPlotMarkets_2.sol @@ -21,6 +21,10 @@ contract AllPlotMarkets_2 is AllPlotMarkets { mapping(address => bool) public authToProxyPrediction; + function addAuthorizedProxyPreditictor(address _proxyAddress) external onlyAuthorized { + authToProxyPrediction[_proxyAddress] = true; + } + /** * @dev Function to deposit prediction token for participation in markets * @param _amount Amount of prediction token to deposit diff --git a/contracts/ProxyPlotPrediction.sol b/contracts/SwapAndPredictWithPlot.sol similarity index 66% rename from contracts/ProxyPlotPrediction.sol rename to contracts/SwapAndPredictWithPlot.sol index ff0481f2..9b7e064a 100644 --- a/contracts/ProxyPlotPrediction.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -16,24 +16,41 @@ pragma solidity 0.5.7; import "./external/openzeppelin-solidity/math/SafeMath.sol"; +import "./external/proxy/OwnedUpgradeabilityProxy.sol"; import "./external/NativeMetaTransaction.sol"; import "./interfaces/IAllMarkets.sol"; import "./interfaces/IToken.sol"; import "./interfaces/ISwapRouter.sol"; +import "./interfaces/IAuth.sol"; -contract ProxyPlotPrediction is NativeMetaTransaction { +contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { using SafeMath for uint; IAllMarkets allPlotMarkets; - IToken plotToken; + IToken predictionToken; IUniswapV2Router router; address public maticAddress; + address public defaultAuthorized; - function initiate(address _allPlotMarkets, address _plotToken, address _router, address _maticAddress) external { + /** + * @dev Changes the master address and update it's instance + * @param _authorizedMultiSig Authorized address to execute critical functions in the protocol. + * @param _defaultAuthorizedAddress Authorized address to trigger initial functions by passing required external values. + */ + function setMasterAddress(address _authorizedMultiSig, address _defaultAuthorizedAddress) public { + OwnedUpgradeabilityProxy proxy = OwnedUpgradeabilityProxy(address(uint160(address(this)))); + require(msg.sender == proxy.proxyOwner()); + authorized = _authorizedMultiSig; + defaultAuthorized = _defaultAuthorizedAddress; + _initializeEIP712("SP"); + } + + function initiate(address _allPlotMarkets, address _predictionToken, address _router, address _maticAddress) external { + require(msg.sender == defaultAuthorized); allPlotMarkets = IAllMarkets(_allPlotMarkets); - plotToken = IToken(_plotToken); + predictionToken = IToken(_predictionToken); router = IUniswapV2Router(_router); maticAddress = _maticAddress; } @@ -41,7 +58,7 @@ contract ProxyPlotPrediction is NativeMetaTransaction { function swapAndPlacePrediction(address[] calldata _path, uint _inputAmount, address _predictFor, uint _marketId, uint _prediction, uint64 _bPLOTPredictionAmount) external payable { uint deadline = now*2; uint amountOutMin = 1; - require(_path[_path.length-1] == address(plotToken)); + require(_path[_path.length-1] == address(predictionToken)); bool _isNativeToken = (_path[0] == maticAddress && msg.value >0); // uint _initialFromTokenBalance = getTokenBalance(_path[0], _isNativeToken); // uint _initialToTokenBalance = getTokenBalance(_path[_path.length-1], false); @@ -67,8 +84,8 @@ contract ProxyPlotPrediction is NativeMetaTransaction { ); } uint _tokenDeposit = _output[1]; - plotToken.approve(address(allPlotMarkets), _inputAmount); - allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(plotToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); + predictionToken.approve(address(allPlotMarkets), _inputAmount); + allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(predictionToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); // require(_initialFromTokenBalance == getTokenBalance(_path[0], _isNativeToken)); // require(_initialToTokenBalance == getTokenBalance(_path[_path.length-1], false)); } @@ -80,7 +97,7 @@ contract ProxyPlotPrediction is NativeMetaTransaction { return IToken(_token).balanceOf(address(this)); } - function transferLeftOverTokens(address _token) external { + function transferLeftOverTokens(address _token) external onlyAuthorized { IToken(_token).transfer(msg.sender, getTokenBalance(_token, false)); } } From a49beaab0ddb676765f632a7a493cc2b72caf6f6 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Wed, 26 May 2021 13:14:11 +0530 Subject: [PATCH 07/33] Updated code comments --- contracts/SwapAndPredictWithPlot.sol | 47 ++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 9b7e064a..55fc207e 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -27,11 +27,13 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { using SafeMath for uint; + event SwapAndPredictFor(address predictFor, uint marketId, address swapFromToken, address swapToToken, uint inputAmount, uint outputAmount); + IAllMarkets allPlotMarkets; IToken predictionToken; IUniswapV2Router router; - address public maticAddress; + address public nativeCurrencyAddress; address public defaultAuthorized; /** @@ -47,23 +49,44 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { _initializeEIP712("SP"); } - function initiate(address _allPlotMarkets, address _predictionToken, address _router, address _maticAddress) external { + /** + * @dev Initiate the contract with required addresses + * @param _allPlotMarkets AllPlotMarkets contract address + * @param _predictionToken Address of token used for placing predictions + * @param _router Router address of exchange to be used for swap transactions + * @param _nativeCurrencyAddress Wrapped token address of Native currency of network/chain + */ + function initiate(address _allPlotMarkets, address _predictionToken, address _router, address _nativeCurrencyAddress) external { require(msg.sender == defaultAuthorized); allPlotMarkets = IAllMarkets(_allPlotMarkets); predictionToken = IToken(_predictionToken); router = IUniswapV2Router(_router); - maticAddress = _maticAddress; + nativeCurrencyAddress = _nativeCurrencyAddress; } + /** + * @dev Swap any allowed token to prediction token and then place prediction + * @param _path Order path for swap transaction + * @param _inputAmount Input amount for swap transaction + * @param _predictFor Address of user on whose behalf the prediction should be placed + * @param _marketId Index of the market to place prediction + * @param _prediction Option in the market to place prediction + * @param _bPLOTPredictionAmount Bplot amount of `_predictFor` user to be used for prediction + */ function swapAndPlacePrediction(address[] calldata _path, uint _inputAmount, address _predictFor, uint _marketId, uint _prediction, uint64 _bPLOTPredictionAmount) external payable { uint deadline = now*2; uint amountOutMin = 1; require(_path[_path.length-1] == address(predictionToken)); - bool _isNativeToken = (_path[0] == maticAddress && msg.value >0); + if(_bPLOTPredictionAmount > 0) { + // bPLOT can not be used if another user is placing proxy prediction + require(_msgSender() == _predictFor); + } + bool _isNativeToken = (_path[0] == nativeCurrencyAddress && msg.value >0); // uint _initialFromTokenBalance = getTokenBalance(_path[0], _isNativeToken); // uint _initialToTokenBalance = getTokenBalance(_path[_path.length-1], false); uint[] memory _output; if(_isNativeToken) { + require(_inputAmount == msg.value); _output = router.swapExactETHForTokens.value(msg.value)( amountOutMin, _path, @@ -84,12 +107,18 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { ); } uint _tokenDeposit = _output[1]; + emit SwapAndPredictFor(_predictFor, _marketId, _path[0], address(predictionToken), _inputAmount, _output[1]); predictionToken.approve(address(allPlotMarkets), _inputAmount); allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(predictionToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); // require(_initialFromTokenBalance == getTokenBalance(_path[0], _isNativeToken)); // require(_initialToTokenBalance == getTokenBalance(_path[_path.length-1], false)); } + /** + * @dev Get contract balance of the given token + * @param _token Address of token to query balance for + * @param _isNativeCurrency Falg defining if the balance needed to be fetched for native currency of the network/chain + */ function getTokenBalance(address _token, bool _isNativeCurrency) public returns(uint) { if(_isNativeCurrency) { return ((address(this)).balance); @@ -97,7 +126,13 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { return IToken(_token).balanceOf(address(this)); } - function transferLeftOverTokens(address _token) external onlyAuthorized { - IToken(_token).transfer(msg.sender, getTokenBalance(_token, false)); + /** + * @dev Transfer any left over token in contract to given address + * @param _token Address of token to transfer + * @param _recipient Address of token recipient + */ + function transferLeftOverTokens(address _token, address _recipient) external onlyAuthorized { + require(_token != address(0)); + IToken(_token).transfer(_recipient, getTokenBalance(_token, false)); } } From b8f2ddc2c28155262b47c519320e3822531ef0c0 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Wed, 26 May 2021 14:36:58 +0530 Subject: [PATCH 08/33] Updated comments --- contracts/AllPlotMarkets_2.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/AllPlotMarkets_2.sol b/contracts/AllPlotMarkets_2.sol index 21c4b355..dec4d6f3 100644 --- a/contracts/AllPlotMarkets_2.sol +++ b/contracts/AllPlotMarkets_2.sol @@ -37,7 +37,8 @@ contract AllPlotMarkets_2 is AllPlotMarkets { } /** - * @dev Deposit and Place prediction on the available options of the market with both PLOT and BPLOT. + * @dev Deposit and Place prediction on behalf of another address + * @param _predictFor Address of user, to place prediction for * @param _marketId Index of the market * @param _tokenDeposit prediction token amount to deposit * @param _asset The asset used by user during prediction whether it is prediction token address or in Bonus token. From 4f83d67c54b275d3c0733b2ecaedadd3e6435cbe Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Thu, 27 May 2021 16:12:14 +0530 Subject: [PATCH 09/33] Fixed approval for prediction after swap --- contracts/SwapAndPredictWithPlot.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 55fc207e..215177b6 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -106,9 +106,9 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { deadline ); } - uint _tokenDeposit = _output[1]; + uint _tokenDeposit = _output[_output.length - 1]; emit SwapAndPredictFor(_predictFor, _marketId, _path[0], address(predictionToken), _inputAmount, _output[1]); - predictionToken.approve(address(allPlotMarkets), _inputAmount); + predictionToken.approve(address(allPlotMarkets), _tokenDeposit); allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(predictionToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); // require(_initialFromTokenBalance == getTokenBalance(_path[0], _isNativeToken)); // require(_initialToTokenBalance == getTokenBalance(_path[_path.length-1], false)); From 1a12453da39f763c1f9881ae1ceaf4301ca21aca Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Thu, 27 May 2021 19:28:22 +0530 Subject: [PATCH 10/33] Updated migrations and added testcases for Swap and predict --- contracts/interfaces/ISwapRouter.sol | 80 +------- contracts/mock/MockRouter.sol | 70 +++++++ migrations/4_initiate_2.js | 30 +++ test/SwapAndPredict.js | 239 ++++++++++++++++++++++++ test/SwapAndPredict_BPLOT.js | 269 +++++++++++++++++++++++++++ 5 files changed, 609 insertions(+), 79 deletions(-) create mode 100644 contracts/mock/MockRouter.sol create mode 100644 migrations/4_initiate_2.js create mode 100644 test/SwapAndPredict.js create mode 100644 test/SwapAndPredict_BPLOT.js diff --git a/contracts/interfaces/ISwapRouter.sol b/contracts/interfaces/ISwapRouter.sol index 531a5ec3..64029003 100644 --- a/contracts/interfaces/ISwapRouter.sol +++ b/contracts/interfaces/ISwapRouter.sol @@ -1,63 +1,7 @@ pragma solidity 0.5.7; interface IUniswapV2Router { - function factory() external pure returns (address); function WETH() external pure returns (address); - - function addLiquidity( - address tokenA, - address tokenB, - uint amountADesired, - uint amountBDesired, - uint amountAMin, - uint amountBMin, - address to, - uint deadline - ) external returns (uint amountA, uint amountB, uint liquidity); - function addLiquidityETH( - address token, - uint amountTokenDesired, - uint amountTokenMin, - uint amountETHMin, - address to, - uint deadline - ) external payable returns (uint amountToken, uint amountETH, uint liquidity); - function removeLiquidity( - address tokenA, - address tokenB, - uint liquidity, - uint amountAMin, - uint amountBMin, - address to, - uint deadline - ) external returns (uint amountA, uint amountB); - function removeLiquidityETH( - address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, - address to, - uint deadline - ) external returns (uint amountToken, uint amountETH); - function removeLiquidityWithPermit( - address tokenA, - address tokenB, - uint liquidity, - uint amountAMin, - uint amountBMin, - address to, - uint deadline, - bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountA, uint amountB); - function removeLiquidityETHWithPermit( - address token, - uint liquidity, - uint amountTokenMin, - uint amountETHMin, - address to, - uint deadline, - bool approveMax, uint8 v, bytes32 r, bytes32 s - ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, @@ -65,31 +9,9 @@ interface IUniswapV2Router { address to, uint deadline ) external returns (uint[] memory amounts); - function swapTokensForExactTokens( - uint amountOut, - uint amountInMax, - address[] calldata path, - address to, - uint deadline - ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); - function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) - external - returns (uint[] memory amounts); - function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) - external - returns (uint[] memory amounts); - function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) - external - payable - returns (uint[] memory amounts); - - function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); - function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); - function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); - function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); -} \ No newline at end of file + } \ No newline at end of file diff --git a/contracts/mock/MockRouter.sol b/contracts/mock/MockRouter.sol new file mode 100644 index 00000000..89ff0afa --- /dev/null +++ b/contracts/mock/MockRouter.sol @@ -0,0 +1,70 @@ +pragma solidity 0.5.7; + +import "../interfaces/ISwapRouter.sol"; +import "../external/openzeppelin-solidity/math/SafeMath.sol"; +import "../interfaces/IToken.sol"; + +contract MockUniswapRouter is IUniswapV2Router { + + using SafeMath for uint; + + uint public priceOfToken = 1e16; + address token; + + constructor(address _token) public { + token = _token; + } + + function WETH() external pure returns (address) { + return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + } + + function setPrice(uint _newPrice) external { + priceOfToken = _newPrice; + } + + + function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) + external + payable + returns (uint[] memory amounts) { + uint ethSent = msg.value; + uint tokenOutput = ethSent.mul(1e18).div(priceOfToken); + IToken(token).transfer(to, tokenOutput); + amounts = new uint[](2); + amounts[0] = ethSent; + amounts[1] = tokenOutput; + } + + function swapExactTokensForTokens( + uint amountIn, + uint amountOutMin, + address[] calldata path, + address to, + uint deadline + ) + external + returns (uint[] memory amounts) { + uint tokenOutput = amountIn.mul(1e18).div(priceOfToken); + IToken(path[0]).transferFrom(msg.sender, address(this), amountIn); + IToken(token).transfer(to, tokenOutput); + amounts = new uint[](2); + amounts[0] = amountIn; + amounts[1] = tokenOutput; + } + + function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts) { + amounts = new uint[](2); + amounts[0] = amountIn; + if(path[0] == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) { + amounts[1] = amountIn.mul(priceOfToken); + } else { + amounts[1] = amountIn.mul(priceOfToken).div(1e18); + } + } + + function () payable external { + + } + +} \ No newline at end of file diff --git a/migrations/4_initiate_2.js b/migrations/4_initiate_2.js new file mode 100644 index 00000000..917397c9 --- /dev/null +++ b/migrations/4_initiate_2.js @@ -0,0 +1,30 @@ +const AllPlotMarkets_2 = artifacts.require('AllPlotMarkets_2'); +const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy'); +const Master = artifacts.require('Master'); +const SwapAndPredictWithPlot = artifacts.require('SwapAndPredictWithPlot'); +const MockUniswapRouter = artifacts.require('MockUniswapRouter'); +const { assert } = require("chai"); + +module.exports = function(deployer, network, accounts){ + deployer.then(async () => { + let master = await OwnedUpgradeabilityProxy.deployed(); + master = await Master.at(master.address); + let allMarketsNewImpl = await await deployer.deploy(AllPlotMarkets_2); + let spImpl = await deployer.deploy(SwapAndPredictWithPlot); + await master.upgradeMultipleImplementations( + [web3.utils.toHex("AM")], + [allMarketsNewImpl.address] + ); + await master.addNewContract(web3.utils.toHex("SP"), spImpl.address) + let swapAnPredict = await SwapAndPredictWithPlot.at(await master.getLatestAddress(web3.utils.toHex('SP'))); + allMarkets = await AllPlotMarkets_2.at(await master.getLatestAddress(web3.utils.toHex('AM'))); + await allMarkets.addAuthorizedProxyPreditictor(swapAnPredict.address); + let router = await deployer.deploy(MockUniswapRouter, await master.dAppToken()); + await swapAnPredict.initiate( + allMarkets.address, + await master.dAppToken(), + router.address, + await router.WETH() + ); + }); +}; \ No newline at end of file diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js new file mode 100644 index 00000000..013e9b0b --- /dev/null +++ b/test/SwapAndPredict.js @@ -0,0 +1,239 @@ +const { assert } = require("chai"); + +const OwnedUpgradeabilityProxy = artifacts.require("OwnedUpgradeabilityProxy"); +const Master = artifacts.require("Master"); +const PlotusToken = artifacts.require("MockPLOT"); +const AllMarkets = artifacts.require("AllPlotMarkets_2"); +const CyclicMarkets = artifacts.require("MockCyclicMarkets"); +const MockUniswapRouter = artifacts.require('MockUniswapRouter'); +const Referral = artifacts.require("Referral"); +const DisputeResolution = artifacts.require('DisputeResolution'); +const SwapAndPredictWithPlot = artifacts.require('SwapAndPredictWithPlot'); +const SampleERC = artifacts.require('SampleERC'); +const BigNumber = require("bignumber.js"); +const { increaseTimeTo } = require("./utils/increaseTime.js"); +const encode1 = require('./utils/encoder.js').encode1; +const encode3 = require("./utils/encoder.js").encode3; +const signAndExecuteMetaTx = require("./utils/signAndExecuteMetaTx.js").signAndExecuteMetaTx; +const BN = require('bn.js'); + +const assertRevert = require("./utils/assertRevert.js").assertRevert; +const increaseTime = require("./utils/increaseTime.js").increaseTime; +const latestTime = require("./utils/latestTime.js").latestTime; +const encode = require("./utils/encoder.js").encode; +const gvProposal = require("./utils/gvProposal.js").gvProposalWithIncentiveViaTokenHolder; +const { toHex, toWei, toChecksumAddress } = require("./utils/ethTools"); +// get etherum accounts +// swap ether with LOT +let timeNow, + marketData, + expireTme, + priceOption1, + priceOption2, + priceOption3, + option1RangeMIN, + option1RangeMAX, + option2RangeMIN, + option2RangeMAX, + option3RangeMIX, + marketStatus, + option3RangeMAX, + disputeResolution, + marketETHBalanceBeforeDispute, + marketIncentives; + +let privateKeyList = ["fb437e3e01939d9d4fef43138249f23dc1d0852e69b0b5d1647c087f869fabbd","7c85a1f1da3120c941b83d71a154199ee763307683f206b98ad92c3b4e0af13e","ecc9b35bf13bd5459350da564646d05c5664a7476fe5acdf1305440f88ed784c","f4470c3fca4dbef1b2488d016fae25978effc586a1f83cb29ac8cb6ab5bc2d50","141319b1a84827e1046e93741bf8a9a15a916d49684ab04925ac4ce4573eea23","d54b606094287758dcf19064a8d91c727346aadaa9388732e73c4315b7c606f9","49030e42ce4152e715a7ddaa10e592f8e61d00f70ef11f48546711f159d985df","b96761b1e7ebd1e8464a78a98fe52f53ce6035c32b4b2b12307a629a551ff7cf","d4786e2581571c863c7d12231c3afb6d4cef390c0ac9a24b243293721d28ea95","ed28e3d3530544f1cf2b43d1956b7bd13b63c612d963a8fb37387aa1a5e11460","05b127365cf115d4978a7997ee98f9b48f0ddc552b981c18aa2ee1b3e6df42c6","9d11dd6843f298b01b34bd7f7e4b1037489871531d14b58199b7cba1ac0841e6","f79e90fa4091de4fc2ec70f5bf67b24393285c112658e0d810e6bd711387fbb9","99f1fc0f09230ce745b6a256ba7082e6e51a2907abda3d9e735a5c8188bb4ba1","477f86cce983b9c91a36fdcd4a7ce21144a08dee9b1aafb91b9c70e57f717ce6","b03d2e6bb4a7d71c66a66ff9e9c93549cae4b593f634a4ea2a1f79f94200f5b4","9ddc0f53a81e631dcf39d5155f41ec12ed551b731efc3224f410667ba07b37dc","cf087ff9ae7c9954ad8612d071e5cdf34a6024ee1ae477217639e63a802a53dd","b64f62b94babb82cc78d3d1308631ae221552bb595202fc1d267e1c29ce7ba60","a91e24875f8a534497459e5ccb872c4438be3130d8d74b7e1104c5f94cdcf8c2","4f49f3d029eeeb3fed14d59625acd088b6b34f3b41c527afa09d29e4a7725c32","179795fd7ac7e7efcba3c36d539a1e8659fb40d77d0a3fab2c25562d99793086","4ba37d0b40b879eceaaca2802a1635f2e6d86d5c31e3ff2d2fd13e68dd2a6d3d","6b7f5dfba9cd3108f1410b56f6a84188eee23ab48a3621b209a67eea64293394","870c540da9fafde331a3316cee50c17ad76ddb9160b78b317bef2e6f6fc4bac0","470b4cccaea895d8a5820aed088357e380d66b8e7510f0a1ea9b575850160241","8a55f8942af0aec1e0df3ab328b974a7888ffd60ded48cc6862013da0f41afbc","2e51e8409f28baf93e665df2a9d646a1bf9ac8703cbf9a6766cfdefa249d5780","99ef1a23e95910287d39493d8d9d7d1f0b498286f2b1fdbc0b01495f10cf0958","6652200c53a4551efe2a7541072d817562812003f9d9ef0ec17995aa232378f8","39c6c01194df72dda97da2072335c38231ced9b39afa280452afcca901e73643","12097e411d948f77b7b6fa4656c6573481c1b4e2864c1fca9d5b296096707c45","cbe53bf1976aee6cec830a848c6ac132def1503cffde82ccfe5bd15e75cbaa72","eeab5dcfff92dbabb7e285445aba47bd5135a4a3502df59ac546847aeb5a964f","5ea8279a578027abefab9c17cef186cccf000306685e5f2ee78bdf62cae568dd","0607767d89ad9c7686dbb01b37248290b2fa7364b2bf37d86afd51b88756fe66","e4fd5f45c08b52dae40f4cdff45e8681e76b5af5761356c4caed4ca750dc65cd","145b1c82caa2a6d703108444a5cf03e9cb8c3cd3f19299582a564276dbbba734","736b22ec91ae9b4b2b15e8d8c220f6c152d4f2228f6d46c16e6a9b98b4733120","ac776cb8b40f92cdd307b16b83e18eeb1fbaa5b5d6bd992b3fda0b4d6de8524c","65ba30e2202fdf6f37da0f7cfe31dfb5308c9209885aaf4cef4d572fd14e2903","54e8389455ec2252de063e83d3ce72529d674e6d2dc2070661f01d4f76b63475","fbbbfb525dd0255ee332d51f59648265aaa20c2e9eff007765cf4d4a6940a849","8de5e418f34d04f6ea947ce31852092a24a705862e6b810ca9f83c2d5f9cda4d","ea6040989964f012fd3a92a3170891f5f155430b8bbfa4976cde8d11513b62d9","14d94547b5deca767137fbd14dae73e888f3516c742fad18b83be333b38f0b88","47f05203f6368d56158cda2e79167777fc9dcb0c671ef3aabc205a1636c26a29"]; + + +contract("Rewards-Market", async function(users) { + describe("Scenario1", async () => { + it("0.0", async () => { + masterInstance = await OwnedUpgradeabilityProxy.deployed(); + masterInstance = await Master.at(masterInstance.address); + plotusToken = await PlotusToken.deployed(); + timeNow = await latestTime(); + router = await MockUniswapRouter.deployed(); + allMarkets = await AllMarkets.at(await masterInstance.getLatestAddress(web3.utils.toHex("AM"))); + cyclicMarkets = await CyclicMarkets.at(await masterInstance.getLatestAddress(web3.utils.toHex("CM"))); + referral = await Referral.deployed(); + disputeResolution = await DisputeResolution.at(await masterInstance.getLatestAddress(web3.utils.toHex("DR"))); + spInstance = await SwapAndPredictWithPlot.at(await masterInstance.getLatestAddress(web3.utils.toHex("SP"))); + + externalToken = await SampleERC.new("USDP", "USDP"); + await externalToken.mint(users[0], toWei(1000000)); + plotTokenPrice = 0.01; + externalTokenPrice = 1/plotTokenPrice; + await plotusToken.transfer(router.address,toWei(10000)); + await increaseTime(5 * 3600); + await plotusToken.transfer(users[12],toWei(100000)); + await plotusToken.transfer(users[11],toWei(100000)); + // await plotusToken.transfer(marketIncentives.address,toWei(500)); + + let nullAddress = "0x0000000000000000000000000000"; + + await plotusToken.transfer(users[11],toWei(100)); + await plotusToken.approve(allMarkets.address,toWei(200000),{from:users[11]}); + await cyclicMarkets.setNextOptionPrice(18); + await cyclicMarkets.claimRelayerRewards(); + await cyclicMarkets.whitelistMarketCreator(users[11]); + await cyclicMarkets.createMarket(0, 0, 0,{from:users[11],gasPrice:500000}); + // await assertRevert(marketIncentives.setMasterAddress(users[0], users[0])); + await assertRevert(allMarkets.setMasterAddress(users[0], users[0])); + await assertRevert(allMarkets.setMarketStatus(6, 1)); + await assertRevert(cyclicMarkets.setReferralContract(users[0])); + var date = Date.now(); + date = Math.round(date/1000); + await assertRevert(cyclicMarkets.addInitialMarketTypesAndStart(date, users[0], users[0], {from:users[10]})); + await assertRevert(cyclicMarkets.handleFee(100, 1, users[0], users[0], {from:users[10]})); + // await marketIncentives.claimCreationReward(100,{from:users[11]}); + }); + + it("Scenario 1: Few user wins", async () => { + let i; + let totalDepositedPlot = [0,100, 400, 210, 123, 500, 700, 200, 50, 300, 150]; + let predictionVal = [0,100, 400, 210, 123, 500, 700, 200, 50, 300, 150]; + let options=[0,2,2,2,3,1,1,2,3,3,2,1]; + let daoCommissions = [0, 1.8, 6.4, 3.36, 1.968, 8, 11.2, 3.2, 0.8, 4.8, 2.4]; + const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; + await assertRevert(referral.setReferrer(ZERO_ADDRESS, ZERO_ADDRESS)); + for(i=1; i<11;i++){ + if(i>1) { + //Should not allow unauthorized address to set referrer + await assertRevert(referral.setReferrer(users[1], users[i], {from:users[i]})); + await referral.setReferrer(users[1], users[i]); + //SHould not add referrer if already set + await assertRevert(referral.setReferrer(users[1], users[i])); + } + if(i == 10) { + await cyclicMarkets.removeReferralContract(); + await assertRevert(cyclicMarkets.removeReferralContract()); + } + let _inputAmount = toWei(predictionVal[i]*plotTokenPrice); + await externalToken.approve(spInstance.address, _inputAmount, {from:users[i]}); + await externalToken.transfer(users[i], _inputAmount); + await cyclicMarkets.setNextOptionPrice(options[i]*9); + let spPlotBalanceBefore = await plotusToken.balanceOf(spInstance.address); + let spTokenBalanceBefore = await externalToken.balanceOf(spInstance.address); + // await spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0) + let functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0); + await signAndExecuteMetaTx( + privateKeyList[i], + users[i], + functionSignature, + spInstance, + "SP" + ); + let spPlotBalanceAfter = await plotusToken.balanceOf(spInstance.address); + let spTokenBalanceAfter = await externalToken.balanceOf(spInstance.address); + await assert.equal(spPlotBalanceAfter/1, spPlotBalanceBefore/1); + await assert.equal(spTokenBalanceAfter/1, spTokenBalanceBefore/1); + } + + //SHould not add referrer if already placed prediction + await assertRevert(referral.setReferrer(users[1], users[2])); + let relayerBalBefore = await plotusToken.balanceOf(users[0]); + await cyclicMarkets.claimRelayerRewards(); + let relayerBalAfter = await plotusToken.balanceOf(users[0]); + + // assert.equal(Math.round((relayerBalAfter-relayerBalBefore)/1e15),11.532*1e3); + + + let betpoints = [0,5444.44444, 21777.77777, 11433.33333, 4464.44444, 54444.44444, 76222.22222, 10888.88888, 1814.81481, 10888.88888, 8166.66666, 1814.81481, 1814.81481, 1814.81481]; + + + for(i=1;i<=11;i++) + { + let betPointUser = (await allMarkets.getUserPredictionPoints(users[i],7,options[i]))/1e5; + if(i == 11) { + let betPointUser1 = (await allMarkets.getUserPredictionPoints(users[i],7,2))/1e5; + assert.equal(betPointUser1,betpoints[i+1]); + let betPointUser2 = (await allMarkets.getUserPredictionPoints(users[i],7,3))/1e5; + assert.equal(betPointUser2,betpoints[i+1]); + } + assert.equal(betPointUser,betpoints[i]); + let unusedBal = await allMarkets.getUserUnusedBalance(users[i]); + if(i != 11) + assert.equal(totalDepositedPlot[i]-unusedBal[0]/1e18,predictionVal[i]); + } + + await cyclicMarkets.settleMarket(7,0); + await increaseTime(8*60*60); + + let daoBalanceBefore = await plotusToken.balanceOf(masterInstance.address); + await cyclicMarkets.settleMarket(7,0); + let daoFee = 5.666; + let daoBalanceAfter = await plotusToken.balanceOf(masterInstance.address); + assert.equal((daoBalanceAfter/1e18).toFixed(2), (daoBalanceBefore/1e18 + daoFee).toFixed(2)); + + + let marketCreatorReward = await cyclicMarkets.getPendingMarketCreationRewards(users[11]); + assert.equal(226640000,Math.round(marketCreatorReward/1e11)); + + // let creationReward = 14.3999; + let marketCreatoFee = 22.664; + let balanceBefore = await plotusToken.balanceOf(users[11]); + await cyclicMarkets.claimCreationReward({ from: users[11] }); + let balanceAfter = await plotusToken.balanceOf(users[11]); + assert.equal(~~(balanceAfter/1e15), ~~((balanceBefore/1e14 + marketCreatoFee*1e4)/10)); + + // assert.equal((daoBalanceAfter/1e18).toFixed(2), (daoBalanceBefore/1e18 + marketCreatoFee+daoFee).toFixed(2)); + await plotusToken.transfer(users[12], "700000000000000000000"); + await plotusToken.approve(disputeResolution.address, "500000000000000000000", { + from: users[12], + }); + await disputeResolution.raiseDispute(7, "500000000000000000000", "", {from: users[12]}); + await increaseTime(604810); + await disputeResolution.declareResult(7); + + await increaseTime(60*61); + + let userRewardPlot = [0,0,0,0,0,1118.14308219,1565.400315,0,0,0,0]; + for(i=1;i<11;i++) + { + let reward = await allMarkets.getReturn(users[i],7); + // assert.equal(Math.round(reward/1e2),userRewardPlot[i]*1e6); + let plotBalBefore = await plotusToken.balanceOf(users[i]); + let plotEthUnused = await allMarkets.getUserUnusedBalance(users[i]); + if((plotEthUnused[0]/1 +plotEthUnused[1]/1) > 0) { + functionSignature = encode3("withdraw(uint,uint)", plotEthUnused[0].iadd(plotEthUnused[1]), 100); + await signAndExecuteMetaTx( + privateKeyList[i], + users[i], + functionSignature, + allMarkets, + "AM" + ); + } + let plotBalAfter = await plotusToken.balanceOf(users[i]); + assert.equal(Math.round((plotBalAfter-plotBalBefore)/1e18),Math.round((totalDepositedPlot[i]-predictionVal[i])/1+reward/1e8)); + } + + }); + + it("Check referral fee", async () => { + let referralRewardPlot = [9.932, 0.8, 0.42, 0.246, 1, 1.4, 0.4, 0.1, 0.6, 0]; + + for(i=1;i<11;i++) + { + let reward = await referral.getReferralFees(users[i], plotusToken.address); + if(i == 1) { + reward = reward[0]; + } else { + reward = reward[1]; + } + assert.equal(reward/1,referralRewardPlot[i-1]*1e8); + let plotBalBefore = await plotusToken.balanceOf(users[i]); + functionSignature = encode3("claimReferralFee(address,address)", users[i], plotusToken.address); + await signAndExecuteMetaTx( + privateKeyList[i], + users[i], + functionSignature, + referral, + "RF" + ); + let plotBalAfter = await plotusToken.balanceOf(users[i]); + assert.equal(Math.round((plotBalAfter/1e13-plotBalBefore/1e13)),reward/1e3); + } + }) + }); +}); diff --git a/test/SwapAndPredict_BPLOT.js b/test/SwapAndPredict_BPLOT.js new file mode 100644 index 00000000..833c184a --- /dev/null +++ b/test/SwapAndPredict_BPLOT.js @@ -0,0 +1,269 @@ +const { assert } = require("chai"); +const OwnedUpgradeabilityProxy = artifacts.require("OwnedUpgradeabilityProxy"); +const Master = artifacts.require("Master"); +const PlotusToken = artifacts.require("MockPLOT"); +const AllMarkets = artifacts.require("MockAllMarkets"); +const CyclicMarkets = artifacts.require("MockCyclicMarkets"); +const EthChainlinkOracle = artifacts.require('MockChainLinkAggregator'); +const MockUniswapRouter = artifacts.require('MockUniswapRouter'); +const SwapAndPredictWithPlot = artifacts.require('SwapAndPredictWithPlot'); +const SampleERC = artifacts.require('SampleERC'); + +const BLOT = artifacts.require("BPLOT"); +const BigNumber = require("bignumber.js"); + +const increaseTime = require("./utils/increaseTime.js").increaseTime; +const assertRevert = require("./utils/assertRevert").assertRevert; +const latestTime = require("./utils/latestTime").latestTime; +const encode = require("./utils/encoder.js").encode; +const encode1 = require("./utils/encoder.js").encode1; + +const encode3 = require("./utils/encoder.js").encode3; +const signAndExecuteMetaTx = require("./utils/signAndExecuteMetaTx.js").signAndExecuteMetaTx; +const BN = require('bn.js'); + +const gvProposal = require("./utils/gvProposal.js").gvProposalWithIncentiveViaTokenHolder; +const { toHex, toWei, toChecksumAddress } = require("./utils/ethTools"); +const to8Power = (number) => String(parseFloat(number) * 1e8); +let pkList = ["fb437e3e01939d9d4fef43138249f23dc1d0852e69b0b5d1647c087f869fabbd","7c85a1f1da3120c941b83d71a154199ee763307683f206b98ad92c3b4e0af13e","ecc9b35bf13bd5459350da564646d05c5664a7476fe5acdf1305440f88ed784c","f4470c3fca4dbef1b2488d016fae25978effc586a1f83cb29ac8cb6ab5bc2d50","141319b1a84827e1046e93741bf8a9a15a916d49684ab04925ac4ce4573eea23","d54b606094287758dcf19064a8d91c727346aadaa9388732e73c4315b7c606f9","49030e42ce4152e715a7ddaa10e592f8e61d00f70ef11f48546711f159d985df","b96761b1e7ebd1e8464a78a98fe52f53ce6035c32b4b2b12307a629a551ff7cf","d4786e2581571c863c7d12231c3afb6d4cef390c0ac9a24b243293721d28ea95","ed28e3d3530544f1cf2b43d1956b7bd13b63c612d963a8fb37387aa1a5e11460","05b127365cf115d4978a7997ee98f9b48f0ddc552b981c18aa2ee1b3e6df42c6","9d11dd6843f298b01b34bd7f7e4b1037489871531d14b58199b7cba1ac0841e6"]; +describe("newPlotusWithBlot", () => { + contract("AllMarket", async function (users) { + // Multiplier Sheet + let masterInstance, + plotusToken, + allMarkets; + let predictionPointsBeforeUser1, predictionPointsBeforeUser2, predictionPointsBeforeUser3, predictionPointsBeforeUser4; + before(async () => { + masterInstance = await OwnedUpgradeabilityProxy.deployed(); + masterInstance = await Master.at(masterInstance.address); + plotusToken = await PlotusToken.deployed(); + allMarkets = await AllMarkets.at(await masterInstance.getLatestAddress(web3.utils.toHex("AM"))); + cyclicMarkets = await CyclicMarkets.at(await masterInstance.getLatestAddress(web3.utils.toHex("CM"))); + router = await MockUniswapRouter.deployed(); + spInstance = await SwapAndPredictWithPlot.at(await masterInstance.getLatestAddress(web3.utils.toHex("SP"))); + externalToken = await SampleERC.new("USDP", "USDP"); + await externalToken.mint(users[0], toWei(1000000)); + plotTokenPrice = 0.01; + externalTokenPrice = 1/plotTokenPrice; + await plotusToken.transfer(router.address,toWei(10000)); + + + await increaseTime(4 * 60 * 60 + 1); + await cyclicMarkets.claimRelayerRewards(); + // await plotusToken.transfer(masterInstance.address,toWei(100000)); + await plotusToken.transfer(users[11],toWei(1000)); + await plotusToken.approve(allMarkets.address, toWei(10000), {from:users[11]}); + await cyclicMarkets.setNextOptionPrice(18); + await cyclicMarkets.whitelistMarketCreator(users[11]); + await cyclicMarkets.createMarket(0, 0, 0,{from: users[11]}); + // await marketIncentives.claimCreationReward(100,{from:users[11]}); + BLOTInstance = await BLOT.at(await masterInstance.getLatestAddress(web3.utils.toHex("BL"))); + await assertRevert(BLOTInstance.convertToPLOT(users[0], users[1],toWei(100))); + }); + it("Add a minter in BLOT", async () => { + await BLOTInstance.addMinter(users[5]); + assert.equal(await BLOTInstance.isMinter(users[5]), true); + }); + + it("Renounce a minter in BLOT", async () => { + await BLOTInstance.renounceMinter({from:users[5]}); + assert.equal(await BLOTInstance.isMinter(users[5]), false); + }); + it("1. Place Prediction", async () => { + + let i; + let predictionVal = [0,100, 400, 210, 123, 200, 100, 300, 500, 200, 100]; + let options=[0,2,2,2,3,1,1,2,3,3,2]; + let withPlot = [0,true,false,true,false,false,true,false,false,true,true]; + + for(i=1;i<11;i++) { + let predictionToken; + let depositAmt; + if(withPlot[i]) + { + depositAmt = toWei(predictionVal[i]); + await plotusToken.transfer(users[i], toWei(predictionVal[i])); + await plotusToken.approve(allMarkets.address, toWei(predictionVal[i]), { from: users[i] }); + predictionToken = plotusToken.address; + + } else { + depositAmt=0; + await plotusToken.approve(BLOTInstance.address, toWei(predictionVal[i]*2)); + await BLOTInstance.mint(users[i], toWei(predictionVal[i]*2)); + predictionToken = BLOTInstance.address; + } + let functionSignature = encode3("depositAndPlacePrediction(uint,uint,address,uint64,uint256)",depositAmt , 7, predictionToken, to8Power(predictionVal[i]), options[i]); + if(i == 5) { + // Predict with only bPLOT + predictionToken = plotusToken.address; + functionSignature = encode3("depositAndPredictWithBoth(uint,uint,address,uint256,uint64,uint64)",depositAmt , 7, predictionToken, options[i], 0, to8Power(predictionVal[i])); + } + if( i==4) { + // Predict with some plot and some bPLOT + + let _inputAmount = toWei(100*plotTokenPrice); + await externalToken.approve(spInstance.address, _inputAmount, {from:users[i]}); + await externalToken.transfer(users[i], _inputAmount); + let functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100)); + await cyclicMarkets.setNextOptionPrice(options[i]*9); + // await spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), {from:users[i]}); + await signAndExecuteMetaTx( + pkList[i], + users[i], + functionSignature, + spInstance, + "SP" + ); + + // predictionToken = plotusToken.address; + // await plotusToken.transfer(users[i], toWei(100)); + // await plotusToken.approve(allMarkets.address, toWei(100), { from: users[i] }); + // depositAmt=toWei(100); + // functionSignature = encode3("depositAndPredictWithBoth(uint,uint,address,uint256,uint64,uint64)",depositAmt , 7, predictionToken, options[i], to8Power(100), to8Power(predictionVal[i]-100)); + } + if(i == 3) { + // Predict with only PLOT + functionSignature = encode3("depositAndPredictWithBoth(uint,uint,address,uint256,uint64,uint64)",depositAmt , 7, predictionToken, options[i], to8Power(predictionVal[i]), 0); + } + await cyclicMarkets.setNextOptionPrice(options[i]*9); + if(i == 4) { + // await signAndExecuteMetaTx( + // pkList[i], + // users[i], + // functionSignature, + // spInstance, + // "SP" + // ); + } else { + await signAndExecuteMetaTx( + pkList[i], + users[i], + functionSignature, + allMarkets, + "AM" + ); + } + if(!withPlot[i]) { + //SHould not allow to predict with bPLOT twice + await assertRevert(signAndExecuteMetaTx( + pkList[i], + users[i], + functionSignature, + allMarkets, + "AM" + )); + } + } + }); + it("1.2 Relayer should get apt reward", async () => { + + let relayerBalBefore = await plotusToken.balanceOf(users[0]); + await cyclicMarkets.claimRelayerRewards(); + let relayerBalAfter = await plotusToken.balanceOf(users[0]); + + assert.equal(Math.round((relayerBalAfter-relayerBalBefore)/1e15),22.33*1e3); + }); + it("1.3 Check Prediction points allocated", async () => { + options = [0,2, 2, 2, 3, 1, 1, 2, 3, 3, 2]; + getPredictionPoints = async (user, option) => { + let predictionPoints = await allMarkets.getUserPredictionPoints(user, 7, option); + predictionPoints = predictionPoints / 1; + return predictionPoints; + }; + PredictionPointsExpected = [0,5444.44444, 21777.77777, 11433.33333, 4464.44444, 21777.77777, 10888.88888, 16333.33333, 18148.14815, 7259.25925, 5444.44444]; + + for (let index = 1; index < 11; index++) { + let PredictionPoints = await getPredictionPoints(users[index], options[index]); + PredictionPoints = PredictionPoints / 1e5; + try{ + assert.equal(PredictionPoints.toFixed(1), PredictionPointsExpected[index].toFixed(1)); + }catch(e){ + console.log(`Not equal!! -> Sheet: ${PredictionPointsExpected[index]} Got: ${PredictionPoints}`); + } + // commented by parv (as already added assert above) + // console.log(`Prediction points : ${PredictionPoints} expected : ${PredictionPointsExpected[index].toFixed(1)} `); + } + // console.log(await plotusToken.balanceOf(user1)); + + let ethChainlinkOracle = await EthChainlinkOracle.deployed(); + await ethChainlinkOracle.setLatestAnswer(1); + // close market + await increaseTime(8 * 60 * 60); + await cyclicMarkets.settleMarket(7, 1); + await increaseTime(8 * 60 * 60); + }); + it("1.4 Check total return for each user Prediction values in plot", async () => { + options = [0,2, 2, 2, 3, 1, 1, 2, 3, 3, 2]; + getReturnsInPLOT = async (user) => { + const response = await allMarkets.getReturn(user, 7); + let returnAmountInPLOT = response / 1e8; + return returnAmountInPLOT; + }; + const returnInPLOTExpected = [0,0,0,0,0,1433.688421,716.8442105,0,0,0,0]; + + for (let index = 1; index < 11; index++) { + let returns = await getReturnsInPLOT(users[index]) / 1; + try{ + assert.equal(returnInPLOTExpected[index].toFixed(2), returns.toFixed(2), ); + }catch(e){ + console.log(`Not equal!! -> Sheet: ${returnInPLOTExpected[index].toFixed(2)} Got: ${returns.toFixed(2)}`); + } + // commented by Parv (as assert already added above) + // console.log(`return : ${returns} Expected :${returnInPLOTExpected[index]}`); + } + }); + it("1.5 Check User Received The appropriate amount", async () => { + const totalReturnLotExpexted = [0,0,0,0,0,1433.688421,716.8442105,0,0,0,0];; + for (let i=1;i<11;i++) { + beforeClaimToken = await plotusToken.balanceOf(users[i]); + try { + let plotEthUnused = await allMarkets.getUserUnusedBalance(users[i]); + let functionSignature = encode3("withdraw(uint,uint)", plotEthUnused[0].iadd(plotEthUnused[1]), 10); + await signAndExecuteMetaTx( + pkList[i], + users[i], + functionSignature, + allMarkets, + "AM" + ); + } catch (e) { } + afterClaimToken = await plotusToken.balanceOf(users[i]); + conv = new BigNumber(1000000000000000000); + + diffToken = afterClaimToken - beforeClaimToken; + diffToken = diffToken / conv; + diffToken = diffToken.toFixed(2); + expectedInLot = totalReturnLotExpexted[i].toFixed(2); + + try{ + assert.equal(diffToken/1, expectedInLot); + }catch(e){ + console.log(`Not equal!! -> Sheet: ${expectedInLot} Got: ${diffToken}`); + } + // commented by Parv (as assert already added above) + // console.log(`User ${accounts.indexOf(account) + 1}`); + // console.log(`Returned in Eth : ${diff} Expected : ${expectedInEth} `); + // console.log(`Returned in Lot : ${diffToken} Expected : ${expectedInLot} `); + } + }); + it("1.6 Market creator should get apt reward", async () => { + let marketCreatorReward = await cyclicMarkets.getPendingMarketCreationRewards(users[11]); + assert.equal(Math.round(1866.39),Math.round(marketCreatorReward/1e16)); + + let plotBalBeforeCreator = await plotusToken.balanceOf(users[11]); + + functionSignature = encode3("claimCreationReward()"); + await signAndExecuteMetaTx( + pkList[11], + users[11], + functionSignature, + cyclicMarkets, + "CM" + ); + + let plotBalAfterCreator = await plotusToken.balanceOf(users[11]); + + assert.equal(Math.round((plotBalAfterCreator-plotBalBeforeCreator)/1e16),Math.round(1866.39)); + }); + }); +}); From 8bb5de0e81dc3a3b20cf55e13130e4c9ce9e959c Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 28 May 2021 10:59:43 +0530 Subject: [PATCH 11/33] Added more checks and refactored swapAndPlacePrediction --- contracts/SwapAndPredictWithPlot.sol | 91 +++++++++++++++++++++------- 1 file changed, 69 insertions(+), 22 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 215177b6..49ed7589 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -29,13 +29,22 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { event SwapAndPredictFor(address predictFor, uint marketId, address swapFromToken, address swapToToken, uint inputAmount, uint outputAmount); - IAllMarkets allPlotMarkets; - IToken predictionToken; - IUniswapV2Router router; + IAllMarkets internal allPlotMarkets; + IUniswapV2Router internal router; + address internal predictionToken; address public nativeCurrencyAddress; address public defaultAuthorized; + modifier holdNoFunds(address[] memory _path) { + bool _isNativeToken = (_path[0] == nativeCurrencyAddress && msg.value >0); + uint _initialFromTokenBalance = getTokenBalance(_path[0], _isNativeToken); + uint _initialToTokenBalance = getTokenBalance(_path[_path.length-1], false); + _; + require(_initialFromTokenBalance == getTokenBalance(_path[0], _isNativeToken)); + require(_initialToTokenBalance == getTokenBalance(_path[_path.length-1], false)); + } + /** * @dev Changes the master address and update it's instance * @param _authorizedMultiSig Authorized address to execute critical functions in the protocol. @@ -59,7 +68,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { function initiate(address _allPlotMarkets, address _predictionToken, address _router, address _nativeCurrencyAddress) external { require(msg.sender == defaultAuthorized); allPlotMarkets = IAllMarkets(_allPlotMarkets); - predictionToken = IToken(_predictionToken); + predictionToken = _predictionToken; router = IUniswapV2Router(_router); nativeCurrencyAddress = _nativeCurrencyAddress; } @@ -73,19 +82,42 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { * @param _prediction Option in the market to place prediction * @param _bPLOTPredictionAmount Bplot amount of `_predictFor` user to be used for prediction */ - function swapAndPlacePrediction(address[] calldata _path, uint _inputAmount, address _predictFor, uint _marketId, uint _prediction, uint64 _bPLOTPredictionAmount) external payable { - uint deadline = now*2; - uint amountOutMin = 1; - require(_path[_path.length-1] == address(predictionToken)); + function swapAndPlacePrediction( + address[] memory _path, + uint _inputAmount, + address _predictFor, + uint _marketId, + uint _prediction, + uint64 _bPLOTPredictionAmount + ) public payable + // Contract should not hold any of input/output tokens provided in transaction + holdNoFunds(_path) + { + require(_path[_path.length-1] == predictionToken); + address payable _msgSenderAddress = _msgSender(); if(_bPLOTPredictionAmount > 0) { // bPLOT can not be used if another user is placing proxy prediction - require(_msgSender() == _predictFor); + require(_msgSenderAddress == _predictFor); } - bool _isNativeToken = (_path[0] == nativeCurrencyAddress && msg.value >0); - // uint _initialFromTokenBalance = getTokenBalance(_path[0], _isNativeToken); - // uint _initialToTokenBalance = getTokenBalance(_path[_path.length-1], false); + + uint _tokenDeposit = _swapUserTokens(_path, _inputAmount, _msgSenderAddress); + + _provideApproval(predictionToken, address(allPlotMarkets), _tokenDeposit); + allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, predictionToken, _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); + emit SwapAndPredictFor(_predictFor, _marketId, _path[0], predictionToken, _inputAmount, _tokenDeposit); + } + + /** + * @dev Internal function to swap given user tokens to desired preditcion token + * @param _path Order path to follow for swap transaction + * @param _inputAmount Amount of tokens to swap from. In Wei + * @param _msgSenderAddress Address of user who signed the transaction + */ + function _swapUserTokens(address[] memory _path, uint256 _inputAmount, address _msgSenderAddress) internal returns(uint256 outputAmount) { uint[] memory _output; - if(_isNativeToken) { + uint deadline = now*2; + uint amountOutMin = 1; + if((_path[0] == nativeCurrencyAddress && msg.value >0)) { require(_inputAmount == msg.value); _output = router.swapExactETHForTokens.value(msg.value)( amountOutMin, @@ -95,9 +127,8 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { ); } else { require(msg.value == 0); - address payable _msgSenderAddress = _msgSender(); - IToken(_path[0]).transferFrom(_msgSenderAddress, address(this), _inputAmount); - IToken(_path[0]).approve(address(router), _inputAmount); + _transferTokenFrom(_path[0], _msgSenderAddress, address(this), _inputAmount); + _provideApproval(_path[0], address(router), _inputAmount); _output = router.swapExactTokensForTokens( _inputAmount, amountOutMin, @@ -106,12 +137,28 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { deadline ); } - uint _tokenDeposit = _output[_output.length - 1]; - emit SwapAndPredictFor(_predictFor, _marketId, _path[0], address(predictionToken), _inputAmount, _output[1]); - predictionToken.approve(address(allPlotMarkets), _tokenDeposit); - allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, address(predictionToken), _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); - // require(_initialFromTokenBalance == getTokenBalance(_path[0], _isNativeToken)); - // require(_initialToTokenBalance == getTokenBalance(_path[_path.length-1], false)); + return _output[_output.length - 1]; + } + + /** + * @dev Internal function to provide approval to external address from this contract + * @param _tokenAddress Address of the ERC20 token + * @param _spender Address, indented to spend the tokens + * @param _amount Amount of tokens to provide approval for. In Wei + */ + function _provideApproval(address _tokenAddress, address _spender, uint256 _amount) internal { + IToken(_tokenAddress).approve(_spender, _amount); + } + + /** + * @dev Internal function to call transferFrom function of a given token + * @param _token Address of the ERC20 token + * @param _from Address from which the tokens are to be received + * @param _to Address to which the tokens are to be transferred + * @param _amount Amount of tokens to transfer. In Wei + */ + function _transferTokenFrom(address _token, address _from, address _to, uint256 _amount) internal { + IToken(_token).transferFrom(_from, _to, _amount); } /** From 7b5daba9965e484932d9001d80eddda906b6b266 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 28 May 2021 15:54:37 +0530 Subject: [PATCH 12/33] Wrap transfer in require condition --- contracts/SwapAndPredictWithPlot.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 49ed7589..62017db0 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -158,7 +158,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { * @param _amount Amount of tokens to transfer. In Wei */ function _transferTokenFrom(address _token, address _from, address _to, uint256 _amount) internal { - IToken(_token).transferFrom(_from, _to, _amount); + require(IToken(_token).transferFrom(_from, _to, _amount)); } /** @@ -180,6 +180,6 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { */ function transferLeftOverTokens(address _token, address _recipient) external onlyAuthorized { require(_token != address(0)); - IToken(_token).transfer(_recipient, getTokenBalance(_token, false)); + require(IToken(_token).transfer(_recipient, getTokenBalance(_token, false))); } } From c4ae3441339b1c6c61b7c6356a55f14e108ab070 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 28 May 2021 19:19:10 +0530 Subject: [PATCH 13/33] Fixed internal audit issues --- contracts/SwapAndPredictWithPlot.sol | 21 +++++++++++++-------- migrations/4_initiate_2.js | 2 -- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 62017db0..cdf77c00 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -22,6 +22,7 @@ import "./interfaces/IAllMarkets.sol"; import "./interfaces/IToken.sol"; import "./interfaces/ISwapRouter.sol"; import "./interfaces/IAuth.sol"; +import "./interfaces/IMaster.sol"; contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { @@ -32,16 +33,18 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { IAllMarkets internal allPlotMarkets; IUniswapV2Router internal router; address internal predictionToken; + IMaster internal master; address public nativeCurrencyAddress; address public defaultAuthorized; + uint public maxSlippage; modifier holdNoFunds(address[] memory _path) { bool _isNativeToken = (_path[0] == nativeCurrencyAddress && msg.value >0); uint _initialFromTokenBalance = getTokenBalance(_path[0], _isNativeToken); uint _initialToTokenBalance = getTokenBalance(_path[_path.length-1], false); _; - require(_initialFromTokenBalance == getTokenBalance(_path[0], _isNativeToken)); + require(_initialFromTokenBalance.sub(msg.value) == getTokenBalance(_path[0], _isNativeToken)); require(_initialToTokenBalance == getTokenBalance(_path[_path.length-1], false)); } @@ -55,20 +58,21 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { require(msg.sender == proxy.proxyOwner()); authorized = _authorizedMultiSig; defaultAuthorized = _defaultAuthorizedAddress; + master = IMaster(msg.sender); _initializeEIP712("SP"); } /** * @dev Initiate the contract with required addresses - * @param _allPlotMarkets AllPlotMarkets contract address - * @param _predictionToken Address of token used for placing predictions * @param _router Router address of exchange to be used for swap transactions * @param _nativeCurrencyAddress Wrapped token address of Native currency of network/chain */ - function initiate(address _allPlotMarkets, address _predictionToken, address _router, address _nativeCurrencyAddress) external { + function initiate(address _router, address _nativeCurrencyAddress) external { require(msg.sender == defaultAuthorized); - allPlotMarkets = IAllMarkets(_allPlotMarkets); - predictionToken = _predictionToken; + require(predictionToken == address(0));// Already Initialized + maxSlippage = 300; //With two decimals + allPlotMarkets = IAllMarkets(master.getLatestAddress("AM")); + predictionToken = master.dAppToken(); router = IUniswapV2Router(_router); nativeCurrencyAddress = _nativeCurrencyAddress; } @@ -116,7 +120,8 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { function _swapUserTokens(address[] memory _path, uint256 _inputAmount, address _msgSenderAddress) internal returns(uint256 outputAmount) { uint[] memory _output; uint deadline = now*2; - uint amountOutMin = 1; + //Min output = Inpute - (Input * Slippage/100) + uint amountOutMin =_inputAmount.sub(_inputAmount.mul(maxSlippage).div(10000)); if((_path[0] == nativeCurrencyAddress && msg.value >0)) { require(_inputAmount == msg.value); _output = router.swapExactETHForTokens.value(msg.value)( @@ -166,7 +171,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { * @param _token Address of token to query balance for * @param _isNativeCurrency Falg defining if the balance needed to be fetched for native currency of the network/chain */ - function getTokenBalance(address _token, bool _isNativeCurrency) public returns(uint) { + function getTokenBalance(address _token, bool _isNativeCurrency) public view returns(uint) { if(_isNativeCurrency) { return ((address(this)).balance); } diff --git a/migrations/4_initiate_2.js b/migrations/4_initiate_2.js index 917397c9..8fe66fb4 100644 --- a/migrations/4_initiate_2.js +++ b/migrations/4_initiate_2.js @@ -21,8 +21,6 @@ module.exports = function(deployer, network, accounts){ await allMarkets.addAuthorizedProxyPreditictor(swapAnPredict.address); let router = await deployer.deploy(MockUniswapRouter, await master.dAppToken()); await swapAnPredict.initiate( - allMarkets.address, - await master.dAppToken(), router.address, await router.WETH() ); From 5ca9b3f1505a1d900af9ea37ac12c10fa2d8ca52 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 28 May 2021 19:28:39 +0530 Subject: [PATCH 14/33] Added null address checks --- contracts/AllPlotMarkets_2.sol | 1 + contracts/SwapAndPredictWithPlot.sol | 2 ++ 2 files changed, 3 insertions(+) diff --git a/contracts/AllPlotMarkets_2.sol b/contracts/AllPlotMarkets_2.sol index dec4d6f3..80c37bea 100644 --- a/contracts/AllPlotMarkets_2.sol +++ b/contracts/AllPlotMarkets_2.sol @@ -22,6 +22,7 @@ contract AllPlotMarkets_2 is AllPlotMarkets { mapping(address => bool) public authToProxyPrediction; function addAuthorizedProxyPreditictor(address _proxyAddress) external onlyAuthorized { + require(_proxyAddress != address(0)); authToProxyPrediction[_proxyAddress] = true; } diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index cdf77c00..2444b2ad 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -70,6 +70,8 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { function initiate(address _router, address _nativeCurrencyAddress) external { require(msg.sender == defaultAuthorized); require(predictionToken == address(0));// Already Initialized + require(_router != address(0)); + require(_nativeCurrencyAddress != address(0)); maxSlippage = 300; //With two decimals allPlotMarkets = IAllMarkets(master.getLatestAddress("AM")); predictionToken = master.dAppToken(); From df99ace0d0390180702ad3da3eda4d34e6bf5445 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 28 May 2021 20:25:49 +0530 Subject: [PATCH 15/33] Fixed broken testcases --- contracts/mock/MockAllMarkets_2.sol | 12 ++++++++++++ migrations/4_initiate_2.js | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 contracts/mock/MockAllMarkets_2.sol diff --git a/contracts/mock/MockAllMarkets_2.sol b/contracts/mock/MockAllMarkets_2.sol new file mode 100644 index 00000000..3b9f53df --- /dev/null +++ b/contracts/mock/MockAllMarkets_2.sol @@ -0,0 +1,12 @@ +pragma solidity 0.5.7; + +import "../AllPlotMarkets_2.sol"; + +contract MockAllMarkets_2 is AllPlotMarkets_2 { + + function postResultMock(uint _val, uint _marketId) external { + _postResult(_val, _marketId); + } + + +} \ No newline at end of file diff --git a/migrations/4_initiate_2.js b/migrations/4_initiate_2.js index 8fe66fb4..b4dd14ac 100644 --- a/migrations/4_initiate_2.js +++ b/migrations/4_initiate_2.js @@ -1,4 +1,4 @@ -const AllPlotMarkets_2 = artifacts.require('AllPlotMarkets_2'); +const AllPlotMarkets_2 = artifacts.require('MockAllMarkets_2'); const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy'); const Master = artifacts.require('Master'); const SwapAndPredictWithPlot = artifacts.require('SwapAndPredictWithPlot'); @@ -9,7 +9,7 @@ module.exports = function(deployer, network, accounts){ deployer.then(async () => { let master = await OwnedUpgradeabilityProxy.deployed(); master = await Master.at(master.address); - let allMarketsNewImpl = await await deployer.deploy(AllPlotMarkets_2); + let allMarketsNewImpl = await deployer.deploy(AllPlotMarkets_2); let spImpl = await deployer.deploy(SwapAndPredictWithPlot); await master.upgradeMultipleImplementations( [web3.utils.toHex("AM")], From 2d607852c26594f5ac19c7abb5ddb84c8d609de9 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 28 May 2021 20:45:21 +0530 Subject: [PATCH 16/33] Added test cases for ETH based predictions --- test/SwapAndPredict.js | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index 013e9b0b..6cbad342 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -115,14 +115,19 @@ contract("Rewards-Market", async function(users) { let spPlotBalanceBefore = await plotusToken.balanceOf(spInstance.address); let spTokenBalanceBefore = await externalToken.balanceOf(spInstance.address); // await spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0) - let functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0); - await signAndExecuteMetaTx( - privateKeyList[i], - users[i], - functionSignature, - spInstance, - "SP" - ); + let functionSignature; + if(i == 2) { + await spInstance.swapAndPlacePrediction(["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount}); + } else { + functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0); + await signAndExecuteMetaTx( + privateKeyList[i], + users[i], + functionSignature, + spInstance, + "SP" + ); + } let spPlotBalanceAfter = await plotusToken.balanceOf(spInstance.address); let spTokenBalanceAfter = await externalToken.balanceOf(spInstance.address); await assert.equal(spPlotBalanceAfter/1, spPlotBalanceBefore/1); From c59cd2be754ff52aa1ab2dd062bef031dad5fbf8 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Fri, 28 May 2021 20:51:37 +0530 Subject: [PATCH 17/33] Updated comments --- contracts/AllPlotMarkets_2.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/AllPlotMarkets_2.sol b/contracts/AllPlotMarkets_2.sol index 80c37bea..a744a487 100644 --- a/contracts/AllPlotMarkets_2.sol +++ b/contracts/AllPlotMarkets_2.sol @@ -21,6 +21,10 @@ contract AllPlotMarkets_2 is AllPlotMarkets { mapping(address => bool) public authToProxyPrediction; + /** + * @dev Function to add an authorized address to place proxy predictions + * @param _proxyAddress Address to whitelist + */ function addAuthorizedProxyPreditictor(address _proxyAddress) external onlyAuthorized { require(_proxyAddress != address(0)); authToProxyPrediction[_proxyAddress] = true; From 30dc70339cc5498bb01a252399bfbe063f10570e Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Sat, 29 May 2021 00:18:43 +0530 Subject: [PATCH 18/33] Updated testcases --- contracts/SwapAndPredictWithPlot.sol | 11 +---------- test/SwapAndPredict.js | 20 +++++++++++++++++++- test/SwapAndPredict_BPLOT.js | 14 ++++++++------ 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 2444b2ad..94664050 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -69,9 +69,9 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { */ function initiate(address _router, address _nativeCurrencyAddress) external { require(msg.sender == defaultAuthorized); - require(predictionToken == address(0));// Already Initialized require(_router != address(0)); require(_nativeCurrencyAddress != address(0)); + require(predictionToken == address(0));// Already Initialized maxSlippage = 300; //With two decimals allPlotMarkets = IAllMarkets(master.getLatestAddress("AM")); predictionToken = master.dAppToken(); @@ -180,13 +180,4 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { return IToken(_token).balanceOf(address(this)); } - /** - * @dev Transfer any left over token in contract to given address - * @param _token Address of token to transfer - * @param _recipient Address of token recipient - */ - function transferLeftOverTokens(address _token, address _recipient) external onlyAuthorized { - require(_token != address(0)); - require(IToken(_token).transfer(_recipient, getTokenBalance(_token, false))); - } } diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index 6cbad342..c7f6d194 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -59,6 +59,21 @@ contract("Rewards-Market", async function(users) { disputeResolution = await DisputeResolution.at(await masterInstance.getLatestAddress(web3.utils.toHex("DR"))); spInstance = await SwapAndPredictWithPlot.at(await masterInstance.getLatestAddress(web3.utils.toHex("SP"))); + let nullAddress = await masterInstance.getLatestAddress("0x0000"); + await assertRevert(allMarkets.addAuthorizedProxyPreditictor(nullAddress)); + await assertRevert(spInstance.initiate( + nullAddress, + await router.WETH() + )); + await assertRevert(spInstance.initiate( + router.address, + nullAddress + )); + await assertRevert(spInstance.initiate( + router.address, + await router.WETH() + )); + await assertRevert(spInstance.initiate(users[0], users[1], {from:users[1]})); externalToken = await SampleERC.new("USDP", "USDP"); await externalToken.mint(users[0], toWei(1000000)); plotTokenPrice = 0.01; @@ -69,7 +84,6 @@ contract("Rewards-Market", async function(users) { await plotusToken.transfer(users[11],toWei(100000)); // await plotusToken.transfer(marketIncentives.address,toWei(500)); - let nullAddress = "0x0000000000000000000000000000"; await plotusToken.transfer(users[11],toWei(100)); await plotusToken.approve(allMarkets.address,toWei(200000),{from:users[11]}); @@ -117,8 +131,12 @@ contract("Rewards-Market", async function(users) { // await spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0) let functionSignature; if(i == 2) { + await assertRevert(allMarkets.depositAndPredictFor(users[0], _inputAmount, 7, plotusToken.address, 1, _inputAmount, 0)); + await assertRevert(spInstance.swapAndPlacePrediction(["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount*2})); await spInstance.swapAndPlacePrediction(["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount}); } else { + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, externalToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i]})); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount})); functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0); await signAndExecuteMetaTx( privateKeyList[i], diff --git a/test/SwapAndPredict_BPLOT.js b/test/SwapAndPredict_BPLOT.js index 833c184a..c0a9a19a 100644 --- a/test/SwapAndPredict_BPLOT.js +++ b/test/SwapAndPredict_BPLOT.js @@ -106,14 +106,16 @@ describe("newPlotusWithBlot", () => { await externalToken.transfer(users[i], _inputAmount); let functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100)); await cyclicMarkets.setNextOptionPrice(options[i]*9); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100))); // await spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), {from:users[i]}); await signAndExecuteMetaTx( - pkList[i], - users[i], - functionSignature, - spInstance, - "SP" - ); + pkList[i], + users[i], + functionSignature, + spInstance, + "SP" + ); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), {from:users[i]})); // predictionToken = plotusToken.address; // await plotusToken.transfer(users[i], toWei(100)); From 529829787b7d1e8ac577098d1e6cf3430a941f72 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Sat, 29 May 2021 10:58:45 +0530 Subject: [PATCH 19/33] Updated badge --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b4dcba0..be6643a0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -[![Build Status](https://travis-ci.org/plotx/smart-contracts-L2.svg?branch=merge-finalDeployment)](https://travis-ci.org/plotx/smart-contracts-L2) +[![Build Status](https://travis-ci.org/plotx/smart-contracts-L2.svg?branch=feature/predictFor)](https://travis-ci.org/plotx/smart-contracts-L2) -[![Coverage Status](https://coveralls.io/repos/github/plotx/smart-contracts-L2/badge.svg?branch=merge-finalDeployment)](https://coveralls.io/github/plotx/smart-contracts-L2) +[![Coverage Status](https://coveralls.io/repos/github/plotx/smart-contracts-L2/badge.svg?branch=feature/predictFor)](https://coveralls.io/github/plotx/smart-contracts-L2)

PlotX SMART CONTRACTS

Smart contracts for PlotX - Curated prediction markets for crypto traders . https://plotx.io/.

From 10c792071364c515917661590ac76a59c318bdff Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 11:14:20 +0530 Subject: [PATCH 20/33] Added function to withdraw any assets left in contract --- contracts/SwapAndPredictWithPlot.sol | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 94664050..cbe4b718 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -15,6 +15,7 @@ pragma solidity 0.5.7; +import "./external/openzeppelin-solidity/utils/ReentrancyGuard.sol"; import "./external/openzeppelin-solidity/math/SafeMath.sol"; import "./external/proxy/OwnedUpgradeabilityProxy.sol"; import "./external/NativeMetaTransaction.sol"; @@ -24,7 +25,7 @@ import "./interfaces/ISwapRouter.sol"; import "./interfaces/IAuth.sol"; import "./interfaces/IMaster.sol"; -contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { +contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth, ReentrancyGuard { using SafeMath for uint; @@ -180,4 +181,19 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { return IToken(_token).balanceOf(address(this)); } + /** + * @dev Transfer `_amount` of assets to `_to` address + */ + function transferAssets(address _asset, address payable _to, uint _amount, bool _isNativeCurrency) external + onlyAuthorized + nonReentrant + { + require(_to != address(0)); + if(_isNativeCurrency && _asset == nativeCurrencyAddress) { + _to.transfer(_amount); + } else { + require(IToken(_asset).transfer(_to, _amount)); + } + } + } From 00d47e20bce98241beeafd32e57a432dbaf0aa48 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 11:52:34 +0530 Subject: [PATCH 21/33] Removed function to withdraw any left over assets --- contracts/SwapAndPredictWithPlot.sol | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index cbe4b718..94664050 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -15,7 +15,6 @@ pragma solidity 0.5.7; -import "./external/openzeppelin-solidity/utils/ReentrancyGuard.sol"; import "./external/openzeppelin-solidity/math/SafeMath.sol"; import "./external/proxy/OwnedUpgradeabilityProxy.sol"; import "./external/NativeMetaTransaction.sol"; @@ -25,7 +24,7 @@ import "./interfaces/ISwapRouter.sol"; import "./interfaces/IAuth.sol"; import "./interfaces/IMaster.sol"; -contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth, ReentrancyGuard { +contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { using SafeMath for uint; @@ -181,19 +180,4 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth, ReentrancyGuard return IToken(_token).balanceOf(address(this)); } - /** - * @dev Transfer `_amount` of assets to `_to` address - */ - function transferAssets(address _asset, address payable _to, uint _amount, bool _isNativeCurrency) external - onlyAuthorized - nonReentrant - { - require(_to != address(0)); - if(_isNativeCurrency && _asset == nativeCurrencyAddress) { - _to.transfer(_amount); - } else { - require(IToken(_asset).transfer(_to, _amount)); - } - } - } From 9a61df93ad264a99463261fc2f896f294030254e Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 11:53:32 +0530 Subject: [PATCH 22/33] Updated testcases --- contracts/mock/MockRouter.sol | 12 ++++++++--- migrations/4_initiate_2.js | 3 +++ test/SwapAndPredict.js | 38 +++++++++++++++++++++++------------ 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/contracts/mock/MockRouter.sol b/contracts/mock/MockRouter.sol index 89ff0afa..f5be24d5 100644 --- a/contracts/mock/MockRouter.sol +++ b/contracts/mock/MockRouter.sol @@ -4,19 +4,25 @@ import "../interfaces/ISwapRouter.sol"; import "../external/openzeppelin-solidity/math/SafeMath.sol"; import "../interfaces/IToken.sol"; -contract MockUniswapRouter is IUniswapV2Router { +contract MockUniswapRouter { using SafeMath for uint; uint public priceOfToken = 1e16; address token; + address weth; constructor(address _token) public { token = _token; + weth = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; } - function WETH() external pure returns (address) { - return 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + function WETH() external view returns (address) { + return weth; + } + + function setWETH(address _weth) external { + weth = _weth; } function setPrice(uint _newPrice) external { diff --git a/migrations/4_initiate_2.js b/migrations/4_initiate_2.js index b4dd14ac..5197f873 100644 --- a/migrations/4_initiate_2.js +++ b/migrations/4_initiate_2.js @@ -3,6 +3,7 @@ const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy'); const Master = artifacts.require('Master'); const SwapAndPredictWithPlot = artifacts.require('SwapAndPredictWithPlot'); const MockUniswapRouter = artifacts.require('MockUniswapRouter'); +const SampleERC = artifacts.require('SampleERC'); const { assert } = require("chai"); module.exports = function(deployer, network, accounts){ @@ -20,6 +21,8 @@ module.exports = function(deployer, network, accounts){ allMarkets = await AllPlotMarkets_2.at(await master.getLatestAddress(web3.utils.toHex('AM'))); await allMarkets.addAuthorizedProxyPreditictor(swapAnPredict.address); let router = await deployer.deploy(MockUniswapRouter, await master.dAppToken()); + _weth = await SampleERC.new("WETH", "WETH"); + await router.setWETH(_weth.address); await swapAnPredict.initiate( router.address, await router.WETH() diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index c7f6d194..2c5a303e 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -59,7 +59,7 @@ contract("Rewards-Market", async function(users) { disputeResolution = await DisputeResolution.at(await masterInstance.getLatestAddress(web3.utils.toHex("DR"))); spInstance = await SwapAndPredictWithPlot.at(await masterInstance.getLatestAddress(web3.utils.toHex("SP"))); - let nullAddress = await masterInstance.getLatestAddress("0x0000"); + nullAddress = await masterInstance.getLatestAddress("0x0000"); await assertRevert(allMarkets.addAuthorizedProxyPreditictor(nullAddress)); await assertRevert(spInstance.initiate( nullAddress, @@ -74,7 +74,10 @@ contract("Rewards-Market", async function(users) { await router.WETH() )); await assertRevert(spInstance.initiate(users[0], users[1], {from:users[1]})); - externalToken = await SampleERC.new("USDP", "USDP"); + externalToken = await SampleERC.new("USDP", "USDP"); + _weth = await SampleERC.at(await spInstance.nativeCurrencyAddress()); + await router.setWETH(_weth.address); + await _weth.mint(users[0], toWei(1000000)); await externalToken.mint(users[0], toWei(1000000)); plotTokenPrice = 0.01; externalTokenPrice = 1/plotTokenPrice; @@ -131,20 +134,29 @@ contract("Rewards-Market", async function(users) { // await spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0) let functionSignature; if(i == 2) { + //Predict with eth await assertRevert(allMarkets.depositAndPredictFor(users[0], _inputAmount, 7, plotusToken.address, 1, _inputAmount, 0)); - await assertRevert(spInstance.swapAndPlacePrediction(["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount*2})); - await spInstance.swapAndPlacePrediction(["0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount}); + await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount*2})); + await spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount}); } else { - await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, externalToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i]})); - await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount})); - functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0); + if(i == 3) { + //Predict with WETH + await _weth.approve(spInstance.address, _inputAmount, {from:users[i]}); + await _weth.transfer(users[i], _inputAmount); + // await spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount}); + functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0); + } else { + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, externalToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i]})); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount})); + functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0); + } await signAndExecuteMetaTx( - privateKeyList[i], - users[i], - functionSignature, - spInstance, - "SP" - ); + privateKeyList[i], + users[i], + functionSignature, + spInstance, + "SP" + ); } let spPlotBalanceAfter = await plotusToken.balanceOf(spInstance.address); let spTokenBalanceAfter = await externalToken.balanceOf(spInstance.address); From 06e9cf4333d0a242b4be6cccb33576c7fd64b145 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 12:37:14 +0530 Subject: [PATCH 23/33] Updated testcases --- test/SwapAndPredict.js | 1 + test/SwapAndPredict_BPLOT.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index 2c5a303e..06f5ba2e 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -96,6 +96,7 @@ contract("Rewards-Market", async function(users) { await cyclicMarkets.createMarket(0, 0, 0,{from:users[11],gasPrice:500000}); // await assertRevert(marketIncentives.setMasterAddress(users[0], users[0])); await assertRevert(allMarkets.setMasterAddress(users[0], users[0])); + await assertRevert(spInstance.setMasterAddress(users[0], users[0])); await assertRevert(allMarkets.setMarketStatus(6, 1)); await assertRevert(cyclicMarkets.setReferralContract(users[0])); var date = Date.now(); diff --git a/test/SwapAndPredict_BPLOT.js b/test/SwapAndPredict_BPLOT.js index c0a9a19a..037af5b2 100644 --- a/test/SwapAndPredict_BPLOT.js +++ b/test/SwapAndPredict_BPLOT.js @@ -102,8 +102,8 @@ describe("newPlotusWithBlot", () => { // Predict with some plot and some bPLOT let _inputAmount = toWei(100*plotTokenPrice); - await externalToken.approve(spInstance.address, _inputAmount, {from:users[i]}); - await externalToken.transfer(users[i], _inputAmount); + await externalToken.approve(spInstance.address, toWei(1000), {from:users[i]}); + await externalToken.transfer(users[i], toWei(1000)); let functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100)); await cyclicMarkets.setNextOptionPrice(options[i]*9); await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100))); From f908547a521fe0f91d02e38b20de248daa2ec78e Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 13:36:10 +0530 Subject: [PATCH 24/33] Removed harcoding --- contracts/SwapAndPredictWithPlot.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 94664050..ccd5c017 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -38,6 +38,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { address public nativeCurrencyAddress; address public defaultAuthorized; uint public maxSlippage; + uint internal decimalDivider; modifier holdNoFunds(address[] memory _path) { bool _isNativeToken = (_path[0] == nativeCurrencyAddress && msg.value >0); @@ -77,6 +78,8 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { predictionToken = master.dAppToken(); router = IUniswapV2Router(_router); nativeCurrencyAddress = _nativeCurrencyAddress; + //Prediction decimals are 10^8, so to convert standard decimal count to prediction supported decimals + decimalDivider = 10**10; } /** @@ -109,7 +112,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { uint _tokenDeposit = _swapUserTokens(_path, _inputAmount, _msgSenderAddress); _provideApproval(predictionToken, address(allPlotMarkets), _tokenDeposit); - allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, predictionToken, _prediction, uint64(_tokenDeposit.div(10**10)), _bPLOTPredictionAmount); + allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, predictionToken, _prediction, uint64(_tokenDeposit.div(decimalDivider)), _bPLOTPredictionAmount); emit SwapAndPredictFor(_predictFor, _marketId, _path[0], predictionToken, _inputAmount, _tokenDeposit); } From 3231417d98a0e0b7b83e6e781ddfa8873f95d6a3 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 14:45:03 +0530 Subject: [PATCH 25/33] Removed slippage --- contracts/SwapAndPredictWithPlot.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index ccd5c017..4e5dc76e 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -37,7 +37,6 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { address public nativeCurrencyAddress; address public defaultAuthorized; - uint public maxSlippage; uint internal decimalDivider; modifier holdNoFunds(address[] memory _path) { @@ -73,7 +72,6 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { require(_router != address(0)); require(_nativeCurrencyAddress != address(0)); require(predictionToken == address(0));// Already Initialized - maxSlippage = 300; //With two decimals allPlotMarkets = IAllMarkets(master.getLatestAddress("AM")); predictionToken = master.dAppToken(); router = IUniswapV2Router(_router); @@ -125,8 +123,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { function _swapUserTokens(address[] memory _path, uint256 _inputAmount, address _msgSenderAddress) internal returns(uint256 outputAmount) { uint[] memory _output; uint deadline = now*2; - //Min output = Inpute - (Input * Slippage/100) - uint amountOutMin =_inputAmount.sub(_inputAmount.mul(maxSlippage).div(10000)); + uint amountOutMin = 1; if((_path[0] == nativeCurrencyAddress && msg.value >0)) { require(_inputAmount == msg.value); _output = router.swapExactETHForTokens.value(msg.value)( From 82ac455acd8ae8c02523a72bdc0fda04fbe64165 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 18:10:41 +0530 Subject: [PATCH 26/33] Read min output amount --- contracts/SwapAndPredictWithPlot.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index 4e5dc76e..e3271ac8 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -95,7 +95,8 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { address _predictFor, uint _marketId, uint _prediction, - uint64 _bPLOTPredictionAmount + uint64 _bPLOTPredictionAmount, + uint _minOutput ) public payable // Contract should not hold any of input/output tokens provided in transaction holdNoFunds(_path) @@ -107,7 +108,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { require(_msgSenderAddress == _predictFor); } - uint _tokenDeposit = _swapUserTokens(_path, _inputAmount, _msgSenderAddress); + uint _tokenDeposit = _swapUserTokens(_path, _inputAmount, _msgSenderAddress, _minOutput); _provideApproval(predictionToken, address(allPlotMarkets), _tokenDeposit); allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, predictionToken, _prediction, uint64(_tokenDeposit.div(decimalDivider)), _bPLOTPredictionAmount); @@ -120,14 +121,13 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { * @param _inputAmount Amount of tokens to swap from. In Wei * @param _msgSenderAddress Address of user who signed the transaction */ - function _swapUserTokens(address[] memory _path, uint256 _inputAmount, address _msgSenderAddress) internal returns(uint256 outputAmount) { + function _swapUserTokens(address[] memory _path, uint256 _inputAmount, address _msgSenderAddress, uint _minOutput) internal returns(uint256 outputAmount) { uint[] memory _output; uint deadline = now*2; - uint amountOutMin = 1; if((_path[0] == nativeCurrencyAddress && msg.value >0)) { require(_inputAmount == msg.value); _output = router.swapExactETHForTokens.value(msg.value)( - amountOutMin, + _minOutput, _path, address(this), deadline @@ -138,7 +138,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { _provideApproval(_path[0], address(router), _inputAmount); _output = router.swapExactTokensForTokens( _inputAmount, - amountOutMin, + _minOutput, _path, address(this), deadline From 248803ece87dbe1fdc2624421e7c94602c607e3b Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 18:18:22 +0530 Subject: [PATCH 27/33] Minor fix --- contracts/SwapAndPredictWithPlot.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index e3271ac8..b77dfb67 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -109,7 +109,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { } uint _tokenDeposit = _swapUserTokens(_path, _inputAmount, _msgSenderAddress, _minOutput); - + require(_tokenDeposit >= _minOutput); _provideApproval(predictionToken, address(allPlotMarkets), _tokenDeposit); allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, predictionToken, _prediction, uint64(_tokenDeposit.div(decimalDivider)), _bPLOTPredictionAmount); emit SwapAndPredictFor(_predictFor, _marketId, _path[0], predictionToken, _inputAmount, _tokenDeposit); From caf6e51bff467ad86d4fa20105f18a5406ce6208 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 18:44:55 +0530 Subject: [PATCH 28/33] Removed stack too deep erro --- contracts/SwapAndPredictWithPlot.sol | 31 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index b77dfb67..b862b1a0 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -101,29 +101,25 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { // Contract should not hold any of input/output tokens provided in transaction holdNoFunds(_path) { - require(_path[_path.length-1] == predictionToken); - address payable _msgSenderAddress = _msgSender(); if(_bPLOTPredictionAmount > 0) { // bPLOT can not be used if another user is placing proxy prediction - require(_msgSenderAddress == _predictFor); + require(_msgSender() == _predictFor); } - uint _tokenDeposit = _swapUserTokens(_path, _inputAmount, _msgSenderAddress, _minOutput); - require(_tokenDeposit >= _minOutput); - _provideApproval(predictionToken, address(allPlotMarkets), _tokenDeposit); - allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, predictionToken, _prediction, uint64(_tokenDeposit.div(decimalDivider)), _bPLOTPredictionAmount); - emit SwapAndPredictFor(_predictFor, _marketId, _path[0], predictionToken, _inputAmount, _tokenDeposit); + _swapAndPlacePrediction(_path, _inputAmount, _minOutput, _predictFor, _marketId, _prediction, _bPLOTPredictionAmount); } /** - * @dev Internal function to swap given user tokens to desired preditcion token + * @dev Internal function to swap given user tokens to desired preditcion token and place prediction * @param _path Order path to follow for swap transaction * @param _inputAmount Amount of tokens to swap from. In Wei - * @param _msgSenderAddress Address of user who signed the transaction + * @param _minOutput Minimum output amount expected in swap */ - function _swapUserTokens(address[] memory _path, uint256 _inputAmount, address _msgSenderAddress, uint _minOutput) internal returns(uint256 outputAmount) { + function _swapAndPlacePrediction(address[] memory _path, uint256 _inputAmount, uint _minOutput, address _predictFor, uint _marketId, uint _prediction, uint64 _bPLOTPredictionAmount) internal { + address payable _msgSenderAddress = _msgSender(); uint[] memory _output; uint deadline = now*2; + require(_path[_path.length-1] == predictionToken); if((_path[0] == nativeCurrencyAddress && msg.value >0)) { require(_inputAmount == msg.value); _output = router.swapExactETHForTokens.value(msg.value)( @@ -144,7 +140,18 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { deadline ); } - return _output[_output.length - 1]; + require(_output[_output.length - 1] >= _minOutput); + // return _output[_output.length - 1]; + _placePrediction(_predictFor, _path[0], _output[_output.length - 1], _marketId, _prediction, _inputAmount, _bPLOTPredictionAmount); + } + + /** + * @dev Internal function to place prediction in given market + */ + function _placePrediction(address _predictFor, address _swapFrom, uint _tokenDeposit, uint _marketId, uint _prediction, uint _inputAmount, uint64 _bPLOTPredictionAmount) internal { + _provideApproval(predictionToken, address(allPlotMarkets), _tokenDeposit); + allPlotMarkets.depositAndPredictFor(_predictFor, _tokenDeposit, _marketId, predictionToken, _prediction, uint64(_tokenDeposit.div(decimalDivider)), _bPLOTPredictionAmount); + emit SwapAndPredictFor(_predictFor, _marketId, _swapFrom, predictionToken, _inputAmount, _tokenDeposit); } /** From 49df691f0a6b73d89c616c60202d1f8d2cebe3cf Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 18:45:05 +0530 Subject: [PATCH 29/33] Fixed broken testcases --- test/SwapAndPredict.js | 13 +++++++------ test/SwapAndPredict_BPLOT.js | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index 06f5ba2e..8c0c31d8 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -137,19 +137,20 @@ contract("Rewards-Market", async function(users) { if(i == 2) { //Predict with eth await assertRevert(allMarkets.depositAndPredictFor(users[0], _inputAmount, 7, plotusToken.address, 1, _inputAmount, 0)); - await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount*2})); - await spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount}); + await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, 1, {from:users[i], value:_inputAmount*2})); + await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, toWei(1000), {from:users[i], value:_inputAmount})); + await spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i], {from:users[i], value:_inputAmount}); } else { if(i == 3) { //Predict with WETH await _weth.approve(spInstance.address, _inputAmount, {from:users[i]}); await _weth.transfer(users[i], _inputAmount); // await spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount}); - functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0); + functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64,uint256)", [await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i]); } else { - await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, externalToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i]})); - await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0, {from:users[i], value:_inputAmount})); - functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, externalToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i], {from:users[i]})); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i], {from:users[i], value:_inputAmount})); + functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64,uint256)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i]); } await signAndExecuteMetaTx( privateKeyList[i], diff --git a/test/SwapAndPredict_BPLOT.js b/test/SwapAndPredict_BPLOT.js index 037af5b2..d9a1e6e8 100644 --- a/test/SwapAndPredict_BPLOT.js +++ b/test/SwapAndPredict_BPLOT.js @@ -104,9 +104,9 @@ describe("newPlotusWithBlot", () => { let _inputAmount = toWei(100*plotTokenPrice); await externalToken.approve(spInstance.address, toWei(1000), {from:users[i]}); await externalToken.transfer(users[i], toWei(1000)); - let functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100)); + let functionSignature = encode3("swapAndPlacePrediction(address[],uint256,address,uint256,uint256,uint64,uint256)", [externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), 1); await cyclicMarkets.setNextOptionPrice(options[i]*9); - await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100))); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), 1)); // await spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), {from:users[i]}); await signAndExecuteMetaTx( pkList[i], @@ -115,7 +115,7 @@ describe("newPlotusWithBlot", () => { spInstance, "SP" ); - await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), {from:users[i]})); + await assertRevert(spInstance.swapAndPlacePrediction([externalToken.address, plotusToken.address], _inputAmount, users[i], 7, options[i], to8Power(predictionVal[i]-100), 1, {from:users[i]})); // predictionToken = plotusToken.address; // await plotusToken.transfer(users[i], toWei(100)); From 5f7268d3e30e1cea7e80ee3f92015356e1b8aa02 Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 19:14:39 +0530 Subject: [PATCH 30/33] Added null address check --- contracts/AllPlotMarkets_2.sol | 1 + test/SwapAndPredict.js | 1 + 2 files changed, 2 insertions(+) diff --git a/contracts/AllPlotMarkets_2.sol b/contracts/AllPlotMarkets_2.sol index a744a487..34c62c30 100644 --- a/contracts/AllPlotMarkets_2.sol +++ b/contracts/AllPlotMarkets_2.sol @@ -54,6 +54,7 @@ contract AllPlotMarkets_2 is AllPlotMarkets { * _plotPredictionAmount and _bPLOTPredictionAmount should be passed with 8 decimals, reduced it to 8 decimals to reduce the storage space of prediction data */ function depositAndPredictFor(address _predictFor, uint _tokenDeposit, uint _marketId, address _asset, uint256 _prediction, uint64 _plotPredictionAmount, uint64 _bPLOTPredictionAmount) external { + require(_predictFor != address(0)); address payable _msgSenderAddress = _msgSender(); require(authToProxyPrediction[_msgSenderAddress]); uint64 _predictionStake = _plotPredictionAmount.add(_bPLOTPredictionAmount); diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index 8c0c31d8..30d5fb24 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -139,6 +139,7 @@ contract("Rewards-Market", async function(users) { await assertRevert(allMarkets.depositAndPredictFor(users[0], _inputAmount, 7, plotusToken.address, 1, _inputAmount, 0)); await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, 1, {from:users[i], value:_inputAmount*2})); await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, toWei(1000), {from:users[i], value:_inputAmount})); + await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, nullAddress, 7, options[i], 0, predictionVal[i], {from:users[i], value:_inputAmount})); await spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i], {from:users[i], value:_inputAmount}); } else { if(i == 3) { From 1e66df461d60adfd6c4f2964138b5f0b081e512c Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 19:45:32 +0530 Subject: [PATCH 31/33] Allow only whitelisted tokens for swap and place prediction --- contracts/SwapAndPredictWithPlot.sol | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/contracts/SwapAndPredictWithPlot.sol b/contracts/SwapAndPredictWithPlot.sol index b862b1a0..6c8c3d8e 100644 --- a/contracts/SwapAndPredictWithPlot.sol +++ b/contracts/SwapAndPredictWithPlot.sol @@ -39,6 +39,8 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { address public defaultAuthorized; uint internal decimalDivider; + mapping(address => bool) public allowedTokens; + modifier holdNoFunds(address[] memory _path) { bool _isNativeToken = (_path[0] == nativeCurrencyAddress && msg.value >0); uint _initialFromTokenBalance = getTokenBalance(_path[0], _isNativeToken); @@ -62,6 +64,25 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { _initializeEIP712("SP"); } + /** + * @dev Allow a token to be used for swap and placing prediction + * @param _token Address of token contract + */ + function whitelistTokenForSwap(address _token) external onlyAuthorized { + require(_token != address(0)); + require(!allowedTokens[_token]); + allowedTokens[_token] = true; + } + + /** + * @dev Remove a token from whitelist to be used for swap and placing prediction + * @param _token Address of token contract + */ + function deWhitelistTokenForSwap(address _token) external onlyAuthorized { + require(allowedTokens[_token]); + allowedTokens[_token] = false; + } + /** * @dev Initiate the contract with required addresses * @param _router Router address of exchange to be used for swap transactions @@ -120,6 +141,7 @@ contract SwapAndPredictWithPlot is NativeMetaTransaction, IAuth { uint[] memory _output; uint deadline = now*2; require(_path[_path.length-1] == predictionToken); + require(allowedTokens[_path[0]],"Not allowed"); if((_path[0] == nativeCurrencyAddress && msg.value >0)) { require(_inputAmount == msg.value); _output = router.swapExactETHForTokens.value(msg.value)( From 2a3cf1c3c9d4b83889344f10ab3cd9b79287722f Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Mon, 31 May 2021 19:46:44 +0530 Subject: [PATCH 32/33] Fix broken testcases --- test/SwapAndPredict.js | 2 ++ test/SwapAndPredict_BPLOT.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index 30d5fb24..b7405229 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -114,6 +114,8 @@ contract("Rewards-Market", async function(users) { let daoCommissions = [0, 1.8, 6.4, 3.36, 1.968, 8, 11.2, 3.2, 0.8, 4.8, 2.4]; const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; await assertRevert(referral.setReferrer(ZERO_ADDRESS, ZERO_ADDRESS)); + await spInstance.whitelistTokenForSwap(await router.WETH()); + await spInstance.whitelistTokenForSwap(externalToken.address); for(i=1; i<11;i++){ if(i>1) { //Should not allow unauthorized address to set referrer diff --git a/test/SwapAndPredict_BPLOT.js b/test/SwapAndPredict_BPLOT.js index d9a1e6e8..a84195f6 100644 --- a/test/SwapAndPredict_BPLOT.js +++ b/test/SwapAndPredict_BPLOT.js @@ -100,6 +100,8 @@ describe("newPlotusWithBlot", () => { } if( i==4) { // Predict with some plot and some bPLOT + await spInstance.whitelistTokenForSwap(await router.WETH()); + await spInstance.whitelistTokenForSwap(externalToken.address); let _inputAmount = toWei(100*plotTokenPrice); await externalToken.approve(spInstance.address, toWei(1000), {from:users[i]}); From e6942e700dc8df3180d1e2e64d509cecc75fa65f Mon Sep 17 00:00:00 2001 From: udkreddySomish Date: Tue, 1 Jun 2021 16:34:39 +0530 Subject: [PATCH 33/33] Added token whitelist testcases --- test/SwapAndPredict.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/SwapAndPredict.js b/test/SwapAndPredict.js index b7405229..db1aeff6 100644 --- a/test/SwapAndPredict.js +++ b/test/SwapAndPredict.js @@ -115,8 +115,10 @@ contract("Rewards-Market", async function(users) { const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; await assertRevert(referral.setReferrer(ZERO_ADDRESS, ZERO_ADDRESS)); await spInstance.whitelistTokenForSwap(await router.WETH()); - await spInstance.whitelistTokenForSwap(externalToken.address); + await assertRevert(spInstance.whitelistTokenForSwap(nullAddress)); + await assertRevert(spInstance.whitelistTokenForSwap(await router.WETH())); for(i=1; i<11;i++){ + await spInstance.whitelistTokenForSwap(externalToken.address); if(i>1) { //Should not allow unauthorized address to set referrer await assertRevert(referral.setReferrer(users[1], users[i], {from:users[i]})); @@ -142,8 +144,11 @@ contract("Rewards-Market", async function(users) { await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, 1, {from:users[i], value:_inputAmount*2})); await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, toWei(1000), {from:users[i], value:_inputAmount})); await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, nullAddress, 7, options[i], 0, predictionVal[i], {from:users[i], value:_inputAmount})); + await spInstance.deWhitelistTokenForSwap(await router.WETH()); + await assertRevert(spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i], {from:users[i], value:_inputAmount})); + await spInstance.whitelistTokenForSwap(await router.WETH()); await spInstance.swapAndPlacePrediction([await router.WETH(), plotusToken.address], _inputAmount, users[i], 7, options[i], 0, predictionVal[i], {from:users[i], value:_inputAmount}); - } else { + } else { if(i == 3) { //Predict with WETH await _weth.approve(spInstance.address, _inputAmount, {from:users[i]}); @@ -167,7 +172,9 @@ contract("Rewards-Market", async function(users) { let spTokenBalanceAfter = await externalToken.balanceOf(spInstance.address); await assert.equal(spPlotBalanceAfter/1, spPlotBalanceBefore/1); await assert.equal(spTokenBalanceAfter/1, spTokenBalanceBefore/1); + await spInstance.deWhitelistTokenForSwap(externalToken.address); } + await assertRevert(spInstance.deWhitelistTokenForSwap(externalToken.address)); //SHould not add referrer if already placed prediction await assertRevert(referral.setReferrer(users[1], users[2]));