Skip to content

Instantly share code, notes, and snippets.

@alexvandesande
Last active December 10, 2017 05:40
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexvandesande/8a3840154fcb1957398482d57bc3ed35 to your computer and use it in GitHub Desktop.
Save alexvandesande/8a3840154fcb1957398482d57bc3ed35 to your computer and use it in GitHub Desktop.
/*
One of the takeaways I had from the ENS workshop, is that if buying domains
from secundary services is easy and quick, it will take the pressure off the
main ENS system to be perfect. So since I didn’t see any decent reselling
contract I decided to try my luck on my own. It's not yet finalized but shows
some characteristics I want on the service.
## Both instant buy and actions
One of the feedbacks I had is that many domain owners do not know how to price
their own names. Some wanted to get rid of some test names they had, others
wanted to make sure they took the best out of their "investment". So this
contract allows both possibilities: an "instant buy" button and a possibility
of an auction. Once someone bids, instant buy is disabled
## Traditional Auctions
Auctions work the traditional way, with the highest bidder paying the top price.
To prevent sniping, new bid, the auction end date is extended for 24 hours.
To prevent someone from indefintely extending an auction by outbidding by a few
cents, it's not enough to outbid the last person, but to outbid them by a margin
that increases proportionally to how much the auction lasts. On the first day,
you need to outbid the last bid for at least 20%, then 40%, 60%, etc.
For example, if you want to participate on an auction that has lasted for
five days already, you need to double the last bidder and so on.
## Referral Fee
We want to encourage many entrepreneurs to be able to build competing
marketplaces without needing to create their own contracts, so I added an
optional referral fee. Referral amounts can be anything from 0% to 20% and
will go to anyone. Interface developers are encouraged to set it to
themselves and select any fee they want. This also means that by executing
the contract directly from the command line the buyer can have up to a 20%
discount on the purchase, something sellers should be aware of when setting prices.
Because of the rolling margin, you always need to outbid by at least 20%,
so bidders can't take advantadge of this to win a bid by underbidding.
*/
pragma solidity ^0.4.2;
contract Deed {
address public owner;
address public previousOwner;
}
contract Registrar {
function transfer(bytes32 _hash, address newOwner);
function entries(bytes32 _hash) constant returns (uint, Deed, uint, uint, uint);
}
contract Reseller {
Registrar public registrar;
mapping (string => name) names;
mapping (address => uint256) canWithdraw;
event NameTransferred(string _name, address from, address to);
event NewInstantPrice(string _name, uint newPrice);
event NewMinimumBid(string _name, uint newMinimumBid);
event NewBid(string _name, uint bid);
struct name {
uint instantPrice;
uint minBid;
uint biddingStarted;
uint lastBid;
address lastBidder;
uint biddingEnds;
address referral;
uint referralAmount;
}
modifier onlyNameOwner(string _name) {
Deed deed;
(,deed,,,) = registrar.entries(sha3(_name));
require(deed.previousOwner() == msg.sender);
_;
}
function Reseller(address hashRegistrar) {
registrar = Registrar(hashRegistrar);
}
function setInstantPrice(string _name, uint instantPrice) onlyNameOwner(_name) {
name storage n = names[_name];
require(n.biddingStarted == 0);
n.instantPrice = instantPrice;
NewInstantPrice(_name, instantPrice);
}
function setMinBid(string _name, uint minimumBid) onlyNameOwner(_name) {
name storage n = names[_name];
require(n.biddingStarted == 0);
n.minBid = minimumBid;
NewMinimumBid(_name, minimumBid);
}
// Only to be used internally
function transferName(string _name, address to, uint value) internal {
// Get the proper information
Deed deed;
(,deed,,,) = registrar.entries(sha3(_name));
address previousOwner = deed.previousOwner();
// transfer the name
registrar.transfer(sha3(_name), to);
NameTransferred(_name, previousOwner, to);
// Clear Name records
names[_name] = name({
instantPrice: 0,
minBid: 0,
biddingStarted: 0,
lastBid:0,
lastBidder:0,
biddingEnds:0,
referral: 0,
referralAmount: 0
});
// and at last, pay the seller
previousOwner.transfer(value);
}
function returnName(string _name) onlyNameOwner(_name) {
name storage n = names[_name];
require(n.biddingStarted == 0);
transferName(_name, msg.sender, 0);
}
function buyNow(string _name, address referral, uint referralAmount) payable {
require(msg.value >= names[_name].instantPrice);
name storage n = names[_name];
require(n.biddingStarted == 0); // Once auctions started, instant buy is disabled
transferName(_name, msg.sender, msg.value);
referral.transfer((1000*n.lastBid*n.referralAmount)/1000);
}
// In order for auctions not to drag on forever bids need to be increasinly bigger
// to be valid. After 1 day, you need to outbid by at least 20%, after 2 days, 40% and so on
function minimumBid(string _name) constant returns (uint minBid) {
name storage n = names[_name];
return n.biddingStarted == 0 ?
n.minBid :
n.lastBid + n.lastBid*20*(1 days + now - n.biddingStarted)/(1 days * 100);
}
function bid(string _name, address referral, uint referralAmount) payable {
require(msg.value > minimumBid(_name));
require(referralAmount < 200);
// try to Withdraw any previous bids
tryWithdraw();
name storage n = names[_name];
require(now < n.biddingEnds);
canWithdraw[n.lastBidder] += n.lastBid;
n.lastBidder = msg.sender;
n.lastBid = msg.value;
n.biddingEnds = now + 24 hours;
// Referral are optional parameters
n.referral = referral;
n.referralAmount = referralAmount;
if (n.biddingStarted == 0) n.biddingStarted = now;
NewBid(_name, msg.value);
}
function finalizeAuction(string _name) {
name storage n = names[_name];
require(now > n.biddingEnds);
n.referral.transfer((1000*n.lastBid*n.referralAmount)/1000);
transferName(_name, n.lastBidder, n.lastBid);
}
function tryWithdraw() {
uint withdrawalAmount = canWithdraw[msg.sender];
canWithdraw[msg.sender] = 0;
// If it cannot withdraw, then revert the change
if(!msg.sender.send(withdrawalAmount)) canWithdraw[msg.sender] = withdrawalAmount;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment