Skip to content

Commit

Permalink
Merge pull request JoinColony#254 from KevinLiLu/feature/185-limit-au…
Browse files Browse the repository at this point in the history
…ctions-to-once-a-month

Only allow auctions to be started at most once a month per token
  • Loading branch information
area authored Jul 11, 2018
2 parents 2e5a28d + bb8ffe4 commit 6674f0c
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 29 deletions.
4 changes: 4 additions & 0 deletions contracts/ColonyNetworkAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@ contract ColonyNetworkAuction is ColonyNetworkStorage {
event AuctionCreated(address auction, address token, uint256 quantity);

function startTokenAuction(address _token) public {
uint lastAuctionTimestamp = recentAuctions[_token];
require(lastAuctionTimestamp == 0 || now - lastAuctionTimestamp >= 30 days, "colony-auction-start-too-soon");
address clny = IColony(metaColony).getToken();
DutchAuction auction = new DutchAuction(clny, _token);
uint availableTokens = ERC20Extended(_token).balanceOf(this);
ERC20Extended(_token).transfer(auction, availableTokens);
auction.start();
recentAuctions[_token] = now;
emit AuctionCreated(address(auction), _token, availableTokens);
}
}
Expand Down
3 changes: 3 additions & 0 deletions contracts/ColonyNetworkStorage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,7 @@ contract ColonyNetworkStorage is DSAuth, DSMath {
uint256 reputationRootHashNNodes;
// Mapping containing how much has been staked by each user
mapping (address => uint) stakedBalances;

// Mapping containing the last auction start timestamp for a token address
mapping (address => uint) recentAuctions;
}
53 changes: 24 additions & 29 deletions test/colony-network-auction.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ contract("ColonyNetworkAuction", accounts => {
let clnyNeededForMaxPriceAuctionSellout;
let clny;
let token;
let createAuctionTxReceipt;

before(async () => {
quantity = new BN(10).pow(new BN(36)).muln(3);
Expand All @@ -42,7 +43,8 @@ contract("ColonyNetworkAuction", accounts => {
token = await Token.new(...args);
await token.mint(quantity.toString());
await token.transfer(colonyNetwork.address, quantity.toString());
const { logs } = await colonyNetwork.startTokenAuction(token.address);
const { logs, receipt } = await colonyNetwork.startTokenAuction(token.address);
createAuctionTxReceipt = receipt;
const auctionAddress = logs[0].args.auction;
tokenAuction = DutchAuction.at(auctionAddress);
});
Expand All @@ -66,16 +68,12 @@ contract("ColonyNetworkAuction", accounts => {
it("should fail with zero quantity", async () => {
const args = getTokenArgs();
const otherToken = await Token.new(...args);
const { logs } = await colonyNetwork.startTokenAuction(otherToken.address);
const auctionAddress = logs[0].args.auction;
tokenAuction = DutchAuction.at(auctionAddress);
await checkErrorRevert(tokenAuction.start());
await checkErrorRevert(colonyNetwork.startTokenAuction(otherToken.address));
});
});

describe("when starting an auction", async () => {
it("should set the `quantity` correctly and minPrice to 1", async () => {
await tokenAuction.start();
const quantityNow = await tokenAuction.quantity.call();
assert.equal(quantityNow.toString(10), quantity.toString());

Expand All @@ -91,36 +89,31 @@ contract("ColonyNetworkAuction", accounts => {
const { logs } = await colonyNetwork.startTokenAuction(otherToken.address);
const auctionAddress = logs[0].args.auction;
tokenAuction = DutchAuction.at(auctionAddress);
await tokenAuction.start();
const minPrice = await tokenAuction.minPrice.call();
assert.equal(minPrice.toString(10), 10);
});

it("should set the `startTime` correctly", async () => {
const tx = await tokenAuction.start();
const txReceiptBlockNumber = tx.receipt.blockNumber;
const blockTime = await getBlockTime(txReceiptBlockNumber);
const createAuctionTxBlockNumber = createAuctionTxReceipt.blockNumber;
const blockTime = await getBlockTime(createAuctionTxBlockNumber);

const startTime = await tokenAuction.startTime.call();
const startTime = await tokenAuction.startTime();
assert.equal(startTime.toNumber(), blockTime);
});

it("should set the `started` property correctly", async () => {
await tokenAuction.start();

const started = await tokenAuction.started.call();
assert.isTrue(started);
});

it("should fail starting the auction twice", async () => {
await tokenAuction.start();
await checkErrorRevert(tokenAuction.start());
await checkErrorRevert(colonyNetwork.startTokenAuction(token.address));
});

it("cannot bid before the auction is open", async () => {
await giveUserCLNYTokens(colonyNetwork, BIDDER_1, "1000000000000000000");
await clny.approve(tokenAuction.address, "1000000000000000000", { from: BIDDER_1 });
await checkErrorRevert(tokenAuction.bid("1000000000000000000", { from: BIDDER_1 }));
it("should fail if the last auction for the same token started less than 30 days", async () => {
await token.mint(quantity.toString());
await token.transfer(colonyNetwork.address, quantity.toString());
await checkErrorRevert(colonyNetwork.startTokenAuction(token.address));
});

const auctionProps = [
Expand Down Expand Up @@ -180,8 +173,6 @@ contract("ColonyNetworkAuction", accounts => {

auctionProps.forEach(async auctionProp => {
it(`should correctly calculate price and remaining CLNY amount to end auction at duration ${auctionProp.duration}`, async () => {
await tokenAuction.start();

await forwardTime(auctionProp.duration, this);
const currentPrice = await tokenAuction.price.call();
// Expect up to 1% error margin because of forwarding block time inaccuracies
Expand All @@ -198,13 +189,21 @@ contract("ColonyNetworkAuction", accounts => {
assert.isTrue(differenceQuantity.lte(errorMarginQuantity));
});
});
});

describe("when bidding", async () => {
beforeEach(async () => {
await tokenAuction.start();
it("should succeed if the last auction for the same token was started at least 30 days ago", async () => {
const previousAuctionStartTime = await tokenAuction.startTime();
// 30 days (in seconds)
await forwardTime(30 * 24 * 60 * 60, this);

await token.mint(quantity.toString());
await token.transfer(colonyNetwork.address, quantity.toString());
await colonyNetwork.startTokenAuction(token.address);
const newAuctionStartTime = await tokenAuction.startTime();
assert.notEqual(previousAuctionStartTime, newAuctionStartTime);
});
});

describe("when bidding", async () => {
it("can bid", async () => {
await giveUserCLNYTokens(colonyNetwork, BIDDER_1, "1000000000000000000");
await clny.approve(tokenAuction.address, "1000000000000000000", { from: BIDDER_1 });
Expand Down Expand Up @@ -288,7 +287,6 @@ contract("ColonyNetworkAuction", accounts => {

describe("when finalizing auction", async () => {
beforeEach(async () => {
await tokenAuction.start();
await giveUserCLNYTokens(colonyNetwork, BIDDER_1, clnyNeededForMaxPriceAuctionSellout.toString());
await clny.approve(tokenAuction.address, clnyNeededForMaxPriceAuctionSellout.toString(), { from: BIDDER_1 });
await tokenAuction.bid(clnyNeededForMaxPriceAuctionSellout.toString(), { from: BIDDER_1 });
Expand Down Expand Up @@ -351,7 +349,6 @@ contract("ColonyNetworkAuction", accounts => {
await clny.approve(tokenAuction.address, bidAmount2.toString(), { from: BIDDER_2 });
await clny.approve(tokenAuction.address, bidAmount3.toString(), { from: BIDDER_3 });

await tokenAuction.start();
await tokenAuction.bid(bidAmount1.toString(), { from: BIDDER_1 }); // Bids at near max price of 1e36 CLNY per 1e18 Tokens
await forwardTime(1382400, this); // Gets us near price of 1e20 CLNY per 1e18 Tokens
await tokenAuction.bid(bidAmount2.toString(), { from: BIDDER_2 });
Expand Down Expand Up @@ -400,7 +397,6 @@ contract("ColonyNetworkAuction", accounts => {
});

it("should set the bid amount to 0", async () => {
await tokenAuction.start();
await giveUserCLNYTokens(colonyNetwork, BIDDER_1, clnyNeededForMaxPriceAuctionSellout.toString());
await clny.approve(tokenAuction.address, clnyNeededForMaxPriceAuctionSellout.toString(), { from: BIDDER_1 });
await tokenAuction.bid(clnyNeededForMaxPriceAuctionSellout.toString(), { from: BIDDER_1 });
Expand All @@ -413,7 +409,6 @@ contract("ColonyNetworkAuction", accounts => {

describe("when closing the auction", async () => {
beforeEach(async () => {
await tokenAuction.start();
await giveUserCLNYTokens(colonyNetwork, BIDDER_1, clnyNeededForMaxPriceAuctionSellout.toString());
await clny.approve(tokenAuction.address, clnyNeededForMaxPriceAuctionSellout.toString(), { from: BIDDER_1 });
await tokenAuction.bid(clnyNeededForMaxPriceAuctionSellout.toString(), { from: BIDDER_1 });
Expand Down

0 comments on commit 6674f0c

Please sign in to comment.