polkamarkets-js 1.0.2 → 3.1.0

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.
Files changed (163) hide show
  1. package/.openzeppelin/unknown-1337.json +2056 -0
  2. package/CONTRIBUTING.md +36 -0
  3. package/README.md +24 -25
  4. package/_book/README.md +590 -0
  5. package/_book/core.md +50 -0
  6. package/_book/gitbook/fonts/fontawesome/FontAwesome.otf +0 -0
  7. package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.eot +0 -0
  8. package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.svg +685 -0
  9. package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.ttf +0 -0
  10. package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff +0 -0
  11. package/_book/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 +0 -0
  12. package/_book/gitbook/gitbook-plugin-fontsettings/fontsettings.js +240 -0
  13. package/_book/gitbook/gitbook-plugin-fontsettings/website.css +291 -0
  14. package/_book/gitbook/gitbook-plugin-highlight/ebook.css +135 -0
  15. package/_book/gitbook/gitbook-plugin-highlight/website.css +434 -0
  16. package/_book/gitbook/gitbook-plugin-lunr/lunr.min.js +7 -0
  17. package/_book/gitbook/gitbook-plugin-lunr/search-lunr.js +59 -0
  18. package/_book/gitbook/gitbook-plugin-search/lunr.min.js +7 -0
  19. package/_book/gitbook/gitbook-plugin-search/search-engine.js +50 -0
  20. package/_book/gitbook/gitbook-plugin-search/search.css +35 -0
  21. package/_book/gitbook/gitbook-plugin-search/search.js +213 -0
  22. package/_book/gitbook/gitbook-plugin-sharing/buttons.js +90 -0
  23. package/_book/gitbook/gitbook.js +4 -0
  24. package/_book/gitbook/images/apple-touch-icon-precomposed-152.png +0 -0
  25. package/_book/gitbook/images/favicon.ico +0 -0
  26. package/_book/gitbook/style.css +9 -0
  27. package/_book/gitbook/theme.js +4 -0
  28. package/_book/index.html +705 -0
  29. package/_book/intro.md +32 -0
  30. package/_book/search_index.json +1 -0
  31. package/book.json +8 -0
  32. package/build/contracts/AccessControl.json +1 -0
  33. package/build/contracts/AccessControlEnumerable.json +1 -0
  34. package/build/contracts/Achievements.json +1 -0
  35. package/build/contracts/Address.json +1 -0
  36. package/build/contracts/BalanceHolder.json +1 -0
  37. package/build/contracts/BalanceHolder_ERC20.json +1 -0
  38. package/build/contracts/CeilDiv.json +1 -0
  39. package/build/contracts/Clones.json +1 -0
  40. package/build/contracts/Context.json +1 -0
  41. package/build/contracts/Counters.json +1 -0
  42. package/build/contracts/ERC165.json +1 -0
  43. package/build/contracts/ERC20.json +1 -0
  44. package/build/contracts/ERC20Burnable.json +1 -0
  45. package/build/contracts/ERC20Pausable.json +1 -0
  46. package/build/contracts/ERC20PresetMinterPauser.json +1 -0
  47. package/build/contracts/ERC721.json +1 -0
  48. package/build/contracts/EnumerableMap.json +1 -0
  49. package/build/contracts/EnumerableSet.json +1 -0
  50. package/build/contracts/FantasyERC20.json +1 -0
  51. package/build/contracts/IAccessControl.json +1 -0
  52. package/build/contracts/IAccessControlEnumerable.json +1 -0
  53. package/build/contracts/IBalanceHolder_ERC20.json +1 -0
  54. package/build/contracts/IERC165.json +1 -0
  55. package/build/contracts/IERC20.json +1 -0
  56. package/build/contracts/IERC20Metadata.json +1 -0
  57. package/build/contracts/IERC20Permit.json +1 -0
  58. package/build/contracts/IERC721.json +1 -0
  59. package/build/contracts/IERC721Enumerable.json +1 -0
  60. package/build/contracts/IERC721Metadata.json +1 -0
  61. package/build/contracts/IERC721Receiver.json +1 -0
  62. package/build/contracts/IFantasyERC20.json +1 -0
  63. package/build/contracts/IPredictionMarketV3.json +1 -0
  64. package/build/contracts/IPredictionMarketV3Factory.json +1 -0
  65. package/build/contracts/IPredictionMarketV3Manager.json +1 -0
  66. package/build/contracts/IRealityETH_ERC20.json +1 -0
  67. package/build/contracts/IRealityETH_IERC20.json +1 -0
  68. package/build/contracts/IWETH.json +1 -0
  69. package/build/contracts/LandFactory.json +1 -0
  70. package/build/contracts/Math.json +1 -0
  71. package/build/contracts/Migrations.json +1 -0
  72. package/build/contracts/Ownable.json +1 -0
  73. package/build/contracts/Pausable.json +1 -0
  74. package/build/contracts/PredictionMarket.json +1 -0
  75. package/build/contracts/PredictionMarketV2.json +1 -0
  76. package/build/contracts/PredictionMarketV3.json +1 -0
  77. package/build/contracts/PredictionMarketV3Controller.json +1 -0
  78. package/build/contracts/PredictionMarketV3Factory.json +1 -0
  79. package/build/contracts/PredictionMarketV3Manager.json +1 -0
  80. package/build/contracts/PredictionMarketV3Querier.json +1 -0
  81. package/build/contracts/RealitioERC20.json +1 -0
  82. package/build/contracts/RealitioForeignArbitrationProxyWithAppeals.json +1 -0
  83. package/build/contracts/RealitioHomeArbitrationProxy.json +1 -0
  84. package/build/contracts/RealitioSafeMath256.json +1 -0
  85. package/build/contracts/RealitioSafeMath32.json +1 -0
  86. package/build/contracts/RealityETH_ERC20_Factory.json +1 -0
  87. package/build/contracts/RealityETH_ERC20_v3_0.json +1 -0
  88. package/build/contracts/ReentrancyGuard.json +1 -0
  89. package/build/contracts/SafeERC20.json +1 -0
  90. package/build/contracts/SafeMath.json +1 -0
  91. package/build/contracts/Strings.json +1 -0
  92. package/build/contracts/Voting.json +1 -0
  93. package/build/contracts/WETH9.json +1 -0
  94. package/build/contracts/test.json +1 -0
  95. package/cleanContracts.js +22 -0
  96. package/contracts/FantasyERC20.sol +81 -0
  97. package/contracts/IFantasyERC20.sol +20 -0
  98. package/contracts/IPredictionMarketV3.sol +207 -0
  99. package/contracts/IPredictionMarketV3Factory.sol +10 -0
  100. package/contracts/IPredictionMarketV3Manager.sol +12 -0
  101. package/contracts/IRealityETH_ERC20.sol +64 -0
  102. package/contracts/LandFactory.sol +248 -0
  103. package/contracts/Migrations.sol +24 -0
  104. package/contracts/PredictionMarketV3.sol +1332 -0
  105. package/contracts/PredictionMarketV3Controller.sol +87 -0
  106. package/contracts/PredictionMarketV3Factory.sol +205 -0
  107. package/contracts/PredictionMarketV3Manager.sol +45 -0
  108. package/contracts/PredictionMarketV3Querier.sol +79 -0
  109. package/contracts/RealityETH_ERC20_Factory.sol +54 -0
  110. package/contracts/Voting.sol +153 -0
  111. package/contracts/WETH9.sol +62 -0
  112. package/help.txt +8 -0
  113. package/index.js +3 -0
  114. package/migrations/10_deploy_weth.js +5 -0
  115. package/migrations/11_deploy_full_flow.js +99 -0
  116. package/migrations/12_deploy_pm_v3_querier.js +7 -0
  117. package/migrations/13_deploy_pm_v3_factory.js +14 -0
  118. package/migrations/1_initial_migration.js +5 -0
  119. package/migrations/2_deploy_erc20.js +10 -0
  120. package/migrations/3_deploy_realitio.js +11 -0
  121. package/migrations/4_deploy_pm.js +20 -0
  122. package/migrations/5_seed_markets.js +51 -0
  123. package/migrations/6_deploy_achievements.js +5 -0
  124. package/migrations/7_deploy_voting.js +14 -0
  125. package/migrations/8_deploy_pm_v2.js +20 -0
  126. package/migrations/9_seed_markets_v2.js +68 -0
  127. package/package.json +106 -13
  128. package/src/Application.js +421 -0
  129. package/src/interfaces/index.js +19 -0
  130. package/src/models/AchievementsContract.js +217 -0
  131. package/src/models/ArbitrationContract.js +69 -0
  132. package/src/models/ArbitrationProxyContract.js +32 -0
  133. package/src/models/ERC20Contract.js +156 -0
  134. package/src/models/FantasyERC20Contract.js +92 -0
  135. package/src/models/IContract.js +1002 -0
  136. package/src/models/PolkamarketsSmartAccount.js +100 -0
  137. package/src/models/PredictionMarketContract.js +562 -0
  138. package/src/models/PredictionMarketV2Contract.js +830 -0
  139. package/src/models/PredictionMarketV3Contract.js +233 -0
  140. package/src/models/PredictionMarketV3ControllerContract.js +102 -0
  141. package/src/models/PredictionMarketV3FactoryContract.js +96 -0
  142. package/src/models/PredictionMarketV3ManagerContract.js +111 -0
  143. package/src/models/PredictionMarketV3QuerierContract.js +24 -0
  144. package/src/models/RealitioERC20Contract.js +286 -0
  145. package/src/models/VotingContract.js +182 -0
  146. package/src/models/WETH9Contract.js +92 -0
  147. package/src/models/index.js +33 -0
  148. package/src/utils/Account.js +40 -0
  149. package/src/utils/Contract.js +120 -0
  150. package/src/utils/Numbers.js +94 -0
  151. package/tests/fantasyERC20Contract.js +225 -0
  152. package/tests/index.js +10 -0
  153. package/tests/predictionMarketContract.js +466 -0
  154. package/tests/predictionMarketV2Contract.js +1042 -0
  155. package/tests/predictionMarketV3Contract.js +1079 -0
  156. package/tests/predictionMarketV3ControllerContract.js +613 -0
  157. package/tests/predictionMarketV3FactoryContract.js +469 -0
  158. package/tests/predictionMarketV3ManagerContract.js +610 -0
  159. package/tests/utils.js +16 -0
  160. package/tests/votingContract.js +490 -0
  161. package/tooling/docs/jsdoc.json +6 -0
  162. package/truffle-config.js +134 -0
  163. package/polkamarkets.js +0 -436
@@ -0,0 +1,830 @@
1
+ const _ = require("lodash");
2
+ const moment = require("moment");
3
+
4
+ const prediction = require("../interfaces").predictionV2;
5
+ const ierc20 = require("../interfaces").ierc20;
6
+
7
+ const Numbers = require( "../utils/Numbers");
8
+ const IContract = require( './IContract');
9
+
10
+ const ERC20Contract = require('./ERC20Contract');
11
+
12
+ const realitioLib = require('@reality.eth/reality-eth-lib/formatters/question');
13
+
14
+ const actions = {
15
+ 0: 'Buy',
16
+ 1: 'Sell',
17
+ 2: 'Add Liquidity',
18
+ 3: 'Remove Liquidity',
19
+ 4: 'Claim Winnings',
20
+ 5: 'Claim Liquidity',
21
+ 6: 'Claim Fees',
22
+ 7: 'Claim Voided',
23
+ }
24
+
25
+ /**
26
+ * PredictionMarket Contract Object
27
+ * @constructor PredictionMarketContract
28
+ * @param {Web3} web3
29
+ * @param {Integer} decimals
30
+ * @param {Address} contractAddress
31
+ */
32
+
33
+ class PredictionMarketV2Contract extends IContract {
34
+ constructor(params) {
35
+ super({...params, abi: params.abi || prediction});
36
+ this.contractName = 'predictionMarketV2';
37
+ }
38
+
39
+ /* Get Functions */
40
+ /**
41
+ * @function getMinimumRequiredBalance
42
+ * @description Returns minimum required ERC20 balance to create markets
43
+ * @returns {Integer} requiredBalance
44
+ */
45
+ async getMinimumRequiredBalance() {
46
+ const requiredBalance = await this.params.contract
47
+ .getContract()
48
+ .methods
49
+ .requiredBalance()
50
+ .call();
51
+
52
+ const requiredBalanceToken = await this.params.contract
53
+ .getContract()
54
+ .methods
55
+ .requiredBalanceToken()
56
+ .call();
57
+
58
+ const decimals = await this.getTokenDecimals({ contractAddress: requiredBalanceToken });
59
+
60
+ return Numbers.fromDecimalsNumber(requiredBalance, decimals)
61
+ }
62
+
63
+ /**
64
+ * @function getWETHAddress
65
+ * @description Returns WETH Address
66
+ * @returns {address}
67
+ */
68
+ async getWETHAddress() {
69
+ const WETHAddress = await this.params.contract.getContract().methods.WETH().call();
70
+
71
+ return WETHAddress;
72
+ }
73
+
74
+ /* Get Functions */
75
+ /**
76
+ * @function getMarkets
77
+ * @description Get Markets
78
+ * @returns {Integer | Array} Get Market Ids
79
+ */
80
+ async getMarkets() {
81
+ let res = await this.params.contract
82
+ .getContract()
83
+ .methods
84
+ .getMarkets()
85
+ .call();
86
+ return res.map((marketId) => Number(Numbers.fromHex(marketId)));
87
+ }
88
+
89
+ /**
90
+ * @function getMarketData
91
+ * @description Get getMarketData
92
+ * @param {Integer} marketId
93
+ * @returns {String} Market Name
94
+ * @returns {Integer} closeDateTime
95
+ * @returns {Integer} state
96
+ * @returns {Address} Oracle Address
97
+ * @returns {Integer} liquidity
98
+ * @returns {Array} outcomeIds
99
+ */
100
+ async getMarketData({marketId}) {
101
+ const marketData = await this.params.contract.getContract().methods.getMarketData(marketId).call();
102
+ const outcomeIds = await this.params.contract.getContract().methods.getMarketOutcomeIds(marketId).call();
103
+ const decimals = await this.getMarketDecimals({marketId});
104
+ const state = parseInt(marketData[0]);
105
+ const resolvedOutcomeId = parseInt(marketData[5]);
106
+
107
+ return {
108
+ closeDateTime: moment.unix(marketData[1]).format("YYYY-MM-DD HH:mm"),
109
+ state,
110
+ oracleAddress: '0x0000000000000000000000000000000000000000',
111
+ liquidity: Numbers.fromDecimalsNumber(marketData[2], decimals),
112
+ outcomeIds: outcomeIds.map((outcomeId) => Numbers.fromBigNumberToInteger(outcomeId, 18)),
113
+ resolvedOutcomeId,
114
+ voided: state === 2 && !outcomeIds.includes(resolvedOutcomeId)
115
+ };
116
+ }
117
+
118
+ /**
119
+ * @function getOutcomeData
120
+ * @description Get Market Outcome Data
121
+ * @param {Integer} marketId
122
+ * @param {Integer} outcomeId
123
+ * @returns {String} name
124
+ * @returns {Integer} price
125
+ * @returns {Integer} sahres
126
+ */
127
+ async getOutcomeData({marketId, outcomeId}) {
128
+ const outcomeData = await this.params.contract.getContract().methods.getMarketOutcomeData(marketId, outcomeId).call();
129
+ const decimals = await this.getMarketDecimals({marketId});
130
+
131
+ return {
132
+ name: '', // TODO: remove; deprecated
133
+ price: Numbers.fromDecimalsNumber(outcomeData[0], 18),
134
+ shares: Numbers.fromDecimalsNumber(outcomeData[1], decimals),
135
+ };
136
+ }
137
+
138
+ /**
139
+ * @function getMarketDetails
140
+ * @description getMarketDetails
141
+ * @param {Integer} marketId
142
+ * @returns {String} name
143
+ * @returns {String} category
144
+ * @returns {String} subcategory
145
+ * @returns {String} image
146
+ * @returns {Array} outcomes
147
+ */
148
+ async getMarketDetails({marketId}) {
149
+ const marketData = await this.params.contract.getContract().methods.getMarketData(marketId).call();
150
+ const outcomeIds = await this.__sendTx(this.getContract().methods.getMarketOutcomeIds(marketId), true);
151
+
152
+ const events = await this.getEvents('MarketCreated', { marketId });
153
+
154
+ if (events.length === 0) {
155
+ // legacy record, returning empty data
156
+ return { name: '', category: '', subcategory: '', image: '', outcomes: [] };
157
+ }
158
+
159
+ // parsing question with realitio standard
160
+ const question = realitioLib.populatedJSONForTemplate(
161
+ '{"title": "%s", "type": "single-select", "outcomes": [%s], "category": "%s", "lang": "%s"}',
162
+ events[0].returnValues.question
163
+ );
164
+
165
+ // splitting name and description with the first occurrence of ';' character
166
+ const name = question.title.split(';')[0];
167
+ const description = question.title.split(';').slice(1).join(';');
168
+
169
+ return {
170
+ name,
171
+ description,
172
+ category: question.category.split(';')[0],
173
+ subcategory: question.category.split(';')[1],
174
+ outcomes: question.outcomes,
175
+ image: events[0].returnValues.image
176
+ };
177
+ }
178
+
179
+ async getMarketIdsFromQuestions({questions}) {
180
+ const events = await this.getEvents('MarketCreated');
181
+
182
+ return events.filter((event) => {
183
+ return questions.includes(event.returnValues.question);
184
+ }).map((event) => event.returnValues.marketId);
185
+ }
186
+
187
+ /**
188
+ * @function getMarketQuestionId
189
+ * @description getMarketQuestionId
190
+ * @param {Integer} marketId
191
+ * @returns {Bytes32} questionId
192
+ */
193
+ async getMarketQuestionId({marketId}) {
194
+ const marketAltData = await this.params.contract.getContract().methods.getMarketAltData(marketId).call();
195
+
196
+ return marketAltData[1];
197
+ }
198
+
199
+ /**
200
+ * @function getAverageOutcomeBuyPrice
201
+ * @description Calculates average buy price of market outcome based on user events
202
+ * @param {Array} events
203
+ * @param {Integer} marketId
204
+ * @param {Integer} outcomeId
205
+ * @returns {Integer} price
206
+ */
207
+ getAverageOutcomeBuyPrice({events, marketId, outcomeId}) {
208
+ // filtering by marketId + outcomeId + buy action
209
+ events = events.filter(event => {
210
+ return (
211
+ event.action === 'Buy' &&
212
+ event.marketId === marketId &&
213
+ event.outcomeId === outcomeId
214
+ );
215
+ });
216
+
217
+ if (events.length === 0) return 0;
218
+
219
+ const totalShares = events.map(item => item.shares).reduce((prev, next) => prev + next);
220
+ const totalAmount = events.map(item => item.value).reduce((prev, next) => prev + next);
221
+
222
+ return totalAmount / totalShares;
223
+ }
224
+
225
+ /**
226
+ * @function getAverageAddLiquidityPrice
227
+ * @description Calculates average add liquidity of market outcome based on user events
228
+ * @param {Array} events
229
+ * @param {Integer} marketId
230
+ * @returns {Integer} price
231
+ */
232
+ getAverageAddLiquidityPrice({events, marketId}) {
233
+ // filtering by marketId + add liquidity action
234
+ events = events.filter(event => {
235
+ return (
236
+ event.action === 'Add Liquidity' &&
237
+ event.marketId === marketId
238
+ );
239
+ });
240
+
241
+ if (events.length === 0) return 0;
242
+
243
+ const totalShares = events.map(item => item.shares).reduce((prev, next) => prev + next);
244
+ const totalAmount = events.map(item => item.value).reduce((prev, next) => prev + next);
245
+
246
+ return totalAmount / totalShares;
247
+ }
248
+
249
+ /**
250
+ * @function isMarketERC20TokenWrapped
251
+ * @description Checks if market ERC20 token is wrapped
252
+ * @param {Integer} marketId
253
+ * @returns {Boolean} boolean
254
+ */
255
+ async isMarketERC20TokenWrapped({marketId}) {
256
+ const WETHAddress = await this.params.contract.getContract().methods.WETH().call();
257
+ const marketAltData = await this.params.contract.getContract().methods.getMarketAltData(marketId).call();
258
+
259
+ return marketAltData[3] === WETHAddress;
260
+ }
261
+
262
+ /**
263
+ * @function getMyPortfolio
264
+ * @description Get My Porfolio
265
+ * @returns {Array} Outcome Shares
266
+ */
267
+ async getMyPortfolio() {
268
+ const account = await this.getMyAccount();
269
+ if (!account) return [];
270
+
271
+ return this.getPortfolio({ user: account });
272
+ }
273
+
274
+ /**
275
+ * @function getPortfolio
276
+ * @description Get My Porfolio
277
+ * @param {Address} user
278
+ * @returns {Array} Outcome Shares
279
+ */
280
+ async getPortfolio({ user }) {
281
+ const allMarketIds = await this.getMarkets();
282
+ let userMarketIds;
283
+ let events = [];
284
+ try {
285
+ events = await this.getActions({ user });
286
+ userMarketIds = events.map(e => e.marketId).filter((x, i, a) => a.indexOf(x) == i);
287
+ } catch (err) {
288
+ // defaulting to allMarketIds if query fails
289
+ userMarketIds = allMarketIds;
290
+ }
291
+
292
+ let voidedMarketIds = [];
293
+ // fetching voided markets
294
+ try {
295
+ // TODO: improve this
296
+ const marketsCreated = await this.getEvents('MarketCreated');
297
+ const marketsResolved = await this.getEvents('MarketResolved');
298
+
299
+ voidedMarketIds = marketsResolved.filter((event) => {
300
+ const resolvedOutcomeId = parseInt(event.returnValues.outcomeId);
301
+ const outcomeCount = marketsCreated.find((market) => {
302
+ return market.returnValues.marketId === event.returnValues.marketId
303
+ }).returnValues.outcomes;
304
+
305
+ return resolvedOutcomeId >= outcomeCount;
306
+ }).map((event) => parseInt(event.returnValues.marketId));
307
+ } catch (err) {
308
+ // skipping voided markets if query fails
309
+ }
310
+
311
+ return await allMarketIds.reduce(async (obj, marketId) => {
312
+ let portfolio;
313
+ if (!userMarketIds.includes(marketId)) {
314
+ // user did not interact with market, no need to fetch holdings
315
+ portfolio = {
316
+ liquidity: { shares: 0, price: 0 },
317
+ outcomes: {
318
+ 0: { shares: 0, price: 0 },
319
+ 1: { shares: 0, price: 0 },
320
+ },
321
+ claimStatus: {
322
+ winningsToClaim: false,
323
+ winningsClaimed: false,
324
+ liquidityToClaim: false,
325
+ liquidityClaimed: false,
326
+ voidedWinningsToClaim: false,
327
+ voidedWinningsClaimed: false,
328
+ liquidityFees: 0
329
+ }
330
+ };
331
+ } else {
332
+ const decimals = await this.getMarketDecimals({marketId});
333
+ const marketShares = await this.getContract().methods.getUserMarketShares(marketId, user).call();
334
+ const claimStatus = await this.getContract().methods.getUserClaimStatus(marketId, user).call();
335
+
336
+ const outcomeShares = Object.fromEntries(marketShares[1].map((item, index) => {
337
+ return [
338
+ index,
339
+ {
340
+ shares: Numbers.fromDecimalsNumber(item, decimals),
341
+ price: this.getAverageOutcomeBuyPrice({events, marketId, outcomeId: index})
342
+ }
343
+ ];
344
+ }));
345
+
346
+ const voidedWinningsToClaim = voidedMarketIds.includes(marketId) && marketShares[1].some(item => item > 0);
347
+ const voidedWinningsClaimed = voidedWinningsToClaim && events.some(event => event.action === 'Claim Voided' && event.marketId === marketId);
348
+
349
+ portfolio = {
350
+ liquidity: {
351
+ shares: Numbers.fromDecimalsNumber(marketShares[0], decimals),
352
+ price: this.getAverageAddLiquidityPrice({events, marketId}),
353
+ },
354
+ outcomes: outcomeShares,
355
+ claimStatus: {
356
+ winningsToClaim: claimStatus[0],
357
+ winningsClaimed: claimStatus[1],
358
+ liquidityToClaim: claimStatus[2],
359
+ liquidityClaimed: claimStatus[3],
360
+ voidedWinningsToClaim,
361
+ voidedWinningsClaimed,
362
+ liquidityFees: Numbers.fromDecimalsNumber(claimStatus[4], decimals)
363
+ }
364
+ };
365
+ }
366
+
367
+ return await {
368
+ ...(await obj),
369
+ [marketId]: portfolio,
370
+ };
371
+ }, {});
372
+ }
373
+
374
+ /**
375
+ * @function getMyMarketShares
376
+ * @description Get My Market Shares
377
+ * @param {Integer} marketId
378
+ * @returns {Integer} Liquidity Shares
379
+ * @returns {Array} Outcome Shares
380
+ */
381
+ async getMyMarketShares({marketId}) {
382
+ const account = await this.getMyAccount();
383
+ if (!account) return [];
384
+
385
+ const decimals = await this.getMarketDecimals({marketId});
386
+ const marketShares = await this.getContract().methods.getUserMarketShares(marketId, account).call();
387
+ const outcomeShares = Object.fromEntries(marketShares[1].map((item, index) => [index, Numbers.fromDecimalsNumber(item, decimals)] ));
388
+
389
+ return {
390
+ liquidityShares: Numbers.fromDecimalsNumber(marketShares[0], decimals),
391
+ outcomeShares
392
+ };
393
+ }
394
+
395
+ async getMyActions() {
396
+ const account = await this.getMyAccount();
397
+ if (!account) return [];
398
+
399
+ return this.getActions({ user: account });
400
+ }
401
+
402
+ async getActions({ user }) {
403
+ const events = await this.getEvents('MarketActionTx', { user });
404
+
405
+ // fetching decimals for each market (unique)
406
+ const marketIds = events.map(event => event.returnValues.marketId).filter((x, i, a) => a.indexOf(x) == i);
407
+ const marketDecimals = await Promise.all(marketIds.map(marketId => this.getMarketDecimals({marketId})));
408
+
409
+ // filtering by address
410
+ return events.map(event => {
411
+ const decimals = marketDecimals[marketIds.indexOf(event.returnValues.marketId)];
412
+
413
+ return {
414
+ action: actions[Numbers.fromBigNumberToInteger(event.returnValues.action, 18)],
415
+ marketId: Numbers.fromBigNumberToInteger(event.returnValues.marketId, 18),
416
+ outcomeId: Numbers.fromBigNumberToInteger(event.returnValues.outcomeId, 18),
417
+ shares: Numbers.fromDecimalsNumber(event.returnValues.shares, decimals),
418
+ value: Numbers.fromDecimalsNumber(event.returnValues.value, decimals),
419
+ timestamp: Numbers.fromBigNumberToInteger(event.returnValues.timestamp, 18),
420
+ transactionHash: event.transactionHash,
421
+ }
422
+ });
423
+ }
424
+
425
+ /**
426
+ * @function getMarketOutcomePrice
427
+ * @description Get Market Price
428
+ * @param {Integer} marketId
429
+ * @param {Integer} outcomeId
430
+ * @return {Integer} price
431
+ */
432
+ async getMarketOutcomePrice({marketId, outcomeId}) {
433
+ return Numbers.fromDecimals(
434
+ await this.__sendTx(
435
+ this.getContract().methods.getMarketOutcomePrice(marketId, outcomeId),
436
+ true
437
+ ),
438
+ 18
439
+ );
440
+ }
441
+
442
+ /**
443
+ * @function getMarketPrices
444
+ * @description Get Market Price
445
+ * @param {Integer} marketId
446
+ * @return {Object} prices
447
+ */
448
+ async getMarketPrices({marketId}) {
449
+ const marketPrices = await this.getContract().methods.getMarketPrices(marketId).call();
450
+
451
+ return {
452
+ liquidity: Numbers.fromDecimalsNumber(marketPrices[0], 18),
453
+ outcomes: Object.fromEntries(marketPrices[1].map((item, index) => [index, Numbers.fromDecimalsNumber(item, 18)] ))
454
+ };
455
+ }
456
+
457
+ /**
458
+ * @function getMarketShares
459
+ * @description Get Market Shares
460
+ * @param {Integer} marketId
461
+ * @return {Object} shares
462
+ */
463
+ async getMarketShares({marketId}) {
464
+ const decimals = await this.getMarketDecimals({marketId});
465
+ const marketShares = await this.getContract().methods.getMarketShares(marketId).call();
466
+
467
+ return {
468
+ liquidity: Numbers.fromDecimalsNumber(marketShares[0], decimals),
469
+ outcomes: Object.fromEntries(marketShares[1].map((item, index) => [index, Numbers.fromDecimalsNumber(item, decimals)] ))
470
+ };
471
+ }
472
+
473
+ /**
474
+ * @function getTokenDecimals
475
+ * @description Get Token Decimals
476
+ * @param {address} contractAddress
477
+ * @return {Integer} decimals
478
+ */
479
+ async getTokenDecimals({contractAddress}) {
480
+ try {
481
+ const erc20Contract = new ERC20Contract({ ...this.params, contractAddress, abi: ierc20 });
482
+
483
+ return await erc20Contract.getDecimalsAsync();
484
+ } catch (err) {
485
+ // defaulting to 18 decimals
486
+ return 18;
487
+ }
488
+ }
489
+
490
+ /**
491
+ * @function getMarketDecimals
492
+ * @description Get Market Decimals
493
+ * @param {Integer} marketId
494
+ * @return {Integer} decimals
495
+ */
496
+ async getMarketDecimals({marketId}) {
497
+ if (this.defaultDecimals) {
498
+ return this.defaultDecimals;
499
+ }
500
+
501
+ const marketAltData = await this.params.contract.getContract().methods.getMarketAltData(marketId).call();
502
+ const contractAddress = marketAltData[3];
503
+
504
+ return await this.getTokenDecimals({ contractAddress });
505
+ }
506
+
507
+ /**
508
+ * @function prepareCreateMarketDescription
509
+ * @description Prepare createMarket function call args
510
+ */
511
+ async prepareCreateMarketDescription({
512
+ value,
513
+ name,
514
+ description = '',
515
+ image,
516
+ duration,
517
+ oracleAddress,
518
+ outcomes,
519
+ category,
520
+ token,
521
+ odds = [],
522
+ fee = 0,
523
+ treasuryFee = 0,
524
+ treasury = '0x0000000000000000000000000000000000000000',
525
+ }) {
526
+ const decimals = await this.getTokenDecimals({ contractAddress: token });
527
+ const valueToWei = Numbers.toSmartContractDecimals(value, decimals);
528
+ const title = `${name};${description}`;
529
+ const question = realitioLib.encodeText('single-select', title, outcomes, category);
530
+ let distribution = [];
531
+
532
+ if (odds.length > 0) {
533
+ if (odds.length !== outcomes.length) {
534
+ throw new Error('Odds and outcomes must have the same length');
535
+ }
536
+
537
+ const oddsSum = odds.reduce((a, b) => a + b, 0);
538
+ // odds must match 100 (0.1 margin)
539
+ if (oddsSum < 99.9 || oddsSum > 100.1) {
540
+ throw new Error('Odds must sum 100');
541
+ }
542
+
543
+ distribution = this.calcDistribution({ odds });
544
+ }
545
+
546
+ return {
547
+ value: valueToWei,
548
+ closesAt: duration,
549
+ outcomes: outcomes.length,
550
+ token,
551
+ distribution,
552
+ question,
553
+ image,
554
+ arbitrator: oracleAddress,
555
+ fee,
556
+ treasuryFee,
557
+ treasury,
558
+ };
559
+ }
560
+
561
+ /* POST User Functions */
562
+ /**
563
+ * @function createMarket
564
+ * @description Create a µarket
565
+ * @param {Integer} value
566
+ * @param {String} name
567
+ * @param {Integer} duration
568
+ * @param {Address} oracleAddress
569
+ * @param {Array} outcomes
570
+ */
571
+ async createMarket ({
572
+ value,
573
+ name,
574
+ description = '',
575
+ image,
576
+ duration,
577
+ oracleAddress,
578
+ outcomes,
579
+ category,
580
+ token,
581
+ odds = [],
582
+ fee = 0,
583
+ treasuryFee = 0,
584
+ treasury = '0x0000000000000000000000000000000000000000',
585
+ }) {
586
+ const desc = await this.prepareCreateMarketDescription({
587
+ value,
588
+ name,
589
+ description,
590
+ image,
591
+ duration,
592
+ oracleAddress,
593
+ outcomes,
594
+ category,
595
+ token,
596
+ odds,
597
+ fee,
598
+ treasuryFee,
599
+ treasury,
600
+ });
601
+
602
+ return await this.__sendTx(this.getContract().methods.createMarket(desc));
603
+ };
604
+
605
+ /**
606
+ * @function createMarketWithETH
607
+ * @description Create a market
608
+ * @param {Integer} value
609
+ * @param {String} name
610
+ * @param {Integer} duration
611
+ * @param {Address} oracleAddress
612
+ * @param {Array} outcomes
613
+ */
614
+ async createMarketWithETH ({
615
+ value,
616
+ name,
617
+ description = '',
618
+ image,
619
+ duration,
620
+ oracleAddress,
621
+ outcomes,
622
+ category,
623
+ odds = [],
624
+ fee = 0,
625
+ treasuryFee = 0,
626
+ treasury = '0x0000000000000000000000000000000000000000',
627
+ }) {
628
+ const token = await this.getWETHAddress();
629
+ const desc = await this.prepareCreateMarketDescription({
630
+ value,
631
+ name,
632
+ description,
633
+ image,
634
+ duration,
635
+ oracleAddress,
636
+ outcomes,
637
+ category,
638
+ token,
639
+ odds,
640
+ fee,
641
+ treasuryFee,
642
+ treasury,
643
+ });
644
+
645
+ return await this.__sendTx(
646
+ this.getContract().methods.createMarketWithETH(desc),
647
+ false,
648
+ desc.value
649
+ );
650
+ };
651
+
652
+ /**
653
+ * @function addLiquidity
654
+ * @description Add Liquidity from Market
655
+ * @param {Integer} marketId
656
+ * @param {Integer} value
657
+ */
658
+ async addLiquidity({marketId, value, wrapped = false}) {
659
+ const decimals = await this.getMarketDecimals({marketId});
660
+ const valueToWei = Numbers.toSmartContractDecimals(value, decimals);
661
+
662
+ if (wrapped) {
663
+ return await this.__sendTx(
664
+ this.getContract().methods.addLiquidityWithETH(marketId),
665
+ false,
666
+ valueToWei
667
+ );
668
+ }
669
+
670
+ return await this.__sendTx(
671
+ this.getContract().methods.addLiquidity(marketId, valueToWei),
672
+ );
673
+ };
674
+
675
+ /**
676
+ * @function removeLiquidity
677
+ * @description Remove Liquidity from Market
678
+ * @param {Integer} marketId
679
+ * @param {Integer} shares
680
+ */
681
+ async removeLiquidity({marketId, shares, wrapped = false}) {
682
+ const decimals = await this.getMarketDecimals({marketId});
683
+ shares = Numbers.toSmartContractDecimals(shares, decimals);
684
+
685
+ if (wrapped) {
686
+ return await this.__sendTx(
687
+ this.getContract().methods.removeLiquidityToETH(marketId, shares),
688
+ null
689
+ );
690
+ }
691
+
692
+ return await this.__sendTx(
693
+ this.getContract().methods.removeLiquidity(marketId, shares)
694
+ );
695
+ };
696
+
697
+ /**
698
+ * @function buy
699
+ * @description Buy Shares of a Market Outcome
700
+ * @param {Integer} marketId
701
+ * @param {Integer} outcomeId
702
+ * @param {Integer} value
703
+ */
704
+ async buy ({ marketId, outcomeId, value, minOutcomeSharesToBuy, wrapped = false}) {
705
+ const decimals = await this.getMarketDecimals({marketId});
706
+ const valueToWei = Numbers.toSmartContractDecimals(value, decimals);
707
+ minOutcomeSharesToBuy = Numbers.toSmartContractDecimals(minOutcomeSharesToBuy, decimals);
708
+
709
+ if (wrapped) {
710
+ return await this.__sendTx(
711
+ this.getContract().methods.buyWithETH(marketId, outcomeId, minOutcomeSharesToBuy),
712
+ false,
713
+ valueToWei,
714
+ );
715
+ }
716
+
717
+ return await this.__sendTx(
718
+ this.getContract().methods.buy(marketId, outcomeId, minOutcomeSharesToBuy, valueToWei),
719
+ );
720
+ };
721
+
722
+ /**
723
+ * @function sell
724
+ * @description Sell Shares of a Market Outcome
725
+ * @param {Integer} marketId
726
+ * @param {Integer} outcomeId
727
+ * @param {Integer} shares
728
+ */
729
+ async sell({marketId, outcomeId, value, maxOutcomeSharesToSell, wrapped = false}) {
730
+ const decimals = await this.getMarketDecimals({marketId});
731
+ const valueToWei = Numbers.toSmartContractDecimals(value, decimals);
732
+ maxOutcomeSharesToSell = Numbers.toSmartContractDecimals(maxOutcomeSharesToSell, decimals);
733
+
734
+ if (wrapped) {
735
+ return await this.__sendTx(
736
+ this.getContract().methods.sellToETH(marketId, outcomeId, valueToWei, maxOutcomeSharesToSell),
737
+ false,
738
+ );
739
+ }
740
+
741
+ return await this.__sendTx(
742
+ this.getContract().methods.sell(marketId, outcomeId, valueToWei, maxOutcomeSharesToSell),
743
+ );
744
+ };
745
+
746
+ async resolveMarketOutcome({marketId}) {
747
+ return await this.__sendTx(
748
+ this.getContract().methods.resolveMarketOutcome(marketId),
749
+ false,
750
+ );
751
+ };
752
+
753
+ async claimWinnings({marketId, wrapped = false}) {
754
+ if (wrapped) {
755
+ return await this.__sendTx(
756
+ this.getContract().methods.claimWinningsToETH(marketId),
757
+ false,
758
+ );
759
+ }
760
+
761
+ return await this.__sendTx(
762
+ this.getContract().methods.claimWinnings(marketId),
763
+ false,
764
+ );
765
+ };
766
+
767
+ async claimVoidedOutcomeShares({marketId, outcomeId, wrapped = false}) {
768
+ if (wrapped) {
769
+ return await this.__sendTx(
770
+ this.getContract().methods.claimVoidedOutcomeSharesToETH(marketId, outcomeId),
771
+ false,
772
+ );
773
+ }
774
+
775
+ return await this.__sendTx(
776
+ this.getContract().methods.claimVoidedOutcomeShares(marketId, outcomeId),
777
+ false,
778
+ );
779
+ };
780
+
781
+ async claimLiquidity({marketId}) {
782
+ return await this.__sendTx(
783
+ this.getContract().methods.claimLiquidity(marketId),
784
+ false,
785
+ );
786
+ };
787
+
788
+ async calcBuyAmount({ marketId, outcomeId, value }) {
789
+ const decimals = await this.getMarketDecimals({marketId});
790
+ const valueToWei = Numbers.toSmartContractDecimals(value, decimals);
791
+
792
+ const amount = await this.getContract()
793
+ .methods.calcBuyAmount(
794
+ valueToWei,
795
+ marketId,
796
+ outcomeId
797
+ )
798
+ .call();
799
+
800
+ return Numbers.fromDecimalsNumber(amount, decimals);
801
+ }
802
+
803
+ async calcSellAmount({ marketId, outcomeId, value }) {
804
+ const decimals = await this.getMarketDecimals({marketId});
805
+ const valueToWei = Numbers.toSmartContractDecimals(value, decimals);
806
+
807
+ const amount = await this.getContract()
808
+ .methods.calcSellAmount(
809
+ valueToWei,
810
+ marketId,
811
+ outcomeId
812
+ )
813
+ .call();
814
+
815
+ return Numbers.fromDecimalsNumber(amount, decimals);
816
+ }
817
+
818
+ calcDistribution({ odds }) {
819
+ const distribution = [];
820
+ const prod = odds.reduce((a, b) => a * b, 1);
821
+
822
+ for (let i = 0; i < odds.length; i++) {
823
+ distribution.push((Math.round(prod / odds[i] * 1000000)).toString());
824
+ }
825
+
826
+ return distribution;
827
+ }
828
+ }
829
+
830
+ module.exports = PredictionMarketV2Contract;