polkamarkets-js 1.0.1 → 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 -422
@@ -0,0 +1,1079 @@
1
+
2
+
3
+ import { expect } from 'chai';
4
+ import moment from 'moment';
5
+
6
+ import { mochaAsync } from './utils';
7
+ import { Application } from '..';
8
+
9
+ context('Prediction Market Contract V3', async () => {
10
+ require('dotenv').config();
11
+
12
+ let app;
13
+ let accountAddress;
14
+ let predictionMarketContract;
15
+ let predictionMarketManagerContract;
16
+ let realitioERC20Contract
17
+ let requiredBalanceERC20Contract;
18
+ let tokenERC20Contract;
19
+ let WETH9Contract;
20
+
21
+ // market / outcome ids we'll make unit tests with
22
+ let outcomeIds = [0, 1];
23
+ const value = 0.01;
24
+
25
+ context('Contract Deployment', async () => {
26
+ it('should start the Application', mochaAsync(async () => {
27
+ app = new Application({
28
+ web3Provider: process.env.WEB3_PROVIDER,
29
+ web3PrivateKey: process.env.WEB3_PRIVATE_KEY
30
+ });
31
+ expect(app).to.not.equal(null);
32
+ }));
33
+
34
+ it('should deploy Prediction Market Contract', mochaAsync(async () => {
35
+ accountAddress = await app.getAddress();
36
+
37
+ // Create Contract
38
+ predictionMarketContract = app.getPredictionMarketV3Contract({});
39
+ predictionMarketManagerContract = app.getPredictionMarketV3ManagerContract({});
40
+ realitioERC20Contract = app.getRealitioERC20Contract({});
41
+ requiredBalanceERC20Contract = app.getERC20Contract({});
42
+ WETH9Contract = app.getWETH9Contract({});
43
+
44
+ // Deploy
45
+ await requiredBalanceERC20Contract.deploy({params: ['Polkamarkets', 'POLK']});
46
+ const requiredBalanceERC20ContractAddress = requiredBalanceERC20Contract.getAddress();
47
+
48
+ await realitioERC20Contract.deploy({});
49
+ let realitioContractAddress = realitioERC20Contract.getAddress();
50
+
51
+ await WETH9Contract.deploy({});
52
+ const WETH9ContractAddress = WETH9Contract.getAddress();
53
+
54
+ await predictionMarketContract.deploy({
55
+ params: [
56
+ WETH9ContractAddress
57
+ ]
58
+ });
59
+ const predictionMarketContractAddress = predictionMarketContract.getAddress();
60
+
61
+ await predictionMarketManagerContract.deploy({
62
+ params: [
63
+ predictionMarketContractAddress,
64
+ requiredBalanceERC20ContractAddress,
65
+ '1000000000000000000',
66
+ realitioContractAddress
67
+ ]
68
+ });
69
+ const predictionMarketManagerContractAddress = predictionMarketManagerContract.getAddress();
70
+ // minting and approving requiredBalanceERC20Contract to spend tokens
71
+ await requiredBalanceERC20Contract.mint({
72
+ address: accountAddress,
73
+ amount: '1000'
74
+ });
75
+ await requiredBalanceERC20Contract.approve({
76
+ address: predictionMarketManagerContractAddress,
77
+ amount: '1000000'
78
+ });
79
+
80
+ // creating land
81
+ await predictionMarketManagerContract.createLand({
82
+ name: 'Token',
83
+ symbol: 'TOKEN',
84
+ tokenAmountToClaim: '1000',
85
+ tokenToAnswer: requiredBalanceERC20ContractAddress,
86
+ });
87
+ const land = await predictionMarketManagerContract.getLandById({ id: 0 });
88
+ // switching realitioERC20 contract addresses
89
+ realitioERC20Contract = app.getRealitioERC20Contract({ contractAddress: land.realitio });
90
+ realitioContractAddress = realitioERC20Contract.getAddress();
91
+
92
+ tokenERC20Contract = app.getFantasyERC20Contract({ contractAddress: land.token });
93
+ const tokenERC20ContractAddress = tokenERC20Contract.getAddress();
94
+ // minting and approving tokenERC20Contract to spend tokens
95
+ await tokenERC20Contract.claimAndApproveTokens();
96
+
97
+ expect(predictionMarketContractAddress).to.not.equal(null);
98
+ expect(predictionMarketManagerContractAddress).to.not.equal(null);
99
+ expect(realitioContractAddress).to.not.equal(null);
100
+ expect(requiredBalanceERC20ContractAddress).to.not.equal(null);
101
+ expect(tokenERC20ContractAddress).to.not.equal(null);
102
+ }));
103
+ });
104
+
105
+ context('Market Creation', async () => {
106
+ let marketId;
107
+
108
+ it('should create a Market', mochaAsync(async () => {
109
+ try {
110
+ const res = await predictionMarketContract.mintAndCreateMarket({
111
+ value,
112
+ name: 'Will BTC price close above 100k$ on May 1st 2024',
113
+ description: 'This is a description',
114
+ image: 'foo-bar',
115
+ category: 'Foo;Bar',
116
+ oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
117
+ duration: moment(moment().add(1, 'month').format('YYYY-MM-DD')).unix(),
118
+ outcomes: ['Yes', 'No'],
119
+ token: tokenERC20Contract.getAddress(),
120
+ realitioAddress: realitioERC20Contract.getAddress(),
121
+ realitioTimeout: 300,
122
+ PM3ManagerAddress: predictionMarketManagerContract.getAddress()
123
+ });
124
+ expect(res.status).to.equal(true);
125
+ } catch(e) {
126
+ console.log(e);
127
+ }
128
+
129
+ const marketIds = await predictionMarketContract.getMarkets();
130
+ marketId = marketIds[marketIds.length - 1];
131
+ expect(marketIds.length).to.equal(1);
132
+ expect(marketIds[marketIds.length - 1]).to.equal(marketId);
133
+ }));
134
+
135
+ it('should create another Market', mochaAsync(async () => {
136
+ const res = await predictionMarketContract.mintAndCreateMarket({
137
+ name: 'Will ETH price close above 10k$ on May 1st 2024',
138
+ image: 'foo-bar',
139
+ category: 'Foo;Bar',
140
+ oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
141
+ duration: moment(moment().add(1, 'month').format('YYYY-MM-DD')).unix(),
142
+ outcomes: ['Yes', 'No'],
143
+ value: 0.001,
144
+ token: tokenERC20Contract.getAddress(),
145
+ realitioAddress: realitioERC20Contract.getAddress(),
146
+ realitioTimeout: 300,
147
+ PM3ManagerAddress: predictionMarketManagerContract.getAddress()
148
+ });
149
+ expect(res.status).to.equal(true);
150
+
151
+ const marketIds = await predictionMarketContract.getMarkets();
152
+ expect(marketIds.length).to.equal(2);
153
+ }));
154
+ });
155
+
156
+ context('Market Data', async () => {
157
+ let marketId = 0;
158
+ it('should get Market data', mochaAsync(async () => {
159
+ const res = await predictionMarketContract.getMarketData({marketId});
160
+ expect(res).to.eql({
161
+ closeDateTime: moment().add(1, 'month').format('YYYY-MM-DD 00:00'),
162
+ state: 0,
163
+ oracleAddress: '0x0000000000000000000000000000000000000000',
164
+ liquidity: 0.01,
165
+ outcomeIds: [0, 1],
166
+ resolvedOutcomeId: -1,
167
+ voided: false
168
+ });
169
+ }));
170
+
171
+ it('should get Market details', mochaAsync(async () => {
172
+ const res = await predictionMarketContract.getMarketDetails({marketId});
173
+ expect(res).to.eql({
174
+ name: 'Will BTC price close above 100k$ on May 1st 2024',
175
+ description: 'This is a description',
176
+ category: 'Foo',
177
+ subcategory: 'Bar',
178
+ outcomes: ['Yes', 'No'],
179
+ image: 'foo-bar'
180
+ });
181
+ }));
182
+
183
+ it('should get Market Outcomes data', mochaAsync(async () => {
184
+ const outcome1Data = await predictionMarketContract.getOutcomeData({marketId, outcomeId: outcomeIds[0]});
185
+ expect(outcome1Data).to.include({
186
+ price: 0.5,
187
+ shares: 0.01
188
+ });
189
+
190
+ const outcome2Data = await predictionMarketContract.getOutcomeData({marketId, outcomeId: outcomeIds[1]});
191
+ expect(outcome2Data).to.include({
192
+ price: 0.5,
193
+ shares: 0.01
194
+ });
195
+
196
+ // outcomes share prices should sum to 1
197
+ expect(outcome1Data.price + outcome2Data.price).to.equal(1);
198
+ // outcomes number of shares should dum to value * 2
199
+ expect(outcome1Data.shares + outcome2Data.shares).to.equal(value * 2);
200
+ }));
201
+ });
202
+
203
+ // context('Market Interaction - WETH Market', async () => {
204
+ // let marketId;
205
+ // const wrapped = true;
206
+
207
+ // before(mochaAsync(async () => {
208
+ // try {
209
+ // const res = await predictionMarketContract.createMarketWithETH({
210
+ // value,
211
+ // name: 'WETH Market',
212
+ // image: 'foo-bar',
213
+ // category: 'Foo;Bar',
214
+ // oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
215
+ // duration: moment('2024-05-01').unix(),
216
+ // outcomes: ['A', 'B']
217
+ // });
218
+ // expect(res.status).to.equal(true);
219
+ // } catch(e) {
220
+ // console.log(e);
221
+ // }
222
+
223
+ // const marketIds = await predictionMarketContract.getMarkets();
224
+ // marketId = marketIds[marketIds.length - 1];
225
+ // }));
226
+
227
+ // it('should match WETH supply', mochaAsync(async () => {
228
+ // const ETHBalance = Number(await WETH9Contract.getBalance());
229
+ // expect(Number(ETHBalance)).to.equal(value);
230
+
231
+ // const WETHSupply = await WETH9Contract.totalSupply();
232
+ // expect(Number(WETHSupply)).to.equal(value);
233
+ // }));
234
+
235
+ // it('should match market WETH balance', mochaAsync(async () => {
236
+ // const contractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
237
+ // expect(contractBalance).to.equal(value);
238
+ // }));
239
+
240
+ // it('should add liquidity', mochaAsync(async () => {
241
+ // const contractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
242
+ // const WETHBalance = Number(await WETH9Contract.getBalance());
243
+
244
+ // try {
245
+ // const res = await predictionMarketContract.addLiquidity({marketId, value, wrapped})
246
+ // expect(res.status).to.equal(true);
247
+ // } catch(e) {
248
+ // console.log(e);
249
+ // }
250
+
251
+ // const newContractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
252
+ // const NewWETHBalance = Number(await WETH9Contract.getBalance());
253
+ // const amountTransferred = Number((newContractBalance - contractBalance).toFixed(5));
254
+
255
+ // expect(amountTransferred).to.equal(value);
256
+ // expect(NewWETHBalance).to.equal(WETHBalance + value);
257
+ // }));
258
+
259
+ // it('should remove liquidity', mochaAsync(async () => {
260
+ // const contractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
261
+ // const WETHBalance = Number(await WETH9Contract.getBalance());
262
+
263
+ // try {
264
+ // const res = await predictionMarketContract.removeLiquidity({marketId, shares: value, wrapped})
265
+ // expect(res.status).to.equal(true);
266
+ // } catch(e) {
267
+ // console.log(e);
268
+ // }
269
+
270
+ // const newContractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
271
+ // const NewWETHBalance = Number(await WETH9Contract.getBalance());
272
+ // const amountTransferred = Number((contractBalance - newContractBalance).toFixed(5));
273
+
274
+ // expect(amountTransferred).to.equal(value);
275
+ // expect(NewWETHBalance).to.equal(WETHBalance - value);
276
+ // }));
277
+
278
+ // it('should buy outcome shares', mochaAsync(async () => {
279
+ // const outcomeId = 0;
280
+ // const minOutcomeSharesToBuy = 0.015;
281
+
282
+ // const contractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
283
+ // const WETHBalance = Number(await WETH9Contract.getBalance());
284
+
285
+ // try {
286
+ // const res = await predictionMarketContract.buy({marketId, outcomeId, value, minOutcomeSharesToBuy, wrapped});
287
+ // expect(res.status).to.equal(true);
288
+ // } catch(e) {
289
+ // console.log(e);
290
+ // }
291
+
292
+ // const newContractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
293
+ // const NewWETHBalance = Number(await WETH9Contract.getBalance());
294
+ // const amountTransferred = Number((newContractBalance - contractBalance).toFixed(5));
295
+
296
+ // expect(amountTransferred).to.equal(value);
297
+ // expect(NewWETHBalance).to.equal(WETHBalance + value);
298
+ // }));
299
+
300
+ // it('should sell outcome shares', mochaAsync(async () => {
301
+ // const outcomeId = 0;
302
+ // const maxOutcomeSharesToSell = 0.015;
303
+
304
+ // const contractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
305
+ // const WETHBalance = Number(await WETH9Contract.getBalance());
306
+
307
+ // try {
308
+ // const res = await predictionMarketContract.sell({marketId, outcomeId, value, maxOutcomeSharesToSell, wrapped});
309
+ // expect(res.status).to.equal(true);
310
+ // } catch(e) {
311
+ // console.log(e);
312
+ // }
313
+
314
+ // const newContractBalance = await WETH9Contract.balanceOf({address: predictionMarketContract.getAddress()});
315
+ // const NewWETHBalance = Number(await WETH9Contract.getBalance());
316
+ // const amountTransferred = Number((contractBalance - newContractBalance).toFixed(5));
317
+
318
+ // expect(amountTransferred).to.equal(value);
319
+ // expect(NewWETHBalance).to.equal(WETHBalance - value);
320
+ // }));
321
+ // });
322
+
323
+ context('Market Interaction - Balanced Market (Same Outcome Odds)', async () => {
324
+ let marketId = 0;
325
+ it('should add liquidity without changing shares balance', mochaAsync(async () => {
326
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
327
+ const marketData = await predictionMarketContract.getMarketData({marketId});
328
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
329
+
330
+ // balanced market - same price in all outcomoes
331
+ expect(outcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[1]);
332
+
333
+ try {
334
+ const res = await predictionMarketContract.addLiquidity({marketId, value})
335
+ expect(res.status).to.equal(true);
336
+ } catch(e) {
337
+ console.log(e);
338
+ }
339
+
340
+ const myNewShares = await predictionMarketContract.getMyMarketShares({marketId});
341
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
342
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
343
+
344
+ expect(newMarketData.liquidity).to.above(marketData.liquidity);
345
+ expect(newMarketData.liquidity).to.equal(marketData.liquidity + value);
346
+
347
+ // Outcome prices shoud remain the same after providing liquidity
348
+ expect(newOutcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[0]);
349
+ expect(newOutcomePrices.outcomes[1]).to.equal(outcomePrices.outcomes[1]);
350
+
351
+ // Price balances are 0.5-0.5, liquidity will be added solely through liquidity shares
352
+ expect(myNewShares.liquidityShares).to.above(myShares.liquidityShares);
353
+ expect(myNewShares.liquidityShares).to.equal(myShares.liquidityShares + value);
354
+ // shares balance remains the same
355
+ expect(myNewShares.outcomeShares[0]).to.equal(myShares.outcomeShares[0]);
356
+ expect(myNewShares.outcomeShares[1]).to.equal(myShares.outcomeShares[1]);
357
+ }));
358
+
359
+ it('should remove liquidity without changing shares balance', mochaAsync(async () => {
360
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
361
+ const marketData = await predictionMarketContract.getMarketData({marketId});
362
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
363
+ const contractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
364
+
365
+ // balanced market - same price in all outcomoes
366
+ expect(outcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[1]);
367
+
368
+ try {
369
+ const res = await predictionMarketContract.removeLiquidity({marketId, shares: value})
370
+ expect(res.status).to.equal(true);
371
+ } catch(e) {
372
+ console.log(e);
373
+ }
374
+
375
+ const myNewShares = await predictionMarketContract.getMyMarketShares({marketId});
376
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
377
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
378
+ const newContractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
379
+
380
+ expect(newMarketData.liquidity).to.below(marketData.liquidity);
381
+ expect(newMarketData.liquidity).to.equal(marketData.liquidity - value);
382
+
383
+ // Outcome prices shoud remain the same after providing liquidity
384
+ expect(newOutcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[0]);
385
+ expect(newOutcomePrices.outcomes[1]).to.equal(outcomePrices.outcomes[1]);
386
+
387
+ // Price balances are 0.5-0.5, liquidity will be added solely through liquidity shares
388
+ expect(myNewShares.liquidityShares).to.below(myShares.liquidityShares);
389
+ expect(myNewShares.liquidityShares).to.equal(myShares.liquidityShares - value);
390
+ // shares balance remains the same
391
+ expect(myNewShares.outcomeShares[0]).to.equal(myShares.outcomeShares[0]);
392
+ expect(myNewShares.outcomeShares[1]).to.equal(myShares.outcomeShares[1]);
393
+
394
+ // User gets liquidity tokens back in ETH
395
+ expect(newContractBalance).to.below(contractBalance);
396
+ const amountTransferred = Number((contractBalance - newContractBalance).toFixed(5));
397
+ expect(amountTransferred).to.equal(value);
398
+ }));
399
+ });
400
+
401
+ context('Market Interaction - Unbalanced Market (Different Outcome Odds)', async () => {
402
+ let marketId = 0;
403
+ it('should display my shares', mochaAsync(async () => {
404
+ const res = await predictionMarketContract.getMyMarketShares({marketId});
405
+ // currently holding liquidity tokens from market creation
406
+ expect(res).to.eql({
407
+ liquidityShares: 0.01,
408
+ outcomeShares: {
409
+ 0: 0.00,
410
+ 1: 0.00,
411
+ }
412
+ });
413
+ }));
414
+
415
+ it('should buy outcome shares', mochaAsync(async () => {
416
+ const outcomeId = 0;
417
+ const minOutcomeSharesToBuy = 0.015;
418
+
419
+ const marketData = await predictionMarketContract.getMarketData({marketId});
420
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
421
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
422
+ const contractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
423
+
424
+ try {
425
+ const res = await predictionMarketContract.buy({marketId, outcomeId, value, minOutcomeSharesToBuy});
426
+ expect(res.status).to.equal(true);
427
+ } catch(e) {
428
+ console.log(e);
429
+ }
430
+
431
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
432
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
433
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
434
+ const newContractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
435
+
436
+ // outcome price should increase
437
+ expect(newOutcomePrices.outcomes[0]).to.above(outcomePrices.outcomes[0]);
438
+ expect(newOutcomePrices.outcomes[0]).to.equal(0.8);
439
+ // opposite outcome price should decrease
440
+ expect(newOutcomePrices.outcomes[1]).to.below(outcomePrices.outcomes[1]);
441
+ expect(newOutcomePrices.outcomes[1]).to.equal(0.2);
442
+ // Prices sum = 1
443
+ // 0.5 + 0.5 = 1
444
+ expect(newOutcomePrices.outcomes[0] + newOutcomePrices.outcomes[1]).to.equal(1);
445
+
446
+ // Liquidity value remains the same
447
+ expect(newMarketData.liquidity).to.equal(marketData.liquidity);
448
+
449
+ // outcome shares should decrease
450
+ expect(newOutcomeShares.outcomes[0]).to.below(outcomeShares.outcomes[0]);
451
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.005);
452
+ // opposite outcome shares should increase
453
+ expect(newOutcomeShares.outcomes[1]).to.above(outcomeShares.outcomes[1]);
454
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.02);
455
+ // # Shares Product = Liquidity^2
456
+ // 0.005 * 0.02 = 0.01^2
457
+ expect(outcomeShares.outcomes[0] * outcomeShares.outcomes[1]).to.equal(newMarketData.liquidity**2);
458
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1]).to.equal(newMarketData.liquidity**2);
459
+
460
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
461
+ expect(myShares).to.eql({
462
+ liquidityShares: 0.01,
463
+ outcomeShares: {
464
+ 0: 0.015,
465
+ 1: 0.00,
466
+ }
467
+ });
468
+
469
+ // Contract adds value to balance
470
+ expect(newContractBalance).to.above(contractBalance);
471
+ // TODO: check amountReceived from internal transactions
472
+ const amountReceived = Number((newContractBalance - contractBalance).toFixed(5));
473
+ expect(amountReceived).to.equal(value);
474
+ }));
475
+
476
+ it('should add liquidity', mochaAsync(async () => {
477
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
478
+ const marketData = await predictionMarketContract.getMarketData({marketId});
479
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
480
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
481
+
482
+ try {
483
+ const res = await predictionMarketContract.addLiquidity({marketId, value})
484
+ expect(res.status).to.equal(true);
485
+ } catch(e) {
486
+ console.log(e);
487
+ }
488
+
489
+ const myNewShares = await predictionMarketContract.getMyMarketShares({marketId});
490
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
491
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
492
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
493
+
494
+ // Outcome prices shoud remain the same after providing liquidity
495
+ expect(newOutcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[0]);
496
+ expect(newOutcomePrices.outcomes[1]).to.equal(outcomePrices.outcomes[1]);
497
+
498
+ // # Shares Product = Liquidity^2
499
+ // 0.0075 * 0.03 = 0.015^2
500
+ expect(newMarketData.liquidity).to.above(marketData.liquidity);
501
+ expect(newMarketData.liquidity).to.equal(0.015);
502
+ expect(newOutcomeShares.outcomes[0]).to.above(outcomeShares.outcomes[0]);
503
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.0075);
504
+ expect(newOutcomeShares.outcomes[1]).to.above(outcomeShares.outcomes[1]);
505
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.03);
506
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1]).to.equal(newMarketData.liquidity**2);
507
+
508
+ // Price balances are not 0.5-0.5, liquidity will be added through shares + liquidity
509
+ expect(myNewShares.liquidityShares).to.above(myShares.liquidityShares);
510
+ expect(myNewShares.liquidityShares).to.equal(0.015);
511
+ // shares balance of higher odd outcome increases
512
+ expect(myNewShares.outcomeShares[0]).to.above(myShares.outcomeShares[0]);
513
+ expect(myNewShares.outcomeShares[0]).to.equal(0.0225);
514
+ // shares balance of lower odd outcome remains
515
+ expect(myNewShares.outcomeShares[1]).to.equal(myShares.outcomeShares[1]);
516
+ expect(myNewShares.outcomeShares[1]).to.equal(0);
517
+ }));
518
+
519
+ it('should remove liquidity', mochaAsync(async () => {
520
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
521
+ const marketData = await predictionMarketContract.getMarketData({marketId});
522
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
523
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
524
+ const contractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
525
+ const liquiditySharesToRemove = 0.005;
526
+
527
+ try {
528
+ const res = await predictionMarketContract.removeLiquidity({marketId, shares: liquiditySharesToRemove});
529
+ expect(res.status).to.equal(true);
530
+ } catch(e) {
531
+ console.log(e);
532
+ }
533
+
534
+ const myNewShares = await predictionMarketContract.getMyMarketShares({marketId});
535
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
536
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
537
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
538
+ const newContractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
539
+
540
+ // Outcome prices shoud remain the same after removing liquidity
541
+ expect(newOutcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[0]);
542
+ expect(newOutcomePrices.outcomes[1]).to.equal(outcomePrices.outcomes[1]);
543
+
544
+ // # Shares Product = Liquidity^2
545
+ // 0.005 * 0.02 = 0.01^2
546
+ expect(newMarketData.liquidity).to.below(marketData.liquidity);
547
+ expect(newMarketData.liquidity).to.equal(0.01);
548
+ expect(newOutcomeShares.outcomes[0]).to.below(outcomeShares.outcomes[0]);
549
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.005);
550
+ expect(newOutcomeShares.outcomes[1]).to.below(outcomeShares.outcomes[1]);
551
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.02);
552
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1]).to.equal(newMarketData.liquidity**2);
553
+
554
+ // Price balances are not 0.5-0.5, liquidity will be added through shares + liquidity
555
+ expect(myNewShares.liquidityShares).to.below(myShares.liquidityShares);
556
+ expect(myNewShares.liquidityShares).to.equal(0.01);
557
+ // shares balance of higher odd outcome remains
558
+ expect(myNewShares.outcomeShares[0]).to.equal(myShares.outcomeShares[0]);
559
+ expect(myNewShares.outcomeShares[0]).to.equal(0.0225);
560
+ // shares balance of lower odd outcome increases
561
+ expect(myNewShares.outcomeShares[1]).to.above(myShares.outcomeShares[1]);
562
+ expect(myNewShares.outcomeShares[1]).to.equal(0.0075);
563
+
564
+ // User gets part of the liquidity tokens back in ETH
565
+ expect(newContractBalance).to.below(contractBalance);
566
+ const amountTransferred = Number((contractBalance - newContractBalance).toFixed(5));
567
+ expect(amountTransferred).to.equal(0.0025);
568
+ }));
569
+
570
+ it('should sell outcome shares', mochaAsync(async () => {
571
+ const outcomeId = 0;
572
+ const maxOutcomeSharesToSell = 0.015;
573
+
574
+ const marketData = await predictionMarketContract.getMarketData({marketId});
575
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
576
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
577
+ const contractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
578
+
579
+ try {
580
+ const res = await predictionMarketContract.sell({marketId, outcomeId, value, maxOutcomeSharesToSell});
581
+ expect(res.status).to.equal(true);
582
+ } catch(e) {
583
+ console.log(e);
584
+ }
585
+
586
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
587
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
588
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
589
+ const newContractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
590
+
591
+ // outcome price should decrease
592
+ expect(newOutcomePrices.outcomes[0]).to.below(outcomePrices.outcomes[0]);
593
+ expect(newOutcomePrices.outcomes[0]).to.equal(0.5);
594
+ // opposite outcome price should increase
595
+ expect(newOutcomePrices.outcomes[1]).to.above(outcomePrices.outcomes[1]);
596
+ expect(newOutcomePrices.outcomes[1]).to.equal(0.5);
597
+ // Prices sum = 1
598
+ // 0.5 + 0.5 = 1
599
+ expect(newOutcomePrices.outcomes[0] + newOutcomePrices.outcomes[1]).to.equal(1);
600
+
601
+ // Liquidity value remains the same
602
+ expect(newMarketData.liquidity).to.equal(marketData.liquidity);
603
+
604
+ // outcome shares should increase
605
+ expect(newOutcomeShares.outcomes[0]).to.above(outcomeShares.outcomes[0]);
606
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.01);
607
+ // opposite outcome shares should increase
608
+ expect(newOutcomeShares.outcomes[1]).to.below(outcomeShares.outcomes[1]);
609
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.01);
610
+ // # Shares Product = Liquidity^2
611
+ // 0.01 * 0.01 = 0.01^2
612
+ expect(outcomeShares.outcomes[0] * outcomeShares.outcomes[1]).to.equal(newMarketData.liquidity**2);
613
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1]).to.equal(newMarketData.liquidity**2);
614
+
615
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
616
+ expect(myShares).to.eql({
617
+ liquidityShares: 0.01,
618
+ outcomeShares: {
619
+ 0: 0.0075,
620
+ 1: 0.0075,
621
+ }
622
+ });
623
+
624
+ // User gets shares value back in ETH
625
+ expect(newContractBalance).to.below(contractBalance);
626
+ const amountTransferred = Number((contractBalance - newContractBalance).toFixed(5));
627
+ expect(amountTransferred).to.equal(0.01);
628
+ }));
629
+ });
630
+
631
+ context('Multiple Outcomes', async () => {
632
+ let marketId = 0;
633
+ context('Market Creation', async () => {
634
+ it('should create a Market with 3 outcomes', mochaAsync(async () => {
635
+ try {
636
+ const res = await predictionMarketContract.mintAndCreateMarket({
637
+ value,
638
+ name: 'Market with 3 outcomes',
639
+ image: 'foo-bar',
640
+ category: 'Foo;Bar',
641
+ oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
642
+ duration: moment(moment().add(1, 'month').format('YYYY-MM-DD')).unix(),
643
+ outcomes: ['A', 'B', 'C'],
644
+ token: tokenERC20Contract.getAddress(),
645
+ realitioAddress: realitioERC20Contract.getAddress(),
646
+ realitioTimeout: 300,
647
+ PM3ManagerAddress: predictionMarketManagerContract.getAddress()
648
+ });
649
+ expect(res.status).to.equal(true);
650
+ } catch(e) {
651
+ console.log(e);
652
+ }
653
+
654
+ const marketIds = await predictionMarketContract.getMarkets();
655
+ marketId = marketIds[marketIds.length - 1];
656
+
657
+ const res = await predictionMarketContract.getMarketData({marketId});
658
+ expect(res.outcomeIds.length).to.eql(3);
659
+ expect(res.outcomeIds).to.eql([0, 1, 2]);
660
+ }));
661
+
662
+ it('should create a Market with 32 outcomes', mochaAsync(async () => {
663
+ // Array with outcomes with outcome id as name
664
+ // TODO: improve gas optimization for markets with 10+ outcomes
665
+ const outcomeCount = 32;
666
+ const outcomes = Array.from(Array(outcomeCount).keys());
667
+
668
+ try {
669
+ const res = await predictionMarketContract.mintAndCreateMarket({
670
+ value,
671
+ name: `Market with ${outcomeCount} outcomes`,
672
+ image: 'foo-bar',
673
+ category: 'Foo;Bar',
674
+ oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
675
+ duration: moment(moment().add(1, 'month').format('YYYY-MM-DD')).unix(),
676
+ outcomes,
677
+ token: tokenERC20Contract.getAddress(),
678
+ realitioAddress: realitioERC20Contract.getAddress(),
679
+ realitioTimeout: 300,
680
+ PM3ManagerAddress: predictionMarketManagerContract.getAddress()
681
+ });
682
+ expect(res.status).to.equal(true);
683
+ } catch(e) {
684
+ console.log(e);
685
+ }
686
+
687
+ const marketIds = await predictionMarketContract.getMarkets();
688
+ marketId = marketIds[marketIds.length - 1];
689
+
690
+ const res = await predictionMarketContract.getMarketData({marketId});
691
+ expect(res.outcomeIds.length).to.eql(outcomeCount);
692
+ expect(res.outcomeIds).to.eql(outcomes);
693
+ }));
694
+
695
+ it('should not create a Market with more than 32 outcomes', mochaAsync(async () => {
696
+ const oldMarketIds = await predictionMarketContract.getMarkets();
697
+ try {
698
+ const outcomes = Array.from(Array(33).keys());
699
+
700
+ const res = await predictionMarketContract.mintAndCreateMarket({
701
+ value,
702
+ name: 'Market with 257 outcomes',
703
+ image: 'foo-bar',
704
+ category: 'Foo;Bar',
705
+ oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
706
+ duration: moment(moment().add(1, 'month').format('YYYY-MM-DD')).unix(),
707
+ outcomes,
708
+ token: tokenERC20Contract.getAddress(),
709
+ realitioAddress: realitioERC20Contract.getAddress(),
710
+ realitioTimeout: 300,
711
+ PM3ManagerAddress: predictionMarketManagerContract.getAddress()
712
+ });
713
+ expect(res.status).to.equal(true);
714
+ } catch(e) {
715
+ // not logging error, as tx is expected to fail
716
+ // console.log(e);
717
+ }
718
+
719
+ const currentMarketIds = await predictionMarketContract.getMarkets();
720
+
721
+ expect(currentMarketIds.length).to.eql(oldMarketIds.length);
722
+ }));
723
+ });
724
+
725
+ context('Market Interaction', async () => {
726
+ let marketId;
727
+ let outcomeIds = [0, 1, 2];
728
+
729
+ before(mochaAsync(async () => {
730
+ try {
731
+ const res = await predictionMarketContract.mintAndCreateMarket({
732
+ value,
733
+ name: 'Market with 3 outcomes',
734
+ image: 'foo-bar',
735
+ category: 'Foo;Bar',
736
+ oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
737
+ duration: moment(moment().add(1, 'month').format('YYYY-MM-DD')).unix(),
738
+ outcomes: ['D', 'E', 'F'],
739
+ token: tokenERC20Contract.getAddress(),
740
+ realitioAddress: realitioERC20Contract.getAddress(),
741
+ realitioTimeout: 300,
742
+ PM3ManagerAddress: predictionMarketManagerContract.getAddress()
743
+ });
744
+ expect(res.status).to.equal(true);
745
+ } catch(e) {
746
+ console.log(e);
747
+ }
748
+
749
+ const marketIds = await predictionMarketContract.getMarkets();
750
+ marketId = marketIds[marketIds.length - 1];
751
+ }));
752
+
753
+ it('should buy outcome shares', mochaAsync(async () => {
754
+ const outcomeId = 0;
755
+ const minOutcomeSharesToBuy = 0.015;
756
+
757
+ const marketData = await predictionMarketContract.getMarketData({marketId});
758
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
759
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
760
+ const contractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
761
+
762
+ try {
763
+ const res = await predictionMarketContract.buy({marketId, outcomeId, value, minOutcomeSharesToBuy});
764
+ expect(res.status).to.equal(true);
765
+ } catch(e) {
766
+ console.log(e);
767
+ }
768
+
769
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
770
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
771
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
772
+ const newContractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
773
+
774
+ // outcome price should increase
775
+ expect(newOutcomePrices.outcomes[0]).to.above(outcomePrices.outcomes[0]);
776
+ expect(newOutcomePrices.outcomes[0]).to.equal(0.8);
777
+ // opposite outcome price should decrease
778
+ expect(newOutcomePrices.outcomes[1]).to.below(outcomePrices.outcomes[1]);
779
+ expect(newOutcomePrices.outcomes[1]).to.equal(0.1);
780
+ expect(newOutcomePrices.outcomes[2]).to.below(outcomePrices.outcomes[2]);
781
+ expect(newOutcomePrices.outcomes[2]).to.equal(0.1);
782
+ // Prices sum = 1
783
+ // 0.1 + 0.1 + 0.8 = 1
784
+ expect(newOutcomePrices.outcomes[0] + newOutcomePrices.outcomes[1] + newOutcomePrices.outcomes[2]).to.equal(1);
785
+
786
+ // Liquidity value remains the same
787
+ expect(newMarketData.liquidity).to.equal(marketData.liquidity);
788
+
789
+ // outcome shares should decrease
790
+ expect(newOutcomeShares.outcomes[0]).to.below(outcomeShares.outcomes[0]);
791
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.0025);
792
+ // opposite outcome shares should increase
793
+ expect(newOutcomeShares.outcomes[1]).to.above(outcomeShares.outcomes[1]);
794
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.02);
795
+ expect(newOutcomeShares.outcomes[2]).to.above(outcomeShares.outcomes[2]);
796
+ expect(newOutcomeShares.outcomes[2]).to.equal(0.02);
797
+ // # Shares Product = Liquidity^2
798
+ // 0.0025 * 0.02 * 0.02 = 0.01^3
799
+ expect(outcomeShares.outcomes[0] * outcomeShares.outcomes[1] * outcomeShares.outcomes[2]).to.equal(newMarketData.liquidity ** 3);
800
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1] * newOutcomeShares.outcomes[2]).to.equal(newMarketData.liquidity ** 3);
801
+
802
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
803
+ expect(myShares).to.eql({
804
+ liquidityShares: 0.01,
805
+ outcomeShares: {
806
+ 0: 0.0175,
807
+ 1: 0.00,
808
+ 2: 0.00,
809
+ }
810
+ });
811
+
812
+ // Contract adds value to balance
813
+ expect(newContractBalance).to.above(contractBalance);
814
+ // TODO: check amountReceived from internal transactions
815
+ const amountReceived = Number((newContractBalance - contractBalance).toFixed(5));
816
+ expect(amountReceived).to.equal(value);
817
+ }));
818
+
819
+ it('should add liquidity', mochaAsync(async () => {
820
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
821
+ const marketData = await predictionMarketContract.getMarketData({marketId});
822
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
823
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
824
+
825
+ try {
826
+ const res = await predictionMarketContract.addLiquidity({marketId, value})
827
+ expect(res.status).to.equal(true);
828
+ } catch(e) {
829
+ console.log(e);
830
+ }
831
+
832
+ const myNewShares = await predictionMarketContract.getMyMarketShares({marketId});
833
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
834
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
835
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
836
+
837
+ // Outcome prices shoud remain the same after providing liquidity
838
+ expect(newOutcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[0]);
839
+ expect(newOutcomePrices.outcomes[1]).to.equal(outcomePrices.outcomes[1]);
840
+ expect(newOutcomePrices.outcomes[2]).to.equal(outcomePrices.outcomes[2]);
841
+
842
+ // # Shares Product = Liquidity^2
843
+ // 0.00375 * 0.03 * 0.03 = 0.015^3
844
+ expect(newMarketData.liquidity).to.above(marketData.liquidity);
845
+ expect(newMarketData.liquidity).to.equal(0.015);
846
+ expect(newOutcomeShares.outcomes[0]).to.above(outcomeShares.outcomes[0]);
847
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.00375);
848
+ expect(newOutcomeShares.outcomes[1]).to.above(outcomeShares.outcomes[1]);
849
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.03);
850
+ expect(newOutcomeShares.outcomes[2]).to.above(outcomeShares.outcomes[2]);
851
+ expect(newOutcomeShares.outcomes[2]).to.equal(0.03);
852
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1] * newOutcomeShares.outcomes[2]).to.be.closeTo(newMarketData.liquidity ** 3, 0.00000001);
853
+
854
+ // Price balances are not 0.5-0.5, liquidity will be added through shares + liquidity
855
+ expect(myNewShares.liquidityShares).to.above(myShares.liquidityShares);
856
+ expect(myNewShares.liquidityShares).to.equal(0.015);
857
+ // shares balance of higher odd outcome increases
858
+ expect(myNewShares.outcomeShares[0]).to.above(myShares.outcomeShares[0]);
859
+ expect(myNewShares.outcomeShares[0]).to.equal(0.02625);
860
+ // shares balance of lower odd outcome remains
861
+ expect(myNewShares.outcomeShares[1]).to.equal(myShares.outcomeShares[1]);
862
+ expect(myNewShares.outcomeShares[1]).to.equal(0);
863
+ expect(myNewShares.outcomeShares[2]).to.equal(myShares.outcomeShares[2]);
864
+ expect(myNewShares.outcomeShares[2]).to.equal(0);
865
+ }));
866
+
867
+ it('should remove liquidity', mochaAsync(async () => {
868
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
869
+ const marketData = await predictionMarketContract.getMarketData({marketId});
870
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
871
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
872
+ const contractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
873
+ const liquiditySharesToRemove = 0.005;
874
+
875
+ try {
876
+ const res = await predictionMarketContract.removeLiquidity({marketId, shares: liquiditySharesToRemove});
877
+ expect(res.status).to.equal(true);
878
+ } catch(e) {
879
+ console.log(e);
880
+ }
881
+
882
+ const myNewShares = await predictionMarketContract.getMyMarketShares({marketId});
883
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
884
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
885
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
886
+ const newContractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
887
+
888
+ // Outcome prices shoud remain the same after removing liquidity
889
+ expect(newOutcomePrices.outcomes[0]).to.equal(outcomePrices.outcomes[0]);
890
+ expect(newOutcomePrices.outcomes[1]).to.equal(outcomePrices.outcomes[1]);
891
+ expect(newOutcomePrices.outcomes[2]).to.equal(outcomePrices.outcomes[2]);
892
+
893
+ // # Shares Product = Liquidity^3
894
+ // 0.0025 * 0.02 * 0.02 = 0.01^3
895
+ expect(newMarketData.liquidity).to.below(marketData.liquidity);
896
+ expect(newMarketData.liquidity).to.equal(0.01);
897
+ expect(newOutcomeShares.outcomes[0]).to.below(outcomeShares.outcomes[0]);
898
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.0025);
899
+ expect(newOutcomeShares.outcomes[1]).to.below(outcomeShares.outcomes[1]);
900
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.02);
901
+ expect(newOutcomeShares.outcomes[2]).to.below(outcomeShares.outcomes[2]);
902
+ expect(newOutcomeShares.outcomes[2]).to.equal(0.02);
903
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1] * newOutcomeShares.outcomes[2]).to.equal(newMarketData.liquidity ** 3);
904
+
905
+ // Price balances are not 0.5-0.5, liquidity will be added through shares + liquidity
906
+ expect(myNewShares.liquidityShares).to.below(myShares.liquidityShares);
907
+ expect(myNewShares.liquidityShares).to.equal(0.01);
908
+ // shares balance of higher odd outcome remains
909
+ expect(myNewShares.outcomeShares[0]).to.equal(myShares.outcomeShares[0]);
910
+ expect(myNewShares.outcomeShares[0]).to.equal(0.02625);
911
+ // shares balance of lower odd outcome increases
912
+ expect(myNewShares.outcomeShares[1]).to.above(myShares.outcomeShares[1]);
913
+ expect(myNewShares.outcomeShares[1]).to.equal(0.00875);
914
+ expect(myNewShares.outcomeShares[2]).to.above(myShares.outcomeShares[2]);
915
+ expect(myNewShares.outcomeShares[2]).to.equal(0.00875);
916
+
917
+ // User gets part of the liquidity tokens back in ETH
918
+ expect(newContractBalance).to.below(contractBalance);
919
+ const amountTransferred = Number((contractBalance - newContractBalance).toFixed(5));
920
+ expect(amountTransferred).to.equal(0.00125);
921
+ }));
922
+
923
+ it('should sell outcome shares', mochaAsync(async () => {
924
+ const outcomeId = 0;
925
+ const maxOutcomeSharesToSell = 0.0175;
926
+
927
+ const marketData = await predictionMarketContract.getMarketData({marketId});
928
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
929
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
930
+ const contractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
931
+
932
+ try {
933
+ const res = await predictionMarketContract.sell({marketId, outcomeId, value, maxOutcomeSharesToSell});
934
+ expect(res.status).to.equal(true);
935
+ } catch(e) {
936
+ console.log(e);
937
+ }
938
+
939
+ const newMarketData = await predictionMarketContract.getMarketData({marketId});
940
+ const newOutcomePrices = await predictionMarketContract.getMarketPrices({marketId});
941
+ const newOutcomeShares = await predictionMarketContract.getMarketShares({marketId});
942
+ const newContractBalance = await tokenERC20Contract.balanceOf({address: predictionMarketContract.getAddress()});
943
+
944
+ // outcome price should decrease
945
+ expect(newOutcomePrices.outcomes[0]).to.below(outcomePrices.outcomes[0]);
946
+ expect(newOutcomePrices.outcomes[0]).to.equal(1/3);
947
+ // opposite outcome price should increase
948
+ expect(newOutcomePrices.outcomes[1]).to.above(outcomePrices.outcomes[1]);
949
+ expect(newOutcomePrices.outcomes[1]).to.equal(1/3);
950
+ expect(newOutcomePrices.outcomes[2]).to.above(outcomePrices.outcomes[2]);
951
+ expect(newOutcomePrices.outcomes[2]).to.equal(1/3);
952
+ // Prices sum = 1
953
+ // 0.333 + 0.333 + 0.333 = 1
954
+ expect(newOutcomePrices.outcomes[0] + newOutcomePrices.outcomes[1] + newOutcomePrices.outcomes[2]).to.equal(1);
955
+
956
+ // Liquidity value remains the same
957
+ expect(newMarketData.liquidity).to.equal(marketData.liquidity);
958
+
959
+ // outcome shares should increase
960
+ expect(newOutcomeShares.outcomes[0]).to.above(outcomeShares.outcomes[0]);
961
+ expect(newOutcomeShares.outcomes[0]).to.equal(0.01);
962
+ // opposite outcome shares should increase
963
+ expect(newOutcomeShares.outcomes[1]).to.below(outcomeShares.outcomes[1]);
964
+ expect(newOutcomeShares.outcomes[1]).to.equal(0.01);
965
+ expect(newOutcomeShares.outcomes[2]).to.below(outcomeShares.outcomes[2]);
966
+ expect(newOutcomeShares.outcomes[2]).to.equal(0.01);
967
+ // # Shares Product = Liquidity^2
968
+ // 0.01 * 0.01 = 0.01^2
969
+ expect(outcomeShares.outcomes[0] * outcomeShares.outcomes[1] * outcomeShares.outcomes[2]).to.equal(newMarketData.liquidity ** 3);
970
+ expect(newOutcomeShares.outcomes[0] * newOutcomeShares.outcomes[1] * newOutcomeShares.outcomes[2]).to.equal(newMarketData.liquidity ** 3);
971
+
972
+ const myShares = await predictionMarketContract.getMyMarketShares({marketId});
973
+ expect(myShares).to.eql({
974
+ liquidityShares: 0.01,
975
+ outcomeShares: {
976
+ 0: 0.00875,
977
+ 1: 0.00875,
978
+ 2: 0.00875,
979
+ }
980
+ });
981
+
982
+ // User gets shares value back in ETH
983
+ expect(newContractBalance).to.below(contractBalance);
984
+ const amountTransferred = Number((contractBalance - newContractBalance).toFixed(5));
985
+ expect(amountTransferred).to.equal(0.01);
986
+ }));
987
+ });
988
+ context('Distributed probabilites', async () => {
989
+ let marketId;
990
+ let outcomeIds = [0, 1, 2];
991
+
992
+ const initialOdds = [
993
+ [
994
+ [50, 50],
995
+ ['50000000', '50000000']
996
+ ],
997
+ [
998
+ [40, 60],
999
+ ['60000000', '40000000']
1000
+ ],
1001
+ [
1002
+ [15, 20, 65],
1003
+ ['1300000000', '975000000', '300000000']
1004
+ ],
1005
+ [
1006
+ [65, 15, 20],
1007
+ ['300000000', '1300000000', '975000000']
1008
+ ],
1009
+ [
1010
+ [10, 20, 30, 40],
1011
+ ['24000000000', '12000000000', '8000000000', '6000000000']
1012
+ ]
1013
+ ];
1014
+
1015
+ for (const [odds, expectedDistribution] of initialOdds) {
1016
+ context(`Odds: ${odds}`, async () => {
1017
+ before(mochaAsync(async () => {
1018
+ const distribution = await predictionMarketContract.calcDistribution({odds});
1019
+
1020
+ expect(distribution).to.eql(expectedDistribution);
1021
+
1022
+ try {
1023
+ const res = await predictionMarketContract.mintAndCreateMarket({
1024
+ value,
1025
+ name: `Market with ${odds} odds`,
1026
+ image: 'foo-bar',
1027
+ category: 'Foo;Bar',
1028
+ oracleAddress: '0x0000000000000000000000000000000000000001', // TODO
1029
+ duration: moment(moment().add(1, 'month').format('YYYY-MM-DD')).unix(),
1030
+ outcomes: ['A', 'B', 'C', 'D', 'E'].slice(0, odds.length),
1031
+ token: tokenERC20Contract.getAddress(),
1032
+ odds,
1033
+ realitioAddress: realitioERC20Contract.getAddress(),
1034
+ realitioTimeout: 300,
1035
+ PM3ManagerAddress: predictionMarketManagerContract.getAddress()
1036
+ });
1037
+ expect(res.status).to.equal(true);
1038
+ } catch(e) {
1039
+ console.log(e);
1040
+ }
1041
+
1042
+ const marketIds = await predictionMarketContract.getMarkets();
1043
+ marketId = marketIds[marketIds.length - 1];
1044
+ }));
1045
+
1046
+ it('market prices should match odds', mochaAsync(async () => {
1047
+ const outcomePrices = await predictionMarketContract.getMarketPrices({marketId});
1048
+
1049
+ for (let i = 0; i < odds.length; i++) {
1050
+ const price = outcomePrices.outcomes[i];
1051
+ const expectedPrice = odds[i] / 100;
1052
+
1053
+ expect(price).to.closeTo(expectedPrice, 0.00000001);
1054
+ }
1055
+ }));
1056
+
1057
+ it('market shares should match shares', mochaAsync(async () => {
1058
+ const outcomeShares = await predictionMarketContract.getMarketShares({marketId});
1059
+
1060
+ for (let i = 0; i < odds.length; i++) {
1061
+ const shares = outcomeShares.outcomes[i];
1062
+ const minOdds = Math.min(...odds);
1063
+ const expectedShares = value * minOdds / odds[i];
1064
+
1065
+ expect(shares).to.closeTo(expectedShares, 0.00000001);
1066
+ }
1067
+ }));
1068
+
1069
+ it('market liquidity should match liquidity', mochaAsync(async () => {
1070
+ const marketData = await predictionMarketContract.getMarketData({marketId});
1071
+ const expectedLiquidity = value;
1072
+
1073
+ expect(marketData.liquidity).to.equal(expectedLiquidity);
1074
+ }));
1075
+ });
1076
+ }
1077
+ });
1078
+ });
1079
+ });