def test_individual_allocation_registry(get_random_checksum_address, test_registry, tempfile_path): empty_allocation_escrow_deployer = PreallocationEscrowDeployer(registry=test_registry) allocation_contract_abi = empty_allocation_escrow_deployer.get_contract_abi() beneficiary = get_random_checksum_address() contract_address = get_random_checksum_address() allocation_registry = IndividualAllocationRegistry(beneficiary_address=beneficiary, contract_address=contract_address) registry_data = allocation_registry.read() assert len(registry_data) == 1 assert allocation_registry.search(beneficiary_address=beneficiary) == [contract_address, allocation_contract_abi] assert allocation_registry.search(contract_address=contract_address) == [beneficiary, allocation_contract_abi] # Check that searching for an unknown beneficiary or unknown contract raises with pytest.raises(IndividualAllocationRegistry.UnknownBeneficiary): allocation_registry.search(beneficiary_address=get_random_checksum_address()) with pytest.raises(IndividualAllocationRegistry.UnknownContract): allocation_registry.search(contract_address=get_random_checksum_address()) # Check that it gets the same data if using a file to create the allocation registry individual_allocation_file_data = { 'beneficiary_address': beneficiary, 'contract_address': contract_address } with open(tempfile_path, 'w') as outfile: json.dump(individual_allocation_file_data, outfile) allocation_registry = IndividualAllocationRegistry.from_allocation_file(filepath=tempfile_path) assert registry_data == allocation_registry.read()
def test_deploy_multiple_preallocations(testerchain, test_registry): deployer_account = testerchain.etherbase_account router = testerchain.get_contract_by_name( registry=test_registry, contract_name=StakingInterfaceRouterDeployer.contract_name) router_address = router.address for index in range(NUMBER_OF_PREALLOCATIONS): deployer = PreallocationEscrowDeployer( deployer_address=deployer_account, registry=test_registry) deployment_receipt = deployer.deploy() assert deployment_receipt['status'] == 1 preallocation_escrow_contract = deployer.contract router = preallocation_escrow_contract.functions.router().call() assert router == router_address preallocation_escrow_contracts.append(preallocation_escrow_contract) # simulates passage of time / blocks if index % 5 == 0: testerchain.w3.eth.web3.testing.mine(1) testerchain.time_travel(seconds=5) assert len(preallocation_escrow_contracts) == NUMBER_OF_PREALLOCATIONS
def agent(testerchain, test_registry, allocation_value, agency, mock_transacting_power_activation) -> PreallocationEscrowAgent: deployer_address, beneficiary_address, *everybody_else = testerchain.client.accounts escrow_deployer = PreallocationEscrowDeployer(deployer_address=deployer_address, registry=test_registry, allocation_registry=TEST_ALLOCATION_REGISTRY) mock_transacting_power_activation(account=deployer_address, password=INSECURE_DEVELOPMENT_PASSWORD) _receipt = escrow_deployer.deploy() escrow_deployer.initial_deposit(value=allocation_value, duration_seconds=TEST_LOCK_DURATION_IN_SECONDS) assert escrow_deployer.contract.functions.getLockedTokens().call() == allocation_value escrow_deployer.assign_beneficiary(checksum_address=beneficiary_address) escrow_deployer.enroll_principal_contract() assert escrow_deployer.contract.functions.getLockedTokens().call() == allocation_value agent = escrow_deployer.make_agent() direct_agent = PreallocationEscrowAgent(registry=test_registry, allocation_registry=TEST_ALLOCATION_REGISTRY, beneficiary=beneficiary_address) assert direct_agent == agent assert direct_agent.contract.abi == agent.contract.abi assert direct_agent.contract.address == agent.contract.address assert agent.principal_contract.address == escrow_deployer.contract.address assert agent.principal_contract.abi == escrow_deployer.contract.abi assert direct_agent.contract.abi == escrow_deployer.contract.abi assert direct_agent.contract.address == escrow_deployer.contract.address yield agent TEST_ALLOCATION_REGISTRY.clear()
def deploy_preallocation_escrow(self, allocation_registry: AllocationRegistry, progress=None) -> PreallocationEscrowDeployer: preallocation_escrow_deployer = PreallocationEscrowDeployer(registry=self.registry, deployer_address=self.deployer_address, allocation_registry=allocation_registry) preallocation_escrow_deployer.deploy(progress=progress) principal_address = preallocation_escrow_deployer.contract.address self.preallocation_escrow_deployers[principal_address] = preallocation_escrow_deployer return preallocation_escrow_deployer
def mock_allocation_registry(testerchain, agency_local_registry, token_economics): # Deploy the PreallocationEscrow contract allocation_registry = InMemoryAllocationRegistry() deployer = PreallocationEscrowDeployer(deployer_address=testerchain.etherbase_account, registry=agency_local_registry, allocation_registry=allocation_registry) deployer.deploy() deployer.assign_beneficiary(checksum_address=testerchain.unassigned_accounts[0]) deployer.initial_deposit(value=2 * token_economics.minimum_allowed_locked, duration_seconds=ONE_YEAR_IN_SECONDS) deployer.enroll_principal_contract() return allocation_registry
def test_deploy_and_allocate(agency, token_economics, test_registry): token_agent, staking_agent, policy_agent = agency testerchain = policy_agent.blockchain origin = testerchain.etherbase_account deployments = dict() allocation = token_economics.minimum_allowed_locked * 1 number_of_deployments = 1 _last_deployment_address = None for index in range(number_of_deployments): escrow_deployer = PreallocationEscrowDeployer(deployer_address=origin, registry=test_registry) _deployment_txhashes = escrow_deployer.deploy() # Ensure we have the correct assembly of address and abi assert escrow_deployer.contract.address == escrow_deployer.contract.address # Ensure each deployment is unique if _last_deployment_address: assert escrow_deployer.contract.address != _last_deployment_address _last_deployment_address = escrow_deployer.contract.address deployments[escrow_deployer.contract.address] = escrow_deployer assert len(deployments) == number_of_deployments # Let some time pass testerchain.time_travel(hours=3) assert token_agent.get_balance(address=origin) > 1 # Start allocating tokens deposit_receipts, approve_hashes = list(), dict() for address, deployer in deployments.items(): assert deployer.deployer_address == origin deposit_receipt = deployer.initial_deposit( value=allocation, duration_seconds=token_economics.maximum_rewarded_periods) deposit_receipts.append(deposit_receipt) beneficiary = random.choice(testerchain.unassigned_accounts) _assign_receipt = deployer.assign_beneficiary(beneficiary) assert len(deposit_receipts) == number_of_deployments == len(deployments)
def _patch_individual_allocation_fetch_latest_publication( agency, test_registry): empty_allocation_escrow_deployer = PreallocationEscrowDeployer( registry=test_registry) allocation_contract_abi = empty_allocation_escrow_deployer.get_contract_abi( ) allocation_template = { "BENEFICIARY_ADDRESS": ["ALLOCATION_CONTRACT_ADDRESS", allocation_contract_abi] } def new_fetch(*args, **kwargs): return json.dumps(allocation_template).encode() original_fetch = GithubRegistrySource.fetch_latest_publication GithubRegistrySource.fetch_latest_publication = new_fetch yield GithubRegistrySource.fetch_latest_publication = original_fetch
def patch_fetch_latest_publication(test_registry): empty_allocation_escrow_deployer = PreallocationEscrowDeployer( registry=test_registry) allocation_contract_abi = empty_allocation_escrow_deployer.get_contract_abi( ) allocation_template = { "BENEFICIARY_ADDRESS": ["ALLOCATION_CONTRACT_ADDRESS", allocation_contract_abi] } new_fetch_result = json.dumps(allocation_template).encode() original_fetch = IndividualAllocationRegistry.fetch_latest_publication def new_fetch(*args, **kwargs): return new_fetch_result IndividualAllocationRegistry.fetch_latest_publication = new_fetch yield IndividualAllocationRegistry.fetch_latest_publication = original_fetch
def deploy_beneficiary_contracts( self, allocations: List[Dict[str, Union[str, int]]], allocation_outfile: str = None, allocation_registry: AllocationRegistry = None, crash_on_failure: bool = True, interactive: bool = True, emitter: StdoutEmitter = None, ) -> Dict[str, dict]: """ The allocation file is a JSON file containing a list of allocations. Each allocation has a: * 'beneficiary_address': Checksum address of the beneficiary * 'name': User-friendly name of the beneficiary (Optional) * 'amount': Amount of tokens locked, in NuNits * 'duration_seconds': Lock duration expressed in seconds Example allocation file: [ {'beneficiary_address': '0xdeadbeef', 'name': 'H. E. Pennypacker', 'amount': 100, 'duration_seconds': 31536000}, {'beneficiary_address': '0xabced120', 'amount': 133432, 'duration_seconds': 31536000}, {'beneficiary_address': '0xf7aefec2', 'amount': 999, 'duration_seconds': 31536000}] """ if interactive and not emitter: raise ValueError( "'emitter' is a required keyword argument when interactive is True." ) if allocation_registry and allocation_outfile: raise self.ActorError( "Pass either allocation registry or allocation_outfile, not both." ) if allocation_registry is None: allocation_registry = AllocationRegistry( filepath=allocation_outfile) if emitter: paint_input_allocation_file(emitter, allocations) if interactive: click.confirm("Continue with the allocation process?", abort=True) total_to_allocate = NU.from_nunits( sum(allocation['amount'] for allocation in allocations)) balance = ContractAgency.get_agent(NucypherTokenAgent, self.registry).get_balance( self.deployer_address) if balance < total_to_allocate: raise ValueError( f"Not enough tokens to allocate. We need at least {total_to_allocate}." ) allocation_receipts, failed, allocated = dict(), list(), list() total_deployment_transactions = len(allocations) * 4 # Create an allocation template file, containing the allocation contract ABI and placeholder values # for the beneficiary and contract addresses. This file will be shared with all allocation users. empty_allocation_escrow_deployer = PreallocationEscrowDeployer( registry=self.registry) allocation_contract_abi = empty_allocation_escrow_deployer.get_contract_abi( ) allocation_template = { "BENEFICIARY_ADDRESS": ["ALLOCATION_CONTRACT_ADDRESS", allocation_contract_abi] } parent_path = Path(allocation_registry.filepath ).parent # Use same folder as allocation registry template_filename = IndividualAllocationRegistry.REGISTRY_NAME template_filepath = os.path.join(parent_path, template_filename) AllocationRegistry(filepath=template_filepath).write( registry_data=allocation_template) if emitter: emitter.echo( f"Saved allocation template file to {template_filepath}", color='blue', bold=True) # Deploy each allocation contract with click.progressbar(length=total_deployment_transactions, label="Allocation progress", show_eta=False) as bar: bar.short_limit = 0 for allocation in allocations: # TODO: Check if allocation already exists in allocation registry beneficiary = allocation['beneficiary_address'] name = allocation.get('name', 'No name provided') if interactive: click.pause( info=f"\nPress any key to continue with allocation for " f"beneficiary {beneficiary} ({name})") if emitter: emitter.echo( f"\nDeploying PreallocationEscrow contract for beneficiary {beneficiary} ({name})..." ) bar._last_line = None bar.render_progress() deployer = self.deploy_preallocation_escrow( allocation_registry=allocation_registry, progress=bar) amount = allocation['amount'] duration = allocation['duration_seconds'] try: receipts = deployer.deliver( value=amount, duration=duration, beneficiary_address=beneficiary, progress=bar) except TransactionFailed as e: if crash_on_failure: raise self.log.debug( f"Failed allocation transaction for {NU.from_nunits(amount)} to {beneficiary}: {e}" ) failed.append(allocation) continue else: allocation_receipts[beneficiary] = receipts allocation_contract_address = deployer.contract_address self.log.info( f"Created {deployer.contract_name} contract at {allocation_contract_address} " f"for beneficiary {beneficiary}.") allocated.append((allocation, allocation_contract_address)) # Create individual allocation file individual_allocation_filename = f'allocation-{beneficiary}.json' individual_allocation_filepath = os.path.join( parent_path, individual_allocation_filename) individual_allocation_file_data = { 'beneficiary_address': beneficiary, 'contract_address': allocation_contract_address } with open(individual_allocation_filepath, 'w') as outfile: json.dump(individual_allocation_file_data, outfile) if emitter: blockchain = BlockchainInterfaceFactory.get_interface() paint_contract_deployment( contract_name=deployer.contract_name, receipts=receipts, contract_address=deployer.contract_address, emitter=emitter, chain_name=blockchain.client.chain_name, open_in_browser=False) emitter.echo( f"Saved individual allocation file to {individual_allocation_filepath}", color='blue', bold=True) if emitter: paint_deployed_allocations(emitter, allocated, failed) csv_filename = f'allocations-{self.deployer_address[:6]}-{maya.now().epoch}.csv' csv_filepath = os.path.join(parent_path, csv_filename) write_deployed_allocations_to_csv(csv_filepath, allocated, failed) if emitter: emitter.echo(f"Saved allocation summary CSV to {csv_filepath}", color='blue', bold=True) if failed: # TODO: More with these failures: send to isolated logfile, and reattempt self.log.critical( f"FAILED TOKEN ALLOCATION - {len(failed)} allocations failed." ) return allocation_receipts