/ test / Discover_spec.js
Discover_spec.js
  1  const TestUtils = require("../utils/testUtils");
  2  const BN = require('bn.js');
  3  const Discover = require('Embark/contracts/Discover');
  4  const SNT = require('Embark/contracts/SNT');
  5  
  6  config({
  7    blockchain: {
  8      accounts: [
  9        {
 10          mnemonic: 'foster gesture flock merge beach plate dish view friend leave drink valley shield list enemy',
 11          balance: '5 ether',
 12          numAddresses: '10',
 13        },
 14      ],
 15    },
 16    contracts: {
 17      deploy: {
 18        'MiniMeToken': { 'deploy': false },
 19        'MiniMeTokenFactory': {},
 20        'SNT': {
 21          'instanceOf': 'MiniMeToken',
 22          'args': [
 23            '$MiniMeTokenFactory',
 24            '0x0000000000000000000000000000000000000000',
 25            0,
 26            'TestMiniMeToken',
 27            18,
 28            'SNT',
 29            true,
 30          ],
 31        },
 32        'Discover': {
 33          args: ['$SNT'],
 34        },
 35        'TestBancorFormula': {},
 36      },
 37    },
 38  }, (_err, web3_accounts) => {
 39    accounts = web3_accounts
 40  });
 41  
 42  contract("Discover", function () {
 43  
 44    this.timeout(0);
 45  
 46    let decimalMultiplier = new BN('1000000000000000000', 10)
 47  
 48    it("should set max and safeMax values correctly", async function () {
 49      let resultMax = await Discover.methods.max().call();
 50      let resultSafeMax = await Discover.methods.safeMax().call();
 51      let expectedMax = 6804870174 * 292 / 1000000;
 52      let expectedSafeMax = expectedMax * 77 / 100 - 1;
 53      assert.strictEqual(parseInt(resultMax, 10), Math.round(expectedMax));
 54      assert.strictEqual(parseInt(resultSafeMax, 10), Math.round(expectedSafeMax));
 55    });
 56  
 57    it("should create a new DApp and initialise it correctly", async function () {
 58      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
 59      let tokens = 1000;
 60      let tokenAmount = new BN(tokens, 10);
 61      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
 62      let amount = temp.toString();
 63      let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue";
 64  
 65      await SNT.methods.generateTokens(accounts[0], amount).send();
 66      const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
 67      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
 68  
 69      let receipt = await Discover.methods.dapps(0).call();
 70      let developer = accounts[0];
 71  
 72      assert.strictEqual(developer, receipt.developer);
 73      assert.strictEqual(id, receipt.id);
 74      assert.strictEqual(metadata, TestUtils.getIpfsHashFromBytes32(receipt.metadata));
 75  
 76      // Check Discover actually receives the SNT!
 77      let bal_receipt = await SNT.methods.balanceOf(Discover.options.address).call();
 78      assert.strictEqual(parseInt(amount, 10), parseInt(bal_receipt, 10));
 79  
 80      // Having received the SNT, check that it updates the particular DApp's storage values
 81      assert.strictEqual(tokens, parseInt(receipt.balance, 10));
 82  
 83      let max = await Discover.methods.max().call();
 84      let decimals = await Discover.methods.decimals().call();
 85      let rate = Math.round(decimals - (tokens * decimals / max));
 86      assert.strictEqual(rate, parseInt(receipt.rate, 10));
 87  
 88      let available = tokens * rate;
 89      assert.strictEqual(available, parseInt(receipt.available, 10));
 90  
 91      let votes_minted = Math.floor((available / decimals) ** (decimals / rate));
 92      assert.strictEqual(votes_minted, parseInt(receipt.votesMinted, 10));
 93  
 94      assert.strictEqual(0, parseInt(receipt.votesCast, 10));
 95      assert.strictEqual(tokens, parseInt(receipt.effectiveBalance, 10));
 96    })
 97  
 98    it("should not create a new DApp with the same ID", async function () {
 99      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
100      let tokenAmount = new BN(1000, 10);
101      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
102      let amount = temp.toString();
103      let metadata = 'QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue';
104  
105      await SNT.methods.generateTokens(accounts[0], amount).send();
106      const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
107  
108      try {
109        await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
110        assert.fail('should have reverted before');
111      } catch (error) {
112        TestUtils.assertJump(error);
113      }
114    })
115  
116    it("should not create a new DApp when exceeding the ceiling or staking nothing", async function () {
117      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
118      let metadata = 'QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue';
119      let initial = await Discover.methods.max().call();
120      let tokens = parseInt(initial, 10);
121      let tokenAmount = new BN(tokens, 10);
122      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
123      let amount = temp.toString();
124      let amount0 = 0;
125  
126      await SNT.methods.generateTokens(accounts[0], amount).send();
127  
128      const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
129      try {
130        await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
131        assert.fail('should have reverted before');
132      } catch (error) {
133        TestUtils.assertJump(error);
134      }
135  
136      const encodedCall0 = Discover.methods.createDApp(id, amount0, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
137      try {
138        await SNT.methods.approveAndCall(Discover.options.address, amount0, encodedCall0).send({ from: accounts[0] });
139        assert.fail('should have reverted before');
140      } catch (error) {
141        TestUtils.assertJump(error);
142      }
143    })
144  
145    it("should update the metadata correctly", async function () {
146      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
147      let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFeu";
148      await Discover.methods.setMetadata(id, TestUtils.getBytes32FromIpfsHash(metadata)).send({ from: accounts[0] });
149      let receipt = await Discover.methods.dapps(0).call();
150      assert.strictEqual(TestUtils.getBytes32FromIpfsHash(metadata), receipt.metadata);
151    })
152  
153    it("should not let anyone other than the developer update the metadata", async function () {
154      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
155      let metadata_actual = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFeu";
156      let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBDDeu";
157      try {
158        await Discover.methods.setMetadata(id, TestUtils.getBytes32FromIpfsHash(metadata)).send({ from: accounts[1] });
159        assert.fail('should have reverted before');
160      } catch (error) {
161        TestUtils.assertJump(error);
162      }
163      let receipt = await Discover.methods.dapps(0).call();
164      assert.strictEqual(TestUtils.getBytes32FromIpfsHash(metadata_actual), receipt.metadata);
165    })
166  
167    it("should handle first upvote correctly", async function () {
168      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
169      let tokens = 100;
170      let tokenAmount = new BN(tokens, 10);
171      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
172      let amount = temp.toString();
173  
174      let initial = await Discover.methods.dapps(0).call();
175      let before = await SNT.methods.balanceOf(Discover.options.address).call();
176      // This is the special case where no downvotes have yet been cast
177      let up_effect = await Discover.methods.upvoteEffect(id, tokens).call();
178  
179      await SNT.methods.generateTokens(accounts[0], amount).send();
180      const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
181      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
182  
183      let receipt = await Discover.methods.dapps(0).call();
184  
185      let developer = accounts[0];
186  
187      assert.strictEqual(developer, receipt.developer);
188      assert.strictEqual(id, receipt.id);
189  
190      // Check Discover actually receives the SNT!
191      let after = await SNT.methods.balanceOf(Discover.options.address).call();
192      let bal_effect = parseInt(after, 10) - parseInt(before, 10);
193      assert.strictEqual(bal_effect, parseInt(amount, 10));
194  
195      // Having received the SNT, check that it updates the particular DApp's storage values
196      let upvotedBalance = parseInt(initial.balance, 10) + tokens;
197      assert.strictEqual(upvotedBalance, parseInt(receipt.balance, 10));
198  
199      let max = await Discover.methods.max().call();
200      let decimals = await Discover.methods.decimals().call();
201      let rate = Math.ceil(decimals - (upvotedBalance * decimals / max));
202      assert.strictEqual(rate, parseInt(receipt.rate, 10));
203  
204      let available = upvotedBalance * rate;
205      assert.strictEqual(available, parseInt(receipt.available, 10));
206  
207      let votes_minted = Math.floor((available / decimals) ** (decimals / rate));
208      assert.strictEqual(votes_minted, parseInt(receipt.votesMinted, 10));
209  
210      // Only true for upvotes made when there are no downvotes
211      assert.strictEqual(0, parseInt(receipt.votesCast, 10));
212  
213      // The effective_balance should match upvoteEffect + initial.effectiveBalance
214      let e_balance = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10);
215      assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
216    })
217  
218    it("should not let you upvote without spending SNT", async function () {
219      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
220      let amount = 0;
221  
222      await SNT.methods.generateTokens(accounts[0], 10000).send();
223      const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
224      try {
225        await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
226        assert.fail('should have reverted before');
227      } catch (error) {
228        TestUtils.assertJump(error);
229      }
230    })
231  
232    it("should not let you upvote by an amount that exceeds the ceiling", async function () {
233      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
234      let initial = await Discover.methods.max().call();
235      let tokens = parseInt(initial, 10);
236      let tokenAmount = new BN(tokens, 10);
237      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
238      let amount = temp.toString();
239  
240      await SNT.methods.generateTokens(accounts[0], amount).send();
241      const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
242      try {
243        await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
244        assert.fail('should have reverted before');
245      } catch (error) {
246        TestUtils.assertJump(error);
247      }
248    })
249  
250    it("should handle first downvote correctly", async function () {
251      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
252      let cost = await Discover.methods.downvoteCost(id).call()
253      let tokens = parseInt(cost.c, 10);
254      let tokenAmount = new BN(tokens, 10);
255      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
256      let amount = temp.toString();
257  
258      let developer = accounts[0];
259      let initial = await Discover.methods.dapps(0).call();
260      let bal_before = await SNT.methods.balanceOf(developer).call();
261  
262      await SNT.methods.generateTokens(accounts[1], amount).send();
263      const encodedCall = Discover.methods.downvote(id, amount).encodeABI();
264      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[1] });
265  
266      let receipt = await Discover.methods.dapps(0).call();
267  
268      assert.strictEqual(developer, receipt.developer);
269      assert.strictEqual(id, receipt.id);
270  
271      // Check the developer actually receives the SNT!
272      let bal_after = await SNT.methods.balanceOf(developer).call();
273      let bal_effect = parseInt(bal_after, 10) - parseInt(bal_before, 10)
274      assert.strictEqual(Math.round(bal_effect/decimalMultiplier), tokens);
275  
276      // Balance, rate, and votes_minted remain unchanged for downvotes
277      assert.strictEqual(initial.balance, receipt.balance);
278      assert.strictEqual(initial.rate, receipt.rate);
279      assert.strictEqual(initial.votesMinted, receipt.votesMinted);
280  
281      let available = parseInt(initial.available, 10) - parseInt(cost.c, 10);
282      assert.strictEqual(available, parseInt(receipt.available, 10));
283  
284      // This is only true for the first downvote
285      assert.strictEqual(parseInt(receipt.votesCast, 10), parseInt(cost.vR, 10));
286  
287      let e_balance = parseInt(initial.effectiveBalance, 10) - parseInt(cost.b, 10);
288      assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
289    })
290  
291    it("should handle second downvote correctly", async function () {
292      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
293      let cost = await Discover.methods.downvoteCost(id).call()
294      let tokens = parseInt(cost.c, 10);
295      let tokenAmount = new BN(tokens, 10);
296      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
297      let amount = temp.toString();
298      let developer = accounts[0];
299  
300      let initial = await Discover.methods.dapps(0).call();
301      let bal_before = await SNT.methods.balanceOf(developer).call();
302  
303      await SNT.methods.generateTokens(accounts[1], amount).send();
304      const encodedCall = Discover.methods.downvote(id, amount).encodeABI();
305      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[1] });
306  
307      let receipt = await Discover.methods.dapps(0).call();
308  
309      assert.strictEqual(developer, receipt.developer);
310      assert.strictEqual(id, receipt.id);
311  
312      // Check the developer actually receives the SNT!
313      let bal_after = await SNT.methods.balanceOf(developer).call();
314      let bal_effect = parseInt(bal_after, 10) - parseInt(bal_before, 10)
315      assert.strictEqual(Math.round(bal_effect/decimalMultiplier), tokens);
316  
317      // Balance, rate, and votes_minted remain unchanged for downvotes
318      assert.strictEqual(initial.balance, receipt.balance);
319      assert.strictEqual(initial.rate, receipt.rate);
320      assert.strictEqual(initial.votesMinted, receipt.votesMinted);
321  
322      let available = parseInt(initial.available, 10) - parseInt(cost.c, 10);
323      assert.strictEqual(available, parseInt(receipt.available, 10));
324  
325      let eff_v_cast = parseInt(receipt.votesCast, 10) - parseInt(initial.votesCast, 10);
326      assert.strictEqual(eff_v_cast, parseInt(cost.vR, 10));
327  
328      let e_balance = parseInt(initial.effectiveBalance, 10) - parseInt(cost.b, 10);
329      assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
330    })
331  
332    it("should not let you downvote by the wrong amount", async function () {
333      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
334      let tokensAbove = 100000;
335      let tokensBelow = 25;
336      let tokenAboveAmount = new BN(tokensAbove, 10);
337      let tokenBelowAmount = new BN(tokensBelow, 10);
338      let temp1 = decimalMultiplier.mul(new BN(tokenAboveAmount, 10));
339      let temp2 = decimalMultiplier.mul(new BN(tokenBelowAmount, 10))
340      let amountAbove = temp1.toString();
341      let amountBelow = temp2.toString();
342  
343      await SNT.methods.generateTokens(accounts[1], amountAbove + amountBelow).send();
344      const encodedCall = Discover.methods.downvote(id, amountAbove).encodeABI();
345      try {
346        await SNT.methods.approveAndCall(Discover.options.address, amountAbove, encodedCall).send({ from: accounts[1] });
347        assert.fail('should have reverted before');
348      } catch (error) {
349        TestUtils.assertJump(error);
350      }
351  
352      const encodedCall1 = Discover.methods.downvote(id, amountBelow).encodeABI();
353      try {
354        await SNT.methods.approveAndCall(Discover.options.address, amountBelow, encodedCall1).send({ from: accounts[1] });
355        assert.fail('should have reverted before');
356      } catch (error) {
357        TestUtils.assertJump(error);
358      }
359    })
360  
361    it("should handle upvotes correctly when votes have been cast", async function () {
362      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
363      let tokens = 500;
364      let tokenAmount = new BN(tokens, 10);
365      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
366      let amount = temp.toString();
367  
368      let initial = await Discover.methods.dapps(0).call();
369      let before = await SNT.methods.balanceOf(Discover.options.address).call();
370      let up_effect = await Discover.methods.upvoteEffect(id, tokens).call();
371  
372      await SNT.methods.generateTokens(accounts[0], amount).send();
373      const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
374      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
375  
376      let receipt = await Discover.methods.dapps(0).call();
377      let developer = accounts[0];
378  
379      assert.strictEqual(developer, receipt.developer);
380      assert.strictEqual(id, receipt.id);
381  
382      // Check Discover actually receives the SNT!
383      let after = await SNT.methods.balanceOf(Discover.options.address).call();
384      let bal_effect = parseInt(after, 10) - parseInt(before, 10);
385      assert.strictEqual(Math.round(bal_effect/decimalMultiplier), tokens);
386  
387      // Having received the SNT, check that it updates the particular DApp's storage values
388      let upvotedBalance = parseInt(initial.balance, 10) + tokens
389      assert.strictEqual(upvotedBalance, parseInt(receipt.balance, 10));
390  
391      let max = await Discover.methods.max().call();
392      let decimals = await Discover.methods.decimals().call();
393      let rate = Math.round(decimals - (upvotedBalance * decimals / max));
394      assert.strictEqual(rate, parseInt(receipt.rate, 10));
395  
396      let available = upvotedBalance * rate;
397      assert.strictEqual(available, parseInt(receipt.available, 10));
398  
399      let votes_minted = parseInt(receipt.votesMinted, 10);
400  
401      // Votes have been cast by this stage, so we need to check how many there are
402      // and confirm that `upvote` still calculates the effective_balance correctly
403      let votes_cast = parseInt(receipt.votesCast, 10);
404  
405      let e_balance = Math.ceil(upvotedBalance - ((votes_cast * rate / decimals) * (available / decimals / votes_minted)));
406      assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
407  
408      // The effective_balance should also match upvoteEffect + initial.effectiveBalance
409      let e_balance_2 = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10);
410      assert.strictEqual(e_balance_2, parseInt(receipt.effectiveBalance, 10));
411    })
412  
413    it("should return correct upvoteEffect for use in UI", async function () {
414      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
415      let amount = 10;
416  
417      let receipt = await Discover.methods.dapps(0).call();
418      let effect = await Discover.methods.upvoteEffect(id, amount).call();
419  
420      // Mock receiving the SNT
421      let mBalance = parseInt(receipt.balance, 10) + amount
422  
423      let max = await Discover.methods.max().call();
424      let decimals = await Discover.methods.decimals().call();
425  
426      let mRate = Math.round(decimals - (mBalance * decimals / max));
427      let mAvailable = mBalance * mRate;
428      let mVMinted = Math.round((mAvailable / decimals) ** (decimals / mRate));
429  
430      // Votes have been cast by this stage, so we need to check how many there are
431      // and confirm that `upvoteEffect` mocks the effect correctly
432      let votes_cast = parseInt(receipt.votesCast, 10);
433      let mEBalance = Math.ceil(mBalance - ((votes_cast * mRate / decimals) * (mAvailable / decimals / mVMinted)));
434      let effect_calc = mEBalance - receipt.effectiveBalance;
435  
436      // Confirm that what is returned is (mEBalance - d.effective_balance)
437      assert.strictEqual(effect_calc, parseInt(effect, 10));
438    })
439  
440    it("should throw already in upvoteEffect if you exceed the ceiling", async function () {
441      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
442      let initial = await Discover.methods.max().call();
443      let amount = parseInt(initial, 10);
444      try {
445        await Discover.methods.upvoteEffect(id, amount).call();
446        assert.fail('should have reverted before');
447      } catch (error) {
448        TestUtils.assertJump(error);
449      }
450    })
451  
452    it("should handle withdrawals correctly", async function () {
453      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
454      let tokens = 100;
455      let tokenAmount = new BN(tokens, 10);
456      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
457      let amount = temp.toString();
458  
459      let initial = await Discover.methods.dapps(0).call();
460      let before = await SNT.methods.balanceOf(Discover.options.address).call();
461      let before_dev = await SNT.methods.balanceOf(accounts[0]).call();
462      let receipt_obj = await Discover.methods.withdraw(id, amount).send({ from: accounts[0] });
463      let receipt = receipt_obj.events.Withdraw.returnValues;
464  
465      assert.strictEqual(id, receipt.id);
466  
467      // Check Discover actually sends SNT to the developer
468      let after = await SNT.methods.balanceOf(Discover.options.address).call();
469      let after_dev = await SNT.methods.balanceOf(accounts[0]).call();
470      let difference = parseInt(before, 10) - parseInt(after, 10);
471      let difference_dev = parseInt(after_dev, 10) - parseInt(before_dev, 10);
472  
473      assert.strictEqual(Math.round(difference/decimalMultiplier), tokens)
474      assert.strictEqual(Math.round(difference_dev/decimalMultiplier), tokens)
475  
476      // Recalculate e_balance manually and check it matches what is returned
477      let max = await Discover.methods.max().call();
478      let decimals = await Discover.methods.decimals().call();
479  
480      let balance = parseInt(initial.balance, 10) - tokens
481      let rate = Math.ceil(decimals - (balance * decimals / max));
482      let available = Math.round(balance * rate);
483      let v_minted = Math.round((available / decimals) ** (decimals / rate));
484      let v_cast = parseInt(initial.votesCast, 10);
485      let e_balance = Math.ceil(balance - ((v_cast * rate / decimals) * (available / decimals / v_minted)));
486  
487      let effective_balance = parseInt(receipt.newEffectiveBalance, 10);
488  
489      // We begin to run into precision limitations in the BancorFormula here.
490      // The higher the amount, the less accurate it becomes. Hence Math.ceil() for now.
491      assert.strictEqual(e_balance, effective_balance);
492  
493      // Having withdrawn the SNT, check that it updates the particular DApp's storage values properly
494      let check = await Discover.methods.dapps(0).call();
495  
496      let withdrawnBalance = parseInt(initial.balance, 10) - tokens;
497      assert.strictEqual(parseInt(check.balance, 10), withdrawnBalance);
498  
499      assert.strictEqual(parseInt(check.rate, 10), rate);
500      assert.strictEqual(parseInt(check.available, 10), available);
501      assert.strictEqual(parseInt(check.votesMinted, 10), v_minted);
502    })
503  
504    it("should not allow withdrawing more than was staked", async function () {
505      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
506      let tokens = 150000;
507      let tokenAmount = new BN(tokens, 10);
508      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
509      let amount = temp.toString();
510      try {
511        await Discover.methods.withdraw(id, amount).send({ from: accounts[0] });
512        assert.fail('should have reverted before');
513      } catch (error) {
514        TestUtils.assertJump(error);
515      }
516    })
517  
518    it("should not allow withdrawing more than was staked minus what has already been received", async function () {
519      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
520      let receipt = await Discover.methods.dapps(0).call();
521      let tokens = parseInt(receipt.available, 10) + 1;
522      let tokenAmount = new BN(tokens, 10);
523      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
524      let amount = temp.toString();
525      try {
526        await Discover.methods.withdraw(id, amount).send({ from: accounts[0] });
527        assert.fail('should have reverted before');
528      } catch (error) {
529        TestUtils.assertJump(error);
530      }
531    })
532  
533    it("should not allow anyone other than the developer to withdraw", async function () {
534      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
535      let tokens = 1000;
536      let tokenAmount = new BN(tokens, 10);
537      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
538      let amount = temp.toString();
539      try {
540        await Discover.methods.withdraw(id, amount).send({ from: accounts[1] });
541      } catch (error) {
542        TestUtils.assertJump(error);
543      }
544    })
545  
546    it("should handle downvotes after withdrawals correctly", async function () {
547      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
548      let cost = await Discover.methods.downvoteCost(id).call()
549      let tokens = parseInt(cost.c, 10);
550      let tokenAmount = new BN(tokens, 10);
551      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
552      let amount = temp.toString();
553      let developer = accounts[0];
554  
555      let initial = await Discover.methods.dapps(0).call();
556      let bal_before = await SNT.methods.balanceOf(developer).call();
557  
558      await SNT.methods.generateTokens(accounts[1], amount).send();
559      const encodedCall = Discover.methods.downvote(id, amount).encodeABI();
560      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[1] });
561  
562      let receipt = await Discover.methods.dapps(0).call();
563  
564      assert.strictEqual(developer, receipt.developer);
565      assert.strictEqual(id, receipt.id);
566  
567      // Check the developer actually receives the SNT!
568      let bal_after = await SNT.methods.balanceOf(developer).call();
569      let bal_effect = parseInt(bal_after, 10) - parseInt(bal_before, 10)
570      assert.strictEqual(Math.round(bal_effect/decimalMultiplier), tokens);
571  
572      // Balance, rate, and votes_minted remain unchanged for downvotes
573      assert.strictEqual(initial.balance, receipt.balance);
574      assert.strictEqual(initial.rate, receipt.rate);
575      assert.strictEqual(initial.votesMinted, receipt.votesMinted);
576  
577      let available = parseInt(initial.available, 10) - parseInt(cost.c, 10);
578      assert.strictEqual(available, parseInt(receipt.available, 10));
579  
580      let eff_v_cast = parseInt(receipt.votesCast, 10) - parseInt(initial.votesCast, 10);
581      assert.strictEqual(eff_v_cast, parseInt(cost.vR, 10));
582  
583      let e_balance = parseInt(initial.effectiveBalance, 10) - parseInt(cost.b, 10);
584      assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
585    })
586  
587    it("should handle upvotes after withdrawals correctly", async function () {
588      let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
589      let tokens = 100;
590      let tokenAmount = new BN(tokens, 10);
591      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
592      let amount = temp.toString();
593  
594      let initial = await Discover.methods.dapps(0).call();
595      let before = await SNT.methods.balanceOf(Discover.options.address).call();
596      let up_effect = await Discover.methods.upvoteEffect(id, tokens).call();
597  
598      await SNT.methods.generateTokens(accounts[0], amount).send();
599      const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
600      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
601  
602      let receipt = await Discover.methods.dapps(0).call();
603      let developer = accounts[0];
604  
605      assert.strictEqual(developer, receipt.developer);
606      assert.strictEqual(id, receipt.id);
607  
608      // Check Discover  actually receives the SNT!
609      let after = await SNT.methods.balanceOf(Discover.options.address).call();
610      let bal_effect = parseInt(after, 10) - parseInt(before, 10);
611      assert.strictEqual(Math.round(bal_effect/decimalMultiplier), tokens);
612  
613      // Having received the SNT, check that it updates the particular DApp's storage values
614      let upvotedBalance = parseInt(initial.balance, 10) + tokens
615      assert.strictEqual(upvotedBalance, parseInt(receipt.balance, 10));
616  
617      let max = await Discover.methods.max().call();
618      let decimals = await Discover.methods.decimals().call();
619      let rate = Math.round(decimals - (upvotedBalance * decimals / max));
620      assert.strictEqual(rate, parseInt(receipt.rate, 10));
621  
622      let available = upvotedBalance * rate;
623      assert.strictEqual(available, parseInt(receipt.available, 10));
624  
625      let votes_minted = parseInt(receipt.votesMinted, 10);
626  
627      // Votes have been cast by this stage, so we need to check how many there are
628      // and confirm that `upvote` still calculates the effective_balance correctly
629      let votes_cast = parseInt(receipt.votesCast, 10);
630  
631      let e_balance = Math.ceil(upvotedBalance - ((votes_cast * rate / decimals) * (available / decimals / votes_minted)));
632      assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
633  
634      // The effective_balance should also match upvoteEffect + initial.effectiveBalance
635      let e_balance_2 = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10);
636      assert.strictEqual(e_balance_2, parseInt(receipt.effectiveBalance, 10));
637    })
638  
639    it("should create a DApp without overflowing", async function () {
640      let id = "0x0000000000000000000000000000000000000000000000000000000000000001";
641      let safeMax = await Discover.methods.safeMax().call();
642      let tokens = parseInt(safeMax, 10);
643      let tokenAmount = new BN(tokens, 10);
644      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
645      let amount = temp.toString();
646      let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue";
647  
648      await SNT.methods.generateTokens(accounts[0], amount).send();
649      const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
650      await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
651  
652      let receipt = await Discover.methods.dapps(1).call();
653      let developer = accounts[0];
654  
655      assert.strictEqual(developer, receipt.developer);
656      assert.strictEqual(id, receipt.id);
657  
658      // Having received the SNT, check that it updates the particular DApp's storage values
659      assert.strictEqual(tokens, parseInt(receipt.balance, 10));
660  
661      let max = await Discover.methods.max().call();
662      let decimals = await Discover.methods.decimals().call();
663      let rate = Math.ceil(decimals - (tokens * decimals / max));
664      assert.strictEqual(rate, parseInt(receipt.rate, 10));
665  
666      let available = tokens * rate;
667      assert.strictEqual(available, parseInt(receipt.available, 10));
668  
669      // It's checking that votesMinted doesn't overflow which we're really interested in here
670      let votes_minted = Math.round((available / decimals) ** (decimals / rate));
671      let returned = parseInt(receipt.votesMinted, 10);
672  
673      // Is going to be less than due to rounding inaccuracies at higher exponents
674      assert.ok(returned <= votes_minted);
675    })
676  
677    // Comment out line 263 in the contract to run this test properly and see
678    // the BancorFormula fail to find a suitable position in the maxExponentArray
679    it("should prove we have the highest safeMax allowed for by Bancor's power approximation", async function () {
680      let id = "0x0000000000000000000000000000000000000000000000000000000000000002";
681      let max = await Discover.methods.max().call();
682      // Choose a safeMax 1% higher than is currently set
683      let percent = 78 / 100;
684      let tokens = Math.round(parseInt(max, 10) * percent);
685      let tokenAmount = new BN(tokens, 10);
686      let temp = decimalMultiplier.mul(new BN(tokenAmount, 10));
687      let amount = temp.toString();
688      let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue";
689  
690      await SNT.methods.generateTokens(accounts[0], amount).send();
691      const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
692  
693    // Comment this try/catch block out to test the safeMax
694     try {
695        await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
696      } catch (error) {
697        TestUtils.assertJump(error);
698      }
699  
700      // Uncomment the next line to check this test. Change percent to 77 or 76 to see it pass.
701      // await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
702    })
703  });