polkamarkets-js 1.0.2 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.openzeppelin/unknown-1337.json +2056 -0
- package/CONTRIBUTING.md +36 -0
- package/README.md +24 -25
- package/_book/README.md +590 -0
- package/_book/core.md +50 -0
- package/_book/gitbook/fonts/fontawesome/FontAwesome.otf +0 -0
- package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.eot +0 -0
- package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.svg +685 -0
- package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.ttf +0 -0
- package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff +0 -0
- package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 +0 -0
- package/_book/gitbook/gitbook-plugin-fontsettings/fontsettings.js +240 -0
- package/_book/gitbook/gitbook-plugin-fontsettings/website.css +291 -0
- package/_book/gitbook/gitbook-plugin-highlight/ebook.css +135 -0
- package/_book/gitbook/gitbook-plugin-highlight/website.css +434 -0
- package/_book/gitbook/gitbook-plugin-lunr/lunr.min.js +7 -0
- package/_book/gitbook/gitbook-plugin-lunr/search-lunr.js +59 -0
- package/_book/gitbook/gitbook-plugin-search/lunr.min.js +7 -0
- package/_book/gitbook/gitbook-plugin-search/search-engine.js +50 -0
- package/_book/gitbook/gitbook-plugin-search/search.css +35 -0
- package/_book/gitbook/gitbook-plugin-search/search.js +213 -0
- package/_book/gitbook/gitbook-plugin-sharing/buttons.js +90 -0
- package/_book/gitbook/gitbook.js +4 -0
- package/_book/gitbook/images/apple-touch-icon-precomposed-152.png +0 -0
- package/_book/gitbook/images/favicon.ico +0 -0
- package/_book/gitbook/style.css +9 -0
- package/_book/gitbook/theme.js +4 -0
- package/_book/index.html +705 -0
- package/_book/intro.md +32 -0
- package/_book/search_index.json +1 -0
- package/book.json +8 -0
- package/build/contracts/AccessControl.json +1 -0
- package/build/contracts/AccessControlEnumerable.json +1 -0
- package/build/contracts/Achievements.json +1 -0
- package/build/contracts/Address.json +1 -0
- package/build/contracts/BalanceHolder.json +1 -0
- package/build/contracts/BalanceHolder_ERC20.json +1 -0
- package/build/contracts/CeilDiv.json +1 -0
- package/build/contracts/Clones.json +1 -0
- package/build/contracts/Context.json +1 -0
- package/build/contracts/Counters.json +1 -0
- package/build/contracts/ERC165.json +1 -0
- package/build/contracts/ERC20.json +1 -0
- package/build/contracts/ERC20Burnable.json +1 -0
- package/build/contracts/ERC20Pausable.json +1 -0
- package/build/contracts/ERC20PresetMinterPauser.json +1 -0
- package/build/contracts/ERC721.json +1 -0
- package/build/contracts/EnumerableMap.json +1 -0
- package/build/contracts/EnumerableSet.json +1 -0
- package/build/contracts/FantasyERC20.json +1 -0
- package/build/contracts/IAccessControl.json +1 -0
- package/build/contracts/IAccessControlEnumerable.json +1 -0
- package/build/contracts/IBalanceHolder_ERC20.json +1 -0
- package/build/contracts/IERC165.json +1 -0
- package/build/contracts/IERC20.json +1 -0
- package/build/contracts/IERC20Metadata.json +1 -0
- package/build/contracts/IERC20Permit.json +1 -0
- package/build/contracts/IERC721.json +1 -0
- package/build/contracts/IERC721Enumerable.json +1 -0
- package/build/contracts/IERC721Metadata.json +1 -0
- package/build/contracts/IERC721Receiver.json +1 -0
- package/build/contracts/IFantasyERC20.json +1 -0
- package/build/contracts/IPredictionMarketV3.json +1 -0
- package/build/contracts/IPredictionMarketV3Factory.json +1 -0
- package/build/contracts/IPredictionMarketV3Manager.json +1 -0
- package/build/contracts/IRealityETH_ERC20.json +1 -0
- package/build/contracts/IRealityETH_IERC20.json +1 -0
- package/build/contracts/IWETH.json +1 -0
- package/build/contracts/LandFactory.json +1 -0
- package/build/contracts/Math.json +1 -0
- package/build/contracts/Migrations.json +1 -0
- package/build/contracts/Ownable.json +1 -0
- package/build/contracts/Pausable.json +1 -0
- package/build/contracts/PredictionMarket.json +1 -0
- package/build/contracts/PredictionMarketV2.json +1 -0
- package/build/contracts/PredictionMarketV3.json +1 -0
- package/build/contracts/PredictionMarketV3Controller.json +1 -0
- package/build/contracts/PredictionMarketV3Factory.json +1 -0
- package/build/contracts/PredictionMarketV3Manager.json +1 -0
- package/build/contracts/PredictionMarketV3Querier.json +1 -0
- package/build/contracts/RealitioERC20.json +1 -0
- package/build/contracts/RealitioForeignArbitrationProxyWithAppeals.json +1 -0
- package/build/contracts/RealitioHomeArbitrationProxy.json +1 -0
- package/build/contracts/RealitioSafeMath256.json +1 -0
- package/build/contracts/RealitioSafeMath32.json +1 -0
- package/build/contracts/RealityETH_ERC20_Factory.json +1 -0
- package/build/contracts/RealityETH_ERC20_v3_0.json +1 -0
- package/build/contracts/ReentrancyGuard.json +1 -0
- package/build/contracts/SafeERC20.json +1 -0
- package/build/contracts/SafeMath.json +1 -0
- package/build/contracts/Strings.json +1 -0
- package/build/contracts/Voting.json +1 -0
- package/build/contracts/WETH9.json +1 -0
- package/build/contracts/test.json +1 -0
- package/cleanContracts.js +22 -0
- package/contracts/FantasyERC20.sol +81 -0
- package/contracts/IFantasyERC20.sol +20 -0
- package/contracts/IPredictionMarketV3.sol +207 -0
- package/contracts/IPredictionMarketV3Factory.sol +10 -0
- package/contracts/IPredictionMarketV3Manager.sol +12 -0
- package/contracts/IRealityETH_ERC20.sol +64 -0
- package/contracts/LandFactory.sol +248 -0
- package/contracts/Migrations.sol +24 -0
- package/contracts/PredictionMarketV3.sol +1332 -0
- package/contracts/PredictionMarketV3Controller.sol +87 -0
- package/contracts/PredictionMarketV3Factory.sol +205 -0
- package/contracts/PredictionMarketV3Manager.sol +45 -0
- package/contracts/PredictionMarketV3Querier.sol +79 -0
- package/contracts/RealityETH_ERC20_Factory.sol +54 -0
- package/contracts/Voting.sol +153 -0
- package/contracts/WETH9.sol +62 -0
- package/hardhat.config.js +4 -0
- package/help.txt +8 -0
- package/index.js +3 -0
- package/migrations/10_deploy_weth.js +5 -0
- package/migrations/11_deploy_full_flow.js +99 -0
- package/migrations/12_deploy_pm_v3_querier.js +7 -0
- package/migrations/13_deploy_pm_v3_factory.js +14 -0
- package/migrations/1_initial_migration.js +5 -0
- package/migrations/2_deploy_erc20.js +10 -0
- package/migrations/3_deploy_realitio.js +11 -0
- package/migrations/4_deploy_pm.js +20 -0
- package/migrations/5_seed_markets.js +51 -0
- package/migrations/6_deploy_achievements.js +5 -0
- package/migrations/7_deploy_voting.js +14 -0
- package/migrations/8_deploy_pm_v2.js +20 -0
- package/migrations/9_seed_markets_v2.js +68 -0
- package/package.json +106 -13
- package/src/Application.js +421 -0
- package/src/interfaces/index.js +19 -0
- package/src/models/AchievementsContract.js +217 -0
- package/src/models/ArbitrationContract.js +69 -0
- package/src/models/ArbitrationProxyContract.js +32 -0
- package/src/models/ERC20Contract.js +156 -0
- package/src/models/FantasyERC20Contract.js +92 -0
- package/src/models/IContract.js +1002 -0
- package/src/models/PolkamarketsSmartAccount.js +100 -0
- package/src/models/PredictionMarketContract.js +562 -0
- package/src/models/PredictionMarketV2Contract.js +830 -0
- package/src/models/PredictionMarketV3Contract.js +256 -0
- package/src/models/PredictionMarketV3ControllerContract.js +102 -0
- package/src/models/PredictionMarketV3FactoryContract.js +96 -0
- package/src/models/PredictionMarketV3ManagerContract.js +111 -0
- package/src/models/PredictionMarketV3QuerierContract.js +24 -0
- package/src/models/RealitioERC20Contract.js +286 -0
- package/src/models/VotingContract.js +182 -0
- package/src/models/WETH9Contract.js +92 -0
- package/src/models/index.js +33 -0
- package/src/utils/Account.js +40 -0
- package/src/utils/Contract.js +120 -0
- package/src/utils/Numbers.js +94 -0
- package/tests/fantasyERC20Contract.js +225 -0
- package/tests/index.js +10 -0
- package/tests/predictionMarketContract.js +466 -0
- package/tests/predictionMarketV2Contract.js +1042 -0
- package/tests/predictionMarketV3Contract.js +1079 -0
- package/tests/predictionMarketV3ControllerContract.js +613 -0
- package/tests/predictionMarketV3FactoryContract.js +469 -0
- package/tests/predictionMarketV3ManagerContract.js +610 -0
- package/tests/utils.js +16 -0
- package/tests/votingContract.js +490 -0
- package/tooling/docs/jsdoc.json +6 -0
- package/truffle-config.js +134 -0
- package/polkamarkets.js +0 -436
|
@@ -0,0 +1,1332 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.18;
|
|
3
|
+
|
|
4
|
+
// openzeppelin imports
|
|
5
|
+
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
|
6
|
+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
7
|
+
|
|
8
|
+
// local imports
|
|
9
|
+
import "./IFantasyERC20.sol";
|
|
10
|
+
import "./IRealityETH_ERC20.sol";
|
|
11
|
+
import "./IPredictionMarketV3Manager.sol";
|
|
12
|
+
|
|
13
|
+
library CeilDiv {
|
|
14
|
+
// calculates ceil(x/y)
|
|
15
|
+
function ceildiv(uint256 x, uint256 y) internal pure returns (uint256) {
|
|
16
|
+
if (x > 0) return ((x - 1) / y) + 1;
|
|
17
|
+
return x / y;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface IWETH {
|
|
22
|
+
function deposit() external payable;
|
|
23
|
+
|
|
24
|
+
function transfer(address to, uint256 value) external returns (bool);
|
|
25
|
+
|
|
26
|
+
function withdraw(uint256) external;
|
|
27
|
+
|
|
28
|
+
function approve(address guy, uint256 wad) external returns (bool);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/// @title Market Contract Factory
|
|
32
|
+
contract PredictionMarketV3 is ReentrancyGuard {
|
|
33
|
+
using SafeERC20 for IERC20;
|
|
34
|
+
using CeilDiv for uint256;
|
|
35
|
+
|
|
36
|
+
// ------ Events ------
|
|
37
|
+
|
|
38
|
+
event MarketCreated(
|
|
39
|
+
address indexed user,
|
|
40
|
+
uint256 indexed marketId,
|
|
41
|
+
uint256 outcomes,
|
|
42
|
+
string question,
|
|
43
|
+
string image,
|
|
44
|
+
IERC20 token
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
event MarketActionTx(
|
|
48
|
+
address indexed user,
|
|
49
|
+
MarketAction indexed action,
|
|
50
|
+
uint256 indexed marketId,
|
|
51
|
+
uint256 outcomeId,
|
|
52
|
+
uint256 shares,
|
|
53
|
+
uint256 value,
|
|
54
|
+
uint256 timestamp
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
event MarketOutcomeShares(uint256 indexed marketId, uint256 timestamp, uint256[] outcomeShares, uint256 liquidity);
|
|
58
|
+
|
|
59
|
+
event MarketOutcomePrice(uint256 indexed marketId, uint256 indexed outcomeId, uint256 value, uint256 timestamp);
|
|
60
|
+
|
|
61
|
+
event MarketLiquidity(
|
|
62
|
+
uint256 indexed marketId,
|
|
63
|
+
uint256 value, // total liquidity
|
|
64
|
+
uint256 price, // value of one liquidity share; max: 1 (even odds situation)
|
|
65
|
+
uint256 timestamp
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
event MarketResolved(
|
|
69
|
+
address indexed user,
|
|
70
|
+
uint256 indexed marketId,
|
|
71
|
+
uint256 outcomeId,
|
|
72
|
+
uint256 timestamp,
|
|
73
|
+
bool admin
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// ------ Events End ------
|
|
77
|
+
|
|
78
|
+
uint256 public constant MAX_UINT_256 = type(uint256).max;
|
|
79
|
+
|
|
80
|
+
uint256 public constant ONE = 10**18;
|
|
81
|
+
|
|
82
|
+
uint256 public constant MAX_OUTCOMES = 2**5;
|
|
83
|
+
|
|
84
|
+
uint256 public constant MAX_FEE = 5 * 10**16; // 5%
|
|
85
|
+
|
|
86
|
+
enum MarketState {
|
|
87
|
+
open,
|
|
88
|
+
closed,
|
|
89
|
+
resolved
|
|
90
|
+
}
|
|
91
|
+
enum MarketAction {
|
|
92
|
+
buy,
|
|
93
|
+
sell,
|
|
94
|
+
addLiquidity,
|
|
95
|
+
removeLiquidity,
|
|
96
|
+
claimWinnings,
|
|
97
|
+
claimLiquidity,
|
|
98
|
+
claimFees,
|
|
99
|
+
claimVoided
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
struct Market {
|
|
103
|
+
// market details
|
|
104
|
+
uint256 closesAtTimestamp;
|
|
105
|
+
uint256 balance; // total stake
|
|
106
|
+
uint256 liquidity; // stake held
|
|
107
|
+
uint256 sharesAvailable; // shares held (all outcomes)
|
|
108
|
+
mapping(address => uint256) liquidityShares;
|
|
109
|
+
mapping(address => bool) liquidityClaims; // wether user has claimed liquidity earnings
|
|
110
|
+
MarketState state; // resolution variables
|
|
111
|
+
MarketResolution resolution; // fees
|
|
112
|
+
MarketFees fees;
|
|
113
|
+
// market outcomes
|
|
114
|
+
uint256 outcomeCount;
|
|
115
|
+
mapping(uint256 => MarketOutcome) outcomes;
|
|
116
|
+
IERC20 token; // ERC20 token market will use for trading
|
|
117
|
+
IPredictionMarketV3Manager manager; // manager contract
|
|
118
|
+
address creator; // market creator
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
struct MarketFees {
|
|
122
|
+
uint256 fee; // fee % taken from every transaction
|
|
123
|
+
uint256 poolWeight; // internal var used to ensure pro-rate fee distribution
|
|
124
|
+
mapping(address => uint256) claimed;
|
|
125
|
+
address treasury; // address to send treasury fees to
|
|
126
|
+
uint256 treasuryFee; // fee % taken from every transaction to a treasury address
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
struct MarketResolution {
|
|
130
|
+
bool resolved;
|
|
131
|
+
uint256 outcomeId;
|
|
132
|
+
bytes32 questionId; // realitio questionId
|
|
133
|
+
// realitio
|
|
134
|
+
IRealityETH_ERC20 realitio;
|
|
135
|
+
uint256 realitioTimeout;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
struct MarketOutcome {
|
|
139
|
+
uint256 marketId;
|
|
140
|
+
uint256 id;
|
|
141
|
+
Shares shares;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
struct Shares {
|
|
145
|
+
uint256 total; // number of shares
|
|
146
|
+
uint256 available; // available shares
|
|
147
|
+
mapping(address => uint256) holders;
|
|
148
|
+
mapping(address => bool) claims; // wether user has claimed winnings
|
|
149
|
+
mapping(address => bool) voidedClaims; // wether user has claimed voided market shares
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
struct CreateMarketDescription {
|
|
153
|
+
uint256 value;
|
|
154
|
+
uint256 closesAt;
|
|
155
|
+
uint256 outcomes;
|
|
156
|
+
IERC20 token;
|
|
157
|
+
uint256[] distribution;
|
|
158
|
+
string question;
|
|
159
|
+
string image;
|
|
160
|
+
address arbitrator;
|
|
161
|
+
uint256 fee;
|
|
162
|
+
uint256 treasuryFee;
|
|
163
|
+
address treasury;
|
|
164
|
+
IRealityETH_ERC20 realitio;
|
|
165
|
+
uint256 realitioTimeout;
|
|
166
|
+
IPredictionMarketV3Manager manager;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
uint256[] marketIds;
|
|
170
|
+
mapping(uint256 => Market) markets;
|
|
171
|
+
uint256 public marketIndex;
|
|
172
|
+
|
|
173
|
+
// weth configs
|
|
174
|
+
IWETH public immutable WETH;
|
|
175
|
+
|
|
176
|
+
// ------ Modifiers ------
|
|
177
|
+
|
|
178
|
+
modifier isMarket(uint256 marketId) {
|
|
179
|
+
require(marketId < marketIndex, "Market not found");
|
|
180
|
+
_;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
modifier timeTransitions(uint256 marketId) {
|
|
184
|
+
if (block.timestamp > markets[marketId].closesAtTimestamp && markets[marketId].state == MarketState.open) {
|
|
185
|
+
nextState(marketId);
|
|
186
|
+
}
|
|
187
|
+
_;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
modifier atState(uint256 marketId, MarketState state) {
|
|
191
|
+
require(markets[marketId].state == state, "Market in incorrect state");
|
|
192
|
+
_;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
modifier notAtState(uint256 marketId, MarketState state) {
|
|
196
|
+
require(markets[marketId].state != state, "Market in incorrect state");
|
|
197
|
+
_;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
modifier transitionNext(uint256 marketId) {
|
|
201
|
+
_;
|
|
202
|
+
nextState(marketId);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
modifier transitionLast(uint256 marketId) {
|
|
206
|
+
_;
|
|
207
|
+
lastState(marketId);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
modifier isWETHMarket(uint256 marketId) {
|
|
211
|
+
require(address(WETH) != address(0), "WETH address is address 0");
|
|
212
|
+
require(address(markets[marketId].token) == address(WETH), "Market token is not WETH");
|
|
213
|
+
_;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ------ Modifiers End ------
|
|
217
|
+
|
|
218
|
+
/// @dev protocol is immutable and has no ownership
|
|
219
|
+
constructor(IWETH _WETH) {
|
|
220
|
+
WETH = _WETH;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
receive() external payable {
|
|
224
|
+
assert(msg.sender == address(WETH)); // only accept ETH via fallback from the WETH contract
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ------ Core Functions ------
|
|
228
|
+
|
|
229
|
+
/// @dev Creates a market, initializes the outcome shares pool and submits a question in Realitio
|
|
230
|
+
function _createMarket(CreateMarketDescription memory desc) private returns (uint256) {
|
|
231
|
+
uint256 marketId = marketIndex;
|
|
232
|
+
marketIds.push(marketId);
|
|
233
|
+
|
|
234
|
+
Market storage market = markets[marketId];
|
|
235
|
+
|
|
236
|
+
require(desc.value > 0, "stake needs to be > 0");
|
|
237
|
+
require(desc.closesAt > block.timestamp, "resolution before current date");
|
|
238
|
+
require(desc.arbitrator != address(0), "invalid arbitrator address");
|
|
239
|
+
require(desc.outcomes > 0 && desc.outcomes <= MAX_OUTCOMES, "outcome count not between 1-32");
|
|
240
|
+
require(desc.fee <= MAX_FEE, "fee must be <= 5%");
|
|
241
|
+
require(desc.treasuryFee <= MAX_FEE, "treasury fee must be <= 5%");
|
|
242
|
+
require(address(desc.realitio) != address(0), "_realitioAddress is address 0");
|
|
243
|
+
require(desc.realitioTimeout > 0, "timeout must be positive");
|
|
244
|
+
require(desc.manager.isAllowedToCreateMarket(desc.token, msg.sender), "not allowed to create market");
|
|
245
|
+
|
|
246
|
+
market.token = desc.token;
|
|
247
|
+
market.closesAtTimestamp = desc.closesAt;
|
|
248
|
+
market.state = MarketState.open;
|
|
249
|
+
market.fees.fee = desc.fee;
|
|
250
|
+
market.fees.treasuryFee = desc.treasuryFee;
|
|
251
|
+
market.fees.treasury = desc.treasury;
|
|
252
|
+
// setting intial value to an integer that does not map to any outcomeId
|
|
253
|
+
market.resolution.outcomeId = MAX_UINT_256;
|
|
254
|
+
market.outcomeCount = desc.outcomes;
|
|
255
|
+
|
|
256
|
+
// creating question in realitio
|
|
257
|
+
market.resolution.questionId = desc.realitio.askQuestionERC20(
|
|
258
|
+
2,
|
|
259
|
+
desc.question,
|
|
260
|
+
desc.arbitrator,
|
|
261
|
+
uint32(desc.realitioTimeout),
|
|
262
|
+
uint32(desc.closesAt),
|
|
263
|
+
0,
|
|
264
|
+
0
|
|
265
|
+
);
|
|
266
|
+
market.resolution.realitio = desc.realitio;
|
|
267
|
+
market.resolution.realitioTimeout = desc.realitioTimeout;
|
|
268
|
+
market.manager = desc.manager;
|
|
269
|
+
market.creator = msg.sender;
|
|
270
|
+
|
|
271
|
+
_addLiquidity(marketId, desc.value, desc.distribution);
|
|
272
|
+
|
|
273
|
+
// emiting initial price events
|
|
274
|
+
emitMarketActionEvents(marketId);
|
|
275
|
+
emit MarketCreated(msg.sender, marketId, desc.outcomes, desc.question, desc.image, desc.token);
|
|
276
|
+
|
|
277
|
+
// incrementing market array index
|
|
278
|
+
marketIndex = marketIndex + 1;
|
|
279
|
+
|
|
280
|
+
return marketId;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function createMarket(CreateMarketDescription calldata desc) external returns (uint256) {
|
|
284
|
+
uint256 marketId = _createMarket(
|
|
285
|
+
CreateMarketDescription({
|
|
286
|
+
value: desc.value,
|
|
287
|
+
closesAt: desc.closesAt,
|
|
288
|
+
outcomes: desc.outcomes,
|
|
289
|
+
token: desc.token,
|
|
290
|
+
distribution: desc.distribution,
|
|
291
|
+
question: desc.question,
|
|
292
|
+
image: desc.image,
|
|
293
|
+
arbitrator: desc.arbitrator,
|
|
294
|
+
fee: desc.fee,
|
|
295
|
+
treasuryFee: desc.treasuryFee,
|
|
296
|
+
treasury: desc.treasury,
|
|
297
|
+
realitio: desc.realitio,
|
|
298
|
+
realitioTimeout: desc.realitioTimeout,
|
|
299
|
+
manager: desc.manager
|
|
300
|
+
})
|
|
301
|
+
);
|
|
302
|
+
// transferring funds
|
|
303
|
+
desc.token.safeTransferFrom(msg.sender, address(this), desc.value);
|
|
304
|
+
|
|
305
|
+
return marketId;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function createMarketWithETH(CreateMarketDescription calldata desc) external payable returns (uint256) {
|
|
309
|
+
require(address(desc.token) == address(WETH), "Market token is not WETH");
|
|
310
|
+
require(msg.value == desc.value, "value does not match arguments");
|
|
311
|
+
uint256 marketId = _createMarket(
|
|
312
|
+
CreateMarketDescription({
|
|
313
|
+
value: desc.value,
|
|
314
|
+
closesAt: desc.closesAt,
|
|
315
|
+
outcomes: desc.outcomes,
|
|
316
|
+
token: desc.token,
|
|
317
|
+
distribution: desc.distribution,
|
|
318
|
+
question: desc.question,
|
|
319
|
+
image: desc.image,
|
|
320
|
+
arbitrator: desc.arbitrator,
|
|
321
|
+
fee: desc.fee,
|
|
322
|
+
treasuryFee: desc.treasuryFee,
|
|
323
|
+
treasury: desc.treasury,
|
|
324
|
+
realitio: desc.realitio,
|
|
325
|
+
realitioTimeout: desc.realitioTimeout,
|
|
326
|
+
manager: desc.manager
|
|
327
|
+
})
|
|
328
|
+
);
|
|
329
|
+
// transferring funds
|
|
330
|
+
IWETH(WETH).deposit{value: msg.value}();
|
|
331
|
+
|
|
332
|
+
return marketId;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function mintAndCreateMarket(CreateMarketDescription calldata desc) external returns (uint256) {
|
|
336
|
+
// mint the amount of tokens to the user
|
|
337
|
+
IFantasyERC20(address(desc.token)).mint(msg.sender, desc.value);
|
|
338
|
+
|
|
339
|
+
uint256 marketId = _createMarket(desc);
|
|
340
|
+
// transferring funds
|
|
341
|
+
desc.token.safeTransferFrom(msg.sender, address(this), desc.value);
|
|
342
|
+
|
|
343
|
+
return marketId;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/// @dev Calculates the number of shares bought with "amount" balance
|
|
347
|
+
function calcBuyAmount(
|
|
348
|
+
uint256 amount,
|
|
349
|
+
uint256 marketId,
|
|
350
|
+
uint256 outcomeId
|
|
351
|
+
) public view returns (uint256) {
|
|
352
|
+
uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
|
|
353
|
+
uint256 fee = getMarketFee(marketId);
|
|
354
|
+
uint256 amountMinusFees = amount - ((amount * fee) / ONE);
|
|
355
|
+
uint256 buyTokenPoolBalance = outcomesShares[outcomeId];
|
|
356
|
+
uint256 endingOutcomeBalance = buyTokenPoolBalance * ONE;
|
|
357
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
358
|
+
if (i != outcomeId) {
|
|
359
|
+
uint256 outcomeShares = outcomesShares[i];
|
|
360
|
+
endingOutcomeBalance = (endingOutcomeBalance * outcomeShares).ceildiv(outcomeShares + amountMinusFees);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
require(endingOutcomeBalance > 0, "must have non-zero balances");
|
|
364
|
+
|
|
365
|
+
return buyTokenPoolBalance + amountMinusFees - (endingOutcomeBalance.ceildiv(ONE));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/// @dev Calculates the number of shares needed to be sold in order to receive "amount" in balance
|
|
369
|
+
function calcSellAmount(
|
|
370
|
+
uint256 amount,
|
|
371
|
+
uint256 marketId,
|
|
372
|
+
uint256 outcomeId
|
|
373
|
+
) public view returns (uint256 outcomeTokenSellAmount) {
|
|
374
|
+
uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
|
|
375
|
+
uint256 fee = getMarketFee(marketId);
|
|
376
|
+
uint256 amountPlusFees = (amount * ONE) / (ONE - fee);
|
|
377
|
+
uint256 sellTokenPoolBalance = outcomesShares[outcomeId];
|
|
378
|
+
uint256 endingOutcomeBalance = sellTokenPoolBalance * ONE;
|
|
379
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
380
|
+
if (i != outcomeId) {
|
|
381
|
+
uint256 outcomeShares = outcomesShares[i];
|
|
382
|
+
endingOutcomeBalance = (endingOutcomeBalance * outcomeShares).ceildiv(outcomeShares - amountPlusFees);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
require(endingOutcomeBalance > 0, "must have non-zero balances");
|
|
386
|
+
|
|
387
|
+
return amountPlusFees + endingOutcomeBalance.ceildiv(ONE) - sellTokenPoolBalance;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/// @dev Buy shares of a market outcome
|
|
391
|
+
function _buy(
|
|
392
|
+
uint256 marketId,
|
|
393
|
+
uint256 outcomeId,
|
|
394
|
+
uint256 minOutcomeSharesToBuy,
|
|
395
|
+
uint256 value
|
|
396
|
+
) private timeTransitions(marketId) atState(marketId, MarketState.open) {
|
|
397
|
+
Market storage market = markets[marketId];
|
|
398
|
+
|
|
399
|
+
uint256 shares = calcBuyAmount(value, marketId, outcomeId);
|
|
400
|
+
require(shares >= minOutcomeSharesToBuy, "minimum buy amount not reached");
|
|
401
|
+
require(shares > 0, "shares amount is 0");
|
|
402
|
+
|
|
403
|
+
// subtracting fee from transaction value
|
|
404
|
+
uint256 feeAmount = (value * market.fees.fee) / ONE;
|
|
405
|
+
market.fees.poolWeight = market.fees.poolWeight + feeAmount;
|
|
406
|
+
uint256 valueMinusFees = value - feeAmount;
|
|
407
|
+
|
|
408
|
+
uint256 treasuryFeeAmount = (value * market.fees.treasuryFee) / ONE;
|
|
409
|
+
valueMinusFees = valueMinusFees - treasuryFeeAmount;
|
|
410
|
+
|
|
411
|
+
MarketOutcome storage outcome = market.outcomes[outcomeId];
|
|
412
|
+
|
|
413
|
+
// Funding market shares with received funds
|
|
414
|
+
addSharesToMarket(marketId, valueMinusFees);
|
|
415
|
+
|
|
416
|
+
require(outcome.shares.available >= shares, "shares pool balance is too low");
|
|
417
|
+
|
|
418
|
+
transferOutcomeSharesfromPool(msg.sender, marketId, outcomeId, shares);
|
|
419
|
+
|
|
420
|
+
emit MarketActionTx(msg.sender, MarketAction.buy, marketId, outcomeId, shares, value, block.timestamp);
|
|
421
|
+
emitMarketActionEvents(marketId);
|
|
422
|
+
|
|
423
|
+
// transfering treasury fee to treasury address
|
|
424
|
+
if (treasuryFeeAmount > 0) {
|
|
425
|
+
market.token.safeTransfer(market.fees.treasury, treasuryFeeAmount);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/// @dev Buy shares of a market outcome
|
|
430
|
+
function buy(
|
|
431
|
+
uint256 marketId,
|
|
432
|
+
uint256 outcomeId,
|
|
433
|
+
uint256 minOutcomeSharesToBuy,
|
|
434
|
+
uint256 value
|
|
435
|
+
) external nonReentrant {
|
|
436
|
+
Market storage market = markets[marketId];
|
|
437
|
+
market.token.safeTransferFrom(msg.sender, address(this), value);
|
|
438
|
+
_buy(marketId, outcomeId, minOutcomeSharesToBuy, value);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function buyWithETH(
|
|
442
|
+
uint256 marketId,
|
|
443
|
+
uint256 outcomeId,
|
|
444
|
+
uint256 minOutcomeSharesToBuy
|
|
445
|
+
) external payable isWETHMarket(marketId) nonReentrant {
|
|
446
|
+
uint256 value = msg.value;
|
|
447
|
+
// wrapping and depositing funds
|
|
448
|
+
IWETH(WETH).deposit{value: value}();
|
|
449
|
+
_buy(marketId, outcomeId, minOutcomeSharesToBuy, value);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/// @dev Sell shares of a market outcome
|
|
453
|
+
function _sell(
|
|
454
|
+
uint256 marketId,
|
|
455
|
+
uint256 outcomeId,
|
|
456
|
+
uint256 value,
|
|
457
|
+
uint256 maxOutcomeSharesToSell
|
|
458
|
+
) private timeTransitions(marketId) atState(marketId, MarketState.open) {
|
|
459
|
+
Market storage market = markets[marketId];
|
|
460
|
+
MarketOutcome storage outcome = market.outcomes[outcomeId];
|
|
461
|
+
|
|
462
|
+
uint256 shares = calcSellAmount(value, marketId, outcomeId);
|
|
463
|
+
|
|
464
|
+
require(shares <= maxOutcomeSharesToSell, "maximum sell amount exceeded");
|
|
465
|
+
require(shares > 0, "shares amount is 0");
|
|
466
|
+
require(outcome.shares.holders[msg.sender] >= shares, "insufficient shares balance");
|
|
467
|
+
|
|
468
|
+
transferOutcomeSharesToPool(msg.sender, marketId, outcomeId, shares);
|
|
469
|
+
|
|
470
|
+
// adding fees to transaction value
|
|
471
|
+
uint256 fee = getMarketFee(marketId);
|
|
472
|
+
{
|
|
473
|
+
uint256 feeAmount = (value * market.fees.fee) / (ONE - fee);
|
|
474
|
+
market.fees.poolWeight = market.fees.poolWeight + feeAmount;
|
|
475
|
+
}
|
|
476
|
+
uint256 valuePlusFees = value + (value * fee) / (ONE - fee);
|
|
477
|
+
|
|
478
|
+
require(market.balance >= valuePlusFees, "insufficient market balance");
|
|
479
|
+
|
|
480
|
+
// Rebalancing market shares
|
|
481
|
+
removeSharesFromMarket(marketId, valuePlusFees);
|
|
482
|
+
|
|
483
|
+
emit MarketActionTx(msg.sender, MarketAction.sell, marketId, outcomeId, shares, value, block.timestamp);
|
|
484
|
+
emitMarketActionEvents(marketId);
|
|
485
|
+
|
|
486
|
+
{
|
|
487
|
+
uint256 treasuryFeeAmount = (value * market.fees.treasuryFee) / (ONE - fee);
|
|
488
|
+
// transfering treasury fee to treasury address
|
|
489
|
+
if (treasuryFeeAmount > 0) {
|
|
490
|
+
market.token.safeTransfer(market.fees.treasury, treasuryFeeAmount);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function sell(
|
|
496
|
+
uint256 marketId,
|
|
497
|
+
uint256 outcomeId,
|
|
498
|
+
uint256 value,
|
|
499
|
+
uint256 maxOutcomeSharesToSell
|
|
500
|
+
) external nonReentrant {
|
|
501
|
+
_sell(marketId, outcomeId, value, maxOutcomeSharesToSell);
|
|
502
|
+
// Transferring funds to user
|
|
503
|
+
Market storage market = markets[marketId];
|
|
504
|
+
market.token.safeTransfer(msg.sender, value);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function sellToETH(
|
|
508
|
+
uint256 marketId,
|
|
509
|
+
uint256 outcomeId,
|
|
510
|
+
uint256 value,
|
|
511
|
+
uint256 maxOutcomeSharesToSell
|
|
512
|
+
) external isWETHMarket(marketId) nonReentrant {
|
|
513
|
+
Market storage market = markets[marketId];
|
|
514
|
+
require(address(market.token) == address(WETH), "market token is not WETH");
|
|
515
|
+
|
|
516
|
+
_sell(marketId, outcomeId, value, maxOutcomeSharesToSell);
|
|
517
|
+
|
|
518
|
+
IWETH(WETH).withdraw(value);
|
|
519
|
+
(bool sent, ) = payable(msg.sender).call{value: value}("");
|
|
520
|
+
require(sent, "Failed to send Ether");
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/// @dev Adds liquidity to a market - external
|
|
524
|
+
function _addLiquidity(
|
|
525
|
+
uint256 marketId,
|
|
526
|
+
uint256 value,
|
|
527
|
+
uint256[] memory distribution
|
|
528
|
+
) private timeTransitions(marketId) atState(marketId, MarketState.open) {
|
|
529
|
+
Market storage market = markets[marketId];
|
|
530
|
+
|
|
531
|
+
require(value > 0, "stake has to be greater than 0.");
|
|
532
|
+
|
|
533
|
+
uint256 liquidityAmount;
|
|
534
|
+
|
|
535
|
+
uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
|
|
536
|
+
uint256[] memory sendBackAmounts = new uint256[](outcomesShares.length);
|
|
537
|
+
uint256 poolWeight = 0;
|
|
538
|
+
|
|
539
|
+
if (market.liquidity > 0) {
|
|
540
|
+
require(distribution.length == 0, "market already funded");
|
|
541
|
+
|
|
542
|
+
// part of the liquidity is exchanged for outcome shares if market is not balanced
|
|
543
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
544
|
+
uint256 outcomeShares = outcomesShares[i];
|
|
545
|
+
if (poolWeight < outcomeShares) poolWeight = outcomeShares;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
549
|
+
uint256 remaining = (value * outcomesShares[i]) / poolWeight;
|
|
550
|
+
sendBackAmounts[i] = value - remaining;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
liquidityAmount = (value * market.liquidity) / poolWeight;
|
|
554
|
+
|
|
555
|
+
// re-balancing fees pool
|
|
556
|
+
rebalanceFeesPool(marketId, liquidityAmount, MarketAction.addLiquidity);
|
|
557
|
+
} else {
|
|
558
|
+
// funding market with no liquidity
|
|
559
|
+
if (distribution.length > 0) {
|
|
560
|
+
require(distribution.length == outcomesShares.length, "distribution length not matching");
|
|
561
|
+
|
|
562
|
+
uint256 maxHint = 0;
|
|
563
|
+
for (uint256 i = 0; i < distribution.length; ++i) {
|
|
564
|
+
uint256 hint = distribution[i];
|
|
565
|
+
if (maxHint < hint) maxHint = hint;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
for (uint256 i = 0; i < distribution.length; ++i) {
|
|
569
|
+
uint256 remaining = (value * distribution[i]) / maxHint;
|
|
570
|
+
require(remaining > 0, "must hint a valid distribution");
|
|
571
|
+
sendBackAmounts[i] = value - remaining;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// funding market with total liquidity amount
|
|
576
|
+
liquidityAmount = value;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// funding market
|
|
580
|
+
market.liquidity = market.liquidity + liquidityAmount;
|
|
581
|
+
market.liquidityShares[msg.sender] = market.liquidityShares[msg.sender] + liquidityAmount;
|
|
582
|
+
|
|
583
|
+
addSharesToMarket(marketId, value);
|
|
584
|
+
|
|
585
|
+
{
|
|
586
|
+
// transform sendBackAmounts to array of amounts added
|
|
587
|
+
for (uint256 i = 0; i < sendBackAmounts.length; ++i) {
|
|
588
|
+
if (sendBackAmounts[i] > 0) {
|
|
589
|
+
transferOutcomeSharesfromPool(msg.sender, marketId, i, sendBackAmounts[i]);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// emitting events, using outcome 0 for price reference
|
|
594
|
+
uint256 referencePrice = getMarketOutcomePrice(marketId, 0);
|
|
595
|
+
|
|
596
|
+
for (uint256 i = 0; i < sendBackAmounts.length; ++i) {
|
|
597
|
+
if (sendBackAmounts[i] > 0) {
|
|
598
|
+
// outcome price = outcome shares / reference outcome shares * reference outcome price
|
|
599
|
+
uint256 outcomePrice = (referencePrice * market.outcomes[0].shares.available) /
|
|
600
|
+
market.outcomes[i].shares.available;
|
|
601
|
+
|
|
602
|
+
emit MarketActionTx(
|
|
603
|
+
msg.sender,
|
|
604
|
+
MarketAction.buy,
|
|
605
|
+
marketId,
|
|
606
|
+
i,
|
|
607
|
+
sendBackAmounts[i],
|
|
608
|
+
(sendBackAmounts[i] * outcomePrice) / ONE, // price * shares
|
|
609
|
+
block.timestamp
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
uint256 liquidityPrice = getMarketLiquidityPrice(marketId);
|
|
616
|
+
uint256 liquidityValue = (liquidityPrice * liquidityAmount) / ONE;
|
|
617
|
+
|
|
618
|
+
emit MarketActionTx(
|
|
619
|
+
msg.sender,
|
|
620
|
+
MarketAction.addLiquidity,
|
|
621
|
+
marketId,
|
|
622
|
+
0,
|
|
623
|
+
liquidityAmount,
|
|
624
|
+
liquidityValue,
|
|
625
|
+
block.timestamp
|
|
626
|
+
);
|
|
627
|
+
emit MarketLiquidity(marketId, market.liquidity, liquidityPrice, block.timestamp);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function addLiquidity(uint256 marketId, uint256 value) external {
|
|
631
|
+
uint256[] memory distribution = new uint256[](0);
|
|
632
|
+
_addLiquidity(marketId, value, distribution);
|
|
633
|
+
|
|
634
|
+
Market storage market = markets[marketId];
|
|
635
|
+
market.token.safeTransferFrom(msg.sender, address(this), value);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function addLiquidityWithETH(uint256 marketId) external payable isWETHMarket(marketId) {
|
|
639
|
+
uint256 value = msg.value;
|
|
640
|
+
uint256[] memory distribution = new uint256[](0);
|
|
641
|
+
_addLiquidity(marketId, value, distribution);
|
|
642
|
+
// wrapping and depositing funds
|
|
643
|
+
IWETH(WETH).deposit{value: value}();
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/// @dev Removes liquidity to a market - external
|
|
647
|
+
function _removeLiquidity(uint256 marketId, uint256 shares)
|
|
648
|
+
private
|
|
649
|
+
timeTransitions(marketId)
|
|
650
|
+
atState(marketId, MarketState.open)
|
|
651
|
+
returns (uint256)
|
|
652
|
+
{
|
|
653
|
+
Market storage market = markets[marketId];
|
|
654
|
+
|
|
655
|
+
require(market.liquidityShares[msg.sender] >= shares, "insufficient shares balance");
|
|
656
|
+
// claiming any pending fees
|
|
657
|
+
claimFees(marketId);
|
|
658
|
+
|
|
659
|
+
// re-balancing fees pool
|
|
660
|
+
rebalanceFeesPool(marketId, shares, MarketAction.removeLiquidity);
|
|
661
|
+
|
|
662
|
+
uint256[] memory outcomesShares = getMarketOutcomesShares(marketId);
|
|
663
|
+
uint256[] memory sendAmounts = new uint256[](outcomesShares.length);
|
|
664
|
+
uint256 poolWeight = MAX_UINT_256;
|
|
665
|
+
|
|
666
|
+
// part of the liquidity is exchanged for outcome shares if market is not balanced
|
|
667
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
668
|
+
uint256 outcomeShares = outcomesShares[i];
|
|
669
|
+
if (poolWeight > outcomeShares) poolWeight = outcomeShares;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
uint256 liquidityAmount = (shares * poolWeight) / market.liquidity;
|
|
673
|
+
|
|
674
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
675
|
+
sendAmounts[i] = (outcomesShares[i] * shares) / market.liquidity;
|
|
676
|
+
sendAmounts[i] = sendAmounts[i] - liquidityAmount;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// removing liquidity from market
|
|
680
|
+
removeSharesFromMarket(marketId, liquidityAmount);
|
|
681
|
+
market.liquidity = market.liquidity - shares;
|
|
682
|
+
// removing liquidity tokens from market creator
|
|
683
|
+
market.liquidityShares[msg.sender] = market.liquidityShares[msg.sender] - shares;
|
|
684
|
+
|
|
685
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
686
|
+
if (sendAmounts[i] > 0) {
|
|
687
|
+
transferOutcomeSharesfromPool(msg.sender, marketId, i, sendAmounts[i]);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// emitting events, using outcome 0 for price reference
|
|
692
|
+
uint256 referencePrice = getMarketOutcomePrice(marketId, 0);
|
|
693
|
+
|
|
694
|
+
for (uint256 i = 0; i < outcomesShares.length; ++i) {
|
|
695
|
+
if (sendAmounts[i] > 0) {
|
|
696
|
+
// outcome price = outcome shares / reference outcome shares * reference outcome price
|
|
697
|
+
uint256 outcomePrice = (referencePrice * market.outcomes[0].shares.available) /
|
|
698
|
+
market.outcomes[i].shares.available;
|
|
699
|
+
|
|
700
|
+
emit MarketActionTx(
|
|
701
|
+
msg.sender,
|
|
702
|
+
MarketAction.buy,
|
|
703
|
+
marketId,
|
|
704
|
+
i,
|
|
705
|
+
sendAmounts[i],
|
|
706
|
+
(sendAmounts[i] * outcomePrice) / ONE, // price * shares
|
|
707
|
+
block.timestamp
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
emit MarketActionTx(
|
|
713
|
+
msg.sender,
|
|
714
|
+
MarketAction.removeLiquidity,
|
|
715
|
+
marketId,
|
|
716
|
+
0,
|
|
717
|
+
shares,
|
|
718
|
+
liquidityAmount,
|
|
719
|
+
block.timestamp
|
|
720
|
+
);
|
|
721
|
+
emit MarketLiquidity(marketId, market.liquidity, getMarketLiquidityPrice(marketId), block.timestamp);
|
|
722
|
+
|
|
723
|
+
return liquidityAmount;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
function removeLiquidity(uint256 marketId, uint256 shares) external {
|
|
727
|
+
uint256 value = _removeLiquidity(marketId, shares);
|
|
728
|
+
// transferring user funds from liquidity removed
|
|
729
|
+
Market storage market = markets[marketId];
|
|
730
|
+
market.token.safeTransfer(msg.sender, value);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function removeLiquidityToETH(uint256 marketId, uint256 shares) external isWETHMarket(marketId) {
|
|
734
|
+
uint256 value = _removeLiquidity(marketId, shares);
|
|
735
|
+
// unwrapping and transferring user funds from liquidity removed
|
|
736
|
+
IWETH(WETH).withdraw(value);
|
|
737
|
+
(bool sent, ) = payable(msg.sender).call{value: value}("");
|
|
738
|
+
require(sent, "Failed to send Ether");
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
/// @dev Fetches winning outcome from Realitio and resolves the market
|
|
742
|
+
function resolveMarketOutcome(uint256 marketId)
|
|
743
|
+
external
|
|
744
|
+
timeTransitions(marketId)
|
|
745
|
+
atState(marketId, MarketState.closed)
|
|
746
|
+
transitionNext(marketId)
|
|
747
|
+
returns (uint256)
|
|
748
|
+
{
|
|
749
|
+
Market storage market = markets[marketId];
|
|
750
|
+
|
|
751
|
+
// will fail if question is not finalized
|
|
752
|
+
uint256 outcomeId = uint256(market.resolution.realitio.resultFor(market.resolution.questionId));
|
|
753
|
+
|
|
754
|
+
market.resolution.outcomeId = outcomeId;
|
|
755
|
+
|
|
756
|
+
emit MarketResolved(msg.sender, marketId, outcomeId, block.timestamp, false);
|
|
757
|
+
emitMarketActionEvents(marketId);
|
|
758
|
+
|
|
759
|
+
return market.resolution.outcomeId;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/// @dev overrides market resolution, instead of using realitio
|
|
763
|
+
function adminResolveMarketOutcome(uint256 marketId, uint256 outcomeId)
|
|
764
|
+
external
|
|
765
|
+
notAtState(marketId, MarketState.resolved)
|
|
766
|
+
transitionLast(marketId)
|
|
767
|
+
returns (uint256)
|
|
768
|
+
{
|
|
769
|
+
Market storage market = markets[marketId];
|
|
770
|
+
|
|
771
|
+
require(market.manager.isAllowedToResolveMarket(market.token, msg.sender), "not allowed to resolve market");
|
|
772
|
+
|
|
773
|
+
market.resolution.outcomeId = outcomeId;
|
|
774
|
+
|
|
775
|
+
emit MarketResolved(msg.sender, marketId, outcomeId, block.timestamp, true);
|
|
776
|
+
emitMarketActionEvents(marketId);
|
|
777
|
+
|
|
778
|
+
return market.resolution.outcomeId;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
/// @dev Allows holders of resolved outcome shares to claim earnings.
|
|
782
|
+
function _claimWinnings(uint256 marketId) private atState(marketId, MarketState.resolved) returns (uint256) {
|
|
783
|
+
Market storage market = markets[marketId];
|
|
784
|
+
MarketOutcome storage resolvedOutcome = market.outcomes[market.resolution.outcomeId];
|
|
785
|
+
|
|
786
|
+
require(resolvedOutcome.shares.holders[msg.sender] > 0, "user doesn't hold outcome shares");
|
|
787
|
+
require(resolvedOutcome.shares.claims[msg.sender] == false, "user already claimed winnings");
|
|
788
|
+
|
|
789
|
+
// 1 share => price = 1
|
|
790
|
+
uint256 value = resolvedOutcome.shares.holders[msg.sender];
|
|
791
|
+
|
|
792
|
+
// assuring market has enough funds
|
|
793
|
+
require(market.balance >= value, "insufficient market balance");
|
|
794
|
+
|
|
795
|
+
market.balance = market.balance - value;
|
|
796
|
+
resolvedOutcome.shares.claims[msg.sender] = true;
|
|
797
|
+
|
|
798
|
+
emit MarketActionTx(
|
|
799
|
+
msg.sender,
|
|
800
|
+
MarketAction.claimWinnings,
|
|
801
|
+
marketId,
|
|
802
|
+
market.resolution.outcomeId,
|
|
803
|
+
resolvedOutcome.shares.holders[msg.sender],
|
|
804
|
+
value,
|
|
805
|
+
block.timestamp
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
return value;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function claimWinnings(uint256 marketId) external {
|
|
812
|
+
uint256 value = _claimWinnings(marketId);
|
|
813
|
+
// transferring user funds from winnings claimed
|
|
814
|
+
Market storage market = markets[marketId];
|
|
815
|
+
market.token.safeTransfer(msg.sender, value);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function claimWinningsToETH(uint256 marketId) external isWETHMarket(marketId) {
|
|
819
|
+
uint256 value = _claimWinnings(marketId);
|
|
820
|
+
// unwrapping and transferring user funds from winnings claimed
|
|
821
|
+
IWETH(WETH).withdraw(value);
|
|
822
|
+
(bool sent, ) = payable(msg.sender).call{value: value}("");
|
|
823
|
+
require(sent, "Failed to send Ether");
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/// @dev Allows holders of voided outcome shares to claim balance back.
|
|
827
|
+
function _claimVoidedOutcomeShares(uint256 marketId, uint256 outcomeId)
|
|
828
|
+
private
|
|
829
|
+
atState(marketId, MarketState.resolved)
|
|
830
|
+
returns (uint256)
|
|
831
|
+
{
|
|
832
|
+
Market storage market = markets[marketId];
|
|
833
|
+
MarketOutcome storage outcome = market.outcomes[outcomeId];
|
|
834
|
+
|
|
835
|
+
require(isMarketVoided(marketId), "market is not voided");
|
|
836
|
+
require(outcome.shares.holders[msg.sender] > 0, "user doesn't hold outcome shares");
|
|
837
|
+
require(outcome.shares.voidedClaims[msg.sender] == false, "user already claimed shares");
|
|
838
|
+
|
|
839
|
+
// voided market - shares are valued at last market price
|
|
840
|
+
uint256 price = getMarketOutcomePrice(marketId, outcomeId);
|
|
841
|
+
uint256 value = (price * outcome.shares.holders[msg.sender]) / ONE;
|
|
842
|
+
|
|
843
|
+
// assuring market has enough funds
|
|
844
|
+
require(market.balance >= value, "insufficient market balance");
|
|
845
|
+
|
|
846
|
+
market.balance = market.balance - value;
|
|
847
|
+
outcome.shares.voidedClaims[msg.sender] = true;
|
|
848
|
+
|
|
849
|
+
emit MarketActionTx(
|
|
850
|
+
msg.sender,
|
|
851
|
+
MarketAction.claimVoided,
|
|
852
|
+
marketId,
|
|
853
|
+
outcomeId,
|
|
854
|
+
outcome.shares.holders[msg.sender],
|
|
855
|
+
value,
|
|
856
|
+
block.timestamp
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
return value;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function claimVoidedOutcomeShares(uint256 marketId, uint256 outcomeId) external {
|
|
863
|
+
uint256 value = _claimVoidedOutcomeShares(marketId, outcomeId);
|
|
864
|
+
// transferring user funds from voided outcome shares claimed
|
|
865
|
+
Market storage market = markets[marketId];
|
|
866
|
+
market.token.safeTransfer(msg.sender, value);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
function claimVoidedOutcomeSharesToETH(uint256 marketId, uint256 outcomeId) external isWETHMarket(marketId) {
|
|
870
|
+
uint256 value = _claimVoidedOutcomeShares(marketId, outcomeId);
|
|
871
|
+
// unwrapping and transferring user funds from voided outcome shares claimed
|
|
872
|
+
IWETH(WETH).withdraw(value);
|
|
873
|
+
(bool sent, ) = payable(msg.sender).call{value: value}("");
|
|
874
|
+
require(sent, "Failed to send Ether");
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
/// @dev Allows liquidity providers to claim earnings from liquidity providing.
|
|
878
|
+
function _claimLiquidity(uint256 marketId) private atState(marketId, MarketState.resolved) returns (uint256) {
|
|
879
|
+
Market storage market = markets[marketId];
|
|
880
|
+
|
|
881
|
+
// claiming any pending fees
|
|
882
|
+
claimFees(marketId);
|
|
883
|
+
|
|
884
|
+
require(market.liquidityShares[msg.sender] > 0, "user doesn't hold shares");
|
|
885
|
+
require(market.liquidityClaims[msg.sender] == false, "user already claimed shares");
|
|
886
|
+
|
|
887
|
+
// value = total resolved outcome pool shares * pool share (%)
|
|
888
|
+
uint256 liquidityPrice = getMarketLiquidityPrice(marketId);
|
|
889
|
+
uint256 value = (liquidityPrice * market.liquidityShares[msg.sender]) / ONE;
|
|
890
|
+
|
|
891
|
+
// assuring market has enough funds
|
|
892
|
+
require(market.balance >= value, "insufficient market balance");
|
|
893
|
+
|
|
894
|
+
market.balance = market.balance - value;
|
|
895
|
+
market.liquidityClaims[msg.sender] = true;
|
|
896
|
+
|
|
897
|
+
emit MarketActionTx(
|
|
898
|
+
msg.sender,
|
|
899
|
+
MarketAction.claimLiquidity,
|
|
900
|
+
marketId,
|
|
901
|
+
0,
|
|
902
|
+
market.liquidityShares[msg.sender],
|
|
903
|
+
value,
|
|
904
|
+
block.timestamp
|
|
905
|
+
);
|
|
906
|
+
|
|
907
|
+
return value;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function claimLiquidity(uint256 marketId) external {
|
|
911
|
+
uint256 value = _claimLiquidity(marketId);
|
|
912
|
+
// transferring user funds from liquidity claimed
|
|
913
|
+
Market storage market = markets[marketId];
|
|
914
|
+
market.token.safeTransfer(msg.sender, value);
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
function claimLiquidityToETH(uint256 marketId) external isWETHMarket(marketId) {
|
|
918
|
+
uint256 value = _claimLiquidity(marketId);
|
|
919
|
+
// unwrapping and transferring user funds from liquidity claimed
|
|
920
|
+
IWETH(WETH).withdraw(value);
|
|
921
|
+
(bool sent, ) = payable(msg.sender).call{value: value}("");
|
|
922
|
+
require(sent, "Failed to send Ether");
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/// @dev Allows liquidity providers to claim their fees share from fees pool
|
|
926
|
+
function _claimFees(uint256 marketId) private returns (uint256) {
|
|
927
|
+
Market storage market = markets[marketId];
|
|
928
|
+
|
|
929
|
+
uint256 claimableFees = getUserClaimableFees(marketId, msg.sender);
|
|
930
|
+
|
|
931
|
+
if (claimableFees > 0) {
|
|
932
|
+
market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender] + claimableFees;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
emit MarketActionTx(
|
|
936
|
+
msg.sender,
|
|
937
|
+
MarketAction.claimFees,
|
|
938
|
+
marketId,
|
|
939
|
+
0,
|
|
940
|
+
market.liquidityShares[msg.sender],
|
|
941
|
+
claimableFees,
|
|
942
|
+
block.timestamp
|
|
943
|
+
);
|
|
944
|
+
|
|
945
|
+
return claimableFees;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
function claimFees(uint256 marketId) public nonReentrant {
|
|
949
|
+
uint256 value = _claimFees(marketId);
|
|
950
|
+
// transferring user funds from fees claimed
|
|
951
|
+
Market storage market = markets[marketId];
|
|
952
|
+
market.token.safeTransfer(msg.sender, value);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
function claimFeesToETH(uint256 marketId) public isWETHMarket(marketId) nonReentrant {
|
|
956
|
+
uint256 value = _claimFees(marketId);
|
|
957
|
+
// unwrapping and transferring user funds from fees claimed
|
|
958
|
+
IWETH(WETH).withdraw(value);
|
|
959
|
+
(bool sent, ) = payable(msg.sender).call{value: value}("");
|
|
960
|
+
require(sent, "Failed to send Ether");
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/// @dev Rebalances the fees pool. Needed in every AddLiquidity / RemoveLiquidity call
|
|
964
|
+
function rebalanceFeesPool(
|
|
965
|
+
uint256 marketId,
|
|
966
|
+
uint256 liquidityShares,
|
|
967
|
+
MarketAction action
|
|
968
|
+
) private {
|
|
969
|
+
Market storage market = markets[marketId];
|
|
970
|
+
|
|
971
|
+
uint256 poolWeight = (liquidityShares * market.fees.poolWeight) / market.liquidity;
|
|
972
|
+
|
|
973
|
+
if (action == MarketAction.addLiquidity) {
|
|
974
|
+
market.fees.poolWeight = market.fees.poolWeight + poolWeight;
|
|
975
|
+
market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender] + poolWeight;
|
|
976
|
+
} else {
|
|
977
|
+
market.fees.poolWeight = market.fees.poolWeight - poolWeight;
|
|
978
|
+
market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender] - poolWeight;
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/// @dev Transitions market to next state
|
|
983
|
+
function nextState(uint256 marketId) private {
|
|
984
|
+
Market storage market = markets[marketId];
|
|
985
|
+
market.state = MarketState(uint256(market.state) + 1);
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/// @dev Transitions market to last state
|
|
989
|
+
function lastState(uint256 marketId) private {
|
|
990
|
+
Market storage market = markets[marketId];
|
|
991
|
+
market.state = MarketState.resolved;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/// @dev Emits a outcome price event for every outcome
|
|
995
|
+
function emitMarketActionEvents(uint256 marketId) private {
|
|
996
|
+
Market storage market = markets[marketId];
|
|
997
|
+
uint256[] memory outcomeShares = new uint256[](market.outcomeCount);
|
|
998
|
+
|
|
999
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1000
|
+
outcomeShares[i] = market.outcomes[i].shares.available;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
emit MarketOutcomeShares(marketId, block.timestamp, outcomeShares, market.liquidity);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/// @dev Adds outcome shares to shares pool
|
|
1007
|
+
function addSharesToMarket(uint256 marketId, uint256 shares) private {
|
|
1008
|
+
Market storage market = markets[marketId];
|
|
1009
|
+
|
|
1010
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1011
|
+
MarketOutcome storage outcome = market.outcomes[i];
|
|
1012
|
+
|
|
1013
|
+
outcome.shares.available = outcome.shares.available + shares;
|
|
1014
|
+
outcome.shares.total = outcome.shares.total + shares;
|
|
1015
|
+
|
|
1016
|
+
// only adding to market total shares, the available remains
|
|
1017
|
+
market.sharesAvailable = market.sharesAvailable + shares;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
market.balance = market.balance + shares;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
/// @dev Removes outcome shares from shares pool
|
|
1024
|
+
function removeSharesFromMarket(uint256 marketId, uint256 shares) private {
|
|
1025
|
+
Market storage market = markets[marketId];
|
|
1026
|
+
|
|
1027
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1028
|
+
MarketOutcome storage outcome = market.outcomes[i];
|
|
1029
|
+
|
|
1030
|
+
outcome.shares.available = outcome.shares.available - shares;
|
|
1031
|
+
outcome.shares.total = outcome.shares.total - shares;
|
|
1032
|
+
|
|
1033
|
+
// only subtracting from market total shares, the available remains
|
|
1034
|
+
market.sharesAvailable = market.sharesAvailable - shares;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
market.balance = market.balance - shares;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
/// @dev Transfer outcome shares from pool to user balance
|
|
1041
|
+
function transferOutcomeSharesfromPool(
|
|
1042
|
+
address user,
|
|
1043
|
+
uint256 marketId,
|
|
1044
|
+
uint256 outcomeId,
|
|
1045
|
+
uint256 shares
|
|
1046
|
+
) private {
|
|
1047
|
+
Market storage market = markets[marketId];
|
|
1048
|
+
MarketOutcome storage outcome = market.outcomes[outcomeId];
|
|
1049
|
+
|
|
1050
|
+
// transfering shares from shares pool to user
|
|
1051
|
+
outcome.shares.holders[user] = outcome.shares.holders[user] + shares;
|
|
1052
|
+
outcome.shares.available = outcome.shares.available - shares;
|
|
1053
|
+
market.sharesAvailable = market.sharesAvailable - shares;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/// @dev Transfer outcome shares from user balance back to pool
|
|
1057
|
+
function transferOutcomeSharesToPool(
|
|
1058
|
+
address user,
|
|
1059
|
+
uint256 marketId,
|
|
1060
|
+
uint256 outcomeId,
|
|
1061
|
+
uint256 shares
|
|
1062
|
+
) private {
|
|
1063
|
+
Market storage market = markets[marketId];
|
|
1064
|
+
MarketOutcome storage outcome = market.outcomes[outcomeId];
|
|
1065
|
+
|
|
1066
|
+
// adding shares back to pool
|
|
1067
|
+
outcome.shares.holders[user] = outcome.shares.holders[user] - shares;
|
|
1068
|
+
outcome.shares.available = outcome.shares.available + shares;
|
|
1069
|
+
market.sharesAvailable = market.sharesAvailable + shares;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
// ------ Core Functions End ------
|
|
1073
|
+
|
|
1074
|
+
// ------ Getters ------
|
|
1075
|
+
|
|
1076
|
+
function getUserMarketShares(uint256 marketId, address user) external view returns (uint256, uint256[] memory) {
|
|
1077
|
+
Market storage market = markets[marketId];
|
|
1078
|
+
uint256[] memory outcomeShares = new uint256[](market.outcomeCount);
|
|
1079
|
+
|
|
1080
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1081
|
+
outcomeShares[i] = market.outcomes[i].shares.holders[user];
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
return (market.liquidityShares[user], outcomeShares);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function getUserClaimStatus(uint256 marketId, address user)
|
|
1088
|
+
external
|
|
1089
|
+
view
|
|
1090
|
+
returns (
|
|
1091
|
+
bool,
|
|
1092
|
+
bool,
|
|
1093
|
+
bool,
|
|
1094
|
+
bool,
|
|
1095
|
+
uint256
|
|
1096
|
+
)
|
|
1097
|
+
{
|
|
1098
|
+
Market storage market = markets[marketId];
|
|
1099
|
+
|
|
1100
|
+
// market still not resolved
|
|
1101
|
+
if (market.state != MarketState.resolved) {
|
|
1102
|
+
return (false, false, false, false, getUserClaimableFees(marketId, user));
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
MarketOutcome storage outcome = market.outcomes[market.resolution.outcomeId];
|
|
1106
|
+
|
|
1107
|
+
return (
|
|
1108
|
+
outcome.shares.holders[user] > 0,
|
|
1109
|
+
outcome.shares.claims[user],
|
|
1110
|
+
market.liquidityShares[user] > 0,
|
|
1111
|
+
market.liquidityClaims[user],
|
|
1112
|
+
getUserClaimableFees(marketId, user)
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
function getUserLiquidityPoolShare(uint256 marketId, address user) external view returns (uint256) {
|
|
1117
|
+
Market storage market = markets[marketId];
|
|
1118
|
+
|
|
1119
|
+
return (market.liquidityShares[user] * ONE) / market.liquidity;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
function getUserClaimableFees(uint256 marketId, address user) public view returns (uint256) {
|
|
1123
|
+
Market storage market = markets[marketId];
|
|
1124
|
+
|
|
1125
|
+
uint256 rawAmount = (market.fees.poolWeight * market.liquidityShares[user]) / market.liquidity;
|
|
1126
|
+
|
|
1127
|
+
// No fees left to claim
|
|
1128
|
+
if (market.fees.claimed[user] > rawAmount) return 0;
|
|
1129
|
+
|
|
1130
|
+
return rawAmount - market.fees.claimed[user];
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
function getMarkets() external view returns (uint256[] memory) {
|
|
1134
|
+
return marketIds;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
function getMarketData(uint256 marketId)
|
|
1138
|
+
external
|
|
1139
|
+
view
|
|
1140
|
+
returns (
|
|
1141
|
+
MarketState,
|
|
1142
|
+
uint256,
|
|
1143
|
+
uint256,
|
|
1144
|
+
uint256,
|
|
1145
|
+
uint256,
|
|
1146
|
+
int256
|
|
1147
|
+
)
|
|
1148
|
+
{
|
|
1149
|
+
Market storage market = markets[marketId];
|
|
1150
|
+
|
|
1151
|
+
return (
|
|
1152
|
+
market.state,
|
|
1153
|
+
market.closesAtTimestamp,
|
|
1154
|
+
market.liquidity,
|
|
1155
|
+
market.balance,
|
|
1156
|
+
market.sharesAvailable,
|
|
1157
|
+
getMarketResolvedOutcome(marketId)
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
function getMarketAltData(uint256 marketId)
|
|
1162
|
+
external
|
|
1163
|
+
view
|
|
1164
|
+
returns (
|
|
1165
|
+
uint256,
|
|
1166
|
+
bytes32,
|
|
1167
|
+
uint256,
|
|
1168
|
+
IERC20,
|
|
1169
|
+
uint256,
|
|
1170
|
+
address,
|
|
1171
|
+
IRealityETH_ERC20,
|
|
1172
|
+
uint256,
|
|
1173
|
+
IPredictionMarketV3Manager
|
|
1174
|
+
)
|
|
1175
|
+
{
|
|
1176
|
+
Market storage market = markets[marketId];
|
|
1177
|
+
|
|
1178
|
+
return (
|
|
1179
|
+
market.fees.fee,
|
|
1180
|
+
market.resolution.questionId,
|
|
1181
|
+
uint256(market.resolution.questionId),
|
|
1182
|
+
market.token,
|
|
1183
|
+
market.fees.treasuryFee,
|
|
1184
|
+
market.fees.treasury,
|
|
1185
|
+
market.resolution.realitio,
|
|
1186
|
+
market.resolution.realitioTimeout,
|
|
1187
|
+
market.manager
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
function getMarketCreator(uint256 marketId) external view returns (address) {
|
|
1192
|
+
Market storage market = markets[marketId];
|
|
1193
|
+
|
|
1194
|
+
return market.creator;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
function getMarketQuestion(uint256 marketId) external view returns (bytes32) {
|
|
1198
|
+
Market storage market = markets[marketId];
|
|
1199
|
+
|
|
1200
|
+
return (market.resolution.questionId);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
function getMarketPrices(uint256 marketId) external view returns (uint256, uint256[] memory) {
|
|
1204
|
+
Market storage market = markets[marketId];
|
|
1205
|
+
uint256[] memory prices = new uint256[](market.outcomeCount);
|
|
1206
|
+
|
|
1207
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1208
|
+
prices[i] = getMarketOutcomePrice(marketId, i);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
return (getMarketLiquidityPrice(marketId), prices);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
function getMarketShares(uint256 marketId) external view returns (uint256, uint256[] memory) {
|
|
1215
|
+
Market storage market = markets[marketId];
|
|
1216
|
+
uint256[] memory outcomeShares = new uint256[](market.outcomeCount);
|
|
1217
|
+
|
|
1218
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1219
|
+
outcomeShares[i] = market.outcomes[i].shares.available;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
return (market.liquidity, outcomeShares);
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
function getMarketLiquidityPrice(uint256 marketId) public view returns (uint256) {
|
|
1226
|
+
Market storage market = markets[marketId];
|
|
1227
|
+
|
|
1228
|
+
if (market.state == MarketState.resolved && !isMarketVoided(marketId)) {
|
|
1229
|
+
// resolved market, outcome prices are either 0 or 1
|
|
1230
|
+
// final liquidity price = outcome shares / liquidity shares
|
|
1231
|
+
return (market.outcomes[market.resolution.outcomeId].shares.available * ONE) / market.liquidity;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// liquidity price = # outcomes / (liquidity * sum (1 / every outcome shares)
|
|
1235
|
+
uint256 marketSharesSum = 0;
|
|
1236
|
+
|
|
1237
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1238
|
+
MarketOutcome storage outcome = market.outcomes[i];
|
|
1239
|
+
|
|
1240
|
+
marketSharesSum = marketSharesSum + (ONE * ONE) / outcome.shares.available;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
return (market.outcomeCount * ONE * ONE * ONE) / market.liquidity / marketSharesSum;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
function getMarketResolvedOutcome(uint256 marketId) public view returns (int256) {
|
|
1247
|
+
Market storage market = markets[marketId];
|
|
1248
|
+
|
|
1249
|
+
// returning -1 if market still not resolved
|
|
1250
|
+
if (market.state != MarketState.resolved) {
|
|
1251
|
+
return -1;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
return int256(market.resolution.outcomeId);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
function isMarketVoided(uint256 marketId) public view returns (bool) {
|
|
1258
|
+
Market storage market = markets[marketId];
|
|
1259
|
+
|
|
1260
|
+
// market still not resolved, still in valid state
|
|
1261
|
+
if (market.state != MarketState.resolved) {
|
|
1262
|
+
return false;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// resolved market id does not match any of the market ids
|
|
1266
|
+
return market.resolution.outcomeId >= market.outcomeCount;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
function getMarketFee(uint256 marketId) public view returns (uint256) {
|
|
1270
|
+
Market storage market = markets[marketId];
|
|
1271
|
+
|
|
1272
|
+
return market.fees.fee + market.fees.treasuryFee;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// ------ Outcome Getters ------
|
|
1276
|
+
|
|
1277
|
+
function getMarketOutcomeIds(uint256 marketId) external view returns (uint256[] memory) {
|
|
1278
|
+
Market storage market = markets[marketId];
|
|
1279
|
+
uint256[] memory outcomeIds = new uint256[](market.outcomeCount);
|
|
1280
|
+
|
|
1281
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1282
|
+
outcomeIds[i] = i;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
return outcomeIds;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
function getMarketOutcomePrice(uint256 marketId, uint256 outcomeId) public view returns (uint256) {
|
|
1289
|
+
Market storage market = markets[marketId];
|
|
1290
|
+
|
|
1291
|
+
if (market.state == MarketState.resolved && !isMarketVoided(marketId)) {
|
|
1292
|
+
// resolved market, price is either 0 or 1
|
|
1293
|
+
return outcomeId == market.resolution.outcomeId ? ONE : 0;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// outcome price = 1 / (1 + sum(outcome shares / every outcome shares))
|
|
1297
|
+
uint256 div = ONE;
|
|
1298
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1299
|
+
if (i == outcomeId) continue;
|
|
1300
|
+
|
|
1301
|
+
div = div + (market.outcomes[outcomeId].shares.available * ONE) / market.outcomes[i].shares.available;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
return (ONE * ONE) / div;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function getMarketOutcomeData(uint256 marketId, uint256 outcomeId)
|
|
1308
|
+
external
|
|
1309
|
+
view
|
|
1310
|
+
returns (
|
|
1311
|
+
uint256,
|
|
1312
|
+
uint256,
|
|
1313
|
+
uint256
|
|
1314
|
+
)
|
|
1315
|
+
{
|
|
1316
|
+
Market storage market = markets[marketId];
|
|
1317
|
+
MarketOutcome storage outcome = market.outcomes[outcomeId];
|
|
1318
|
+
|
|
1319
|
+
return (getMarketOutcomePrice(marketId, outcomeId), outcome.shares.available, outcome.shares.total);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
function getMarketOutcomesShares(uint256 marketId) private view returns (uint256[] memory) {
|
|
1323
|
+
Market storage market = markets[marketId];
|
|
1324
|
+
|
|
1325
|
+
uint256[] memory shares = new uint256[](market.outcomeCount);
|
|
1326
|
+
for (uint256 i = 0; i < market.outcomeCount; ++i) {
|
|
1327
|
+
shares[i] = market.outcomes[i].shares.available;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
return shares;
|
|
1331
|
+
}
|
|
1332
|
+
}
|