def addhooktoevent(ctx_obj, contractaddress, hookid, events): """ Receive smart contract events as JSON payloads. This delivers JSON payloads to the webhook endpoint registered against the HOOKID. HOOKID is received after calling `mv-cli registerhook` EVENTS is a list of events on which the hook will be registered. For example: ['*'] or ['Transfer', 'Approve']. If you do not pass this argument, all events will be pushed to the hook endpoint. """ msg = 'dummystring' message_hash = encode_defunct(text=msg) sig_msg = Account.sign_message(message_hash, ctx_obj['settings']['PRIVATEKEY']) events_to_be_registered_on = list() if not events: events_to_be_registered_on.append('*') else: for each in events.split(','): events_to_be_registered_on.append(each) method_args = { "msg": msg, "sig": sig_msg.signature.hex(), "key": ctx_obj['settings']['MATICVIGIL_API_KEY'], "type": "web", "contract": contractaddress, "id": hookid, "events": events_to_be_registered_on } headers = { 'accept': 'application/json', 'Content-Type': 'application/json', 'X-API-KEY': ctx_obj['settings']['MATICVIGIL_API_KEY'] } click.echo( f'Registering | hook ID: {hookid} | events: {events_to_be_registered_on} | contract: {contractaddress}' ) r = requests.post( url= f'{ctx_obj["settings"]["INTERNAL_API_ENDPOINT"]}/hooks/updateEvents', json=method_args, headers=headers) click.echo(r.text) if r.status_code == requests.codes.ok: r = r.json() if r['success']: click.echo('Succeeded in adding hook') else: click.echo('Failed to add hook') return else: click.echo('Failed to add hook') return
def sign(self, username, password, info): keystore_file = "{}/{}".format(client_config.account_keyfile_path, username + ".keystore") if os.path.exists(keystore_file) is False: return -1 try: with open(keystore_file, "r") as dump_f: keytext = json.load(dump_f) privkey = Account.decrypt(keytext, password) msg = encode_defunct(text=info) signed_msg = Account.sign_message(msg, privkey) v = signed_msg['v'] r = signed_msg['r'] s = signed_msg['s'] return v, r, s except Exception as e: print(e) return -1
def enabletxmonitor(ctx_obj, contractaddress, hookid): """ Receive transactions on contracts as JSON payloads. CONTRACTADDRESS is the address of a deployed and registered contract on your MaticVigil account. HOOKID is received after calling `mv-cli registerhook` """ # enable tx monitoring on contract msg = 'dummystring' message_hash = encode_defunct(text=msg) sig_msg = Account.sign_message(message_hash, ctx_obj['settings']['PRIVATEKEY']) method_args = { "msg": msg, "sig": sig_msg.signature.hex(), "key": ctx_obj['settings']['MATICVIGIL_API_KEY'], "type": "web", "contract": contractaddress, "id": hookid, "action": "set" } headers = { 'accept': 'application/json', 'Content-Type': 'application/json', 'X-API-KEY': ctx_obj["settings"]["MATICVIGIL_API_KEY"] } r = requests.post( url= f'{ctx_obj["settings"]["INTERNAL_API_ENDPOINT"]}/hooks/transactions', json=method_args, headers=headers) click.echo(r.text) if r.status_code == requests.codes.ok: r = r.json() if r['success']: click.echo('Succeded in adding hook to monitor all contract txs') else: click.echo('Failed to add hook to monitor on all contract txs...') else: click.echo('Failed to add hook to monitor on all contract txs...')
def ev_signup(internal_api_endpoint, invite_code, private_key, verbose): msg = "Trying to signup" message_hash = encode_defunct(text=msg) signed_msg = Account.sign_message(message_hash, private_key) # --MATICVIGIL API CALL to /signup--- try: r = requests.post(internal_api_endpoint + '/signup', json={ 'msg': msg, 'sig': signed_msg.signature.hex(), 'code': invite_code }) except: return False else: if verbose: print(r.url) print(r.text) if r.status_code == requests.codes.ok: return r.json() else: return False
def ev_login(internal_api_endpoint, private_key, verbose=False): msg = "Trying to login" message_hash = encode_defunct(text=msg) signed_msg = Account.sign_message(message_hash, private_key) # --MATICVIGIL API CALL--- headers = { 'accept': 'application/json', 'Content-Type': 'application/json' } r = requests.post(internal_api_endpoint + '/login', json={ 'msg': msg, 'sig': signed_msg.signature.hex() }, headers=headers) if verbose: click.echo(r.text) if r.status_code == requests.codes.ok: r = r.json() return r['data'] else: return None
def registerhook(ctx_obj, contract, url): """ Registers a webhook endpoint and returns an ID for hook """ headers = { 'accept': 'application/json', 'Content-Type': 'application/json', 'X-API-KEY': ctx_obj['settings']['MATICVIGIL_API_KEY'] } msg = 'dummystring' message_hash = encode_defunct(text=msg) sig_msg = Account.sign_message(message_hash, ctx_obj['settings']['PRIVATEKEY']) method_args = { "msg": msg, "sig": sig_msg.signature.hex(), "key": ctx_obj['settings']['MATICVIGIL_API_KEY'], "type": "web", "contract": contract, "web": url } r = requests.post( url=f'{ctx_obj["settings"]["INTERNAL_API_ENDPOINT"]}/hooks/add', json=method_args, headers=headers) click.echo(r.text) if r.status_code == requests.codes.ok: r = r.json() if not r['success']: click.echo('Failed to register webhook with MaticVigil API...') else: hook_id = r["data"]["id"] click.echo( 'Succeeded in registering webhook with MaticVigil API...') click.echo(f'MaticVigil Hook ID: {hook_id}') else: click.echo('Failed to register webhook with MaticVigil API...')
def test_verify_eip191(testerchain, signature_verifier): message = os.urandom(100) # Generate Umbral key umbral_privkey = SecretKey.random() umbral_pubkey = umbral_privkey.public_key() umbral_pubkey_bytes = pubkey_as_uncompressed_bytes(umbral_pubkey) # # Check EIP191 signatures: Version E # # Produce EIP191 signature (version E) signable_message = encode_defunct(primitive=message) signature = Account.sign_message( signable_message=signable_message, private_key=umbral_privkey.to_secret_bytes()) signature = bytes(signature.signature) # Off-chain verify, just in case checksum_address = to_checksum_address( canonical_address_from_umbral_key(umbral_pubkey)) assert verify_eip_191(address=checksum_address, message=message, signature=signature) # Verify signature on-chain version_E = b'E' assert signature_verifier.functions.verifyEIP191(message, signature, umbral_pubkey_bytes, version_E).call() # Of course, it'll fail if we try using version 0 version_0 = b'\x00' assert not signature_verifier.functions.verifyEIP191( message, signature, umbral_pubkey_bytes, version_0).call() # Check that the hash-based method also works independently hash = signature_verifier.functions.hashEIP191(message, version_E).call() eip191_header = "\x19Ethereum Signed Message:\n" + str(len(message)) assert hash == keccak_digest(eip191_header.encode() + message) address = signature_verifier.functions.recover(hash, signature).call() assert address == checksum_address # # Check EIP191 signatures: Version 0 # # Produce EIP191 signature (version 0) validator = to_canonical_address(signature_verifier.address) signable_message = SignableMessage(version=HexBytes(version_0), header=HexBytes(validator), body=HexBytes(message)) signature = Account.sign_message( signable_message=signable_message, private_key=umbral_privkey.to_secret_bytes()) signature = bytes(signature.signature) # Off-chain verify, just in case checksum_address = to_checksum_address( canonical_address_from_umbral_key(umbral_pubkey)) assert checksum_address == Account.recover_message( signable_message=signable_message, signature=signature) # On chain verify signature assert signature_verifier.functions.verifyEIP191(message, signature, umbral_pubkey_bytes, version_0).call() # Of course, now it fails if we try with version E assert not signature_verifier.functions.verifyEIP191( message, signature, umbral_pubkey_bytes, version_E).call() # Check that the hash-based method also works independently hash = signature_verifier.functions.hashEIP191(message, version_0).call() eip191_header = b"\x19\x00" + validator assert hash == keccak_digest(eip191_header + message) address = signature_verifier.functions.recover(hash, signature).call() assert address == checksum_address
def verifycontract(ctx_obj, verbose, interactive, contract_address, contract_name, solidity_compiler, optimization, contract_file): """ Verify and add a contract to your MaticVigil account that was previously deployed through a different interface, for eg. https://remix.ethereum.org """ headers = { 'accept': 'application/json', 'Content-Type': 'application/json', 'X-API-KEY': ctx_obj['settings']['MATICVIGIL_API_KEY'] } main_contract_src = '' if not contract_file or interactive: contract_address = click.prompt('Contract address to be verified') contract_address = to_normalized_address(contract_address) contract_name = click.prompt('Contract name') contract_file = click.prompt('Location of Solidity file', type=click.Path(exists=True, dir_okay=False)) with open(contract_file, 'r') as f: while True: chunk = f.read(1024) if not chunk: break main_contract_src += chunk click.secho('Getting a list of compiler versions...', fg='green') # get list of compilers compilers = dict() try: c_r = make_http_call( request_type='get', url=ctx_obj['settings']['INTERNAL_API_ENDPOINT'] + '/compilers', headers={'accept': 'application/json'}) except Exception as e: click.echo('Exception retrieving list of compilers', err=True) if isinstance(e, EVHTTPError): click.echo( 'Possible HTTP error. Try with --verbose flag for more information', err=True) if verbose: click.echo(e.__str__(), err=True) elif isinstance(e, EVAPIError): click.echo( 'Possible API error.Try with --verbose flag for more information', err=True) if verbose: click.echo(e.__str__(), err=True) elif isinstance(e, EVBaseException) and verbose: click.echo(e.__str__(), err=True) sys.exit(1) if type(c_r['data']) == list: if len(c_r['data']) < 1: click.echo('Got empty list of compilers. Exiting...', err=True) sys.exit(1) for idx, each in enumerate(c_r['data']): compilers[idx] = each click.echo_via_pager(_gen_compilers_list(compilers)) i = click.prompt('Select option from compiler versions above. Eg. 2', type=int) solidity_compiler = compilers[i - 1]['full'] optimization = click.confirm('Optimization enabled?') else: with open(contract_file, 'r') as f: while True: chunk = f.read(1024) if not chunk: break main_contract_src += chunk msg = 'dummystring' message_hash = encode_defunct(text=msg) sig_msg = Account.sign_message(message_hash, ctx_obj['settings']['PRIVATEKEY']) method_args = { 'msg': msg, 'sig': sig_msg.signature.hex(), 'contractAddress': contract_address, 'skipCompiling': False, 'name': contract_name, 'version': solidity_compiler, 'optimization': optimization, 'code': main_contract_src } click.secho( f'Verifying contract {contract_name} at {contract_address} from source {contract_file}...', fg='bright_white') try: c_r = make_http_call(request_type='post', url=ctx_obj['settings']['INTERNAL_API_ENDPOINT'] + '/verify', headers={'accept': 'application/json'}, params=method_args) except Exception as e: click.echo('Exception verifying contract', err=True) if isinstance(e, EVHTTPError): click.echo( 'Possible HTTP error. Try with --verbose flag for more information', err=True) if verbose: click.echo(e.__str__(), err=True) elif isinstance(e, EVAPIError): click.echo( 'Possible API error.Try with --verbose flag for more information', err=True) if verbose: click.echo(e.__str__(), err=True) elif isinstance(e, EVBaseException) and verbose: click.echo(e.__str__(), err=True) sys.exit(1) else: click.secho('Contract verified!', fg='green')
def deploy(ctx_obj, contract_name, inputs, verbose, contract): """ Deploys a smart contract from the solidity source code specified CONTRACT: path to the solidity file Usage example: mv-cli deploy contracts/Microblog.sol --contractName=Microblog --constructorInputs='JSON representation of the constructor arguments in an array' """ constructor_input_prompt = False if contract_name: if verbose: click.echo('Got contract name: ') click.echo(contract_name) else: contract_name = click.prompt('Enter the contract name') if verbose: click.echo('Got constructor inputs: ') click.echo(inputs) if inputs: if verbose: click.echo('Got constructor inputs: ') click.echo(inputs) c_inputs = json.loads(inputs) else: constructor_input_prompt = True c_inputs = list() # an empty list sources = dict() if contract[0] == '~': contract_full_path = os.path.expanduser(contract) else: contract_full_path = contract resident_directory = ''.join( map(lambda x: x + '/', contract_full_path.split('/')[:-1])) contract_file_name = contract_full_path.split('/')[-1] contract_file_obj = open(file=contract_full_path) main_contract_src = '' while True: chunk = contract_file_obj.read(1024) if not chunk: break main_contract_src += chunk sources[f'mv-cli/{contract_file_name}'] = {'content': main_contract_src} # loop through imports and add them to sources source_unit = parser.parse(main_contract_src) source_unit_obj = parser.objectify(source_unit) for each in source_unit_obj.imports: import_location = each['path'].replace("'", "") # TODO: follow specified relative paths and import such files too if import_location[:2] != './': click.echo( f'You can only import files from within the same directory as of now', err=True) return # otherwise read the file into the contents mapping full_path = resident_directory + import_location[2:] imported_contract_obj = open(full_path, 'r') contract_src = '' while True: chunk = imported_contract_obj.read(1024) if not chunk: break contract_src += chunk sources[f'mv-cli/{import_location[2:]}'] = {'content': contract_src} if len(c_inputs) == 0 and constructor_input_prompt: abi_json = extract_abi(ctx_obj['settings'], { 'sources': sources, 'sourceFile': f'mv-cli/{contract_file_name}' }) abp = ABIParser(abi_json=abi_json) abp.load_abi() if len(abp.constructor_params()) > 0: click.echo('Enter constructor inputs...') for idx, each_param in enumerate(abp.constructor_params()): param_type = abp._constructor_mapping["constructor"][ "input_types"][idx] param_type_cat = abp.type_category(param_type) arg = click.prompt(f'{each_param}({param_type})') if param_type_cat == 'integer': arg = int(arg) elif param_type_cat == 'array': # check if it can be deserialized into a python dict try: arg_dict = json.loads(arg) except json.JSONDecodeError: click.echo( f'Parameter {each_param} of type {param_type} ' f'should be correctly passed as a JSON array', err=True) sys.exit(1) c_inputs.append(arg) msg = "Trying to deploy" message_hash = encode_defunct(text=msg) # deploy from alpha account signed_msg = Account.sign_message(message_hash, ctx_obj['settings']['PRIVATEKEY']) deploy_json = { 'msg': msg, 'sig': signed_msg.signature.hex(), 'name': contract_name, 'inputs': c_inputs, 'sources': sources, 'sourceFile': f'mv-cli/{contract_file_name}' } # click.echo(deploy_json) # --MATICVIGIL API CALL--- r = requests.post(ctx_obj['settings']['INTERNAL_API_ENDPOINT'] + '/deploy', json=deploy_json) if verbose: click.echo('MaticVigil deploy response: ') click.echo(r.text) if r.status_code == requests.codes.ok: click.echo(f'Contract {contract_name} deployed successfully') r = r.json() click.echo(f'Contract Address: {r["data"]["contract"]}') click.echo(f'Deploying tx: {r["data"]["hash"]}') else: click.echo('Contract deployment failed')