Skip to main content

Auction

Introduction

Auction process to transfer a FA2 NFT to best bidder.

The best bid is escrowed by the contract til ownership is claimed. Previous best bidder gets its bid back. When asset ownership claimed, it is transfered to asset owner.

The contract calls the FA2 contract to check NFT ownership, and transfer ownership when auction is over.

The benefit of splitting the auction process from the FA2 ledger is that it makes it possible to change or select the appropriate auction process, while keeping the ledger intact.

API

Storage

NameTypeDescription
assetidbytesId of sold item.
owneraddressAddress to collect best bid.
auction_durdurationAuction duration.
dur_incrdurationIncrement of auction duration when a bid is placed.
nftcollectionAn NFT auction is defined by:
  • nft id
  • owner of the NFT
  • bestbidder option of address of best bidder
  • best best bid amount
  • endofbid date of the end of bid

Entrypoints

The transfer of ownership performed by calim supposes that the NFT owner calls the update_operators entrypoint of the FA2 contract.

NameParametersDescription
upforsaleid, priceOwner sets the NFT id up for sale.

Sets date of end of bid to now + auction_dur.

FA2 NFT contract entrypoint balance_of is called to check that caller is the owner of NFT id.
bididPlaces a bid for NFT id. Bid amount is transferred.

If this is best bid, previous best bid amount is transferred back to previous bet bidder.
claimidTransfers escrowed bid amount to owner if auction is over for NFT id. New owner value is set to bestbidder.

FA2 NFT contract entrypoint transfer is called to transfer ownership to best bidder.

Code

auction.arl

archetype auction(
nftoken : address,
auction_dur : duration,
dur_incr : duration
)

asset nft {
nftid : nat;
owner : address;
bestbidder : option<address>;
best : tez;
endofbid : date;
}

record operator_param {
opp_owner : address;
opp_operator : address;
opp_token_id : nat
} as ((owner, (operator, token_id)))

record balance_of_request {
bo_owner : address;
btoken_id : nat;
} as ((owner, token_id))

record balance_of_response {
request : balance_of_request;
balance_ : nat;
} as ((request, balance))

function get_addop_param(
powner : address,
popp : address,
pid : nat
) : list<or<operator_param, operator_param>> {
return ([
left<operator_param>({
opp_owner = powner;
opp_operator = popp;
opp_token_id = pid
})
])
}

entry check_ownership(brl : list<balance_of_response>) {
called by nftoken
effect {
match brl with
| hd::tl -> begin
do_require(hd.balance_ = 1, "Caller Is Not Owner");
transfer 0tz to nftoken
call update_operators<list<or<operator_param, operator_param>>>(
get_addop_param(hd.request.bo_owner, self_address, hd.request.btoken_id));
end
| [] -> fail("Empty Response")
end
}
}

entry upforsale (id : nat, price : tez) {
require {
r1: if nft.contains(id) then nft[id].endofbid < now else true
}
effect {
nft.add_update(id, {
owner = caller;
bestbidder = none;
best = price;
endofbid = (now + auction_dur)
});
(* check ownership with FA2 balance_of *)
transfer 0tz to nftoken
call balance_of<
list<balance_of_request> *
contract<list<balance_of_response>>
>(([ { bo_owner = caller; btoken_id = id } ], self.check_ownership));
}
}

entry bid (id : nat) {
require {
r2: now < nft[id].endofbid otherwise "No Auction";
r3: (match nft[id] with
| some(n) -> (n.bestbidder ? transferred > n.best : transferred >= n.best)
| none -> false
end) otherwise "Not Best Bid"
}
effect {
match nft[id].bestbidder with
| none -> ()
| some bidder -> transfer nft[id].best to bidder
end;
nft.update(id, {
bestbidder = some(caller);
best = transferred;
endofbid +=
(if nft[id].endofbid - now < dur_incr
then dur_incr
else 0s)
})
}
}

record transfer_destination {
to_dest : address;
token_id_dest : nat;
token_amount_dest : nat
} as ((to_, (token_id, amount)))

function get_transfer_param(
%from : address,
%to : address,
id : nat) : list<address * list<transfer_destination>> {
return ([
(%from, [{
to_dest = %to;
token_id_dest = id;
token_amount_dest = 1
}])
])
}

function get_rmop_param(
powner : address,
popp : address,
pid : nat
) : list<or<operator_param, operator_param>> {
return ([
right<operator_param>({
opp_owner = powner;
opp_operator = popp;
opp_token_id = pid
})
])
}

entry claim (id : nat) {
require {
r4: nft[id].endofbid < now otherwise "Auction Is Still On"
}
effect {
transfer 0tz to nftoken
call update_operators<list<or<operator_param, operator_param>>>(
get_rmop_param(nft[id].owner, self_address, id));
match nft[id].bestbidder with
| none -> ()
| some bidder -> begin
transfer 0tz to nftoken
call %transfer<list<address * list<transfer_destination>>>(
get_transfer_param(nft[id].owner, bidder, id));
transfer nft[id].best to nft[id].owner;
end
end;
nft.remove(id);
}
}