Golem Crowdfunding Funded To 820,000 ETH Cap

Original post Oct 24 2016. Updated Nov 12 2016 with deployed contract and source code, and crowdfunding statistics.
This is not advise as to whether Golem is a good investment or not. This is just a quick look at what Golem is offering and how they are doing it.

Golem is going create the first decentralized global market for computing power combined with flexible tools for developers to distribute and monetize their software.

The first transaction 0x0a75…be15 in block 2,607,801 at Nov-11-2016 03:12:28 PM +UTC.
The last valid transaction 0xa8d9…2ea7 was for 0.85579574 ETH in block 2,607,934 at Nov-11-2016 03:41:36 PM +UTC.
The crowdfunding was finalized in transaction 0x1bdf…e681 in block 2,607,939 at Nov-11-2016 03:43:05 PM +UTC.
The crowdfunding raised 820,000 ETH (~ USD 8,620,660) within 30 minutes and 37 seconds from commencement, with 522 addresses and 647 events.
From https://etherscan.io/token/Golem:
golemtokendistribution_20161112_032131

Links:


Deployed Source Code

Following is the source code deployed to the Ethereum blockchain at address 0xa74476443119a942de498590fe1f2454d7d4ac0d.

The deployed contract has the following parameters:

To participate in the crowdfunding, you will have to send the data field “0xefc81a8c” with your ether transaction to 0xa74476443119a942de498590fe1f2454d7d4ac0d. This calls the create() method (web3.sha3("create()").substring(0,10)="0xefc81a8c"). Gas needs to be set to at least 70,000. Crowdfunding starts ~ 15:00 Nov 11 2016 UTC or 02:00 Nov 12 2016 AEST.

Note that the contract below does not implement the ERC20 functions transferFrom(...) and approve(...).

pragma solidity ^0.4.4;


/// @title Golem Network Token (GNT) - crowdfunding code for Golem Project
contract GolemNetworkToken {
    string public constant name = "Golem Network Token";
    string public constant symbol = "GNT";
    uint8 public constant decimals = 18;  // 18 decimal places, the same as ETH.

    uint256 public constant tokenCreationRate = 1000;

    // The funding cap in weis.
    uint256 public constant tokenCreationCap = 820000 ether * tokenCreationRate;
    uint256 public constant tokenCreationMin = 150000 ether * tokenCreationRate;

    uint256 public fundingStartBlock;
    uint256 public fundingEndBlock;

    // The flag indicates if the GNT contract is in Funding state.
    bool public funding = true;

    // Receives ETH and its own GNT endowment.
    address public golemFactory;

    // Has control over token migration to next version of token.
    address public migrationMaster;

    GNTAllocation lockedAllocation;

    // The current total token supply.
    uint256 totalTokens;

    mapping (address => uint256) balances;

    address public migrationAgent;
    uint256 public totalMigrated;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Migrate(address indexed _from, address indexed _to, uint256 _value);
    event Refund(address indexed _from, uint256 _value);

    function GolemNetworkToken(address _golemFactory,
                               address _migrationMaster,
                               uint256 _fundingStartBlock,
                               uint256 _fundingEndBlock) {

        if (_golemFactory == 0) throw;
        if (_migrationMaster == 0) throw;
        if (_fundingStartBlock <= block.number) throw;
        if (_fundingEndBlock   <= _fundingStartBlock) throw;

        lockedAllocation = new GNTAllocation(_golemFactory);
        migrationMaster = _migrationMaster;
        golemFactory = _golemFactory;
        fundingStartBlock = _fundingStartBlock;
        fundingEndBlock = _fundingEndBlock;
    }

    /// @notice Transfer `_value` GNT tokens from sender's account
    /// `msg.sender` to provided account address `_to`.
    /// @notice This function is disabled during the funding.
    /// @dev Required state: Operational
    /// @param _to The address of the tokens recipient
    /// @param _value The amount of token to be transferred
    /// @return Whether the transfer was successful or not
    function transfer(address _to, uint256 _value) returns (bool) {
        // Abort if not in Operational state.
        if (funding) throw;

        var senderBalance = balances[msg.sender];
        if (senderBalance >= _value && _value > 0) {
            senderBalance -= _value;
            balances[msg.sender] = senderBalance;
            balances[_to] += _value;
            Transfer(msg.sender, _to, _value);
            return true;
        }
        return false;
    }

    function totalSupply() external constant returns (uint256) {
        return totalTokens;
    }

    function balanceOf(address _owner) external constant returns (uint256) {
        return balances[_owner];
    }

    // Token migration support:

    /// @notice Migrate tokens to the new token contract.
    /// @dev Required state: Operational Migration
    /// @param _value The amount of token to be migrated
    function migrate(uint256 _value) external {
        // Abort if not in Operational Migration state.
        if (funding) throw;
        if (migrationAgent == 0) throw;

        // Validate input value.
        if (_value == 0) throw;
        if (_value > balances[msg.sender]) throw;

        balances[msg.sender] -= _value;
        totalTokens -= _value;
        totalMigrated += _value;
        MigrationAgent(migrationAgent).migrateFrom(msg.sender, _value);
        Migrate(msg.sender, migrationAgent, _value);
    }

    /// @notice Set address of migration target contract and enable migration
	/// process.
    /// @dev Required state: Operational Normal
    /// @dev State transition: -> Operational Migration
    /// @param _agent The address of the MigrationAgent contract
    function setMigrationAgent(address _agent) external {
        // Abort if not in Operational Normal state.
        if (funding) throw;
        if (migrationAgent != 0) throw;
        if (msg.sender != migrationMaster) throw;
        migrationAgent = _agent;
    }

    function setMigrationMaster(address _master) external {
        if (msg.sender != migrationMaster) throw;
        if (_master == 0) throw;
        migrationMaster = _master;
    }

    // Crowdfunding:

    /// @notice Create tokens when funding is active.
    /// @dev Required state: Funding Active
    /// @dev State transition: -> Funding Success (only if cap reached)
    function create() payable external {
        // Abort if not in Funding Active state.
        // The checks are split (instead of using or operator) because it is
        // cheaper this way.
        if (!funding) throw;
        if (block.number < fundingStartBlock) throw;
        if (block.number > fundingEndBlock) throw;

        // Do not allow creating 0 or more than the cap tokens.
        if (msg.value == 0) throw;
        if (msg.value > (tokenCreationCap - totalTokens) / tokenCreationRate)
            throw;

        var numTokens = msg.value * tokenCreationRate;
        totalTokens += numTokens;

        // Assign new tokens to the sender
        balances[msg.sender] += numTokens;

        // Log token creation event
        Transfer(0, msg.sender, numTokens);
    }

    /// @notice Finalize crowdfunding
    /// @dev If cap was reached or crowdfunding has ended then:
    /// create GNT for the Golem Factory and developer,
    /// transfer ETH to the Golem Factory address.
    /// @dev Required state: Funding Success
    /// @dev State transition: -> Operational Normal
    function finalize() external {
        // Abort if not in Funding Success state.
        if (!funding) throw;
        if ((block.number <= fundingEndBlock ||
             totalTokens < tokenCreationMin) &&
            totalTokens < tokenCreationCap) throw;

        // Switch to Operational state. This is the only place this can happen.
        funding = false;

        // Create additional GNT for the Golem Factory and developers as
        // the 18% of total number of tokens.
        // All additional tokens are transfered to the account controller by
        // GNTAllocation contract which will not allow using them for 6 months.
        uint256 percentOfTotal = 18;
        uint256 additionalTokens =
            totalTokens * percentOfTotal / (100 - percentOfTotal);
        totalTokens += additionalTokens;
        balances[lockedAllocation] += additionalTokens;
        Transfer(0, lockedAllocation, additionalTokens);

        // Transfer ETH to the Golem Factory address.
        if (!golemFactory.send(this.balance)) throw;
    }

    /// @notice Get back the ether sent during the funding in case the funding
    /// has not reached the minimum level.
    /// @dev Required state: Funding Failure
    function refund() external {
        // Abort if not in Funding Failure state.
        if (!funding) throw;
        if (block.number <= fundingEndBlock) throw;
        if (totalTokens >= tokenCreationMin) throw;

        var gntValue = balances[msg.sender];
        if (gntValue == 0) throw;
        balances[msg.sender] = 0;
        totalTokens -= gntValue;

        var ethValue = gntValue / tokenCreationRate;
        Refund(msg.sender, ethValue);
        if (!msg.sender.send(ethValue)) throw;
    }
}


/// @title Migration Agent interface
contract MigrationAgent {
    function migrateFrom(address _from, uint256 _value);
}


/// @title GNT Allocation - Time-locked vault of tokens allocated
/// to developers and Golem Factory
contract GNTAllocation {
    // Total number of allocations to distribute additional tokens among
    // developers and the Golem Factory. The Golem Factory has right to 20000
    // allocations, developers to 10000 allocations, divides among individual
    // developers by numbers specified in  `allocations` table.
    uint256 constant totalAllocations = 30000;

    // Addresses of developer and the Golem Factory to allocations mapping.
    mapping (address => uint256) allocations;

    GolemNetworkToken gnt;
    uint256 unlockedAt;

    uint256 tokensCreated = 0;

    function GNTAllocation(address _golemFactory) internal {
        gnt = GolemNetworkToken(msg.sender);
        unlockedAt = now + 6 * 30 days;

        // For the Golem Factory:
        allocations[_golemFactory] = 20000; // 12/18 pp of 30000 allocations.

        // For developers:
        allocations[0x9d3F257827B17161a098d380822fa2614FF540c8] = 2500; // 25.0% of developers' allocations (10000).
        allocations[0xd7406E50b73972Fa4aa533a881af68B623Ba3F66] =  730; //  7.3% of developers' allocations.
        allocations[0xd15356D05A7990dE7eC94304B0fD538e550c09C0] =  730;
        allocations[0x3971D17B62b825b151760E2451F818BfB64489A7] =  730;
        allocations[0x95e337d09f1bc67681b1cab7ed1125ea2bae5ca8] =  730;
        allocations[0x0025C58dB686b8CEce05CB8c50C1858b63Aa396E] =  730;
        allocations[0xB127FC62dE6ca30aAc9D551591daEDdeBB2eFD7A] =  630; //  6.3% of developers' allocations.
        allocations[0x21AF2E2c240a71E9fB84e90d71c2B2AddE0D0e81] =  630;
        allocations[0x682AA1C3b3E102ACB9c97B861d595F9fbfF0f1B8] =  630;
        allocations[0x6edd429c77803606cBd6Bb501CC701a6CAD6be01] =  630;
        allocations[0x5E455624372FE11b39464e93d41D1F6578c3D9f6] =  310; //  3.1% of developers' allocations.
        allocations[0xB7c7EaD515Ca275d53e30B39D8EBEdb3F19dA244] =  138; //  1.38% of developers' allocations.
        allocations[0xD513b1c3fe31F3Fe0b1E42aa8F55e903F19f1730] =  135; //  1.35% of developers' allocations.
        allocations[0x70cac7f8E404EEFce6526823452e428b5Ab09b00] =  100; //  1.0% of developers' allocations.
        allocations[0xe0d5861e7be0fac6c85ecde6e8bf76b046a96149] =  100;
        allocations[0x17488694D2feE4377Ec718836bb9d4910E81D9Cf] =  100;
        allocations[0xb481372086dEc3ca2FCCD3EB2f462c9C893Ef3C5] =  100;
        allocations[0xFB6D91E69CD7990651f26a3aa9f8d5a89159fC92] =   70; //  0.7% of developers' allocations.
        allocations[0xE2ABdAe2980a1447F445cb962f9c0bef1B63EE13] =   70;
        allocations[0x729A5c0232712caAf365fDd03c39cb361Bd41b1C] =   70;
        allocations[0x12FBD8fef4903f62e30dD79AC7F439F573E02697] =   70;
        allocations[0x657013005e5cFAF76f75d03b465cE085d402469A] =   42; //  0.42% of developers' allocations.
        allocations[0xD0AF9f75EA618163944585bF56aCA98204d0AB66] =   25; //  0.25% of developers' allocations.
    }

    /// @notice Allow developer to unlock allocated tokens by transferring them
    /// from GNTAllocation to developer's address.
    function unlock() external {
        if (now < unlockedAt) throw;

        // During first unlock attempt fetch total number of locked tokens.
        if (tokensCreated == 0)
            tokensCreated = gnt.balanceOf(this);

        var allocation = allocations[msg.sender];
        allocations[msg.sender] = 0;
        var toTransfer = tokensCreated * allocation / totalAllocations;

        // Will fail if allocation (and therefore toTransfer) is 0.
        if (!gnt.transfer(msg.sender, toTransfer)) throw;
    }
}


Source code from Github

Crowdfunding source code from https://github.com/imapp-pl/golem-crowdfunding at Oct 28 2016 14:57:36 UTC. Still to be audited.

Token.sol

pragma solidity ^0.4.1;

contract MigrationAgent {
    function migrateFrom(address _from, uint256 _value);
}

contract GolemNetworkToken {
    string public constant name = "Golem Network Token";
    string public constant symbol = "GNT";
    uint8 public constant decimals = 18;  // 18 decimal places, the same as ETH.

    uint256 public constant tokenCreationRate = 1000;

    // The funding cap in weis.
    uint256 public constant tokenCreationCap = 820000 ether * tokenCreationRate;
    uint256 public constant tokenCreationMin = 150000 ether * tokenCreationRate;

    uint256 fundingStartBlock;
    uint256 fundingEndBlock;

    // The flag indicates if the GNT contract is in "funding" mode.
    bool fundingMode = true;

    // Receives ETH and its own GNT endowment.
    address public golemFactory;

    // Has control over token migration to next version of token.
    address public migrationMaster;


    // The currect total token supply.
    uint256 totalTokens;

    mapping (address => uint256) balances;

    address public migrationAgent;
    uint256 public totalMigrated;

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Migrate(address indexed _from, address indexed _to, uint256 _value);
    event Refund(address indexed _from, uint256 _value);

    // Checks if in Funding Active state. Aborts transaction otherwise.
    modifier inFundingActive {
        if (!fundingMode) throw;
        // FundingActive: b ≥ Start and b ≤ End and t < Max
        if (block.number < fundingStartBlock ||
            block.number > fundingEndBlock ||
            totalTokens >= tokenCreationCap) throw;
        _;
    }

    // Checks if in Funding Failure state. Aborts transaction otherwise.
    modifier inFundingFailure {
        if (!fundingMode) throw;
        // FundingFailure: b > End and t < Min
        if (block.number <= fundingEndBlock ||
            totalTokens >= tokenCreationMin) throw;
        _;
    }

    // Checks if in Funding Success state. Aborts transaction otherwise.
    modifier inFundingSuccess {
        if (!fundingMode) throw;
        // FundingSuccess: (b > End and t ≥ Min) or t ≥ Max
        if ((block.number <= fundingEndBlock ||
             totalTokens < tokenCreationMin) &&
            totalTokens < tokenCreationCap) throw;
        _;
    }

    // Checks if in Operational state. Aborts transaction otherwise.
    modifier inOperational {
        if (fundingMode) throw;
        _;
    }

    // Checks if in Operational Normal state. Aborts transaction otherwise.
    modifier inNormal {
        if (fundingMode) throw;
        if (migrationAgent != 0) throw;
        _;
    }

    // Checks if in Operational Migration state. Aborts transaction otherwise.
    modifier inMigration {
        if (fundingMode) throw;
        if (migrationAgent == 0) throw;
        _;
    }

    function GolemNetworkToken(address _golemFactory,
                               address _migrationMaster,
                               uint256 _fundingStartBlock,
                               uint256 _fundingEndBlock) {
        migrationMaster = _migrationMaster;
        golemFactory = _golemFactory;
        fundingStartBlock = _fundingStartBlock;
        fundingEndBlock = _fundingEndBlock;
    }

    function transfer(address _to, uint256 _value) inOperational returns (bool) {
        var senderBalance = balances[msg.sender];
        if (senderBalance >= _value && _value > 0) {
            senderBalance -= _value;
            balances[msg.sender] = senderBalance;
            balances[_to] += _value;
            Transfer(msg.sender, _to, _value);
            return true;
        }
        return false;
    }

    function totalSupply() external constant returns (uint256) {
        return totalTokens;
    }

    function balanceOf(address _owner) external constant returns (uint256) {
        return balances[_owner];
    }

    // Token migration support:

    function migrate(uint256 _value) inMigration external {
        if (_value == 0 || _value > balances[msg.sender]) throw;

        balances[msg.sender] -= _value;
        totalTokens -= _value;
        totalMigrated += _value;
        MigrationAgent(migrationAgent).migrateFrom(msg.sender, _value);
        Migrate(msg.sender, migrationAgent, _value);
    }

    function setMigrationAgent(address _agent) inNormal external {
        if (msg.sender != migrationMaster) throw;
        migrationAgent = _agent;
    }

    function setMigrationMaster(address _master) external {
        if (msg.sender != migrationMaster) throw;
        migrationMaster = _master;
    }

    // Crowdfunding:

    function fundingActive() constant external returns (bool) {
        // Copy of inFundingActive.
        if (!fundingMode) return false;

        // b ≥ Start and b ≤ End and t < Max
        if (block.number < fundingStartBlock ||
            block.number > fundingEndBlock ||
            totalTokens >= tokenCreationCap) return false;
        return true;
    }

    // Helper function to get number of tokens left during the funding.
    function numberOfTokensLeft() constant external returns (uint256) {
        if (!fundingMode) return 0;
        if (block.number > fundingEndBlock) return 0;
        return tokenCreationCap - totalTokens;
    }

    function finalized() constant external returns (bool) {
        return !fundingMode;
    }

    // Create tokens when funding is active
    // Update state when funding period lapses and/or min/max funding occurs
    function() payable inFundingActive external {
        if (msg.value == 0) throw;

        // Do not create more than cap
        var numTokens = msg.value * tokenCreationRate;
        totalTokens += numTokens;
        if (totalTokens > tokenCreationCap) throw;

        // Assign new tokens to the sender
        balances[msg.sender] += numTokens;

        // Log token creation event
        Transfer(0, msg.sender, numTokens);
    }

    // If cap was reached or crowdfunding has ended then:
    // Transfer ETH to the golemFactory address
    // Create GNT for the golemFactory (representing the company)
    // Create GNT for the developers
    // Update GNT state (number of tokens)
    function finalize() inFundingSuccess external {
        // Switch to Operational state. This is the only place this can happen.
        fundingMode = false;

        // 1. Transfer ETH to the golemFactory address
        if (!golemFactory.send(this.balance)) throw;

        // Create additional GNT for the Factory (representing the company)
        // and developers.
        createAdditionalTokens();
    }

    function refund() inFundingFailure external {
        var gntValue = balances[msg.sender];
        if (gntValue == 0) throw;
        balances[msg.sender] = 0;
        totalTokens -= gntValue;

        var ethValue = gntValue / tokenCreationRate;
        if (!msg.sender.send(ethValue)) throw;
        Refund(msg.sender, ethValue);
    }

    struct Dev {
        address addr;
        uint share;
    }

    // Creates additional 12% of tokens for the Factory and 6% for developers.
    function createAdditionalTokens() internal {
        // TODO: SET before THE CROWDFUNDING!
        // Invariants:
        // dev0Percent + dev1Percent + dev2Percent + dev3Percent + dev4Percent + dev5Percent = 100
        // dev0Percent > 0 && dev1Percent > 0 && dev2Percent > 0 && dev3Percent > 0 && dev4Percent > 0 && dev5Percent > 0

        uint256 percentTokensGolemFactory = 12;
        uint256 percentTokensDevelopers = 6;

        // List of developer addresses and their shares.
        // The sum of shares is 10000.
        var devs = [
            Dev(0xde00, 2500)
            Dev(0xde01,  730)
            Dev(0xde02,  730)
            Dev(0xde03,  730)
            Dev(0xde04,  730)
            Dev(0xde05,  730)
            Dev(0xde06,  630)
            Dev(0xde07,  630)
            Dev(0xde08,  630)
            Dev(0xde09,  630)
            Dev(0xde10,  310)
            Dev(0xde11,  153)
            Dev(0xde12,  150)
            Dev(0xde13,  100)
            Dev(0xde14,  100)
            Dev(0xde15,  100)
            Dev(0xde16,   70)
            Dev(0xde17,   70)
            Dev(0xde18,   70)
            Dev(0xde19,   70)
            Dev(0xde20,   70)
            Dev(0xde21,   42)
            Dev(0xde22,   25)
        ];

        var numAdditionalTokens =
            totalTokens * (percentTokensGolemFactory + percentTokensDevelopers) /
            (100 - percentTokensGolemFactory - percentTokensDevelopers);
        var numTokensForDevs =
            numAdditionalTokens * percentTokensDevelopers /
            (percentTokensGolemFactory + percentTokensDevelopers);

        uint256 numTokensAssigned = 0;
        var len = devs.length;
        for (uint256 i = 0; i < len; ++i) {
            var dev = devs[i];
            var n = dev.share * numTokensForDevs / 10000;
            numTokensAssigned += n;
            balances[dev.addr] += n;
            // Log token creation event for developers
            Transfer(0, dev.addr, n);
        }

        var numTokensForGolemFactory = numAdditionalTokens - numTokensAssigned;
        balances[golemFactory] += numTokensForGolemFactory;
        // Log token creation event for golemFactory
        Transfer(0, golemFactory, numTokensForGolemFactory);

        // Update GNT state (number of tokens)
        totalTokens += numAdditionalTokens;
    }
}

ProxyAccount.sol

pragma solidity ^0.4.1;

import "./Token.sol";

contract TimeLockedGNTProxyAccount {

    address public owner;

    uint256 public availableAfter;
    GolemNetworkToken public gnt;

    // Modifiers

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

    modifier notLocked {
    	if (now < availableAfter) throw;
    	_;
    }

    // Creation and initialization

    function TimeLockedGNTProxyAccount(uint256 _availableAfter) {
        owner = msg.sender;
        availableAfter = _availableAfter;
    }

    function setGNTContract(address _gnt) ownerOnly external {
        gnt = GolemNetworkToken(_gnt);
    }

    // Token interface

    function transfer(address _to, uint256 _value) notLocked ownerOnly returns (bool success) {
        return gnt.transfer(_to, _value);
    }

    // Migration interface

    function migrate(uint256 _value) ownerOnly external {
        gnt.migrate(_value);
    }

    // Default function - do not allow any eth transfers to this contract

    function() payable {
        throw;
    }
}


contract TimeLockedGolemFactoryProxyAccount is TimeLockedGNTProxyAccount {

    // Creation and initialization

    function TimeLockedGolemFactoryProxyAccount(uint256 _availableAfter) TimeLockedGNTProxyAccount(_availableAfter) {
    }
    
    // Modifiers
    
    modifier gntOnly {
        if (msg.sender != address(gnt)) throw;
        _;
    }

    // Golem Factory privileged API

    function setMigrationMaster(address _migrationMaster) ownerOnly external {
        gnt.setMigrationMaster(_migrationMaster);
    }
    // Migration interface

    function setMigrationAgent(address _agent) ownerOnly external {
        gnt.setMigrationAgent(_agent);
    }

    // Default function - allow transfers from the GNT contract only  
    
    function() gntOnly payable {
    }

    // Withdraw - transfer ETH to to the Golem Factory
    
    function withdraw() ownerOnly {
        if (!owner.send(this.balance)) throw;
    }
}

ExampleMigration.sol

pragma solidity ^0.4.1;

import * as Source from "./Token.sol";

contract GNTTargetToken {

    address migrationAgent;

    // ERC20 variables
    uint256 totalTokens;
    mapping (address => uint256) balances;

    // ERC20 events
    event Transfer(address indexed _from, address indexed _to, uint256 _value);

    function GNTTargetToken(address _migrationAgent) {
        migrationAgent = _migrationAgent;
        //Additional constructor code gets here
    }

    // Migration related methods
    function createToken(address _target, uint256 _amount) {
        if (msg.sender != migrationAgent) throw;

        balances[_target] += _amount;
        totalTokens += _amount;

        Transfer(migrationAgent, _target, _amount);
    }

    function finalizeMigration() {
        if (msg.sender != migrationAgent) throw;

        migrationAgent = 0;
    }

    // ERC20 interface (implemented according to the requirements)
    function transfer(address _to, uint256 _value) returns (bool success) {
        if (balances[msg.sender] >= _value && _value > 0) {
            balances[msg.sender] -= _value;
            balances[_to] += _value;
            Transfer(msg.sender, _to, _value);
            return true;
        }
        return false;
    }

    function totalSupply() constant returns (uint256) {
        return totalTokens;
    }

    function balanceOf(address _owner) constant returns (uint256 balance) {
        return balances[_owner];
    }
}

//Test the whole process against this: https://www.kingoftheether.com/contract-safety-checklist.html
contract MigrationAgent {

    address owner;
    address gntSourceToken;
    address gntTargetToken;

    uint256 tokenSupply;

    function MigrationAgent(address _gntSourceToken) {
        owner = msg.sender;
        gntSourceToken = _gntSourceToken;

        if (!Source.GolemNetworkToken(gntSourceToken).finalized()) throw;

        tokenSupply = Source.GolemNetworkToken(gntSourceToken).totalSupply();
    }

    function safetyInvariantCheck(uint256 _value) private {
        if (gntTargetToken == 0) throw;
        if (Source.GolemNetworkToken(gntSourceToken).totalSupply() + GNTTargetToken(gntTargetToken).totalSupply() != tokenSupply - _value) throw;
    }

    function setTargetToken(address _gntTargetToken) {
        if (msg.sender != owner) throw;
        if (gntTargetToken != 0) throw; //Allow this change once only

        gntTargetToken = _gntTargetToken;
    }

    //Interface implementation
    function migrateFrom(address _from, uint256 _value) {
        if (msg.sender != gntSourceToken) throw;
        if (gntTargetToken == 0) throw;

        //Right here gntSourceToken has already been updated, but corresponding GNT have not been created in the gntTargetToken contract yet
        safetyInvariantCheck(_value);

        GNTTargetToken(gntTargetToken).createToken(_from, _value);

        //Right here totalSupply invariant must hold
        safetyInvariantCheck(0);
    }

    function finalizeMigration() {
        if (msg.sender != owner) throw;

        safetyInvariantCheck(0);

        //Additional, strict test
        //if (gntSourceToken.totalSupply() > 0) throw;

        GNTTargetToken(gntTargetToken).finalizeMigration();

        gntSourceToken = 0;
        gntTargetToken = 0;

        tokenSupply = 0;
    }

}

BadWallet.sol

pragma solidity ^0.4.1;

import * as Source from "./Token.sol";

contract BadWallet {
    uint16 extra_work = 0; // amount of work to be done when accepting payment
    uint16 public out_i;
    address wallet;
    
    function BadWallet() {
    }

    function get_out_i() returns (uint16 a) {
        return out_i;
    }

    function set_extra_work(uint16 _extra_work) {
        extra_work = _extra_work;
    }

    function get_extra_work() returns (uint16 a) {
        return extra_work;
    }
    
    function deploy_contract(address _golemFactory, uint256 _fundingStartBlock,
                             uint256 _fundingEndBlock)
        returns (address a) {

        wallet = new Source.GolemNetworkToken(_golemFactory, _golemFactory,
                                              _fundingStartBlock, _fundingEndBlock);
        return wallet;
    }
    
    function finalize(address _crowdfundingContract) {
        Source.GolemNetworkToken(_crowdfundingContract).finalize();
    }

    /* trap function which will burn gas, causing send to fail */
    function() payable {
        for (uint16 i = 1; i <= extra_work; i++) {
            out_i = i;
        }
    }
}
This entry was posted in Blog and tagged , , , , . Bookmark the permalink.