Skip to content
Open
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
17 changes: 16 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# IDE
.idea/

# Vim
# Swap
[._]*.s[a-v][a-z]
[._]*.sw[a-p]
[._]s[a-v][a-z]
[._]sw[a-p]

# Session
Session.vim

# Temporary
.netrwhist
*~
# Auto-generated tag files
tags

# Build
build/
Expand Down
31 changes: 21 additions & 10 deletions contracts/MicroLotto.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ contract MicroLotto {
uint public maxNumber;
uint public deadlineBlock = 0;
mapping(uint => Ticket[]) public ticketsPerNumber;
mapping(address => uint) public wonPrizes;

event TicketFilled(address indexed account, uint selectedNumber);
event Won(address indexed account, uint selectedNumber, uint profit);
Expand All @@ -31,13 +32,14 @@ contract MicroLotto {
Random _random,
uint _lottoFeePercent,
uint _maxNumber,
uint _lotteryDuration
uint _lotteryDuration,
uint _ticketFee
) {
require(_maxNumber >= 2);

owner = msg.sender;
random = _random;
ticketFee = 0.1 ether; // TODO: Make it configurable during deployment
ticketFee = _ticketFee;
lottoFeePercent = _lottoFeePercent;
maxNumber = _maxNumber;
lotteryDuration = _lotteryDuration;
Expand Down Expand Up @@ -78,8 +80,11 @@ contract MicroLotto {

for (uint i = 0; i < wonTickets.length; i++) {
Ticket storage ticket = wonTickets[i];
// TODO: Do you see a problem here?
ticket.account.transfer(profit);
address ticketAccount = ticket.account;

if (wonPrizes[ticketAccount] > 0) wonPrizes[ticketAccount] += profit;
else wonPrizes[ticketAccount] = profit;

Won(ticket.account, drawnNumber, profit);
}

Expand All @@ -95,15 +100,21 @@ contract MicroLotto {
}

function ownerCollect() public {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accumulatedValue is resetted in draw() and blocks the funds forever. Split accumulatedValue to separate fields prize and fees.

// TODO: Implementation
// Make sure that only owner can call this method
// Make sure that correct amount can be collected
OwnerCollected(0);
require(msg.sender == owner);

uint realValue = accumulatedValue - prize();

owner.transfer(realValue);
OwnerCollected(realValue);
}

function redeemPrize() public {
// TODO: Implementation
PrizeRedeemed(msg.sender, 0);
uint wonPrize = wonPrizes[msg.sender];

if (wonPrize > 0) {
msg.sender.transfer(wonPrize);
PrizeRedeemed(msg.sender, wonPrize);
}
}

function prize() public constant returns (uint) {
Expand Down
3 changes: 2 additions & 1 deletion migrations/3_micro_lotto.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ async function deploy(deployer) {
Random.address,
LOTTO_FEE_PERCENT,
MAX_NUMBER,
LOTTERY_DURATION
LOTTERY_DURATION,
web3.toWei(0.1, 'ether')
Copy link
Copy Markdown
Contributor

@jksf jksf Nov 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move 0.1 ether to a named constant.

);
}

Expand Down
48 changes: 37 additions & 11 deletions test/MicroLotto.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,65 @@ const {
assertThrowsInvalidOpcode,
assertNumberEqual,
assertValueEqual,
assertValueAlmostEqual
assertValueAlmostEqual,
} = require('./Helpers.js');

const MAX_NUMBER = 10;
const LOTTERY_DURATION = 10;

const TICKET_FEE_WEI = web3.toWei(0.1, 'ether');
const EXPECTED_TICKET_FEE_WEI = web3.toWei(0.1, 'ether');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for a separate const.


const LOTTO_FEE_PERCENT = 0.01;
const LOTTO_FEE_PERCENT_WEI = web3.toWei(LOTTO_FEE_PERCENT, 'ether');

const EXPECTED_PRIZE = TICKET_FEE_WEI - (LOTTO_FEE_PERCENT * TICKET_FEE_WEI);


contract(`MicroLotto with max number of ${MAX_NUMBER} and fee percent ${LOTTO_FEE_PERCENT}`, accounts => {
contract(
`MicroLotto with max number of ${MAX_NUMBER} and fee percent ${LOTTO_FEE_PERCENT}`,
accounts => {
const OWNER = accounts[0];
const PLAYER = accounts[1];
const EXPECTED_NUMBER = 1;
const UNLUCKY_NUMBER = 2;
const EXPECTED_BALANCE = web3.toWei(5, 'ether');
const EXPECTED_MAX_FUNCTION_FEE = web3.toWei(0.01, 'ether');

let lotto;
let initialBalance;

beforeEach(async () => {
const random = await RandomMock.new(EXPECTED_NUMBER, {
from: OWNER
from: OWNER,
});

lotto = await MicroLotto.new(
random.address,
LOTTO_FEE_PERCENT_WEI,
MAX_NUMBER,
LOTTERY_DURATION,
TICKET_FEE_WEI,
{ from: OWNER }
);
});

it('Should create lotto object with ticketFee', async () => {
const ticketFee = await lotto.ticketFee();
assert.equal(TICKET_FEE_WEI, EXPECTED_TICKET_FEE_WEI);
});

it('Should transfer ether from contract address to owner address', async () => {
const balanceBefore = await getBalance(OWNER);
await lotto.ownerCollect();
const balanceAfter = await getBalance(OWNER);
assertValueAlmostEqual(balanceBefore, balanceAfter, EXPECTED_MAX_FUNCTION_FEE);
});

it('Should exit when ownerCollect is called by not owner user', async () => {
const action = lotto.ownerCollect.bind(lotto, { from: PLAYER });
assertThrowsInvalidOpcode(action);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Convert to lambda.

});

context(`Given filled ticket on expected number ${EXPECTED_NUMBER}`, () => {
let fillTicketResult;
let event;
Expand Down Expand Up @@ -79,7 +101,7 @@ contract(`MicroLotto with max number of ${MAX_NUMBER} and fee percent ${LOTTO_FE
beforeEach(async () => {
await waitUntilClosed();
drawResult = await lotto.draw({
from: PLAYER
from: PLAYER,
});
});

Expand All @@ -93,6 +115,14 @@ contract(`MicroLotto with max number of ${MAX_NUMBER} and fee percent ${LOTTO_FE

});

it('Should be able to redeem prize', async () => {
const balanceBefore = await getBalance(PLAYER);
await lotto.redeemPrize({ from: PLAYER });
const wonPrize = await lotto.wonPrizes(PLAYER);
const balanceAfter = await getBalance(PLAYER);
assertValueAlmostEqual(balanceBefore, balanceAfter - wonPrize, EXPECTED_MAX_FUNCTION_FEE);
});

});

context(`Given filled ticket for unlucky number ${UNLUCKY_NUMBER}`, () => {
Expand All @@ -110,7 +140,7 @@ contract(`MicroLotto with max number of ${MAX_NUMBER} and fee percent ${LOTTO_FE
beforeEach(async () => {
await waitUntilClosed();
drawResult = await lotto.draw({
from: PLAYER
from: PLAYER,
});
});

Expand All @@ -120,14 +150,10 @@ contract(`MicroLotto with max number of ${MAX_NUMBER} and fee percent ${LOTTO_FE
assertNumberEqual(event.args.drawnNumber, EXPECTED_NUMBER);
assertNumberEqual(event.args.value, TICKET_FEE_WEI);
});

});

});

});


async function waitUntilClosed () {
async function waitUntilClosed() {
await mineBlocks(LOTTERY_DURATION + 1);
}