‼️ This is a Work in Progress.
Current status: Milestone-2 ✔️ completed.
Use at your own risk.
This is an Ink! smartcontract implementing a candle auction logic.
With this contract, one can set up a candle auction for a NFT collection or a domain name!
See my blogpost with the rationale and detailed design description of the contract.
Basic features
- Contract logic is heavily inspired by the parachain auction implementation.
- Auction is initialized by setting Opening\Ending periods in block numbers.
// example of an auction schedule: // [1][2][3][4][5][6][7][8][9][10][11][12][13] // | opening | ending |
- The contract accepts payments and records participants` balances.
- Bidders balances are stored as a HashMap which effectively presents top bid per user.
- Bids are made by transferring a bid amount to contract with invoking
bid()
payable method. - Pluggable reward logic: auction reward method can be one of provided options and should be specified on contract initiation.
- Reward logic is executed by cross-contract method invocation: this very contract communicates with specified
ERC721
orDNS
contract instance, depending on which auction subject has been set up.
Low-level ink_env::call::CallBuilder is preferred over ink-as-dependency way, for the sake of loosely coupling. - Auction finalization, i.e. winner determination, is invoked by calling
find_winner()
method.
This can be done by anyone generous enough to pay for gas. However, due to specifics of secure random number generation on-chain, this is allowed to be done not earlier thanRF_DELAY
blocks after the last block of the Ending period has beem sealed to chain. - Payouts can be claimed once auction is finalized, on per user basic by
payout()
method invocation:- winner is paid by the specified reward logic
(e.g. a domain name transferral or an approval to became some NFT tokens operator);
in case not the highest of her bids wins, the winner also gets the change paid back; - other bidders are paid by recieving their bidded amounts back;
- auction owner is paid by recieving the winning bid amount.
- winner is paid by the specified reward logic
Candle-fashioned
- In order to make candle logic possible, we also store
winning_data
in featured StorageVec which holds bids for every sample. - Sample is a number of consequent blocks identifying a time interval inside Ending period.
In PoC version, sample equals to a single block. This could be enhanced later to be a configurable parameter. - The winning sample (i.e. in which candle "went out") will be selected retrospectively after Ending period ends.
- Major feature what makes this auction candle-fashioned is the randomness of winning sample selection.
The contract allows you to configure the source of this randomness (see entropy module). By default, it usesink_env::random()
function which in turn utilizes randomness-collective-flip module. The latter provides generator of low-influence random values based on the block hashes from the last81
blocks. It means that when using this particular random function, it is required to wait at least 81 blocks after the last block of Ending period until invoking the function to get a random block inside that period.
Please see cargo docs and comments in code for deeper details.
Please follow installation instructions provided here.
git clone https://github.com/agryaznov/candle-auction-ink
cd candle-auction-ink
cargo +nightly test
cargo +nightly contract build
First we deploy rewarding contracts which represent entities being auctioned. After that, we deploy the auction itself.
Two pluggable reward options are available:
- NFT collection: by utilizing the ERC721 contract
winner gets set_approval_for_all() tokens belonging to the auction contract - Doman name ownership: by utilizing the DNS contract
which transfers to winner the domain name put up for the auction
In order to make the auction contract preferably loosely coupled with other contracts, this very contract doesn't use their sources as-a-dependency. Instead, we rely just on these external contracts ABI, and hope that their main methods selectors will stay consistent.
Okay, though, to guarantee this, let's use our fork of their codebase repo with explicit selectors:
git clone -b candle-auction git@github.com:agryaznov/ink.git
Then build the contracts:
cd ink/examples/erc721
cargo +nightly contract build
cd ../dns
cargo +nightly contract build
Then deploy them through the PolkadotJS apps /contracts tab
Find candle_auction.contract
in the target/ink
folder,
and deploy it.
Prepare/Launch:
- Instantiate the contract by setting following parameters:
start_block
number of block in which the auction starts; by default it's the next block after contract instantiation;opening_period
duration of Opening Period in blocksending_period
duration of Ending Period in blockssubject
auction subject:0
= NFTs1
= DNS2..255
= reserved for further reward methods
domain
in case of DNS subject, the domain name to bid forreward_contract_address
address of the rewarding contract: ERC721 or DNS
- Pass the auctioned entities ownership to the contract:
transfer NFT tokens / domain names to the instantiated auction contract.
❗NOTE that sanity checks, like: does the auction contract really possess the entities being bidded for? - those ones are left totally to user's discretion.
Action!:
-
Place bids by invoking
bid()
method with an attached payment. -
Get current auction status by
get_status()
and current winning bid and account byget_winning()
methods invocation. -
Once auction is ended, anyone can invoke
find_winner()
method to randomly detect a block during Ending period and set the auction winner to be the top bidder of that block. This effectively emulates candle blow for the auction.❗NOTE-1 that
random()
function implementation used in substrate-contract-node takes 81 block back in time to produce seed secure enough to use. As follows from the function docs, the returned seed should be used only to distinguish commitments made after the first block of that 81 blocks sequence.
In other words,find_winner()
should be called not earlier than 81 block after the auction ended.❗NOTE-2 If first bids come in block late enough, it is possible that candle "goes out" before that block. In such a case, a finalized auction with
None
winner is expected outcome. Every bidders get claim their money back.
Settlement:
- Once auction is done, participants (including the contract owner) can claim their payouts/rewards with
payout()
.❗NOTE that in NFT auction winner gets approval to transer all contract's ERC721 tokens with this. She should then transer these tokens by herself by manually calling
transfer_from()
on that ERC721 contract.
cargo +nightly doc --lib --no-deps --document-private-items --open
For newbies, it is highly recommended to go through the gorgeous Ink! Workshop on substrate.dev the portal.
See Ink! docs for developer documentation.
Apache License 2.0 © Alexander Gryaznov (agryaznov.com)