Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit fixes #3

Merged
merged 6 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,6 @@ The owner can only change the game parameters.
The Proxy Admin can upgrade the logic of the contracts. This role will be abolished after an audit and some testing time.

*`Note: All methods are described in code.`*

## Dependencies
The project uses the modified library `SortitionSumTreeFactory` with added `total()` and `numberOfNodes()` functions. You can find the modified implementation here: [@poanetwork/kleros](https://github.com/poanetwork/kleros). These changes were not audited but they add minimal risk.
2 changes: 1 addition & 1 deletion contracts/DrawManager.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity 0.5.16;

import "@kleros/kleros/contracts/data-structures/SortitionSumTreeFactory.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
Expand Down
2 changes: 1 addition & 1 deletion contracts/IPOSDAORandom.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity 0.5.16;

interface IPOSDAORandom {
function collectRoundLength() external view returns(uint256);
Expand Down
32 changes: 24 additions & 8 deletions contracts/PoaMania.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity 0.5.16;

import "@openzeppelin/contracts-ethereum-package/contracts/ownership/Ownable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
Expand Down Expand Up @@ -155,6 +155,8 @@ contract PoaMania is Ownable {
require(_owner != address(0), "zero address");
// initializes all params
Ownable.initialize(_owner);
random.init(_randomContract);
_setBlockTime(_blockTime);
_setRoundDuration(_roundDuration);
_setFee(_fee);
_setFeeReceiver(_feeReceiver);
Expand All @@ -163,11 +165,9 @@ contract PoaMania is Ownable {
_setExecutorShare(_executorShare);
_validateSumOfShares();
_setPrizeSizes(_prizeSizes);
_setBlockTime(_blockTime);
_setMinDeposit(_minDeposit);
_setMaxDeposit(_maxDeposit);
_setMinDeposit(_minDeposit);
jackpot = 0;
random.init(_randomContract);
// creates a tree of nodes
drawManager.create();
// starts the first round
Expand Down Expand Up @@ -418,8 +418,16 @@ contract PoaMania is Ownable {
* @return The timestamp when deposits and withdrawals will be locked
*/
function getLockStart() public view returns (uint256) {
uint256 lockDuration = getLockDuration();
return startedAt.add(roundDuration).sub(lockDuration);
}

/**
* @dev Returns lock duration
*/
function getLockDuration() public view returns (uint256) {
uint256 randomUpdateInterval = random.getUpdateInterval();
return startedAt.add(roundDuration).sub(randomUpdateInterval.mul(2).mul(blockTime));
return randomUpdateInterval.mul(2).mul(blockTime);
}

/**
Expand Down Expand Up @@ -472,11 +480,12 @@ contract PoaMania is Ownable {

/**
* @dev Sets the round duration
* Reverts if the value is zero
* Reverts if the value is less than 2 lock durations
* @param _roundDuration The round duration (in seconds)
*/
function _setRoundDuration(uint256 _roundDuration) internal {
require(_roundDuration > 0, "should be greater than 0");
uint256 lockDuration = getLockDuration();
require(_roundDuration >= lockDuration.mul(2), "should be greater than or equal to 2 lock durations");
roundDuration = _roundDuration;
}

Expand Down Expand Up @@ -531,7 +540,12 @@ contract PoaMania is Ownable {
*/
function _setPrizeSizes(uint256[2] memory _prizeSizes) internal {
uint256 sum = _prizeSizes[0].add(_prizeSizes[1]);
require(sum > 0 && sum <= 1 ether, "should be less than or equal to 1 ether");
require(sum > 0 && sum < 1 ether, "should be greater than 0 and less than 1 ether");
uint256 thirdPrize = uint256(1 ether).sub(sum);
require(
_prizeSizes[0] >= _prizeSizes[1] && _prizeSizes[1] >= thirdPrize,
"should satisfy: 1st prize >= 2nd prize >= 3rd prize"
);
prizeSizes = _prizeSizes;
}

Expand All @@ -550,6 +564,7 @@ contract PoaMania is Ownable {
* @param _minDeposit The minimum deposit
*/
function _setMinDeposit(uint256 _minDeposit) internal {
require(_minDeposit <= maxDeposit, "should be less than or equal to max deposit");
minDeposit = _minDeposit;
}

Expand All @@ -558,6 +573,7 @@ contract PoaMania is Ownable {
* @param _maxDeposit The maximum deposit
*/
function _setMaxDeposit(uint256 _maxDeposit) internal {
require(_maxDeposit >= minDeposit, "should be greater than or equal to min deposit");
maxDeposit = _maxDeposit;
}

Expand Down
2 changes: 1 addition & 1 deletion contracts/Random.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity 0.5.16;

import "./IPOSDAORandom.sol";

Expand Down
2 changes: 1 addition & 1 deletion contracts/Sacrifice.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity 0.5.16;

contract Sacrifice {
constructor(address payable _recipient) public payable {
Expand Down
2 changes: 1 addition & 1 deletion contracts/mocks/RandomMock.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity 0.5.16;

import '../IPOSDAORandom.sol';

Expand Down
2 changes: 1 addition & 1 deletion contracts/mocks/ReceiverMock.sol
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pragma solidity ^0.5.16;
pragma solidity 0.5.16;

import "../PoaMania.sol";

Expand Down
124 changes: 113 additions & 11 deletions test/PoaMania.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const ReceiverMock = artifacts.require('ReceiverMock');

contract('PoaMania', accounts => {
const [owner, firstParticipant, secondParticipant, thirdParticipant, fourthParticipant, fifthParticipant] = accounts;
const roundDuration = new BN(600); // in seconds
const roundDuration = new BN(800); // in seconds
const blockTime = new BN(5); // in seconds
const minDeposit = ether('10'); // 10 POA
const maxDeposit = ether('500000'); // 500,000 POA
Expand Down Expand Up @@ -151,7 +151,7 @@ contract('PoaMania', accounts => {
jackpotShare.toString(),
jackpotChance.toString(),
),
'should be greater than 0'
'should be greater than or equal to 2 lock durations'
);
await expectRevert(
initialize(
Expand All @@ -170,6 +170,23 @@ contract('PoaMania', accounts => {
),
'should be greater than 0'
);
await expectRevert(
initialize(
owner,
randomContract.address,
roundDuration.toString(),
blockTime.toString(),
minDeposit.toString(),
maxDeposit.toString(),
[0, 0],
fee.toString(),
feeReceiver,
roundCloserShare.toString(),
jackpotShare.toString(),
jackpotChance.toString(),
),
'should be greater than 0 and less than 1 ether'
);
await expectRevert(
initialize(
owner,
Expand All @@ -185,7 +202,41 @@ contract('PoaMania', accounts => {
jackpotShare.toString(),
jackpotChance.toString(),
),
'should be less than or equal to 1 ether'
'should be greater than 0 and less than 1 ether'
);
await expectRevert(
initialize(
owner,
randomContract.address,
roundDuration.toString(),
blockTime.toString(),
minDeposit.toString(),
maxDeposit.toString(),
[ether('0.4'), ether('0.5')].map(item => item.toString()),
fee.toString(),
feeReceiver,
roundCloserShare.toString(),
jackpotShare.toString(),
jackpotChance.toString(),
),
'should satisfy: 1st prize >= 2nd prize >= 3rd prize'
);
await expectRevert(
initialize(
owner,
randomContract.address,
roundDuration.toString(),
blockTime.toString(),
minDeposit.toString(),
maxDeposit.toString(),
[ether('0.3'), ether('0.2')].map(item => item.toString()),
fee.toString(),
feeReceiver,
roundCloserShare.toString(),
jackpotShare.toString(),
jackpotChance.toString(),
),
'should satisfy: 1st prize >= 2nd prize >= 3rd prize'
);
await expectRevert(
initialize(
Expand Down Expand Up @@ -238,6 +289,23 @@ contract('PoaMania', accounts => {
),
'should be less than or equal to 1 ether'
);
await expectRevert(
initialize(
owner,
randomContract.address,
roundDuration.toString(),
blockTime.toString(),
ether('10').toString(),
ether('9').toString(),
prizeSizes.map(item => item.toString()),
fee.toString(),
feeReceiver,
roundCloserShare.toString(),
jackpotShare.toString(),
jackpotChance.toString(),
),
'should be less than or equal to max deposit'
);
});
});
describe('deposit', () => {
Expand Down Expand Up @@ -297,39 +365,39 @@ contract('PoaMania', accounts => {
});
it('should withdraw specified amount', async () => {
await poaMania.deposit({ from: firstParticipant, value: ether('5') });
const receipt = await poaMania.withdraw(ether('5'), { from: firstParticipant });
const receipt = await poaMania.methods['withdraw(uint256)'](ether('5'), { from: firstParticipant });
expect(await poaMania.numberOfParticipants()).to.be.bignumber.equal(new BN(1));
expect(await poaMania.totalDepositedBalance()).to.be.bignumber.equal(minDeposit);
expect(await poaMania.balanceOf(firstParticipant)).to.be.bignumber.equal(minDeposit);
expectEvent(receipt, 'Withdrawn', { user: firstParticipant, amount: ether('5') });
});
it('fails if zero value', async () => {
await expectRevert(poaMania.methods['withdraw()']({ from: secondParticipant }), 'zero value');
await expectRevert(poaMania.withdraw(0, { from: firstParticipant }), 'zero value');
await expectRevert(poaMania.methods['withdraw(uint256)'](0, { from: firstParticipant }), 'zero value');
});
it('fails if less than min deposit', async () => {
await expectRevert(
poaMania.withdraw(ether('5'), { from: firstParticipant }),
poaMania.methods['withdraw(uint256)'](ether('5'), { from: firstParticipant }),
'should be greater than or equal to min deposit'
);
});
it('fails if greater than user deposit', async () => {
await expectRevert(
poaMania.withdraw(ether('11'), { from: firstParticipant }),
poaMania.methods['withdraw(uint256)'](ether('11'), { from: firstParticipant }),
'SafeMath: subtraction overflow'
);
});
it('fails if locked', async () => {
await poaMania.deposit({ from: firstParticipant, value: ether('1') });
await poaMania.withdraw(ether('1'), { from: firstParticipant });
await poaMania.methods['withdraw(uint256)'](ether('1'), { from: firstParticipant });
const lockStart = await poaMania.getLockStart();
await time.increaseTo(lockStart);
await expectRevert(
poaMania.methods['withdraw()']({ from: firstParticipant }),
'locked'
);
await expectRevert(
poaMania.withdraw(minDeposit, { from: firstParticipant }),
poaMania.methods['withdraw(uint256)'](minDeposit, { from: firstParticipant }),
'locked'
);
});
Expand Down Expand Up @@ -635,8 +703,13 @@ contract('PoaMania', accounts => {
it('fails if wrong value', async () => {
await expectRevert(
poaMania.setRoundDuration(0, { from: owner }),
'should be greater than 0'
'should be greater than or equal to 2 lock durations'
);
await expectRevert(
poaMania.setRoundDuration(799, { from: owner }),
'should be greater than or equal to 2 lock durations'
);
await poaMania.setRoundDuration(800, { from: owner });
});
});
describe('setFee', () => {
Expand Down Expand Up @@ -751,8 +824,21 @@ contract('PoaMania', accounts => {
it('fails if wrong value', async () => {
await expectRevert(
poaMania.setPrizeSizes([ether('0.8'), ether('0.25')], { from: owner }),
'should be less than or equal to 1 ether'
'should be greater than 0 and less than 1 ether'
);
await expectRevert(
poaMania.setPrizeSizes([0, 0], { from: owner }),
'should be greater than 0 and less than 1 ether'
);
await expectRevert(
poaMania.setPrizeSizes([ether('0.4'), ether('0.5')], { from: owner }),
'should satisfy: 1st prize >= 2nd prize >= 3rd prize'
);
await expectRevert(
poaMania.setPrizeSizes([ether('0.3'), ether('0.3')], { from: owner }),
'should satisfy: 1st prize >= 2nd prize >= 3rd prize'
);
await poaMania.setPrizeSizes([ether('0.4'), ether('0.3')], { from: owner });
});
});
describe('setBlockTime', () => {
Expand Down Expand Up @@ -786,6 +872,14 @@ contract('PoaMania', accounts => {
'Ownable: caller is not the owner'
);
});
it('fails if greater than max deposit', async () => {
await poaMania.setMaxDeposit(ether('100'));
await expectRevert(
poaMania.setMinDeposit(ether('101')),
'should be less than or equal to max deposit'
);
await poaMania.setMinDeposit(ether('100'));
});
});
describe('setMaxDeposit', () => {
it('should set', async () => {
Expand All @@ -799,5 +893,13 @@ contract('PoaMania', accounts => {
'Ownable: caller is not the owner'
);
});
it('fails if less than min deposit', async () => {
await poaMania.setMinDeposit(ether('100'));
await expectRevert(
poaMania.setMaxDeposit(ether('99')),
'should be greater than or equal to min deposit'
);
await poaMania.setMaxDeposit(ether('100'));
});
});
});