Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
309 lines (245 sloc) 11.7 KB
import "../token/TokenFactoryInterface.sol";
import "../token/FactoryTokenInterface.sol";
import "../ownership/ZapCoordinatorInterface.sol";
import "../../platform/bondage/BondageInterface.sol";
import "../../platform/bondage/currentCost/CurrentCostInterface.sol";
import "../../platform/registry/RegistryInterface.sol";
import "../../platform/bondage/currentCost/CurrentCostInterface.sol";
Contest where users can bond to contestant curves which mint tokens( unbondabe*),
winner decided by oracle
contract unbonds from loser curves
holders of winning token allowed to take share of reserve token(zap) which was unbonded from loser curves
Starting Contest:
deploys with contest uninitialized: status = Uninitialized
anyone can initialize new token:backed curve
owner initializes contest with oracle: status = Initialized
Ending Contest:
owner calls close: status = ReadyToSettle
oracle calls judge to set winning curve: status = Judged
anyone calls settle, contest unbonds from losing curves: status = Settled
holders of winnning token call redeem to retrieve their share of reserve token
based on their holding of winning token
*holders of winning token can optionally unbond
contract SampleContest is Ownable {
CurrentCostInterface currentCost;
FactoryTokenInterface public reserveToken;
ZapCoordinatorInterface public coord;
TokenFactoryInterface public tokenFactory;
BondageInterface bondage;
enum ContestStatus {
Uninitialized, //
Initialized, // ready for buys
// ReadyToSettle, // ready for judgement
Judged, // winner determined
Settled, // value of winning tokens determined
Canceled // oracle did not respond in time
address public oracle; // address of oracle who will choose the winner
uint256 public ttl; // time allowed before, close and judge. if time expired, allow unbond from all curves
// uint256 public expired = 2**256 -1; // time allowed before, close and judge. if time expired, allow unbond from all curves
bytes32 public winner; // curve identifier of the winner
uint256 public winValue; // final value of the winning token
ContestStatus public status; //state of contest
mapping(bytes32 => address) public curves; // map of endpoint specifier to token-backed dotaddress
bytes32[] public curves_list; // array of endpoint specifiers
mapping(address => uint8) public redeemed; // map of address redemption state
address[] public redeemed_list;
event DotTokenCreated(address tokenAddress);
event Bonded(bytes32 indexed endpoint, uint256 indexed numDots, address indexed sender);
event Unbonded(bytes32 indexed endpoint,uint256 indexed amount, address indexed sender);
event Initialized(address indexed oracle);
event Closed();
event Judged(bytes32 winner);
event Settled(uint256 winValue, uint256 winTokens);
event Reset();
address coordinator,
address factory,
uint256 providerPubKey,
bytes32 providerTitle
coord = ZapCoordinatorInterface(coordinator);
reserveToken = FactoryTokenInterface(coord.getContract("ZAP_TOKEN"));
//always allow bondage to transfer from wallet
reserveToken.approve(coord.getContract("BONDAGE"), ~uint256(0));
tokenFactory = TokenFactoryInterface(factory);
RegistryInterface registry = RegistryInterface(coord.getContract("REGISTRY"));
registry.initiateProvider(providerPubKey, providerTitle);
status = ContestStatus.Uninitialized;
// contest lifecycle
function initializeContest(
address oracleAddress,
uint256 _ttl
) onlyOwner public {
require( status == ContestStatus.Uninitialized, "Contest already initialized");
oracle = oracleAddress;
ttl = _ttl + block.number;
status = ContestStatus.Initialized;
emit Initialized(oracle);
function judge(bytes32 endpoint) {
require( status == ContestStatus.Initialized, "Contest not initialized" );
require( msg.sender == oracle, "Only designated Oracle can judge");
require(block.number < ttl, "Contest expired, refund in process");
winner = endpoint;
status = ContestStatus.Judged;
emit Judged(winner);
function settle() public {
require( status == ContestStatus.Judged, "winner not determined");
bondage = BondageInterface(coord.getContract("BONDAGE"));
// how many winning dots
uint256 numWin = bondage.getDotsIssued(address(this), winner);
// redeemable value of each dot token
uint256 dots;
for( uint256 i = 0; i < curves_list.length; i++) {
dots = bondage.getDotsIssued(address(this), curves_list[i]);
if( dots > 0) {
bondage.unbond(address(this), curves_list[i], dots);
winValue = reserveToken.balanceOf(address(this)) / numWin;
status = ContestStatus.Settled;
emit Settled(winValue, numWin);
//TODO ensure all has been redeemed or enough time has elasped
function reset() public {
require(msg.sender == oracle);
require(status == ContestStatus.Settled || status == ContestStatus.Canceled, "contest not settled");
if( status == ContestStatus.Canceled ) {
require(reserveToken.balanceOf(address(this)) == 0, "funds remain");
delete redeemed_list;
delete curves_list;
status = ContestStatus.Initialized;
emit Reset();
/// TokenDotFactory methods
function initializeCurve(
bytes32 endpoint,
bytes32 symbol,
int256[] curve
) public returns(address) {
// require(status==ContestStatus.Initialized,"Contest is not initalized")
require(curves[endpoint] == 0, "Curve endpoint already exists or used in the past. Please choose a new endpoint");
RegistryInterface registry = RegistryInterface(coord.getContract("REGISTRY"));
registry.initiateProviderCurve(endpoint, curve, address(this));
curves[endpoint] = newToken(bytes32ToString(endpoint), bytes32ToString(symbol));
registry.setProviderParameter(endpoint, toBytes(curves[endpoint]));
return curves[endpoint];
//whether this contract holds tokens or coming from msg.sender,etc
function bond(bytes32 endpoint, uint numDots) public {
require( status == ContestStatus.Initialized, " contest is not initiated");
bondage = BondageInterface(coord.getContract("BONDAGE"));
uint256 issued = bondage.getDotsIssued(address(this), endpoint);
CurrentCostInterface cost = CurrentCostInterface(coord.getContract("CURRENT_COST"));
uint256 numReserve = cost._costOfNDots(address(this), endpoint, issued + 1, numDots - 1);
reserveToken.transferFrom(msg.sender, address(this), numReserve),
"insufficient accepted token numDots approved for transfer"
reserveToken.approve(address(bondage), numReserve);, endpoint, numDots);
FactoryTokenInterface(curves[endpoint]).mint(msg.sender, numDots);
emit Bonded(endpoint, numDots, msg.sender);
//whether this contract holds tokens or coming from msg.sender,etc
function unbond(bytes32 endpoint, uint numDots) public returns(uint256) {
bondage = BondageInterface(coord.getContract("BONDAGE"));
uint issued = bondage.getDotsIssued(address(this), endpoint);
//unbond dots
bondage.unbond(address(this), winner, numDots);
currentCost = CurrentCostInterface(coord.getContract("CURRENT_COST"));
//get reserve value to send
uint reserveCost = currentCost._costOfNDots(address(this), endpoint, issued + 1 - numDots, numDots - 1);
FactoryTokenInterface curveToken = FactoryTokenInterface(curves[endpoint]);
if( status == ContestStatus.Initialized || status == ContestStatus.Canceled) {
//oracle has taken too long to judge winner so unbonds will be allowed for all
require(block.number > ttl, "oracle query not expired.");
// require(status == ContestStatus.Settled, "contest not settled");
status = ContestStatus.Canceled;
//unbond dots
//TODO get bound dot then unbond the correct amount ? or unbond all in 1 call
// bondage.unbond(address(this), endpoint, numDots);
//burn dot backed token
//FIXME only burn the bound tokens ?
curveToken.burnFrom(msg.sender, numDots);
require(reserveToken.transfer(msg.sender, reserveCost), "transfer failed");
emit Unbonded(endpoint, reserveCost, msg.sender);
return reserveCost;
else {
require( status == ContestStatus.Settled, " contest not settled");
require(redeemed[msg.sender] == 0, "already redeeemed");
require(winner==endpoint, "only winners can unbond for rewards");
//reward user's winning tokens unbond value + share of losing curves reserve token proportional to winning token holdings
uint reward = ( winValue * FactoryTokenInterface(getTokenAddress(winner)).balanceOf(msg.sender) ) + reserveCost;
//burn user's unbonded tokens
// curveToken.approve(address(this),numDots);
curveToken.burnFrom(msg.sender, numDots);
reserveToken.transfer(msg.sender, reward);
redeemed[msg.sender] = 1;
// emit Unbonded(winner, reward, msg.sender);
return reward;
function newToken(
string name,
string symbol
returns (address tokenAddress)
FactoryTokenInterface token = tokenFactory.create(name, symbol);
tokenAddress = address(token);
return tokenAddress;
function getTokenAddress(bytes32 endpoint) public view returns(address) {
RegistryInterface registry = RegistryInterface(coord.getContract("REGISTRY"));
return bytesToAddr(registry.getProviderParameter(address(this), endpoint));
function getEndpoints() public view returns(bytes32[]){
return curves_list;
function getStatus() public view returns(uint256){
return uint(status);
function isEndpointValid(bytes32 _endpoint) public view returns(bool){
for(uint256 i=0; i<curves_list.length;i++){
if(_endpoint == curves_list[i]){
return true;
return false;
function toBytes(address x) public pure returns (bytes b) {
b = new bytes(20);
for (uint i = 0; i < 20; i++)
b[i] = byte(uint8(uint(x) / (2**(8*(19 - i)))));
function bytes32ToString(bytes32 x) public pure returns (string) {
bytes memory bytesString = new bytes(32);
bytesString = abi.encodePacked(x);
return string(bytesString);
function bytesToAddr (bytes b) public pure returns (address) {
uint result = 0;
for (uint i = b.length-1; i+1 > 0; i--) {
uint c = uint(b[i]);
uint to_inc = c * ( 16 ** ((b.length - i-1) * 2));
result += to_inc;
return address(result);
You can’t perform that action at this time.