def test_create_project_happy_case(self, github_organization_mock: MagicMock, create_repo_mock: MagicMock, get_w3_mock: MagicMock, transaction_mock: MagicMock, redis_lock_mock: MagicMock): Project.create_mycro_dao(constants.MYCRO_ADDRESS, constants.MYCRO_PROJECT_SYMBOL, constants.DECIMALS) project = Project.objects.create(symbol=constants.PROJECT_SYMBOL, repo_name=constants.PROJECT_NAME, decimals=constants.DECIMALS, initial_balances=constants.CREATORS_BALANCES) # TODO make this more of an integration test by using constants.W3 like the create_asc tests w3 = get_w3_mock.return_value w3.eth.waitForTransactionReceipt.return_value = { 'contractAddress' : constants.PROJECT_ADDRESS, 'cumulativeGasUsed': 2, 'gasUsed' : 1, 'blockNumber' : 3, 'status' : 4} w3.eth.getBalance.return_value = int(10e18) tasks.create_project(project.pk) # two transactions, one for deploying and once for registering transaction_mock.objects.create.assert_any_call( wallet=self.wallet, hash=get_w3_mock.return_value.eth.sendRawTransaction.return_value.hex.return_value, value=ANY, chain_id=ANY, nonce=ANY, gas_limit=ANY, gas_price=ANY, data=ANY, to=ANY, contract_address=constants.PROJECT_ADDRESS, cumulative_gas_used=2, gas_used=1, block_number=3, status=4 ) # called twice for deployment and twice for registrations self.assertEqual(4, get_w3_mock.return_value.eth.waitForTransactionReceipt.call_count) self.assertEqual(4, w3.eth.sendRawTransaction.call_count) create_repo_mock.assert_called_once_with( repo_name=constants.PROJECT_NAME, organization=github_organization_mock.return_value) project.refresh_from_db() self.assertEqual(constants.PROJECT_ADDRESS, project.dao_address) self.assertEqual(BlockchainState.COMPLETED, project.blockchain_state) self.assertNotEqual('', project.merge_module_address) redis_lock_mock.assert_called_once_with(tasks.REDIS_CREATE_CONTRACT_LOCK)
def mutate(self, info, project_name: str, creator_address: str): CreateProject._validate_project_name( proposed_project_name=project_name) mycro_project = Project.get_mycro_dao() if mycro_project is None: raise ProjectException( "Could not find mycro dao. Cannot create new project.") if not Web3.isAddress(creator_address): raise ProjectException('Supplied address is not valid') total_supply = 1000 symbol = project_name[:3] decimals = 18 initial_balances = {creator_address: total_supply} # create a row in the a db and a repository in github project = Project.objects.create( repo_name=project_name, last_merge_event_block=0, is_mycro_dao=False, symbol=symbol, decimals=decimals, blockchain_state=BlockchainState.PENDING.value, initial_balances=initial_balances) create_project.delay(project.id) return CreateProject(project=project)
def create_project(self, project_id: int) -> None: # TODO add mechanism to retry if anything in here fails # TODO there's lots of opportunity for failure in this function, we should be # more resilient logger.info(f'Attempting to get lock to create project {project_id}') # TODO extend to support locking based on wallets with REDIS_CLIENT.lock('create-project'): logger.info(f'Acquired lock to create project {project_id}') project = Project.objects.get(pk=project_id) project.blockchain_state = BlockchainState.STARTED.value project.save() compiler = ContractCompiler() mycro_project = Project.get_mycro_dao() w3 = deploy.get_w3() contract_interface = compiler.get_contract_interface('mycro.sol', 'MycroCoin') mycro_contract = w3.eth.contract(abi=contract_interface['abi'], address=mycro_project.dao_address) base_dao_interface = compiler.get_contract_interface('base_dao.sol', 'BaseDao') merge_module_interface = compiler.get_contract_interface( 'merge_module.sol', 'MergeModule') creators = [] creator_balances = [] logger.info(f'Project balances are {project.initial_balances}') for creator, balance in project.initial_balances.items(): creators.append(creator) creator_balances.append(balance) total_supply = sum(creator_balances) # TODO add validation of project properties w3, dao_contract, dao_address, _ = deploy.deploy(base_dao_interface, project.symbol, project.repo_name, project.decimals, total_supply, creators, creator_balances, private_key=Wallet.objects.first().private_key) _, _, merge_module_address, _ = deploy.deploy(merge_module_interface, private_key=Wallet.objects.first().private_key) deploy.call_contract_function( dao_contract.functions.registerModule, merge_module_address, private_key=Wallet.objects.first().private_key) deploy.call_contract_function( mycro_contract.functions.registerProject, dao_address, private_key=Wallet.objects.first().private_key) project.blockchain_state = BlockchainState.COMPLETED.value project.dao_address = dao_address project.merge_module_address = merge_module_address project.save() github.create_repo(repo_name=project.repo_name, organization=settings.github_organization()) logger.info(f'Finished creating project {project_id}')
# if the wallet isn't funded, fund it main_wallet = Wallet.objects.first() deploy.fund_account_if_needed(w3, settings.ethereum_private_key(), main_wallet.private_key) # TODO remove this when we no longer need to deploy the mycro contract on init # maybe we should hide this behind an env variable because this is useful for testing mycro_project = Project.objects.filter(is_mycro_dao=True) if len(mycro_project) == 0: compiler = ContractCompiler() _, mycro_contract, mycro_address, mycro_instance = deploy.deploy( compiler.get_contract_interface('mycro.sol', 'MycroCoin'), private_key=main_wallet.private_key, timeout=None) Project.create_mycro_dao(mycro_address, symbol=mycro_instance.symbol(), decimals=mycro_instance.decimals()) elif len(mycro_project) > 1: raise ValueError("Shit there are two mycro DAOs") # Set up background tasks which monitor blockchain for events schedule, created = IntervalSchedule.objects.get_or_create( every=5, period=IntervalSchedule.SECONDS, ) if not PeriodicTask.objects.filter(name=MERGE_PR_BEAT_NAME): PeriodicTask.objects.create(interval=schedule, name=MERGE_PR_BEAT_NAME, task='backend.server.tasks.process_merges')