Created
June 6, 2018 14:34
-
-
Save stefek99/e7acf11b40df03473425125f00e7b108 to your computer and use it in GitHub Desktop.
Auction contract - getting better at Solidity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* eslint-disable no-undef */ // Avoid the linter considering truffle elements as undef. | |
const Auction = artifacts.require('Auction.sol') | |
const { expectThrow, increaseTime } = require('./helpers') | |
contract('Auction', function (accounts) { | |
let owner = accounts[0] | |
let bidderA = accounts[1] | |
let bidderB = accounts[2] | |
let bidderC = accounts[3] | |
let duration = 3600; | |
let auction; | |
let timestampEnd | |
beforeEach(async function() { | |
timestampEnd = web3.eth.getBlock('latest').timestamp + duration; // 1 hour from now | |
auction = await Auction.new(1e18, "item", timestampEnd, {from: owner}); | |
}); | |
it('Should be able to set up the constructor auction', async function() { | |
assert.equal(await auction.owner(), owner, 'The owner is not set correctly') | |
assert.equal(await auction.description(), "item", 'The description is not set correctly') | |
assert.equal(await auction.timestampEnd(), timestampEnd, 'The endtime is not set correctly') | |
}) | |
it('Should be able to send a bid above the initial price', async function() { | |
await auction.sendTransaction({ value: 1e18, from: bidderA }); | |
assert.equal(await auction.price(), 1e18, "Price not set up correctly"); | |
assert.equal(await auction.winner(), bidderA, "Winner not set up correctly"); | |
}) | |
it('Should not be able to send a bid below the initial price', async function() { | |
await expectThrow(auction.sendTransaction({ value: 0.5e18, from: bidderA })); | |
}) | |
it('Should not be able to send a bid after the end of auction', async function() { | |
increaseTime(duration + 1); | |
await expectThrow(auction.sendTransaction({ value: 1e18, from: bidderA })); | |
}) | |
it('Should be able to outbid', async function() { | |
await auction.sendTransaction({ value: 1e18, from: bidderA }); | |
await auction.sendTransaction({ value: 1.25e18, from: bidderB }); | |
assert.equal(await auction.price(), 1.25e18, "Price not set up correctly"); | |
assert.equal(await auction.winner(), bidderB, "Winner not set up correctly"); | |
}) | |
it('Should not be able to outbid if bid too low', async function() { | |
await auction.sendTransaction({ value: 1e18, from: bidderA }); | |
await expectThrow(auction.sendTransaction({ value: 1.2e18, from: bidderB })) | |
assert.equal(await auction.price(), 1e18, "Price not set up correctly"); | |
assert.equal(await auction.winner(), bidderA, "Winner not set up correctly"); | |
}); | |
it('Should be able to outbid - partially (we already have some ETH', async function() { | |
await auction.sendTransaction({ value: 1e18, from: bidderA }); | |
await auction.sendTransaction({ value: 1.25e18, from: bidderB }); | |
await auction.sendTransaction({ value: 0.5625e18, from: bidderA }); | |
assert.equal(await auction.price(), 1.5625e18, "Price not set up correctly"); | |
assert.equal(await auction.winner(), bidderA, "Winner not set up correctly"); | |
}); | |
it('Winner should be able to set delivery instructions', async function() { | |
await auction.sendTransaction({ value: 1e18, from: bidderA }); | |
increaseTime(3700); | |
await expectThrow(auction.setInstructions("whitehouse", {from: bidderB })); | |
await auction.setInstructions("whitehouse", {from: bidderA }); | |
assert.equal(await auction.instructions(), "whitehouse", "Delivery instructions not set up correctly"); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pragma solidity ^0.4.23; | |
contract Auction { | |
string public description; | |
string public instructions; // will be used for delivery address or email | |
uint public price; | |
bool public initialPrice = true; // at first asking price is OK, then +25% required | |
uint public timestampEnd; | |
address public owner; | |
address public winner; | |
mapping(address => uint) public bids; | |
// TODO - expand time factor | |
// a) 24 hours or less before the end | |
// b) 4 hours the expansion | |
uint public increaseTimeIfBidBeforeEnd = 24 * 60 * 60; // Naming things: https://www.instagram.com/p/BSa_O5zjh8X/ | |
uint public increaseTimeBy = 24 * 60 * 60; | |
event Bid(address indexed winner, uint indexed price, uint indexed timestamp); | |
modifier onlyOwner { require(owner == msg.sender, "only owner"); _; } | |
modifier onlyWinner { require(winner == msg.sender, "only winner"); _; } | |
modifier ended { require(timestampEnd > now, "not ended yet"); _; } | |
function setDescription(string _description) public onlyOwner() { | |
description = _description; | |
} | |
function setInstructions(string _instructions) public ended() onlyWinner() { | |
instructions = _instructions; | |
} | |
constructor(uint _price, string _description, uint _timestampEnd) public { | |
require(_timestampEnd > now, "end of the auction must be in the future"); | |
owner = msg.sender; | |
price = _price; | |
description = _description; | |
timestampEnd = _timestampEnd; | |
} | |
function() public payable { | |
require(now < timestampEnd, "auction has ended"); | |
if (bids[msg.sender] > 0) { // First we add the bid to an existing bid | |
bids[msg.sender] += msg.value; | |
} else { | |
bids[msg.sender] = msg.value; | |
} | |
if (initialPrice) { | |
require(bids[msg.sender] >= price, "big too low, minimum is the initial price"); | |
} else { | |
require(bids[msg.sender] >= (price * 5 / 4), "big too low, minimum 25% increment"); | |
} | |
if (now > timestampEnd - increaseTimeIfBidBeforeEnd) { | |
timestampEnd = now + increaseTimeBy; | |
} | |
initialPrice = false; | |
price = bids[msg.sender]; | |
winner = msg.sender; | |
emit Bid(winner, price, now); | |
} | |
function withdraw() public { | |
// TODO WIP | |
} | |
function withdrawBeneficiary() public ended() onlyOwner() { | |
// TODO WIP | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module.exports = { | |
increaseTime: addSeconds => { | |
web3.currentProvider.send({jsonrpc: "2.0", method: "evm_increaseTime", params: [addSeconds], id: 0}) | |
}, | |
mineBlock: () => { | |
web3.currentProvider.send({jsonrpc: "2.0", method: "evm_mine"}) | |
}, | |
expectThrow: async promise => { | |
try { | |
await promise | |
} catch (error) { | |
const invalidJump = error.message.search('invalid JUMP') >= 0 | |
const invalidOpcode = error.message.search('invalid opcode') >= 0 | |
const outOfGas = error.message.search('out of gas') >= 0 | |
// TODO: Test if the error is a revert. | |
return | |
} | |
assert.fail('Expected throw not received') | |
}, | |
waitForMined: tx => { | |
return new Promise((resolve, reject) => { | |
let setIntervalId = setInterval(() => web3.eth.getTransactionReceipt(tx, (err, receipt) => { | |
if (err) reject(err.message) | |
if (receipt) { | |
clearInterval(setIntervalId) | |
resolve(receipt) | |
} | |
}), 1000) | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment