Inspecting Ethereum Smart Contracts #2: Digital Developers Fund

Screenshot 2017-07-10 11.25.47.png

Hello Everyone

This will be a series of me dissecting Ethereum based ICO smart contracts. Hopefully, we can all learn a bit or two from doing this and at the same inspecting if there are any nefarious code. In my previous episode, I dissected the EOS smart contract. You can read more about it here: @gaman/eos-crowdsale-contract-source-code-inspection-and-analysis-line-by-line

If you are interested in the latest blockchain development, Ethereum smart contract is the hottest thing right now. Ethereum is now half of the market cap of bitcoin. Follow this series to learn more about programming Ethereum smart contract or if you are investing in this particular ICO and want to know more about it.

What is Digital Developers Fund


In this episode, we will be dissecting the Digital Developers Fund ICO. The ICO will be starting this July 10th, 12:00 GMT+2. To give a little background on Digital Developers Fund:

DDF offers investors an opportunity to participate in high growth digital assets such as domain names and crypto currency. The roots of the DDF go back to 2010, when DDF was incorporated as “Domain Developers Fund” in the Cayman Island. DDF holds over 1,350 premium domains such as Swords.com, Audits.com, Exhibitions.com or PR.uk. In 2017, the fund rebrands to “Digital Developers Fund” and raises funds via an ICO to expand its asset allocation.

According to their ICO page, the below are the details of the token. We will be inspecting the source code if it really is so:

Smart Contract Source: https://github.com/digitaldevelopersfund/ddf
Token Supply: 247,500,000 DDF
2,500,000 Internal Fund
Exchange Rate 1,000 DDF = 1 ETH
Unused Tokens: Burnt After ICO
Accepted Currencies: ETH

How It Works

  1. ICO participants send funds to an Ethereum Smart Contract above. DO NOT SEND FUNDS BEFORE THE ICO HAS STARTED.
  2. The Smart Contract mints DDF tokens instantly and sends them directly to the user’s wallet.
  3. Once the ICO has been completed (total duration or maximum tokens issued) the Ethereum funds are transferred to the fund’s wallet.

Initializing the Smart Contract

So, let us begin.

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./SafeMath.sol";
import "./splitter.sol";

// note introduced onlyPayloadSize in StandardToken.sol to protect against short address attacks

So, the program started off stating to the compiler that this code will be for solidity version 0.4.11 and up (but below 0.5). No functions will break even if it is compiled in newer solidity versions as long as it is below version 0.5. This is standard practise among the smart contracts.

They will be importing 3 libraries: StandardToken, SafeMath and splitter. The StandardToken would make the contract ERC20 compliant. Basically, being ERC20 compliant means that the contract must in minimum define a list of functions that the contract must have. This is beneficial as it will allow exchanges to easily list the coin.

SafeMath library is to make sure that the addition, subtraction and multiplication are correct. For example: in solidity, when you add two variables and the resulting value is more than the variable can hold, it will start again from 0. So the SafeMath library ensures the resulting value is greater than the addends.

The splitter library is to facilitate the extraction of the token holders at a particular period of time. Basically, this will be used when giving out the quarterly earnings.

contract DDFToken is StandardToken, SafeMath {

    // metadata
    string public constant name = "Digital Developers Fund Token";
    string public constant symbol = "DDF";
    uint256 public constant decimals = 18;
    string public version = "1.0";

    // contracts
    address public ethFundDeposit;      // deposit address for ETH for Domain Development Fund
    address public ddftFundDeposit;     // deposit address for Domain Development Fund reserve
    address public splitter;          // DA 8/6/2017 - splitter contract

    // crowdsale parameters
    bool public isFinalized;              // switched to true in operational state
    uint256 public fundingStartTime;
    uint256 public fundingEndTime;
    uint256 public constant ddftFund = 25 * (10**5) * 10**decimals;   // 2.5m DDFT reserved for DDF use
    uint256 public constant tokenExchangeRate = 1000;               // 1000 DDFT tokens per 1 ETH
    uint256 public constant tokenCreationCap =  250 * (10**6) * 10**decimals;
    uint256 public constant tokenCreationMin =  1 * (10**6) * 10**decimals;


    // events
    event LogRefund(address indexed _to, uint256 _value);
    event CreateDDFT(address indexed _to, uint256 _value);

From here, we see that they will be reserving 2.5 million token in the ddftFund variable. The max token cap will be 250 million tokens and the minimum will be 1 million token.

    // constructor
    function DDFToken(
        address _ethFundDeposit,
        address _ddftFundDeposit,
        address _splitter, // DA 8/6/2017
        uint256 _fundingStartTime,
        uint256 duration)
    {
      isFinalized = false;                   //controls pre through crowdsale state
      ethFundDeposit = _ethFundDeposit;
      ddftFundDeposit = _ddftFundDeposit;
      splitter =  _splitter ;                  // DA 8/6/2017
      fundingStartTime = _fundingStartTime;
      fundingEndTime = fundingStartTime + duration * 1 days;
      totalSupply = ddftFund;
      balances[ddftFundDeposit] = ddftFund;    // Deposit DDF share
      CreateDDFT(ddftFundDeposit, ddftFund);  // logs DDF fund
    }

The constructor starts with initializing the isFinalize variable to false. Basically, the finalize() function will be called once the ICO is over. This will set the isFinalize to true and transfer the fund to the ethFundDeposit address.

The ethFundDeposit is the address with which all the collected eth will be sent to. The ddftFundDeposit is the address where the reserve token of 2.5 million will be sent to.

My problem about this is that if the ICO only receives eth equivalent to 2.5 million DDF tokens, the developer's fund will be owning the other 50% of tokens.

Sending Tokens

    function () payable {           // DA 8/6/2017 prefer to use fallback function
      createTokens(msg.value);
    }

    /// @dev Accepts ether and creates new DDFT tokens.
    function createTokens(uint256 _value)  internal {
      if (isFinalized) throw;
      if (now < fundingStartTime) throw;
      if (now > fundingEndTime) throw;
      if (msg.value == 0) throw;

      uint256 tokens = safeMult(_value, tokenExchangeRate); // check that we're not over totals
      uint256 checkedSupply = safeAdd(totalSupply, tokens);

      // DA 8/6/2017 to fairly allocate the last few tokens
      if (tokenCreationCap < checkedSupply) {
        if (tokenCreationCap <= totalSupply) throw;  // CAP reached no more please
        uint256 tokensToAllocate = safeSubtract(tokenCreationCap,totalSupply);
        uint256 tokensToRefund   = safeSubtract(tokens,tokensToAllocate);
        totalSupply = tokenCreationCap;
        balances[msg.sender] += tokensToAllocate;  // safeAdd not needed; bad semantics to use here
        uint256 etherToRefund = tokensToRefund / tokenExchangeRate;
        msg.sender.transfer(etherToRefund);
        CreateDDFT(msg.sender, tokensToAllocate);  // logs token creation
        LogRefund(msg.sender,etherToRefund);
        splitterContract(splitter).update(msg.sender,balances[msg.sender]);
        return;
      }
      // DA 8/6/2017 end of fair allocation code
      totalSupply = checkedSupply;
      balances[msg.sender] += tokens;  // safeAdd not needed; bad semantics to use here
      CreateDDFT(msg.sender, tokens);  // logs token creation
      splitterContract(splitter).update(msg.sender,balances[msg.sender]);
    }

The function without the function name will be the default function when it receives eth. This will call the createTokens function. First it makes sure that the ICO is not over. Then, it calculates if the contribution will result in surpassing the hard cap. If it will surpass the hard cap, it needs to refund the excess eth. So, a whale can send in a big amount and not be afraid of it going over the hard cap and have all his money refunded. The excess eth will simply be the only one refunded.

If it doesn't surpass the hard cap, the token is simply allocated and the total supply is increased.

Ending the ICO


    /// @dev Ends the funding period and sends the ETH home
    function finalize() external {
      if (isFinalized) throw;
      if (msg.sender != ethFundDeposit) throw; // locks finalize to the ultimate ETH owner
      if(totalSupply < tokenCreationMin + ddftFund) throw;      // have to sell minimum to move to operational
      if(now <= fundingEndTime && totalSupply != tokenCreationCap) throw;
      // move to operational
      isFinalized = true;
      // DA 8/6/2017 change send/throw to transfer
      ethFundDeposit.transfer(this.balance);  // send the eth to DDF
    }

The contract owner calls the finalize() function at the end of the ICO. It checks if it at least 1 million token have been sold (this is aside from the developer's fund). Then, it closes the ICO by setting the isFinalize to true and transferring all the eth to the address ethFundDeposit, which is set during contract initialization.

    /// @dev Allows contributors to recover their ether in the case of a failed funding campaign.
    function refund() external {
      if(isFinalized) throw;                       // prevents refund if operational
      if (now <= fundingEndTime) throw; // prevents refund until sale period is over
      if(totalSupply >= tokenCreationMin + ddftFund) throw;  // no refunds if we sold enough
      if(msg.sender == ddftFundDeposit) throw;    // DDF not entitled to a refund
      uint256 ddftVal = balances[msg.sender];
      if (ddftVal == 0) throw;
      balances[msg.sender] = 0;
      totalSupply = safeSubtract(totalSupply, ddftVal); // extra safe
      uint256 ethVal = ddftVal / tokenExchangeRate;     // should be safe; previous throws covers edges
      LogRefund(msg.sender, ethVal);               // log it 
      // DA 8/6/2017 change send/throw to transfer
      msg.sender.transfer(ethVal);                 // if you're using a contract; make sure it works with .send gas limits
    }

If the ICO period ends and not enough tokens are sold, anybody can call the refund function for their contribution to be refunded to them. To note is that the developer's fund of 2.5 million token cannot be refunded.

    // DA 8/6/2017
    /// @dev Updates splitter contract with ownership changes
    function transfer(address _to, uint _value) returns (bool success)  {
      success = super.transfer(_to,_value);
      splitterContract sc = splitterContract(splitter);
      sc.update(msg.sender,balances[msg.sender]);
      sc.update(_to,balances[_to]);
      return;
    }

}

The last function basically records if someone transfers the ownership of their token to another person. This recording of who owns the token facilitates the giving of quarterly earnings later on.

Conclusion

My main problem is when only 1 million tokens are sold, the developer fund will be having 2.5 million tokens. With the state of the ICO craze, maybe this is highly unlikely.

H2
H3
H4
3 columns
2 columns
1 column
6 Comments