diff --git a/contracts/DAppStore.sol b/contracts/DAppStore.sol index 1a022ff..e9616f5 100644 --- a/contracts/DAppStore.sol +++ b/contracts/DAppStore.sol @@ -44,7 +44,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula { mapping(bytes32 => uint) public id2index; mapping(bytes32 => bool) existingIDs; - event DAppCreated(bytes32 indexed id, uint votesMint, uint amount); + event DAppCreated(bytes32 indexed id, uint newEffectiveBalance); event Upvote(bytes32 indexed id, uint newEffectiveBalance); event Downvote(bytes32 indexed id, uint newEffectiveBalance); event Withdraw(bytes32 indexed id, uint newEffectiveBalance); @@ -61,7 +61,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula { max = (total * ceiling) / decimals; - safeMax = 98 * max / 100; + safeMax = 77 * max / 100; // Limited by accuracy of BancorFormula } /** @@ -197,7 +197,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula { uint dappIdx = id2index[_id]; Data memory d = dapps[dappIdx]; require(d.id == _id, "Error fetching correct data"); - require(d.balance + _amount < safeMax, "You cannot upvote by this much, try with a lower amount"); + require(d.balance + _amount <= safeMax, "You cannot upvote by this much, try with a lower amount"); // Special case - no downvotes yet cast if (d.votesCast == 0) { @@ -250,7 +250,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula { require(!existingIDs[_id], "You must submit a unique ID"); require(_amount > 0, "You must spend some SNT to submit a ranking in order to avoid spam"); - require (_amount < safeMax, "You cannot stake more SNT than the ceiling dictates"); + require (_amount <= safeMax, "You cannot stake more SNT than the ceiling dictates"); uint dappIdx = dapps.length; @@ -283,7 +283,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula { require(SNT.allowance(_from, address(this)) >= _amount, "Not enough SNT allowance"); require(SNT.transferFrom(_from, address(this), _amount), "Transfer failed"); - emit DAppCreated(_id, d.votesMinted, d.effectiveBalance); + emit DAppCreated(_id, d.effectiveBalance); } function _upvote(address _from, bytes32 _id, uint _amount) internal { @@ -293,7 +293,7 @@ contract DAppStore is ApproveAndCallFallBack, BancorFormula { Data storage d = dapps[dappIdx]; require(d.id == _id, "Error fetching correct data"); - require(d.balance + _amount < safeMax, "You cannot upvote by this much, try with a lower amount"); + require(d.balance + _amount <= safeMax, "You cannot upvote by this much, try with a lower amount"); uint precision; uint result; diff --git a/test/dappstore_spec.js b/test/dappstore_spec.js index d09f163..2a2645a 100644 --- a/test/dappstore_spec.js +++ b/test/dappstore_spec.js @@ -45,10 +45,10 @@ contract("DAppStore", function () { it("should set max and safeMax values correctly", async function () { let resultMax = await DAppStore.methods.max().call(); let resultSafeMax = await DAppStore.methods.safeMax().call(); - let expectedMax = Math.round(3470483788 * 588 / 1000000); - let expectedSafeMax = Math.round(expectedMax * 0.98); - assert.strictEqual(parseInt(resultMax, 10), expectedMax); - assert.strictEqual(parseInt(resultSafeMax, 10), expectedSafeMax); + let expectedMax = 3470483788 * 588 / 1000000; + let expectedSafeMax = expectedMax * 77 / 100 - 1; + assert.strictEqual(parseInt(resultMax, 10), Math.round(expectedMax)); + assert.strictEqual(parseInt(resultSafeMax, 10), Math.round(expectedSafeMax)); }); it("should create a new DApp and initialise it correctly", async function () { @@ -590,4 +590,55 @@ contract("DAppStore", function () { let e_balance_2 = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10); assert.strictEqual(e_balance_2, parseInt(receipt.effectiveBalance, 10)); }) + + it("should create a DApp without overflowing", async function () { + let id = "0x0000000000000000000000000000000000000000000000000000000000000001"; + let temp = await DAppStore.methods.safeMax().call(); + let amount = parseInt(temp, 10); + + await SNT.methods.generateTokens(accounts[0], amount).send(); + const encodedCall = DAppStore.methods.createDApp(id,amount).encodeABI(); + await SNT.methods.approveAndCall(DAppStore.options.address, amount, encodedCall).send({from: accounts[0]}); + + let receipt = await DAppStore.methods.dapps(1).call(); + let developer = accounts[0]; + + assert.strictEqual(developer, receipt.developer); + assert.strictEqual(id, receipt.id); + + // Having received the SNT, check that it updates the particular DApp's storage values + assert.strictEqual(amount, parseInt(receipt.balance, 10)); + + let max = await DAppStore.methods.max().call(); + let decimals = await DAppStore.methods.decimals().call(); + let rate = Math.ceil(decimals - (amount * decimals/max)); + assert.strictEqual(rate, parseInt(receipt.rate, 10)); + + let available = amount * rate; + assert.strictEqual(available, parseInt(receipt.available, 10)); + + // It's checking that votesMinted doesn't overflow which we're really interested in here + let votes_minted = Math.round((available/decimals) ** (decimals/rate)); + let returned = parseInt(receipt.votesMinted, 10); + + // Is going to be less than due to rounding inaccuracies at higher exponents + assert.ok(returned <= votes_minted); + }) + + it("should prove we have the highest safeMax allowed for by Bancor's power approximation", async function () { + let id = "0x0000000000000000000000000000000000000000000000000000000000000002"; + let max = await DAppStore.methods.max().call(); + // Choose a safeMax 1% higher than is currently set + let safe = await DAppStore.methods.safeMax().call(); + let amount = parseInt(max, 10) * (parseInt(safe, 10) + 1); + + await SNT.methods.generateTokens(accounts[0], amount).send(); + const encodedCall = DAppStore.methods.createDApp(id,amount).encodeABI(); + + try { + await SNT.methods.approveAndCall(DAppStore.options.address, amount, encodedCall).send({from: accounts[0]}); + } catch (error) { + TestUtils.assertJump(error); + } + }) });