6

I understand that transactions to contracts require more gas than normal transactions. But what exactly is happening that requires more gas?

  • I don't really understand if you would like to know the difference between the DAO contract and some other simpler contracts or the difference between TX to contract and TX to account. – Nicolas Massart May 16 '16 at 17:20
  • I think the best answer here would be a high level, accessible summary using The DAO contract as an example as to why it requires more gas than a standard transaction. I tried to answer this elsewhere and failed as it got too technical and unclear. – tayvano May 16 '16 at 17:25
  • It's a challenging one I admit. – Nicolas Massart May 16 '16 at 17:53
3

Standard Vs The DAO Transaction Gas Cost

A standard transaction involving a simple value transfer cost 21,000 gas (see etherscan.io - tx 0x1f37ffec78f92a9b83d0602ae56ac2e92f1e02a85dfbb32d7ee97d554dcfef13.

A transaction sending ethers to purchase DAO tokens cost 50,960 gas (see etherscan.io - tx 0xe3082f5b22fe62309e792669a391d6805146b6943d3594c3c4e99f57979aeb21.


The DAO Source Code

Following are the snippets of code executed when a transaction sends ethers to purchase DAO tokens (from etherscan.io - The DAO - Source Code). The transaction does not have any additional data and so ends up calling the default function DAO.():

function () returns (bool success) {
    if (now < closingTime + creationGracePeriod && msg.sender != address(extraBalance))
        return createTokenProxy(msg.sender);
    else
        return receiveEther();
}

The first branch of the if (now < closingTime ...) statement evaluates to true and so the VM will call TokenCreation.createTokenProxy(...) listed below:

function createTokenProxy(address _tokenHolder) returns (bool success) {
    if (now < closingTime && msg.value > 0
        && (privateCreation == 0 || privateCreation == msg.sender)) {

        uint token = (msg.value * 20) / divisor();
        extraBalance.call.value(msg.value - token)();
        balances[_tokenHolder] += token;
        totalSupply += token;
        weiGiven[_tokenHolder] += msg.value;
        CreatedToken(_tokenHolder, token);
        if (totalSupply >= minTokensToCreate && !isFueled) {
            isFueled = true;
            FuelingToDate(totalSupply);
        }
        return true;
    }
    throw;
}

From live.ether.camp - tx 0xe3082f5b22fe62309e792669a391d6805146b6943d3594c3c4e99f57979aeb21:

closingTime       = 1464426000
isFueled          = true
minTokensToCreate = 50000000000000000000000
privateCreation   = 0000000000000000000000000000000000000000

The if (now < closingTime ...) code will evaluate to true and the VM will therefore execute the following code within the TokenCreation.createTokenProxy(...) function:

uint token = (msg.value * 20) / divisor();
extraBalance.call.value(msg.value - token)();
balances[_tokenHolder] += token;
totalSupply += token;
weiGiven[_tokenHolder] += msg.value;
CreatedToken(_tokenHolder, token);
return true;

One of the calls in the code above is to the TokenCreation.divisor() function with the following code where the first part of the if statement will evaluate to false and the second part else if (closingTime...) will evaluate to true:

function divisor() constant returns (uint divisor) {
    // The number of (base unit) tokens per wei is calculated
    // as `msg.value` * 20 / `divisor`
    // The fueling period starts with a 1:1 ratio
    if (closingTime - 2 weeks > now) {
        return 20;
    // Followed by 10 days with a daily creation rate increase of 5%
    } else if (closingTime - 4 days > now) {
        return (20 + (now - (closingTime - 2 weeks)) / (1 days));
    // The last 4 days there is a constant creation rate ratio of 1:1.5
    } else {
        return 30;
    }
}


Simplified Code

Here is a simplified version of The DAO contract containing the code that is executed when ethers are sent to The DAO to purchase tokens. Note that I've simplified extraBalance by using SimplifiedManagedAccount instead of ManagedAccount :

contract SimplifiedTheDAOCode {
    uint public closingTime = 1464426000;
    address public privateCreation;
    SimplifiedManagedAccount public extraBalance;
    mapping (address => uint256) balances;
    uint256 public totalSupply;
    mapping (address => uint256) weiGiven;
    uint public minTokensToCreate = 50000000000000000000000;
    bool public isFueled = true;
    event FuelingToDate(uint value);
    uint constant creationGracePeriod = 40 days;

    function createTokenProxy(address _tokenHolder) returns (bool success) {
        if (now < closingTime && msg.value > 0
            && (privateCreation == 0 || privateCreation == msg.sender)) {

            uint token = (msg.value * 20) / divisor();
            extraBalance.call.value(msg.value - token)();
            balances[_tokenHolder] += token;
            totalSupply += token;
            weiGiven[_tokenHolder] += msg.value;
            CreatedToken(_tokenHolder, token);
            if (totalSupply >= minTokensToCreate && !isFueled) {
                isFueled = true;
                FuelingToDate(totalSupply);
            }
            return true;
        }
        throw;
    }

    function divisor() constant returns (uint divisor) {
        // The number of (base unit) tokens per wei is calculated
        // as `msg.value` * 20 / `divisor`
        // The fueling period starts with a 1:1 ratio
        if (closingTime - 2 weeks > now) {
            return 20;
        // Followed by 10 days with a daily creation rate increase of 5%
        } else if (closingTime - 4 days > now) {
            return (20 + (now - (closingTime - 2 weeks)) / (1 days));
        // The last 4 days there is a constant creation rate ratio of 1:1.5
        } else {
            return 30;
        }
    }

    function () returns (bool success) {
        if (now < closingTime + creationGracePeriod && msg.sender != address(extraBalance))
            return createTokenProxy(msg.sender);
        else
            return receiveEther();
    }

    function receiveEther() returns (bool) {
        return true;
    }

    event CreatedToken(address indexed to, uint amount);
}

The call to the extraBalance ManagedAccount seems to be an area where a transaction can run out of gas (eg tx 0x16b92a84769cc44db03d993b3eee2f8b2c85581c9ac3114466d1c1c523426abf).

Here is a simplified version of the ManagedAccount code showing the relevant code that is called when a transaction sends ethers to The DAO to purchase tokens:

contract SimplifiedManagedAccount {
    // If true, only the owner of the account can receive ether from it
    bool public payOwnerOnly = true;
    // The sum of ether (in wei) which has been sent to this contract
    uint public accumulatedInput;

    function() {
        accumulatedInput += msg.value;
    }
}

The gas estimate for calling the default () function above is 20,097 (from the Solidity realtime compiler and runtime).


Gas Estimate

Here is the gas estimate for the simplified The DAO code above from the Solidity realtime compiler and runtime that does not give any estimates for the functions, perhaps because of the conditional statements within the functions:

Creation: 60464 + 315800
External:
  : unknown
  closingTime(): unknown
  createTokenProxy(address): unknown
  divisor(): unknown
  extraBalance(): unknown
  isFueled(): unknown
  minTokensToCreate(): unknown
  privateCreation(): unknown
  receiveEther(): unknown
  totalSupply(): unknown
Internal:
  (): unknown

I tried to post the optcodes for the simplified The DAO but it was too long for this answer. The best estimate for the gas is to look at a few transactions sending ethers to The DAO for token purchase, which is 50,960 units of gas.

-4

The DAO contract is described in white paper link.

  • 1
    Reading the white paper is the best way to have the correct information but admit that it's not easy for anyone. So could you try to sum up the most gas hungry parts of the dao contract to explain why it requires more gas than a small contract. That's what I would have done if I tried to answer this question... – Nicolas Massart May 16 '16 at 17:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.