function upgradeContract(string name, address successor) external onlySuperAdmins unlessUpgraded returns (bytes32) {
    bytes32 hash = keccak256(abi.encodePacked(name));
    require(successor != 0x0);
    require(contractForHash[hash] != 0x0);

    address predecessor = contractForHash[hash];
    require(freezable(predecessor).frozenToken());

    contractForHash[hash] = successor;

    uint256 remainingContractBalance;
    
    
    if (hash == keccak256("cst")) {
      remainingContractBalance = ERC20(predecessor).balanceOf(predecessor);
    }

    upgradeable(predecessor).upgradeTo(successor,
                                       remainingContractBalance);
    upgradeable(successor).upgradedFrom(predecessor);

    address successorStorageAddress = storageForHash[IStorable(successor).getStorageNameHash()];
    address successorLedgerAddress = storageForHash[IStorable(successor).getLedgerNameHash()];
    address predecessorStorageAddress = storageForHash[IStorable(predecessor).getStorageNameHash()];
    address predecessorLedgerAddress = storageForHash[IStorable(predecessor).getLedgerNameHash()];

    if (successorStorageAddress != 0x0) {
      ExternalStorage(successorStorageAddress).addAdmin(successor);
    }
    if (predecessorStorageAddress != 0x0) {
      ExternalStorage(predecessorStorageAddress).removeAdmin(predecessor);
    }

    if (successorLedgerAddress != 0x0) {
      CstLedger(successorLedgerAddress).addAdmin(successor);
    }
    if (predecessorLedgerAddress != 0x0) {
      CstLedger(predecessorLedgerAddress).removeAdmin(predecessor);
    }

    configurable(successor).configureFromStorage();

    if (hashForNamehash[BARE_DOMAIN_NAMEHASH] == hash) {
      emit AddrChanged(BARE_DOMAIN_NAMEHASH, successor);
    }
    if (namehashForHash[hash] != 0x0 && namehashForHash[hash] != BARE_DOMAIN_NAMEHASH) {
      emit AddrChanged(namehashForHash[hash], successor);
    }

    emit ContractUpgraded(successor, predecessor, name, namehashForHash[hash]);
    return hash;
  }