Auction is a very popular feature that is widely used today in the blockchain environment. Crypto-markets, NFT-markets, games and many other blockchain projects use Auctions to perform various activities and increase engagement of the audience. 

Today we want to introduce you to the Guide to development of the Auction on one of the most prospective blockchains – WAX blockchain to help build auction features for your project.

In our case we’ve developed Auctions for the Collect.Social project, the social NFT-platform for collectors to track their progress, achieve and collaborate.

Below you’ll find sections of C++ code, and detailed description of what we hade developed.

Let’s start with constants and then move on to the next parts. 

Constants which will be used in the code later:

constexpr name colname = “collectwhale”_n;

constexpr name schemaname_keys = “key”_n;

constexpr name schemaname_gallery = “gallery”_n;

constexpr name PUZZLE_ACCOUNT = “collectwhale”_n;constexprsymbol COIN_SYMBOL = symbol(“RACOON”, 4);

Structures and tables that we use to store Auction data:

struct member

   {

      name      participant;

      asset     bid;

      uint64_t  key_id;

   };

   struct [[eosio::table]] auction_j

   {

      uint64_t      id;

      std::string   auction_name;

      uint64_t      asset_id_prize;

      uint8_t       min_percent_bid;

      asset         minimum_bid;

      uint64_t      start_at;

      uint64_t      end_at;

      uint64_t primary_key()const { return id; }

   };

   typedef eosio::multi_index< “auctions”_n, auction_j > auction_t;

   struct [[eosio::table]] participants_j

   {

      std::uint64_t         auction_id;

      uint32_t              participants_count;

      member                top_participant;

      std::vector<member>   participants;

      uint64_t primary_key()const { return auction_id; }

   };

   typedef eosio::multi_index< “auctmembers”_n, participants_j > participants_t;

member structure was created to store participant data:

  • the participant name, 
  • his bid, 
  • the key that was dropped to participate.

the auction_j table saves data distinctly about the auction:

  • ID, 
  • the name of the auction, 
  • prize that is raffled, 
  • bid increment (%), 
  • the first minimum bid, 
  • the beginning and the end of the Auction.

the table participants_j saves the information about participants

  • auction ID, 
  • number of participants, 
  • top participant, 
  • array of the participants.

The main auctions action:

[[eosio::action]]

   void creatauct(std::string auctname, uint64_t start_at, uint64_t end_at, uint64_t prize, uint8_t min_percent_bid,asset minimum_bid);

   [[eosio::action]]

   void delauct(uint64_t id);

   [[eosio::action]]

   void claimreward(name winner, uint64_t id);

   [[eosio::action]]

   void exitauct(name user, uint64_t id);

[[eosio::on_notify(“atomicassets::transfer”)]] 

   void receive_asset_transfer(

        name from,

        name to,

        vector <uint64_t> asset_ids,

        string memo

   );

createauct with parameters:

  • auction name, 
  • start and end, prize, 
  • bid increment (%), 
  • the first minimum bid.

This action can only trigger the contract itself (admin) to create new auctions.

delauct with auction ID parameter can also trigger a contract and is used to remove erroneous auctions.

claimreward with input parameters: winner’s account, auction ID.

The winner must call this action to receive his prize at the very end of the Auction.

exitauct with parameters (player account,auction ID) is used to exit the auction in case User is not a top participant.

receive_asset_transfer listens for all transfers to our contract.

transfer is used to send tokens between accounts.
In case of an auction event, it helps to make a bid at the auction.

Now let’s take a closer look at these actions in the code.

1st Setup Action

void puzzle::creatauct(std::string auctname, uint64_t start_at, uint64_t end_at, uint64_t prize, uint8_t min_percent_bid,asset minimum_bid)

{

    require_auth(PUZZLE_ACCOUNT);

    atomicassets::assets_t assets (atomicassets::ATOMICASSETS_ACCOUNT, PUZZLE_ACCOUNT.value);

    auto assets_itr = assets.require_find(prize, “Could not find asset id”);

    check(assets_itr->collection_name == colname, “Asset (” + std::to_string(prize) + “) prize is not from our collection”);

    check(assets_itr->schema_name == schemaname_gallery,  “Asset (” + std::to_string(prize) + “) prize is not a gallery place”);

    auction_t auct_table(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    uint64_t auct_id = auct_table.available_primary_key();

    if (auct_id == 0) 

    {

        auct_id = 1;

    }

    auct_table.emplace(PUZZLE_ACCOUNT,[&](auto &new_row)

    {

        new_row.id = auct_id;

        new_row.auction_name = auctname;

        new_row.start_at = start_at;

        new_row.end_at = end_at;

        new_row.asset_id_prize = prize;

        new_row.min_percent_bid = min_percent_bid;

        new_row.minimum_bid = minimum_bid;

    });

    participants_t table_patics (PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    table_patics.emplace(PUZZLE_ACCOUNT,[&](auto &new_row)

    {

        new_row.auction_id = auct_id;

        new_row.participants_count = 0;

    });

Here we get access to the AtomicHub table with assets to check whether our account indeed owns the specified assets.

The require_find function requires to find this asset within the account.
In case of failure it causes an error.

atomicassets::assets_t assets (atomicassets::ATOMICASSETS_ACCOUNT, PUZZLE_ACCOUNT.value);

    auto assets_itr = assets.require_find(prize, “Could not find asset id”);

Here we check whether the prize is from our collection, and whether the scheme matches.
In the below mentioned case the place in the gallery was raffled.

check(assets_itr->collection_name == colname, “Asset (” + std::to_string(prize) + “) prize is not from our collection”);

    check(assets_itr->schema_name == schemaname_gallery,  “Asset (” + std::to_string(prize) + “) prize is not a gallery place”);

Then the table with auctions is extracted, and by means of function

available_primary_key () extract the next available key to provide the number of the auction ID.

The next step is to make a new entry in the auction table and record the participants of the auction.
For this purpose we use the emplace function (it indicate who pays for the memory, [] () {} – lambda function)

 auction_t auct_table(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    uint64_t auct_id = auct_table.available_primary_key();

    if (auct_id == 0) 

    {

        auct_id = 1;

    }

    auct_table.emplace(PUZZLE_ACCOUNT,[&](auto &new_row)

    {

        new_row.id = auct_id;

        new_row.auction_name = auctname;

        new_row.start_at = start_at;

        new_row.end_at = end_at;

        new_row.asset_id_prize = prize;

        new_row.min_percent_bid = min_percent_bid;

        new_row.minimum_bid = minimum_bid;

    });

    participants_t table_patics (PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    table_patics.emplace(PUZZLE_ACCOUNT,[&](auto &new_row)

    {

        new_row.auction_id = auct_id;

        new_row.participants_count = 0;

    });

2) The action of  deleting wrong auction

void puzzle::delauct(uint64_t id)

{

    require_auth(PUZZLE_ACCOUNT);

    auction_t table_auct(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto auct_itr = table_auct.require_find(id, “Could not find auction with this id”);

    table_auct.erase(auct_itr);

    participants_t table_partics(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto partics_itr = table_partics.require_find(id, “Could not find auction participants table”);

    if(is_account(partics_itr->top_participant.participant))

    {

        transfer_action transfer{PUZZLE_ACCOUNT,{PUZZLE_ACCOUNT, “active”_n}};

        transfer.send(PUZZLE_ACCOUNT, partics_itr->top_participant.participant,partics_itr->top_participant.bid, “auction was deleted, you receive your tokens back”);

    }

    for(member const members : partics_itr->participants)

    {

        action

        (

            permission_level{PUZZLE_ACCOUNT,”active”_n},

            atomicassets::ATOMICASSETS_ACCOUNT,

            “transfer”_n,

            std::make_tuple

            (

                PUZZLE_ACCOUNT,

                members.participant,

                (std::vector<uint64_t>){members.key_id},

                std::string(“auction was deleted, you receive your key back”)

            )

        ).send();

        if(members.bid.amount > 0)

        {

            transfer_action transfer{PUZZLE_ACCOUNT,{PUZZLE_ACCOUNT, “active”_n}};

            transfer.send(PUZZLE_ACCOUNT, members.participant, members.bid, “auction was deleted, you receive your tokens back”);

        }

    }

    table_partics.erase(partics_itr);

}

We take out the tables and request to find the exact auction with the specified ID.

We delete the specified auction from the auction table immediately. Participants will be deleted later as we need to refund tokens and keys if there were any.

 auction_t table_auct(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto auct_itr = table_auct.require_find(id, “Could not find aucion with this id”);

    table_auct.erase(auct_itr);

    participants_t table_partics(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto partics_itr = table_partics.require_find(id, “Could not find auction participants table”);

Сheck if we had the top participant.

If so, we refund his RACOONs.

It is necessary to do right away, as when we will iterate on every participant, we will not be able to explore how many RACOONs the top participant has spent.

 if(is_account(partics_itr->top_participant.participant))

    {

        transfer_action transfer{PUZZLE_ACCOUNT,{PUZZLE_ACCOUNT, “active”_n}};

        transfer.send(PUZZLE_ACCOUNT, partics_itr->top_participant.participant,partics_itr->top_participant.bid, “auction was deleted, you receive your tokens back”);

    }

Next step is to iterate on all participants.

We return the key and RACOONs if User participated in the auction.

At the end we delete the record from the table of participants.

for(member const members : partics_itr->participants)

    {

        action

        (

            permission_level{PUZZLE_ACCOUNT,”active”_n},

            atomicassets::ATOMICASSETS_ACCOUNT,

            “transfer”_n,

            std::make_tuple

            (

                PUZZLE_ACCOUNT,

                members.participant,

                (std::vector<uint64_t>){members.key_id},

                std::string(“auction was deleted, you receive your key back”)

            )

        ).send();

        if(members.bid.amount > 0)

        {

            transfer_action transfer{PUZZLE_ACCOUNT,{PUZZLE_ACCOUNT, “active”_n}};

            transfer.send(PUZZLE_ACCOUNT, members.participant, members.bid, “auction was deleted, you receive your tokens back”);

        }

    }

    table_partics.erase(partics_itr);

3) The action of claiming reward

void puzzle::claimreward(name winner, uint64_t id)

{

    require_auth(winner);

    uint64_t time_now = current_time_point().sec_since_epoch();

    auction_t table_auct(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto auct_itr = table_auct.require_find(id, “Could not find auction with this id”);

    check(time_now > auct_itr->start_at, “Auction has not started yet”);

    check(time_now > auct_itr->end_at, “Auction is not over”);

    participants_t table_partics(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto partics_table_itr = table_partics.require_find(id, “Could not find auction participant table with this id”);

    check(partics_table_itr->top_participant.participant == winner, “You are not a winner of this auction”);

    auto participants_itr = std::find_if(begin(partics_table_itr->participants),end(partics_table_itr->participants),[&](const member &p)

    {

        return(p.participant == winner);

    });

    check(participants_itr != end(partics_table_itr->participants), “You have already claimed reward”);

    action

    (

        permission_level{PUZZLE_ACCOUNT,”active”_n},

        atomicassets::ATOMICASSETS_ACCOUNT,

        “transfer”_n,

        std::make_tuple

        (

            PUZZLE_ACCOUNT,

            winner,

            (std::vector<uint64_t>){auct_itr->asset_id_prize},

            std::string(“Congratulations, you have won the auction!!!”)

        )

    ).send();

    action

    (

        permission_level{PUZZLE_ACCOUNT,”active”_n},

        atomicassets::ATOMICASSETS_ACCOUNT,

        “burnasset”_n,

        std::make_tuple

        (

            PUZZLE_ACCOUNT,

            participants_itr->key_id

        )

    ).send();

    if(partics_table_itr->participants_count > 1)

    {

        table_partics.modify(partics_table_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

        {

            new_row.participants.erase(participants_itr);

            new_row.participants_count = partics_table_itr->participants_count – 1;

        });

    }

    else

    {

        table_partics.erase(partics_table_itr);

        table_auct.erase(auct_itr);

    }

}

First we get the exact time in seconds for further verification at the end of the auction.

We extract the required auction and data from the table.

 uint64_t time_now = current_time_point().sec_since_epoch();

    auction_t table_auct(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto auct_itr = table_auct.require_find(id, “Could not find auction with this id”);

    check(time_now > auct_itr->start_at, “Auction has not started yet”);

    check(time_now > auct_itr->end_at, “Auction is not over”);

Using the find_if search algorithm we find our top participant among the list of all participants.
If there is no top participant, it means he has already claimed his prize.

auto participants_itr = std::find_if(begin(partics_table_itr->participants),end(partics_table_itr->participants),[&](const member &p)

    {

        return(p.participant == winner);

    });

    check(participants_itr != end(partics_table_itr->participants), “You have already claimed reward”);

If all goes well, we give the winner a prize and burn his key.

Then remove him from the list of participants.

If he is the only one in the list, we completely delete the auction record.

permission_level{PUZZLE_ACCOUNT,”active”_n},

        atomicassets::ATOMICASSETS_ACCOUNT,

        “transfer”_n,

        std::make_tuple

        (

            PUZZLE_ACCOUNT,

            winner,

            (std::vector<uint64_t>){auct_itr->asset_id_prize},

            std::string(“Congratulations, you have won the auction!!!”)

        )

    ).send();

    action

    (

        permission_level{PUZZLE_ACCOUNT,”active”_n},

        atomicassets::ATOMICASSETS_ACCOUNT,

        “burnasset”_n,

        std::make_tuple

        (

            PUZZLE_ACCOUNT,

            participants_itr->key_id

        )

    ).send();

    if(partics_table_itr->participants_count > 1)

    {

        table_partics.modify(partics_table_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

        {

            new_row.participants.erase(participants_itr);

            new_row.participants_count = partics_table_itr->participants_count – 1;

        });

    }

    else

    {

        table_partics.erase(partics_table_itr);

        table_auct.erase(auct_itr);

    }

}

4) The action of exiting from auction

void puzzle::exitauct(name user, uint64_t id)

{

    if(has_auth(user))

        require_auth(user);

    else

        require_auth(PUZZLE_ACCOUNT);

    auction_t table_auct(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto auct_itr = table_auct.require_find(id, “Could not find auction with this id”);

    participants_t table_partics(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto participants_table_itr = table_partics.require_find(id, “Could not find participant table”);

    check(participants_table_itr->top_participant.participant != user, 

    “You can’t leave the auction because you are the top participant. If it’s over, you can collect reward”);

    auto participants_vector_itr = std::find_if(begin(participants_table_itr->participants),end(participants_table_itr->participants),[&](const member &p)

    {

        return(p.participant == user);

    });

    check(participants_vector_itr != end(participants_table_itr->participants), “You are not involved in this auction”);

    if(participants_vector_itr->bid.amount > 0)

    {

        transfer_action transfer{PUZZLE_ACCOUNT,{PUZZLE_ACCOUNT, “active”_n}};

        transfer.send(PUZZLE_ACCOUNT, participants_vector_itr->participant, participants_vector_itr->bid, “You have successfully exited the auction (receive your tokens back)”);

    }

    action

    (

        permission_level{PUZZLE_ACCOUNT,”active”_n},

        atomicassets::ATOMICASSETS_ACCOUNT,

        “transfer”_n,

        std::make_tuple

        (

            PUZZLE_ACCOUNT,

            participants_vector_itr->participant,

            (std::vector<uint64_t>){participants_vector_itr->key_id},

            std::string(“You have successfully exited the auction (receive your key back)”)

        )

    ).send();

    table_partics.modify(participants_table_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

    {

        new_row.participants.erase(participants_vector_itr);

        new_row.participants_count = participants_table_itr->participants_count – 1;

    });

    if(participants_table_itr->participants_count == 0 && auct_itr->end_at < current_time_point().sec_since_epoch())

    {

        table_partics.erase(participants_table_itr);

        table_auct.erase(auct_itr);

    }

}

Here’s how we extract auction data and check if the user is not a top bidder.

If the user is a top bidder, he will not be able to exit the auction.

auction_t table_auct(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto auct_itr = table_auct.require_find(id, “Could not find auction with this id”);

    participants_t table_partics(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto participants_table_itr = table_partics.require_find(id, “Could not find participant table”);

    check(participants_table_itr->top_participant.participant != user, 

    “You can’t leave the auction because you are the top participant. If it’s over, you can collect reward”);

The code below describes how to find a user who requests the auction exit among all participants and check if such a user exists.

auto participants_vector_itr = std::find_if(begin(participants_table_itr->participants),end(participants_table_itr->participants),[&](const member &p)

    {

        return(p.participant == user);

    });

    check(participants_vector_itr != end(participants_table_itr->participants), “You are not involved in this auction”);

Then give the user his RACOONs (if they were spent before) and the key back.

After that we delete the user from the participants list.

If the number of participants = 0, check whether the auction is over.

If it is over, we delete the record from the table.

if(participants_vector_itr->bid.amount > 0)

    {

        transfer_action transfer{PUZZLE_ACCOUNT,{PUZZLE_ACCOUNT, “active”_n}};

        transfer.send(PUZZLE_ACCOUNT, participants_vector_itr->participant, participants_vector_itr->bid, “You have successfully exited the auction (receive your tokens back)”);

    }

    action

    (

        permission_level{PUZZLE_ACCOUNT,”active”_n},

        atomicassets::ATOMICASSETS_ACCOUNT,

        “transfer”_n,

        std::make_tuple

        (

            PUZZLE_ACCOUNT,

            participants_vector_itr->participant,

            (std::vector<uint64_t>){participants_vector_itr->key_id},

            std::string(“You have successfully exited the auction (receive your key back)”)

        )

    ).send();

    table_partics.modify(participants_table_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

    {

        new_row.participants.erase(participants_vector_itr);

        new_row.participants_count = participants_table_itr->participants_count – 1;

    });

    if(participants_table_itr->participants_count == 0 && auct_itr->end_at < current_time_point().sec_since_epoch())

    {

        table_partics.erase(participants_table_itr);

        table_auct.erase(auct_itr);

    }

5) The action for the bid creation

void transfer( const name&    from,

                  const name&    to,

                  const asset&   quantity,

                  const string&  memo );

Let’s look closer into the part related to the auction.

The user must make a transfer, where the memo must include the auction ID.

else if(check_auct_id(memo) == true && to == PUZZLE_ACCOUNT)

    {

        auction_t table_aucts(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

        auto aucts_itr = table_aucts.require_find(std::stoull(memo), “Could not find auction with this id”);

        uint64_t time_now = current_time_point().sec_since_epoch();

        check(time_now > aucts_itr->start_at, “This auction has not started yet”);

        check(time_now < aucts_itr->end_at, “This auction is over”);

        participants_t table_partics (PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

        auto participants_table_itr = table_partics.require_find(std::stoull(memo), “Could not find auction with this id”);

        auto participants_itr = std::find_if(begin(participants_table_itr->participants),end(participants_table_itr->participants),[&](const member &p)

        {

            return(p.participant == from);

        });

        check(participants_itr != end(participants_table_itr->participants),”Transfer key to take part in auction”);

        if(participants_table_itr->top_participant.bid.amount == 0)

            check(aucts_itr->minimum_bid.amount <= quantity.amount, “This auction has minimum bid ” + aucts_itr->minimum_bid.to_string());

        asset current_bid = asset(quantity.amount + participants_itr->bid.amount,COIN_SYMBOL);

        check(participants_table_itr->top_participant.bid.amount + (participants_table_itr->top_participant.bid.amount * aucts_itr->min_percent_bid / 100)  <= current_bid.amount,

            “Your bid should be ” + std::to_string(aucts_itr->min_percent_bid) + “\% higher than the top participant”);

        check(participants_table_itr->top_participant.participant != from, “You are already top participant”);

        // prev top participant in vector participants

        auto prev_top_participant = std::find_if(begin(participants_table_itr->participants),end(participants_table_itr->participants),[&](const member &p)

        {

            return(p.participant == participants_table_itr->top_participant.participant);

        });

        table_partics.modify(participants_table_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

        {

            new_row.participants[prev_top_participant – participants_table_itr->participants.begin()].bid = participants_table_itr->top_participant.bid;

            new_row.top_participant.participant = from;

            new_row.top_participant.bid = current_bid;

            new_row.top_participant.key_id = participants_itr->key_id;

            new_row.participants[participants_itr – participants_table_itr->participants.begin()].bid.amount = 0;

        });

               // continue time

        if(aucts_itr->end_at – time_now <= 120)  // 2 min left

        {

            table_aucts.modify(aucts_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

            {

                new_row.end_at = aucts_itr->end_at + 120;

            });

        }

    }

Next step: we extract the data by auction number and check whether the auction is active.

auction_t table_aucts(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

        auto aucts_itr = table_aucts.require_find(std::stoull(memo), “Could not find auction with this id”);

        uint64_t time_now = current_time_point().sec_since_epoch();

        check(time_now > aucts_itr->start_at, “This auction has not started yet”);

        check(time_now < aucts_itr->end_at, “This auction is over”);

Check whether the user dropped the key to participate.
This action is a must.

auto participants_table_itr = table_partics.require_find(std::stoull(memo), “Could not find auction with this id”);

        auto participants_itr = std::find_if(begin(participants_table_itr->participants),end(participants_table_itr->participants),[&](const member &p)

        {

            return(p.participant == from);

        });

        check(participants_itr != end(participants_table_itr->participants),”Transfer key to take part in auction”);

If the user is the first bidder, we need to check whether he is betting more than the minimum bid rate.

If he tries to lead over the top participant, then his RACOONs are added to those that he had previously stacked (in case he sent some).

Then it is checked whether the bet is a given percentage higher than the top participant’s bet.

if(participants_table_itr->top_participant.bid.amount == 0)

            check(aucts_itr->minimum_bid.amount <= quantity.amount, “This auction has minimum bid ” + aucts_itr->minimum_bid.to_string());

        asset current_bid = asset(quantity.amount + participants_itr->bid.amount,COIN_SYMBOL);

        check(participants_table_itr->top_participant.bid.amount + (participants_table_itr->top_participant.bid.amount * aucts_itr->min_percent_bid / 100)  <= current_bid.amount,

            “Your bid should be ” + std::to_string(aucts_itr->min_percent_bid) + “\% higher than the top participant”);check(participants_table_itr->top_participant.participant != from, “You are already top participant”);

We are looking for our previous top member through the list of participants. Then modify our table with auction participants:

  • return his RACOONs to the previous top participant;
  • record a new top participant in his place;
  • reset his unused RACOONs (because they have been added up to the current rate).

// prev top participant in vector participants

        auto prev_top_participant = std::find_if(begin(participants_table_itr->participants),end(participants_table_itr->participants),[&](const member &p)

        {

            return(p.participant == participants_table_itr->top_participant.participant);

        });

        table_partics.modify(participants_table_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

        {

            new_row.participants[prev_top_participant – participants_table_itr->participants.begin()].bid = participants_table_itr->top_participant.bid;

            new_row.top_participant.participant = from;

            new_row.top_participant.bid = current_bid;

            new_row.top_participant.key_id = participants_itr->key_id;

            new_row.participants[participants_itr – participants_table_itr->participants.begin()].bid.amount = 0;

        });

We also check the terms of the auction finishing.

If a user made a bet and led over the top participant 2 minutes before the end of the auction, another 2 minutes is added to the total time.

     // continue time

        if(aucts_itr->end_at – time_now <= 120)  // 2 min left

        {

            table_aucts.modify(aucts_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

            {

                new_row.end_at = aucts_itr->end_at + 120;

            });

        }

6) With the help of this action we get data on the transfer of the asset.

If the user wants to participate in the auction, he has to make a transfer of one key with the auction ID mentioned in the memo.

We review the actions on the asset:
– check whether the specified auction exists,
– number of keys – 1 Key,
– check the collection,
– check the scheme,
– check the active auction.

If the review is successful, we register the user as a participant.

void puzzle::receive_asset_transfer(

    name from,

    name to,

    vector <uint64_t> asset_ids,

    string memo

)

{

check(asset_ids.size() == 1, “You have to transfer only 1 key”);

    atomicassets::assets_t assets (atomicassets::ATOMICASSETS_ACCOUNT, to.value);

    auto assets_itr = assets.require_find(asset_ids[0], “Could not find asset id”);

    check(assets_itr->collection_name == colname, “This asset is not from our collection”);

    check(assets_itr->schema_name == schemaname_keys, “Asset schema is not a key”);

    auction_t table_aucts(PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    uint64_t time_now = current_time_point().sec_since_epoch();

    auto aucts_itr = table_aucts.require_find(std::stoull(memo), “Could not find auction with this id”);

    check(time_now > aucts_itr->start_at, “This auction has not started yet”);

    check(time_now < aucts_itr->end_at, “This auction is over”);

    participants_t table_partics (PUZZLE_ACCOUNT,PUZZLE_ACCOUNT.value);

    auto participants_table_itr = table_partics.require_find(std::stoull(memo), “Could not find auction participant table with this id”);

    auto participants_itr = std::find_if(begin(participants_table_itr->participants),end(participants_table_itr->participants),[&](const member &p)

    {

        return(p.participant == from);

    });

    check(participants_itr == end(participants_table_itr->participants),”You are already taking part in this auction”);

    //push new participant to vector

    table_partics.modify(participants_table_itr,PUZZLE_ACCOUNT,[&](auto &new_row)

    {

        const member m {from, {0,COIN_SYMBOL}, asset_ids[0]};

        uint32_t count = participants_table_itr->participants_count;

        new_row.participants.push_back(m);

        new_row.participants_count = ++count;

   });

  }

}

And… that’s it! Now youu can use this code as a source of inspiration and experiment with your own needs and ideas.

Dapplica team wishes you efficient coding!