Building Smart Contracts for Dutch Auction (Part 2)

In the previous article, we’ve figured out how typical ICOs work from a technical POV, picked up the proper toolbox for development, and learned how to deal with potential EVM problems.

Now it’s time to write an actual Dutch Auction in Solidity!

From the Wikipedia’s definition of Dutch Auction:

Most commonly, it means an auction in which the auctioneer begins with a high asking price, and lowers it until some participant accepts the price, or it reaches a predetermined reserve price.

A Dutch Auction has several advantages over a classic crowdsale:

A 1957 Dutch Auction in Germany to sell fruit

Just like a real-life auction, we have to accept the bids to-be-claimed-later and save them in contract storage. In every bid we have to fix current token price, stake amount, and have a couple of helper notes (e.g. whether bid tokens were already claimed or the bid was placed using Bitcoin donation):

contract DutchAuction is PriceDecay150 { 
    // Auction Bid 
    struct Bid {
        uint256 price; 
        uint256 transfer; 
        bool placed; 
        bool claimed; 
        bool isBitcoin;
    }

// Address to bid mapping 
mapping (address => Bid) public bids; 

As you can notice, a Dutch Auction in Ethereum is as simple as a ERC20 Token — main variable called bids defines the details of the bid, placed by a specific Ethereum address.

This, however, introduces us to the first limitation — only a single bid can be placed by a unique address. Of course, we can modify our mapping to store an array of bids per address and then add additional loops in placing / claiming functions. But this complication will increase the overall contract interaction gas costs (because of expensive storage) and gas costs for each new address bid. To avoid the unnecessary increase in cost per user, we delegate this problem to our fellow bidders — generating a new address in almost every Ethereum wallet is as easy as a couple of mouse clicks!

Every real-life Dutch Auction is split into two main stages — the bidding process and the actual goods selling after the final price is set. Obviously, you can’t sell anything before the bidding has started and you can’t accept the bids after the price is finalized. These kinds of security limitations can be achieved through Solidity’s enumerations and function modifiers:

// Auction Stages
enum Stages {
    AuctionDeployed, 
    AuctionStarted, 
    AuctionEnded, 
    TokensDistributed
} 

// Current stage 
Stages public current_stage;

// Stage modifier 
modifier atStage(Stages _stage) { 
    require(current_stage == _stage); 
} 

After the auction contract is deployed to the network, it will have initial AuctionDeployed state. Then, startAuction() method will be called by the contract owner, allowing investors to place bids using default fallback function (). Under some conditions, stage will be changed to AuctionEnded, allowing participants to start claiming their tokens by calling claimTokens() function. Every function listed above is limited by atStage modifier, which will throw EVM error and return any transferred funds back to the sender if the stage is incorrect:

// Setup auction 
function startAuction(
    address _tokenAddress, 
    uint256 offering, 
    uint256 bonus
) external isOwner atStage (Stages.AuctionDeployed) {
    // Setup logic
} 
 
// Default fallback function 
function () public payable atStage(Stages.AuctionStarted) { 
    placeBidGeneric(msg.sender, msg.value, false); 
} 

// Claim tokens 
function claimTokens() external atStage(Stages.AuctionEnded) {
    // Claim logic
} 

Why not start an auction immediately after the contract is deployed? In startAuction(), we’re creating a ShopToken instance using it’s address in order to automatically transfer tokens to their owners. But this can be done directly in the constructor function, which implies that ShopToken contract is deployed before DutchAuction. We’ve decided to do it vice versa, ensuring the auction contract receives specified number of tokens for sale, using scriptable Truffle migrations:

// Deploy contracts and start auction 
deployer.deploy(
    DutchAuction, 
    priceStart, 
    pricePrecision, 
    minimumBid, 
    claimPeriod, 
    walletAddress
).then(
    deployer.deploy(
        ShopToken, 
        DutchAuction.address, 
        initialSupply, 
        auctionSupply
    ).then(function() { 
        DutchAuction.deployed().then(
            function(instance) { 
                instance.startAuction(
                    ShopToken.address, 
                    offering.toString(), 
                    bonus.toString()
                )
            }
        )
    )
);

So far, we’ve learned the core concepts of a Dutch Auction and its representation in the Solidity language. In the next part, we will deal with the exponential price decay, Bitcoin-funded bids, and DApp development problems.

Originally published on EVERY @ Medium