{"contractName":"FixedProductMarketMakerData","abi":[{"anonymous":false,"inputs":[{"indexed":true,"name":"funder","type":"address"},{"indexed":false,"name":"amountsAdded","type":"uint256[]"},{"indexed":false,"name":"sharesMinted","type":"uint256"}],"name":"FPMMFundingAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"funder","type":"address"},{"indexed":false,"name":"amountsRemoved","type":"uint256[]"},{"indexed":false,"name":"collateralRemovedFromFeePool","type":"uint256"},{"indexed":false,"name":"sharesBurnt","type":"uint256"}],"name":"FPMMFundingRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"buyer","type":"address"},{"indexed":false,"name":"investmentAmount","type":"uint256"},{"indexed":false,"name":"feeAmount","type":"uint256"},{"indexed":true,"name":"outcomeIndex","type":"uint256"},{"indexed":false,"name":"outcomeTokensBought","type":"uint256"}],"name":"FPMMBuy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"seller","type":"address"},{"indexed":false,"name":"returnAmount","type":"uint256"},{"indexed":false,"name":"feeAmount","type":"uint256"},{"indexed":true,"name":"outcomeIndex","type":"uint256"},{"indexed":false,"name":"outcomeTokensSold","type":"uint256"}],"name":"FPMMSell","type":"event"}],"metadata":"{\"compiler\":{\"version\":\"0.5.10+commit.5a6ea5b1\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"funder\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountsAdded\",\"type\":\"uint256[]\"},{\"indexed\":false,\"name\":\"sharesMinted\",\"type\":\"uint256\"}],\"name\":\"FPMMFundingAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"funder\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"amountsRemoved\",\"type\":\"uint256[]\"},{\"indexed\":false,\"name\":\"collateralRemovedFromFeePool\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"sharesBurnt\",\"type\":\"uint256\"}],\"name\":\"FPMMFundingRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"buyer\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"investmentAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"indexed\":true,\"name\":\"outcomeIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"outcomeTokensBought\",\"type\":\"uint256\"}],\"name\":\"FPMMBuy\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"seller\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"returnAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"feeAmount\",\"type\":\"uint256\"},{\"indexed\":true,\"name\":\"outcomeIndex\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"outcomeTokensSold\",\"type\":\"uint256\"}],\"name\":\"FPMMSell\",\"type\":\"event\"}],\"devdoc\":{\"methods\":{}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/home/alan/src/github.com/gnosis/conditional-tokens-market-makers/contracts/FixedProductMarketMaker.sol\":\"FixedProductMarketMakerData\"},\"evmVersion\":\"petersburg\",\"libraries\":{},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"/home/alan/src/github.com/gnosis/conditional-tokens-market-makers/contracts/ERC20.sol\":{\"keccak256\":\"0x40a58d43f7c61c93c3e31fa2e398cbbd2768635703066fc37fc98ec19c1da70f\",\"urls\":[\"bzzr://535a311aa4c5d1d4c082822608b7d41eecdc45e4be9df6dc190f158546c809d9\",\"dweb:/ipfs/QmUacDZ7YZ7spzy2XsQuaZeMnKNTQuJUs7cXgQAjbGNScW\"]},\"/home/alan/src/github.com/gnosis/conditional-tokens-market-makers/contracts/FixedProductMarketMaker.sol\":{\"keccak256\":\"0x68003703f48a2b244f5f0fe19473bb1602791d1277882ab3a159af3b1a31daec\",\"urls\":[\"bzzr://4d0cb66cc5c935d8631414bd811452c0eb2901bf6ef794350ebc393085b075fe\",\"dweb:/ipfs/QmdZskHkHDANmxM4XVcXLLeUjRJTuUKpWYYRFbR1FseVwv\"]},\"@gnosis.pm/conditional-tokens-contracts/contracts/CTHelpers.sol\":{\"keccak256\":\"0x921eadb1d6cd0e742448334fb84c39b358a18cdeb7c6badbb89cae847d44a201\",\"urls\":[\"bzzr://54ea8cd3a2ef100710306ce15475573542ad5235ced471d59fa1744cfaea51c8\",\"dweb:/ipfs/Qme871evGa1PBSBnV9PisLhYnFnnahLmBuy5KakLSUKRPF\"]},\"@gnosis.pm/conditional-tokens-contracts/contracts/ConditionalTokens.sol\":{\"keccak256\":\"0x11a610a3fa07ac8a59a09a76fdb944f058fdb06d3414fe49995595e68cd17d41\",\"urls\":[\"bzzr://aec70f70bf6166a9ae46146c7d293156967b345273a250667732f6f8a4355342\",\"dweb:/ipfs/QmWUFR7eKVsMS61UCK1yY2bQQhcqEQHAAmJ2FJ3GozWV8k\"]},\"@gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/ERC1155.sol\":{\"keccak256\":\"0xd8e6583f45a98380ff10a5656a7ae7ba6a275e0d051043e5842f0d541abd7caa\",\"urls\":[\"bzzr://978cc82d056d10d8cc14b02f35801b10c0c2f87b0adbf6c41bbd622e20ab8cee\",\"dweb:/ipfs/QmYWqCK6moph6oeBEUc1nDaGz4GRdGXmykwKD5rykVbhcU\"]},\"@gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/ERC1155TokenReceiver.sol\":{\"keccak256\":\"0x5d1e709c759b9bd72865c8608582d66fc338fde3a77f41abb04ae943915a5695\",\"urls\":[\"bzzr://55e5a597486430ad437bb8c8f0c93cf573b833fa84ccf64a25afaa01c761b839\",\"dweb:/ipfs/QmQ727N1676jG9F5iwakSG2RNeV82TVBGJHgHPmHxib8ah\"]},\"@gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/IERC1155.sol\":{\"keccak256\":\"0x4971631d7de74464fed3e0abac04553e917be5e8cd10b3f825e2e7c39ccc2734\",\"urls\":[\"bzzr://e1e88dfe7440ceab59d4cd604d12e5dc93409a3c5058e497763703027ea7b9e6\",\"dweb:/ipfs/QmRzsL1o6iVEFmqBYCo3XpM4kpvbK7iSJWssXHQ3gHb5uq\"]},\"@gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/IERC1155TokenReceiver.sol\":{\"keccak256\":\"0xca815b5ca57df8f1056b962c2728d6a1e56fc7d9a7869ccee8f5a1ac6075b75d\",\"urls\":[\"bzzr://61df3e61bf24c80714e326ffdc274aaefc342241de3e72374131f613cddbd042\",\"dweb:/ipfs/QmPnF3rGuY2H3Gifvha4dW7fJPptP7wJerHzjz4dpzfTJW\"]},\"openzeppelin-solidity/contracts/introspection/ERC165.sol\":{\"keccak256\":\"0xac2eacd7e7762e275442f28f21d821544df5aae2ed7698af13be8c41b7005e2e\",\"urls\":[\"bzzr://43e901f6f210568ebc1d3591da3ce6a9d10796b854767a9c6e3da10305a8a332\",\"dweb:/ipfs/QmQhfx2Ufr8a2gFXm3KogL66xGgAuAWMwcamkWFKGG6Vya\"]},\"openzeppelin-solidity/contracts/introspection/IERC165.sol\":{\"keccak256\":\"0x661553e43d7c4fbb2de504e5999fd5c104d367488350ed5bf023031bd1ba5ac5\",\"urls\":[\"bzzr://fc2ba15143ce3a00268ecd15fc98eb2469b18bfe27a64bbab0ac6446f161c739\",\"dweb:/ipfs/QmV7wjtRf11ibUHh4g8JjuhMpy68pPhV95L2y46UBoRfsZ\"]},\"openzeppelin-solidity/contracts/math/SafeMath.sol\":{\"keccak256\":\"0x4ccf2d7b51873db1ccfd54ca2adae5eac3b184f9699911ed4490438419f1c690\",\"urls\":[\"bzzr://1604f5b6d6e916c154efd8c6720cda069e5ba32dfa0a9dedf2b42e5b02d07f89\",\"dweb:/ipfs/QmV3yVktya1s617QmuzQR2CfuJgUi3dR2xEZY9ecmqZ2G1\"]},\"openzeppelin-solidity/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0x90e8c2521653bbb1768b05889c5760031e688d9cd361f167489b89215e201b95\",\"urls\":[\"bzzr://aa8b45b57edafc3d67bc5d916327ea16807fae33f753ca163ae0c4061b789766\",\"dweb:/ipfs/QmP5NaEwZthQeM2ESz4WTT3osrP7jhbvu7ocbttBi2JAw6\"]},\"openzeppelin-solidity/contracts/utils/Address.sol\":{\"keccak256\":\"0xf3358e5819ca73357abd6c90bdfffd0474af54364897f6b3e3234c4b71fbe9a1\",\"urls\":[\"bzzr://f7f6da60a184233fd666ac44e6fb2bd51ca6ebdc4867a310d368049aa4e62786\",\"dweb:/ipfs/Qmb3kNCoBUZdah1AgBBD4zMk898j5Qw8ahT1w5cCMYp5Y3\"]}},\"version\":1}","bytecode":"0x6080604052348015600f57600080fd5b50603e80601d6000396000f3fe6080604052600080fdfea265627a7a7230582011f7242dbdc179c23a781f4c12a141c341f59ce4375744202f1f38243128b73964736f6c634300050a0032","deployedBytecode":"0x6080604052600080fdfea265627a7a7230582011f7242dbdc179c23a781f4c12a141c341f59ce4375744202f1f38243128b73964736f6c634300050a0032","sourceMap":"13102:1365:3:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;13102:1365:3;;;;;;;","deployedSourceMap":"13102:1365:3:-;;;;;","source":"pragma solidity ^0.5.1;\n\nimport { SafeMath } from \"openzeppelin-solidity/contracts/math/SafeMath.sol\";\nimport { IERC20 } from \"openzeppelin-solidity/contracts/token/ERC20/IERC20.sol\";\nimport { ConditionalTokens } from \"@gnosis.pm/conditional-tokens-contracts/contracts/ConditionalTokens.sol\";\nimport { CTHelpers } from \"@gnosis.pm/conditional-tokens-contracts/contracts/CTHelpers.sol\";\nimport { ERC1155TokenReceiver } from \"@gnosis.pm/conditional-tokens-contracts/contracts/ERC1155/ERC1155TokenReceiver.sol\";\nimport { ERC20 } from \"./ERC20.sol\";\n\n\nlibrary CeilDiv {\n    // calculates ceil(x/y)\n    function ceildiv(uint x, uint y) internal pure returns (uint) {\n        if(x > 0) return ((x - 1) / y) + 1;\n        return x / y;\n    }\n}\n\n\ncontract FixedProductMarketMaker is ERC20, ERC1155TokenReceiver {\n    event FPMMFundingAdded(\n        address indexed funder,\n        uint[] amountsAdded,\n        uint sharesMinted\n    );\n    event FPMMFundingRemoved(\n        address indexed funder,\n        uint[] amountsRemoved,\n        uint collateralRemovedFromFeePool,\n        uint sharesBurnt\n    );\n    event FPMMBuy(\n        address indexed buyer,\n        uint investmentAmount,\n        uint feeAmount,\n        uint indexed outcomeIndex,\n        uint outcomeTokensBought\n    );\n    event FPMMSell(\n        address indexed seller,\n        uint returnAmount,\n        uint feeAmount,\n        uint indexed outcomeIndex,\n        uint outcomeTokensSold\n    );\n\n    using SafeMath for uint;\n    using CeilDiv for uint;\n\n    uint constant ONE = 10**18;\n\n    ConditionalTokens public conditionalTokens;\n    IERC20 public collateralToken;\n    bytes32[] public conditionIds;\n    uint public fee;\n    uint internal feePoolWeight;\n\n    uint[] outcomeSlotCounts;\n    bytes32[][] collectionIds;\n    uint[] positionIds;\n    mapping (address => uint256) withdrawnFees;\n    uint internal totalWithdrawnFees;\n\n    function getPoolBalances() private view returns (uint[] memory) {\n        address[] memory thises = new address[](positionIds.length);\n        for(uint i = 0; i < positionIds.length; i++) {\n            thises[i] = address(this);\n        }\n        return conditionalTokens.balanceOfBatch(thises, positionIds);\n    }\n\n    function generateBasicPartition(uint outcomeSlotCount)\n        private\n        pure\n        returns (uint[] memory partition)\n    {\n        partition = new uint[](outcomeSlotCount);\n        for(uint i = 0; i < outcomeSlotCount; i++) {\n            partition[i] = 1 << i;\n        }\n    }\n\n    function splitPositionThroughAllConditions(uint amount)\n        private\n    {\n        for(uint i = conditionIds.length - 1; int(i) >= 0; i--) {\n            uint[] memory partition = generateBasicPartition(outcomeSlotCounts[i]);\n            for(uint j = 0; j < collectionIds[i].length; j++) {\n                conditionalTokens.splitPosition(collateralToken, collectionIds[i][j], conditionIds[i], partition, amount);\n            }\n        }\n    }\n\n    function mergePositionsThroughAllConditions(uint amount)\n        private\n    {\n        for(uint i = 0; i < conditionIds.length; i++) {\n            uint[] memory partition = generateBasicPartition(outcomeSlotCounts[i]);\n            for(uint j = 0; j < collectionIds[i].length; j++) {\n                conditionalTokens.mergePositions(collateralToken, collectionIds[i][j], conditionIds[i], partition, amount);\n            }\n        }\n    }\n\n    function collectedFees() external view returns (uint) {\n        return feePoolWeight.sub(totalWithdrawnFees);\n    }\n\n    function feesWithdrawableBy(address account) public view returns (uint) {\n        uint rawAmount = feePoolWeight.mul(balanceOf(account)) / totalSupply();\n        return rawAmount.sub(withdrawnFees[account]);\n    }\n\n    function withdrawFees(address account) public {\n        uint rawAmount = feePoolWeight.mul(balanceOf(account)) / totalSupply();\n        uint withdrawableAmount = rawAmount.sub(withdrawnFees[account]);\n        if(withdrawableAmount > 0){\n            withdrawnFees[account] = rawAmount;\n            totalWithdrawnFees = totalWithdrawnFees.add(withdrawableAmount);\n            require(collateralToken.transfer(account, withdrawableAmount), \"withdrawal transfer failed\");\n        }\n    }\n\n    function _beforeTokenTransfer(address from, address to, uint256 amount) internal {\n        if (from != address(0)) {\n            withdrawFees(from);\n        }\n\n        uint totalSupply = totalSupply();\n        uint withdrawnFeesTransfer = totalSupply == 0 ?\n            amount :\n            feePoolWeight.mul(amount) / totalSupply;\n\n        if (from != address(0)) {\n            withdrawnFees[from] = withdrawnFees[from].sub(withdrawnFeesTransfer);\n            totalWithdrawnFees = totalWithdrawnFees.sub(withdrawnFeesTransfer);\n        } else {\n            feePoolWeight = feePoolWeight.add(withdrawnFeesTransfer);\n        }\n        if (to != address(0)) {\n            withdrawnFees[to] = withdrawnFees[to].add(withdrawnFeesTransfer);\n            totalWithdrawnFees = totalWithdrawnFees.add(withdrawnFeesTransfer);\n        } else {\n            feePoolWeight = feePoolWeight.sub(withdrawnFeesTransfer);\n        }\n    }\n\n    function addFunding(uint addedFunds, uint[] calldata distributionHint)\n        external\n    {\n        require(addedFunds > 0, \"funding must be non-zero\");\n\n        uint[] memory sendBackAmounts = new uint[](positionIds.length);\n        uint poolShareSupply = totalSupply();\n        uint mintAmount;\n        if(poolShareSupply > 0) {\n            require(distributionHint.length == 0, \"cannot use distribution hint after initial funding\");\n            uint[] memory poolBalances = getPoolBalances();\n            uint poolWeight = 0;\n            for(uint i = 0; i < poolBalances.length; i++) {\n                uint balance = poolBalances[i];\n                if(poolWeight < balance)\n                    poolWeight = balance;\n            }\n\n            for(uint i = 0; i < poolBalances.length; i++) {\n                uint remaining = addedFunds.mul(poolBalances[i]) / poolWeight;\n                sendBackAmounts[i] = addedFunds.sub(remaining);\n            }\n\n            mintAmount = addedFunds.mul(poolShareSupply) / poolWeight;\n        } else {\n            if(distributionHint.length > 0) {\n                require(distributionHint.length == positionIds.length, \"hint length off\");\n                uint maxHint = 0;\n                for(uint i = 0; i < distributionHint.length; i++) {\n                    uint hint = distributionHint[i];\n                    if(maxHint < hint)\n                        maxHint = hint;\n                }\n\n                for(uint i = 0; i < distributionHint.length; i++) {\n                    uint remaining = addedFunds.mul(distributionHint[i]) / maxHint;\n                    require(remaining > 0, \"must hint a valid distribution\");\n                    sendBackAmounts[i] = addedFunds.sub(remaining);\n                }\n            }\n\n            mintAmount = addedFunds;\n        }\n\n        require(collateralToken.transferFrom(msg.sender, address(this), addedFunds), \"funding transfer failed\");\n        require(collateralToken.approve(address(conditionalTokens), addedFunds), \"approval for splits failed\");\n        splitPositionThroughAllConditions(addedFunds);\n\n        _mint(msg.sender, mintAmount);\n\n        conditionalTokens.safeBatchTransferFrom(address(this), msg.sender, positionIds, sendBackAmounts, \"\");\n\n        // transform sendBackAmounts to array of amounts added\n        for (uint i = 0; i < sendBackAmounts.length; i++) {\n            sendBackAmounts[i] = addedFunds.sub(sendBackAmounts[i]);\n        }\n\n        emit FPMMFundingAdded(msg.sender, sendBackAmounts, mintAmount);\n    }\n\n    function removeFunding(uint sharesToBurn)\n        external\n    {\n        uint[] memory poolBalances = getPoolBalances();\n\n        uint[] memory sendAmounts = new uint[](poolBalances.length);\n\n        uint poolShareSupply = totalSupply();\n        for(uint i = 0; i < poolBalances.length; i++) {\n            sendAmounts[i] = poolBalances[i].mul(sharesToBurn) / poolShareSupply;\n        }\n\n        uint collateralRemovedFromFeePool = collateralToken.balanceOf(address(this));\n\n        _burn(msg.sender, sharesToBurn);\n        collateralRemovedFromFeePool = collateralRemovedFromFeePool.sub(\n            collateralToken.balanceOf(address(this))\n        );\n\n        conditionalTokens.safeBatchTransferFrom(address(this), msg.sender, positionIds, sendAmounts, \"\");\n\n        emit FPMMFundingRemoved(msg.sender, sendAmounts, collateralRemovedFromFeePool, sharesToBurn);\n    }\n\n    function onERC1155Received(\n        address operator,\n        address from,\n        uint256 id,\n        uint256 value,\n        bytes calldata data\n    )\n        external\n        returns (bytes4)\n    {\n        if (operator == address(this)) {\n            return this.onERC1155Received.selector;\n        }\n        return 0x0;\n    }\n\n    function onERC1155BatchReceived(\n        address operator,\n        address from,\n        uint256[] calldata ids,\n        uint256[] calldata values,\n        bytes calldata data\n    )\n        external\n        returns (bytes4)\n    {\n        if (operator == address(this) && from == address(0)) {\n            return this.onERC1155BatchReceived.selector;\n        }\n        return 0x0;\n    }\n\n    function calcBuyAmount(uint investmentAmount, uint outcomeIndex) public view returns (uint) {\n        require(outcomeIndex < positionIds.length, \"invalid outcome index\");\n\n        uint[] memory poolBalances = getPoolBalances();\n        uint investmentAmountMinusFees = investmentAmount.sub(investmentAmount.mul(fee) / ONE);\n        uint buyTokenPoolBalance = poolBalances[outcomeIndex];\n        uint endingOutcomeBalance = buyTokenPoolBalance.mul(ONE);\n        for(uint i = 0; i < poolBalances.length; i++) {\n            if(i != outcomeIndex) {\n                uint poolBalance = poolBalances[i];\n                endingOutcomeBalance = endingOutcomeBalance.mul(poolBalance).ceildiv(\n                    poolBalance.add(investmentAmountMinusFees)\n                );\n            }\n        }\n        require(endingOutcomeBalance > 0, \"must have non-zero balances\");\n\n        return buyTokenPoolBalance.add(investmentAmountMinusFees).sub(endingOutcomeBalance.ceildiv(ONE));\n    }\n\n    function calcSellAmount(uint returnAmount, uint outcomeIndex) public view returns (uint outcomeTokenSellAmount) {\n        require(outcomeIndex < positionIds.length, \"invalid outcome index\");\n\n        uint[] memory poolBalances = getPoolBalances();\n        uint returnAmountPlusFees = returnAmount.mul(ONE) / ONE.sub(fee);\n        uint sellTokenPoolBalance = poolBalances[outcomeIndex];\n        uint endingOutcomeBalance = sellTokenPoolBalance.mul(ONE);\n        for(uint i = 0; i < poolBalances.length; i++) {\n            if(i != outcomeIndex) {\n                uint poolBalance = poolBalances[i];\n                endingOutcomeBalance = endingOutcomeBalance.mul(poolBalance).ceildiv(\n                    poolBalance.sub(returnAmountPlusFees)\n                );\n            }\n        }\n        require(endingOutcomeBalance > 0, \"must have non-zero balances\");\n\n        return returnAmountPlusFees.add(endingOutcomeBalance.ceildiv(ONE)).sub(sellTokenPoolBalance);\n    }\n\n    function buy(uint investmentAmount, uint outcomeIndex, uint minOutcomeTokensToBuy) external {\n        uint outcomeTokensToBuy = calcBuyAmount(investmentAmount, outcomeIndex);\n        require(outcomeTokensToBuy >= minOutcomeTokensToBuy, \"minimum buy amount not reached\");\n\n        require(collateralToken.transferFrom(msg.sender, address(this), investmentAmount), \"cost transfer failed\");\n\n        uint feeAmount = investmentAmount.mul(fee) / ONE;\n        feePoolWeight = feePoolWeight.add(feeAmount);\n        uint investmentAmountMinusFees = investmentAmount.sub(feeAmount);\n        require(collateralToken.approve(address(conditionalTokens), investmentAmountMinusFees), \"approval for splits failed\");\n        splitPositionThroughAllConditions(investmentAmountMinusFees);\n\n        conditionalTokens.safeTransferFrom(address(this), msg.sender, positionIds[outcomeIndex], outcomeTokensToBuy, \"\");\n\n        emit FPMMBuy(msg.sender, investmentAmount, feeAmount, outcomeIndex, outcomeTokensToBuy);\n    }\n\n    function sell(uint returnAmount, uint outcomeIndex, uint maxOutcomeTokensToSell) external {\n        uint outcomeTokensToSell = calcSellAmount(returnAmount, outcomeIndex);\n        require(outcomeTokensToSell <= maxOutcomeTokensToSell, \"maximum sell amount exceeded\");\n\n        conditionalTokens.safeTransferFrom(msg.sender, address(this), positionIds[outcomeIndex], outcomeTokensToSell, \"\");\n\n        uint feeAmount = returnAmount.mul(fee) / (ONE.sub(fee));\n        feePoolWeight = feePoolWeight.add(feeAmount);\n        uint returnAmountPlusFees = returnAmount.add(feeAmount);\n        mergePositionsThroughAllConditions(returnAmountPlusFees);\n\n        require(collateralToken.transfer(msg.sender, returnAmount), \"return transfer failed\");\n\n        emit FPMMSell(msg.sender, returnAmount, feeAmount, outcomeIndex, outcomeTokensToSell);\n    }\n}\n\n\n// for proxying purposes\ncontract FixedProductMarketMakerData {\n    mapping (address => uint256) internal _balances;\n    mapping (address => mapping (address => uint256)) internal _allowances;\n    uint256 internal _totalSupply;\n\n\n    bytes4 internal constant _INTERFACE_ID_ERC165 = 0x01ffc9a7;\n    mapping(bytes4 => bool) internal _supportedInterfaces;\n\n\n    event FPMMFundingAdded(\n        address indexed funder,\n        uint[] amountsAdded,\n        uint sharesMinted\n    );\n    event FPMMFundingRemoved(\n        address indexed funder,\n        uint[] amountsRemoved,\n        uint collateralRemovedFromFeePool,\n        uint sharesBurnt\n    );\n    event FPMMBuy(\n        address indexed buyer,\n        uint investmentAmount,\n        uint feeAmount,\n        uint indexed outcomeIndex,\n        uint outcomeTokensBought\n    );\n    event FPMMSell(\n        address indexed seller,\n        uint returnAmount,\n        uint feeAmount,\n        uint indexed outcomeIndex,\n        uint outcomeTokensSold\n    );\n    ConditionalTokens internal conditionalTokens;\n    IERC20 internal collateralToken;\n    bytes32[] internal conditionIds;\n    uint internal fee;\n    uint internal feePoolWeight;\n\n    uint[] internal outcomeSlotCounts;\n    bytes32[][] internal collectionIds;\n    uint[] internal positionIds;\n    mapping (address => uint256) internal withdrawnFees;\n    uint internal totalWithdrawnFees;\n}\n","compiler":{"name":"solc","version":"0.5.10+commit.5a6ea5b1.Emscripten.clang"},"networks":{},"schemaVersion":"3.0.16","updatedAt":"2020-03-16T19:03:53.071Z","devdoc":{"methods":{}},"userdoc":{"methods":{}}}