Back to Home

SingularDTVCrowdfunding

Unknown
0xbdf5c4f1c1a9...1d5f40cf5520
DAO ForkContract #24KSource VerifiedEdit this contract
Deployed October 2, 2016 (9 years ago)Block 2,364,411

SingularDTV crowdfunding contract — managed the October 2016 token sale for the Ethereum-based decentralized entertainment platform, raising $7.5M in 17 minutes

Key Facts

Deployment Block
2,364,411
Deployment Date
Oct 2, 2016, 08:55 AM
Code Size
3.0 KB
Gas at Deploy
944,053
Transactions by Year
2016632
20179

Description

The SingularDTVCrowdfunding contract was the crowdfunding mechanism for SingularDTV, a ConsenSys-incubated project building a decentralized entertainment platform on Ethereum. The contract was authored by Stefan George (stefan.george@consensys.net), one of the earliest ConsenSys smart contract developers who later co-founded Gnosis.

The SingularDTV system was deployed as a trio of interlinked contracts: SingularDTVCrowdfunding (this contract, at 0xbdf5c4f1...), SingularDTVToken (0xaec2e87e..., the SNGLS ERC-20 token), and SingularDTVFund (0xe736091f..., the treasury). This multi-contract architecture was a sophisticated design pattern for 2016, separating crowdfunding logic, token logic, and fund management.

The crowdfunding launched on October 2, 2016 and reached its cap of approximately $7.5 million in ETH within just 17 minutes, with only about 145 participants able to contribute before the cap was hit. The SNGLS token had 0 decimals (whole tokens only) and a total supply of 1 billion.

SingularDTV aimed to be an Ethereum-powered "Netflix-like" platform where content creators could tokenize their creative output and receive transparent royalty management. The project was led by Zach LeBeau and built in partnership with ConsenSys and MME (a Swiss cryptocurrency law firm). It later rebranded to Breaker in 2018.

Source Verified

Etherscan verified

Heuristic Analysis

The following characteristics were detected through bytecode analysis and may not be accurate.

Detected Type: Unknown

DAO Fork Era

The controversial fork to recover funds from The DAO hack.

Block span: 1,920,0002,462,999
July 20, 2016October 18, 2016

Bytecode Overview

Opcodes3,028
Unique Opcodes184
Jump Instructions248
Storage Operations134

Verified Source Available

Source verified on Etherscan.

Show source code (Solidity)
contract Token {
    
    function totalSupply() constant returns (uint256 supply) {}
    function balanceOf(address owner) constant returns (uint256 balance);
    function transfer(address to, uint256 value) returns (bool success);
    function transferFrom(address from, address to, uint256 value) returns (bool success);
    function approve(address spender, uint256 value) returns (bool success);
    function allowance(address owner, address spender) constant returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}



contract SingularDTVToken is Token {
    function issueTokens(address _for, uint tokenCount) returns (bool);
}
contract SingularDTVFund {
    function workshop() returns (address);
    function softWithdrawRevenueFor(address forAddress) returns (uint);
}




contract SingularDTVCrowdfunding {

    /*
     *  External contracts
     */
    SingularDTVToken public singularDTVToken;
    SingularDTVFund public singularDTVFund;

    /*
     *  Constants
     */
    uint constant public CAP = 1000000000; 
    uint constant public CROWDFUNDING_PERIOD = 4 weeks; 
    uint constant public TOKEN_LOCKING_PERIOD = 2 years; 
    uint constant public TOKEN_TARGET = 534000000; 

    /*
     *  Enums
     */
    enum Stages {
        CrowdfundingGoingAndGoalNotReached,
        CrowdfundingEndedAndGoalNotReached,
        CrowdfundingGoingAndGoalReached,
        CrowdfundingEndedAndGoalReached
    }

    /*
     *  Storage
     */
    address public owner;
    uint public startDate;
    uint public fundBalance;
    uint public baseValue = 1250 szabo; 
    uint public valuePerShare = baseValue; 

    
    mapping (address => uint) public investments;

    
    Stages public stage = Stages.CrowdfundingGoingAndGoalNotReached;

    /*
     *  Modifiers
     */
    modifier noEther() {
        if (msg.value > 0) {
            throw;
        }
        _
    }

    modifier onlyOwner() {
        
        if (msg.sender != owner) {
            throw;
        }
        _
    }

    modifier minInvestment() {
        
        if (msg.value < valuePerShare) {
            throw;
        }
        _
    }

    modifier atStage(Stages _stage) {
        if (stage != _stage) {
            throw;
        }
        _
    }

    modifier atStageOR(Stages _stage1, Stages _stage2) {
        if (stage != _stage1 && stage != _stage2) {
            throw;
        }
        _
    }

    modifier timedTransitions() {
        uint crowdfundDuration = now - startDate;
        if (crowdfundDuration >= 22 days) {
            valuePerShare = baseValue * 1500 / 1000;
        }
        else if (crowdfundDuration >= 18 days) {
            valuePerShare = baseValue * 1375 / 1000;
        }
        else if (crowdfundDuration >= 14 days) {
            valuePerShare = baseValue * 1250 / 1000;
        }
        else if (crowdfundDuration >= 10 days) {
            valuePerShare = baseValue * 1125 / 1000;
        }
        else {
            valuePerShare = baseValue;
        }
        if (crowdfundDuration >= CROWDFUNDING_PERIOD) {
            if (stage == Stages.CrowdfundingGoingAndGoalNotReached) {
                stage = Stages.CrowdfundingEndedAndGoalNotReached;
            }
            else if (stage == Stages.CrowdfundingGoingAndGoalReached) {
                stage = Stages.CrowdfundingEndedAndGoalReached;
            }
        }
        _
    }

    /*
     *  Contract functions
     */
    
    function checkInvariants() constant internal {
        if (fundBalance > this.balance) {
            throw;
        }
    }

    
    function emergencyCall()
        external
        noEther
        returns (bool)
    {
        if (fundBalance > this.balance) {
            if (this.balance > 0 && !singularDTVFund.workshop().send(this.balance)) {
                throw;
            }
            return true;
        }
        return false;
    }

    
    function fund()
        external
        timedTransitions
        atStageOR(Stages.CrowdfundingGoingAndGoalNotReached, Stages.CrowdfundingGoingAndGoalReached)
        minInvestment
        returns (uint)
    {
        uint tokenCount = msg.value / valuePerShare; 
        if (singularDTVToken.totalSupply() + tokenCount > CAP) {
            
            tokenCount = CAP - singularDTVToken.totalSupply();
        }
        uint investment = tokenCount * valuePerShare; 
        
        if (msg.value > investment && !msg.sender.send(msg.value - investment)) {
            throw;
        }
        
        fundBalance += investment;
        investments[msg.sender] += investment;
        if (!singularDTVToken.issueTokens(msg.sender, tokenCount)) {
            
            throw;
        }
        
        if (stage == Stages.CrowdfundingGoingAndGoalNotReached) {
            if (singularDTVToken.totalSupply() >= TOKEN_TARGET) {
                stage = Stages.CrowdfundingGoingAndGoalReached;
            }
        }
        
        if (stage == Stages.CrowdfundingGoingAndGoalReached) {
            if (singularDTVToken.totalSupply() == CAP) {
                stage = Stages.CrowdfundingEndedAndGoalReached;
            }
        }
        checkInvariants();
        return tokenCount;
    }

    
    function withdrawFunding()
        external
        noEther
        timedTransitions
        atStage(Stages.CrowdfundingEndedAndGoalNotReached)
        returns (bool)
    {
        
        uint investment = investments[msg.sender];
        investments[msg.sender] = 0;
        fundBalance -= investment;
        
        if (investment > 0  && !msg.sender.send(investment)) {
            throw;
        }
        checkInvariants();
        return true;
    }

    
    function withdrawForWorkshop()
        external
        noEther
        timedTransitions
        atStage(Stages.CrowdfundingEndedAndGoalReached)
        returns (bool)
    {
        uint value = fundBalance;
        fundBalance = 0;
        if (value > 0  && !singularDTVFund.workshop().send(value)) {
            throw;
        }
        checkInvariants();
        return true;
    }

    
    
    function changeBaseValue(uint valueInWei)
        external
        noEther
        onlyOwner
        returns (bool)
    {
        baseValue = valueInWei;
        return true;
    }

    
    function twoYearsPassed()
        constant
        external
        noEther
        returns (bool)
    {
        return now - startDate >= TOKEN_LOCKING_PERIOD;
    }

    
    function campaignEndedSuccessfully()
        constant
        external
        noEther
        returns (bool)
    {
        if (stage == Stages.CrowdfundingEndedAndGoalReached) {
            return true;
        }
        return false;
    }

    
    
    
    function updateStage()
        external
        timedTransitions
        noEther
        returns (Stages)
    {
        return stage;
    }

    
    
    
    function setup(address singularDTVFundAddress, address singularDTVTokenAddress)
        external
        onlyOwner
        noEther
        returns (bool)
    {
        if (address(singularDTVFund) == 0 && address(singularDTVToken) == 0) {
            singularDTVFund = SingularDTVFund(singularDTVFundAddress);
            singularDTVToken = SingularDTVToken(singularDTVTokenAddress);
            return true;
        }
        return false;
    }

    
    function SingularDTVCrowdfunding() noEther {
        
        owner = msg.sender;
        
        startDate = now;
    }

    
    function () {
        throw;
    }
}

External Links