def __init__(self): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'nrve-niche-config.json'), 'r') as f: config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'network-wallets.json'), 'r') as f: network_wallets_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'db-config.json'), 'r') as f: self.db_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'smtp-config.json'), 'r') as f: self.smtp_config = json.load(f) super().__init__(NetworkType[config['network']], 'nrve-niche-payment-handler') self.smart_contract = SmartContract(config['smart_contract']) self.niche_payment_address = config['niche_payment_address'] self.niche_payment_storage_address = config[ 'niche_payment_storage_address'] self.setup_wallet( network_wallets_config[config['network']]['wallet_path']) # decorate the event handler methods dynamically now that we have loaded the SC self.sc_notify = self.smart_contract.on_notify(self.sc_notify)
def __init__(self, contract_hash, wallet_path, wallet_pass): super(SurTokenContract, self).__init__() self.daemon = True self.contract_hash = contract_hash self.wallet_path = wallet_path self.wallet_pass = to_aes_key(wallet_pass) self.smart_contract = SmartContract(contract_hash) self.invoke_queue = Queue() self.tx_in_progress = None self.wallet = None settings.set_log_smart_contract_events(False) @self.smart_contract.on_notify def sc_notify(event): logger.info("SmartContract Runtime.Notify event: %s", event) # Make sure that the event payload list has at least one element. if not len(event.event_payload): return # The event payload list has at least one element. As developer of the smart contract # you should know what data-type is in the bytes, and how to decode it. In this example, # it's just a string, so we decode it with utf-8: logger.info("- payload part 1: %s", event.event_payload[0].decode("utf-8"))
def __init__(self): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'neo-nrve-config.json'), 'r') as f: config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'network-wallets.json'), 'r') as f: network_wallets_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'db-config.json'), 'r') as f: self.db_config = json.load(f) super().__init__(NetworkType[config['network']], 'neo-nrve-eventhandler') self.smart_contract_hash = config['smart_contract'] self.smart_contract = SmartContract(self.smart_contract_hash) self.wait_whitelist_tx_processing_seconds = config[ 'wait_whitelist_tx_processing_seconds'] self.wait_load_addresses_to_whitelist_seconds = config[ 'wait_load_addresses_to_whitelist_seconds'] self.addresses_to_whitelist_count = config[ 'addresses_to_whitelist_count'] self.setup_wallet( network_wallets_config[config['network']]['wallet_path'])
def __init__(self): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'bulk-tx-config.json'), 'r') as f: config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', config['job_config_file']), 'r') as f: job_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'network-wallets.json'), 'r') as f: network_wallets_config = json.load(f) super().__init__(NetworkType[config['network']], 'bulk-process-tx') self.test_only = config['test_only'] self.operation = job_config['operation'] self.operation_args_array_length = job_config[ 'operation_args_array_length'] self.expected_result_count = job_config['expected_result_count'] try: self.from_addr = job_config['from_addr'] except KeyError: pass self.jobs = job_config['jobs'] # Setup the smart contract instance self.smart_contract_hash = config['smart_contract'] self.smart_contract = SmartContract(self.smart_contract_hash) # decorate the event handler methods dynamically now that we have loaded the SC self.sc_notify = self.smart_contract.on_notify(self.sc_notify) self.sc_storage = self.smart_contract.on_storage(self.sc_storage) self.sc_execution = self.smart_contract.on_execution(self.sc_execution) self.setup_wallet( network_wallets_config[config['network']]['wallet_path'])
def __init__(self, disable_auto_whitelist): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'neo-nrve-config.json'), 'r') as f: config = json.load(f) if not disable_auto_whitelist: with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'network-wallets.json'), 'r') as f: network_wallets_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'db-config.json'), 'r') as f: self.db_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'smtp-config.json'), 'r') as f: self.smtp_config = json.load(f) super().__init__(NetworkType[config['network']], 'neo-nrve-eventhandler') self.smart_contract_hash = config['smart_contract'] self.smart_contract = SmartContract(self.smart_contract_hash) self.old_smart_contract = SmartContract(config['old_smart_contract']) self.ignore_blocks_older_than = config['ignore_blocks_older_than'] # decorate the event handler method dynamically now that we have loaded the SCs self.sc_notify = self.old_smart_contract.on_notify(self.sc_notify) self.sc_notify = self.smart_contract.on_notify(self.sc_notify) if not disable_auto_whitelist: self.setup_wallet( network_wallets_config[config['network']]['wallet_path']) else: self.setup_network() self.disable_auto_whitelist = disable_auto_whitelist
class TokenSaleEventHandler(BlockchainMain): smart_contract_hash = None # Setup the smart contract instance smart_contract = None old_smart_contract = None db_config = None smtp_config = None ignore_blocks_older_than = None disable_auto_whitelist = None wallet_needs_recovery = False whitelists_to_process = [] whitelist_tx_processing = None def __init__(self, disable_auto_whitelist): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'neo-nrve-config.json'), 'r') as f: config = json.load(f) if not disable_auto_whitelist: with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'network-wallets.json'), 'r') as f: network_wallets_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'db-config.json'), 'r') as f: self.db_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'smtp-config.json'), 'r') as f: self.smtp_config = json.load(f) super().__init__(NetworkType[config['network']], 'neo-nrve-eventhandler') self.smart_contract_hash = config['smart_contract'] self.smart_contract = SmartContract(self.smart_contract_hash) self.old_smart_contract = SmartContract(config['old_smart_contract']) self.ignore_blocks_older_than = config['ignore_blocks_older_than'] # decorate the event handler method dynamically now that we have loaded the SCs self.sc_notify = self.old_smart_contract.on_notify(self.sc_notify) self.sc_notify = self.smart_contract.on_notify(self.sc_notify) if not disable_auto_whitelist: self.setup_wallet( network_wallets_config[config['network']]['wallet_path']) else: self.setup_network() self.disable_auto_whitelist = disable_auto_whitelist def sc_notify(self, event): event_payload = event.event_payload if not isinstance( event_payload, ContractParameter ) or event_payload.Type != ContractParameterType.Array: self.logger.info( "[invalid event_payload] SmartContract Runtime.Notify event: %s", event) return payload = event_payload.Value # Make sure that the event payload list has at least one element. if not len(payload): self.logger.info( "[no event_payload] SmartContract Runtime.Notify event: %s", event) return if event.test_mode: self.logger.info( "[test_mode] SmartContract Runtime.Notify event: %s", event) return if not event.execution_success: self.logger.info( "[execution_success=false] SmartContract Runtime.Notify event: %s", event) return # The event payload list has at least one element. As developer of the smart contract # you should know what data-type is in the bytes, and how to decode it. In this example, # it's just a string, so we decode it with utf-8: event_type = payload[0].Value.decode("utf-8") block_number = event.block_number if self.ignore_blocks_older_than and block_number < self.ignore_blocks_older_than: return timestamp = self.blockchain.GetHeaderByHeight(block_number).Timestamp # bl: event.contract_hash is a UInt160, so convert it to a hex string contract_hash = event.contract_hash.ToString() # bl: event.tx_hash is a UInt256, so convert it to a hex string tx_hash = event.tx_hash.ToString() # bl: we only care about refunds for the old smart contract if contract_hash == self.old_smart_contract.contract_hash and event_type != 'refund': return connection = self.get_connection() try: with connection.cursor() as cursor: if event_type == 'kyc_registration' or event_type == 'kyc_deregistration': address = self.get_address(payload[1].Value) self.logger.info("- %s: %s", event_type, address) sql = "update `NarrativeUserNeoAddress` set whitelisted = %s where neoAddress = %s" args = (1 if event_type == 'kyc_registration' else 0, address) elif event_type == 'contribution': # from, neo, tokens address = self.get_address(payload[1].Value) # based on the smart contract, we know these should always be whole numbers neo = (int)(payload[2].Value / 100000000) tokens = (int)(payload[3].Value / 100000000) self.logger.info("- %s: %s: %s NEO (%s NRVE) (tx: %s)", event_type, address, neo, tokens, tx_hash) sql = ( "insert into `NarrativeContribution` (transactionId, neo, nrveTokens, transactionDate, neoAddress_oid)\n" "select %s, %s, %s, from_unixtime(%s), na.oid\n" "from NarrativeUserNeoAddress na\n" "where na.neoAddress = %s") args = (tx_hash, neo, tokens, timestamp, address) elif event_type == 'refund': # to, amount address = self.get_address(payload[1].Value) # based on the smart contract, the amount should always be a whole number amount = (int)(payload[2].Value / 100000000) log = "%s: %s: %s NEO [%s] (tx: %s)" % ( event_type, address, amount, contract_hash, tx_hash) self.logger.info('- ' + log) sql = ( "insert into `NarrativeRefund` (transactionId, contractHash, neo, transactionDate, neoAddress)\n" "values (%s, %s, %s, from_unixtime(%s), %s)") args = (tx_hash, contract_hash, amount, timestamp, address) self.send_email("Narrative Refund Required", log) elif event_type == 'transfer' or event_type == 'approve': # bl: ignore NEP5 transfers and approvals. don't care about those, and there will be a lot! return else: self.logger.warn("Unhandled event: %s", event) return # Create a new record cursor.execute(sql, args) if cursor.rowcount != 1: self.logger.error('ERROR: Failed recording event: %s', event) # connection is not autocommit by default. So you must commit to save # your changes. connection.commit() except MySQLError as e: self.logger.error('ERROR: event %s: {!r}, errno is {}'.format( event, e, e.args[0])) finally: connection.close() # if this is the whitelist tx we are waiting for, then clear it out so the next can be processed! if not self.disable_auto_whitelist and self.whitelist_tx_processing and tx_hash == self.whitelist_tx_processing.ToString( ): self.whitelist_tx_processing = None def get_connection(self): # Connect to the database return pymysql.connect(host=self.db_config['host'], user=self.db_config['user'], password=self.db_config['password'], db=self.db_config['db'], charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) def send_email(self, subject, body): msg = MIMEText(body) msg['Subject'] = subject msg['From'] = self.smtp_config['from_address'] msg['To'] = self.smtp_config['to_address'] # Send the message via our own SMTP server. # bl: production servers user port 587 s = smtplib.SMTP(self.smtp_config['host'], self.smtp_config['port']) if self.smtp_config['use_tls']: s.starttls() s.send_message(msg) s.quit() async def custom_background_code(self): count = 0 while True: await asyncio.sleep(1) count += 1 if (count % 60) == 0: self.logger.info("Block %s / %s", str(Blockchain.Default().Height), str(Blockchain.Default().HeaderHeight)) count = 0 # when disabling auto-whitelisting, nothing further to do here if self.disable_auto_whitelist: continue # already have a whitelist that we are waiting to process? then just keep waiting until that transaction comes through if self.whitelist_tx_processing: continue # load addresses to whitelist every 15 seconds, but only if the list is empty if not self.whitelists_to_process: # look for NEO addresses to whitelist every 15 seconds if (count % 15) != 0: continue self.load_addresses_to_whitelist() # no whitelists to process? then keep waiting if not self.whitelists_to_process: continue if self.wallet_needs_recovery: await self.recover_wallet() self.wallet_needs_recovery = False else: await self.wallet_sync() addresses_to_whitelist = self.whitelists_to_process[0:6] self.whitelists_to_process = self.whitelists_to_process[6:] self.logger.debug('whitelisting addresses: %s', addresses_to_whitelist) result = await self.test_invoke([ self.smart_contract_hash, 'crowdsale_register', str(addresses_to_whitelist) ], len(addresses_to_whitelist), False) if not result: # transaction failed? wallet probably out-of-sync (insufficient funds) so reload it self.wallet_needs_recovery = True # we need to try to process this refund again, so add it back in to the list self.whitelists_to_process = addresses_to_whitelist + self.whitelists_to_process else: # transaction successfully relayed? then let's set the tx Hash that we're waiting for self.whitelist_tx_processing = result.Hash def load_addresses_to_whitelist(self): connection = self.get_connection() try: with connection.cursor() as cursor: sql = ( "select na.neoAddress from `NarrativeUser` u\n" "inner join `NarrativeUserNeoAddress` na on na.oid = u.primaryNeoAddress_oid\n" "where na.whitelisted = 0\n" "and u.hasVerifiedEmailAddress = 1\n" "and u.kycStatus = 3;") cursor.execute(sql) rows = cursor.fetchall() for row in rows: self.whitelists_to_process.append(row['neoAddress']) except MySQLError as e: self.logger.error( 'ERROR: selecting whitelist addresses: {!r}, errno is {}'. format(e, e.args[0])) finally: connection.close()
from logzero import logger from twisted.internet import reactor, task from neo.contrib.smartcontract import SmartContract from neo.SmartContract.ContractParameter import ContractParameter, ContractParameterType from neo.Network.NodeLeader import NodeLeader from neo.Core.Blockchain import Blockchain from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain from neo.Settings import settings # If you want the log messages to also be saved in a logfile, enable the # next line. This configures a logfile with max 10 MB and 3 rotations: # settings.set_logfile("/tmp/logfile.log", max_bytes=1e7, backup_count=3) # Setup the smart contract instance smart_contract = SmartContract("6537b4bd100e514119e3a7ab49d520d20ef2c2a4") # Register an event handler for Runtime.Notify events of the smart contract. @smart_contract.on_notify def sc_notify(event): logger.info("SmartContract Runtime.Notify event: %s", event) # Make sure that the event payload list has at least one element. if not isinstance( event.event_payload, ContractParameter ) or event.event_payload.Type != ContractParameterType.Array or not len( event.event_payload.Value): return # The event payload list has at least one element. As developer of the smart contract
from neocore.KeyPair import KeyPair import coinmarketcap from neocore.BigInteger import BigInteger from neo.Core.Helper import Helper import random # If you want the log messages to also be saved in a logfile, enable the # next line. This configures a logfile with max 10 MB and 3 rotations: # settings.set_logfile("/tmp/logfile.log", max_bytes=1e7, backup_count=3) # Setup the smart contract instance # This is online voting v0.5 #smart_contract_hash = "7dc2db1227a8518146dc41c55dfafa97d9a83c27" smart_contract = SmartContract(smart_contract_hash) #wallet_hash = 'Aaaapk3CRx547bFvkemgc7z2xXewzaZtdP' #wallet_arr = Helper.AddrStrToScriptHash(wallet_hash).ToArray() Wallet = None buffer = None normalisation = 300 def test_invoke_contract(args): if not Wallet: print("where's the wallet") return if args and len(args) > 0:
from time import sleep from logzero import logger from twisted.internet import reactor, task from neo.contrib.smartcontract import SmartContract from neo.Network.NodeLeader import NodeLeader from neo.Core.Blockchain import Blockchain from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain from neo.Settings import settings from google.cloud import pubsub_v1 from neocore.Cryptography import Crypto smart_contract = SmartContract(os.environ["CONTRACT"]) publisher = pubsub_v1.PublisherClient() topic = publisher.topic_path("astrum-world", "neo") def parse_arg(input): if isinstance(input, int): return input try: return input.decode('utf-8') except: pass if len(input) == 20: try: return Crypto.scripthash_to_address(input) except:
from logzero import logger from twisted.internet import reactor, task from neo.contrib.smartcontract import SmartContract from neo.Network.NodeLeader import NodeLeader from neo.Core.Blockchain import Blockchain from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain from neo.Settings import settings from channel_manager.channel import get_channelnames_via_address, Channel, State from channel_manager.manager import close_channel from utils.channel import split_channel_name from crypto.Cryptography.Helper import hash_to_wallet_address, bytes_to_hex_string, hex2interger smart_contract = SmartContract(Configure["TNC"].replace("0x", "")) ContractAddr = Configure["ContractAddr"] DepositIN = [] DepositOut = [] # Register an event handler for Runtime.Notify events of the smart contract. @smart_contract.on_notify def sc_notify(event): logger.info("SmartContract Runtime.Notify event: %s", event) if not len(event.event_payload): return logger.info("- payload part 1: %s", event.event_payload[0].decode("utf-8")) tx_type = event.event_payload[0]
class BulkProcess(BlockchainMain): # from InputParser parser = ZeroOrMore( Regex(r'\[[^]]*\]') | Regex(r'"[^"]*"') | Regex(r'\'[^\']*\'') | Regex(r'[^ ]+')) smart_contract_hash = None operation = None operation_args_array_length = None expected_result_count = None from_addr = None test_only = False wallet_needs_recovery = False smart_contract = None job = None jobs = None jobs_processed = 0 tx_processing = None def __init__(self): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'bulk-tx-config.json'), 'r') as f: config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', config['job_config_file']), 'r') as f: job_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'network-wallets.json'), 'r') as f: network_wallets_config = json.load(f) super().__init__(NetworkType[config['network']], 'bulk-process-tx') self.test_only = config['test_only'] self.operation = job_config['operation'] self.operation_args_array_length = job_config[ 'operation_args_array_length'] self.expected_result_count = job_config['expected_result_count'] try: self.from_addr = job_config['from_addr'] except KeyError: pass self.jobs = job_config['jobs'] # Setup the smart contract instance self.smart_contract_hash = config['smart_contract'] self.smart_contract = SmartContract(self.smart_contract_hash) # decorate the event handler methods dynamically now that we have loaded the SC self.sc_notify = self.smart_contract.on_notify(self.sc_notify) self.sc_storage = self.smart_contract.on_storage(self.sc_storage) self.sc_execution = self.smart_contract.on_execution(self.sc_execution) self.setup_wallet( network_wallets_config[config['network']]['wallet_path']) def pre_start(self): # trigger the first job to be processed self.process_job() def sc_notify(self, event): if not event.execution_success: return prefix = "" if event.test_mode: prefix = "[test_mode]" elif event.tx_hash != self.tx_processing: # only emit notify events for the transaction that we are waiting on return self.logger.info( prefix + "[SmartContract.Runtime.Notify] [%s] [tx %s] %s", event.contract_hash, event.tx_hash, event.event_payload) def sc_storage(self, event): prefix = "" if event.test_mode: prefix = "[test_mode]" elif event.tx_hash != self.tx_processing: # only emit notify events for the transaction that we are waiting on return self.logger.info(prefix + "[%s] [%s] [tx %s] %s", event.event_type, event.contract_hash, event.tx_hash, event.event_payload) def sc_execution(self, event): # only emit execution events for the transaction that we are waiting on if event.tx_hash != self.tx_processing: return if not event.execution_success: self.logger.error( "[execution_success=false][SmartContract.Runtime.Notify] [%s] [tx %s] %s", event.contract_hash, event.tx_hash, event.event_payload) return prefix = "" if event.test_mode: prefix = "[test_mode]" self.logger.info( prefix + "[SmartContract.Execution.Success] [%s] [tx %s] %s", event.contract_hash, event.tx_hash, event.event_payload) if not event.test_mode: self.jobs_processed += 1 self.process_job() def process_job(self): jobs_remaining = len(self.jobs) self.logger.debug("%s jobs processed. %s jobs remaining.", self.jobs_processed, jobs_remaining) self.tx_processing = None if jobs_remaining > 0: # just pop a job off the array to process next self.job = self.jobs[0] self.jobs = self.jobs[1:] else: # change the jobs array to None (from an empty array) to indicate we are done and can shut down self.jobs = None def custom_background_code(self): """ Custom code run in a background thread. Prints the current block height. This function is run in a daemonized thread, which means it can be instantly killed at any moment, whenever the main thread quits. If you need more safety, don't use a daemonized thread and handle exiting this thread in another way (eg. with signals and events). """ while True: sleep(1) if not self.job: # no more jobs? then shut 'er down! if self.jobs is None: self.shutdown() # if it's a refund job, then check to see if we have the transaction recorded yet. if not, keep waiting. # note that this will give an info log "Could not find transaction for hash b'xxx'" every second until the tx is processed. if self.is_refund_job() and self.tx_processing: tx, height = Blockchain.Default().GetTransaction( self.tx_processing) # the tx will have a height once it's completed! if height > -1: # the tx has been processed, so process the next refund! self.jobs_processed += 1 self.process_job() continue if self.wallet_needs_recovery: self.recover_wallet() self.wallet_needs_recovery = False else: self.wallet_sync() # special handling for sending refunds if self.is_refund_job(): self.process_refund_job() else: self.process_testinvoke_job() def is_refund_job(self): return self.operation == 'send' def process_refund_job(self): if len(self.job) != self.operation_args_array_length: self.logger.error( 'ERROR! must have exactly %d operation args, not %d. skipping! %s', self.operation_args_array_length, len(self.job), self.job) self.job = None self.process_job() return # bl: tx can fail if there are no connected peers, so wait for one self.wait_for_peers() self.logger.debug('processing refund: %s', self.job) # in case we have to rebuild the wallet and try the job again, pass in a new list to construct_and_send # since internally the method actually has a side effect of modifying the array to strip out the from address result = construct_and_send(None, self.wallet, list(self.job), False) if not result: self.wallet_needs_recovery = True else: self.job = None self.tx_processing = result.Hash def process_testinvoke_job(self): job_args = self.parser.parseString(self.operation + " " + str(self.job)) job_args = job_args[0:] if len(job_args) != 2: self.logger.error( 'ERROR! must have only 2 args (operation, params). skipping! %s', job_args) self.job = None self.process_job() return operation_params = parse_param(job_args[1]) if len(operation_params) != self.operation_args_array_length: self.logger.error( 'ERROR! must have exactly %d operation args, not %d. skipping! %s', self.operation_args_array_length, len(operation_params), job_args) self.job = None self.process_job() return args = [self.smart_contract_hash] + job_args self.logger.debug('processing job: %s', args) result = self.test_invoke(args, self.expected_result_count, self.test_only, self.from_addr) if not result: # transaction failed? wallet probably out-of-sync (insufficient funds) so reload it self.wallet_needs_recovery = True else: # this job has been invoked, so clear it out. on to the next. self.job = None if self.test_only: # when testing but not relaying transactions, we just continue to the next job self.jobs_processed += 1 self.process_job() else: # transaction successfully relayed? then let's set the tx Hash that we're waiting for self.tx_processing = result.Hash
class NichePaymentHandler(BlockchainMain): # Setup the smart contract instance smart_contract = None nrve_token_symbol = "NRVE" niche_payment_address = None db_config = None smtp_config = None wallet_needs_recovery = False transfers_to_process = [] transfer_tx_processing = None def __init__(self): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'nrve-niche-config.json'), 'r') as f: config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'db-config.json'), 'r') as f: self.db_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'smtp-config.json'), 'r') as f: self.smtp_config = json.load(f) super().__init__(NetworkType[config['network']], 'nrve-niche-payment-handler') self.smart_contract = SmartContract(config['smart_contract']) self.niche_payment_address = config['niche_payment_address'] self.setup_network() # decorate the event handler methods dynamically now that we have loaded the SC self.sc_notify = self.smart_contract.on_notify(self.sc_notify) def sc_notify(self, event): try: self.do_sc_notify(event) except Exception as e: print("Could not process notify event: %s" % e) traceback.print_stack() traceback.print_exc() raise e def do_sc_notify(self, event): event_payload = event.event_payload if not isinstance( event_payload, ContractParameter ) or event_payload.Type != ContractParameterType.Array: self.logger.info( "[invalid event_payload] SmartContract Runtime.Notify event: %s", event) return payload = event_payload.Value # Make sure that the event payload list has at least one element. if not len(payload): self.logger.info( "[no event_payload] SmartContract Runtime.Notify event: %s", event) return if event.test_mode: self.logger.info( "[test_mode] SmartContract Runtime.Notify event: %s", event) return if not event.execution_success: self.logger.info( "[execution_success=false] SmartContract Runtime.Notify event: %s", event) return # The event payload list has at least one element. As developer of the smart contract # you should know what data-type is in the bytes, and how to decode it. In this example, # it's just a string, so we decode it with utf-8: event_type = payload[0].Value.decode("utf-8") # Only looking for transfer events, so ignore everything else if event_type != 'transfer': return self.logger.info("[event_payload] Processing event: %s", event) # To address to_address = self.get_address(payload[2].Value) # Ignore transfers between other accounts. only care about payments to the niche payment address if to_address != self.niche_payment_address: self.logger.info("- ignoring unknown %s: to %s; not %s", event_type, to_address, self.niche_payment_address) return # From address & NRVE amount from_address = self.get_address(payload[1].Value) raw_nrve_amount = payload[3].Value # bl: there can be different data types returned in the amount payload for some reason, so detect which it is (BigInteger/int or bytes) if isinstance(raw_nrve_amount, int): nrve_amount = raw_nrve_amount else: nrve_amount = int.from_bytes(raw_nrve_amount, 'little') # bl: event.tx_hash is a UInt256, so convert it to a hex string tx_hash = event.tx_hash.ToString() self.process_nrve_transaction(event, event_type, from_address, nrve_amount, tx_hash) def process_nrve_transaction(self, event, event_type, from_address, nrve_amount, tx_hash): # Connect to the database connection = pymysql.connect(host=self.db_config['host'], user=self.db_config['user'], password=self.db_config['password'], db=self.db_config['db'], charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) try: with connection.cursor() as cursor: log = "- payment %s: from %s: %s NRVE (tx: %s)" % ( event_type, from_address, nrve_amount, tx_hash) self.logger.info(log) sql = ("select oid from `NrvePayment`\n" "where fromNeoAddress = %s\n" "and nrveAmount = %s\n" "and paymentStatus = 0\n" "and transactionId is null\n" "for update;") args = (from_address, nrve_amount) cursor.execute(sql, args) if cursor.rowcount == 0: # This could be one of two scenarios: # 1. Transaction does not exist, making it invalid (Refund). # 2. Transaction was processed by a different thread (transactionId has been updated to a non-null value). self.handle_unknown_transaction(connection, event, from_address, nrve_amount, tx_hash) return elif cursor.rowcount > 1: subject = 'FATAL! Identified multiple payments by unique key. Should not be possible!' self.logger.error(subject + ': %s', event) self.send_email( subject, self.format_error_message( {"Transaction Id": tx_hash}, {"From Address": from_address}, {"To Address": self.niche_payment_address}, {"NRVE Amount": nrve_amount}, {"Number of Transactions": cursor.rowcount})) return block = self.blockchain.GetHeaderByHeight(event.block_number) # when a payment is outstanding, it will be recorded with the expected "from address", the proper # nrveAmount (in "neurons") and a paymentStatus of 0 which indicates it's pending payment sql = ("update `NrvePayment`\n" "set transactionId = %s\n" ", transactionDate = from_unixtime(%s)\n" ", foundByExternalApi = 0\n" "where fromNeoAddress = %s\n" "and nrveAmount = %s\n" "and paymentStatus = 0\n" "and transactionId is null;") args = (tx_hash, block.Timestamp, from_address, nrve_amount) # Create a new record cursor.execute(sql, args) if cursor.rowcount != 1: subject = 'Failed updating payment. Should not be possible since it was already locked for update' self.logger.error(subject + ': %s', event) self.send_email( subject, self.format_error_message( {"Transaction Id": tx_hash}, {"From Address": from_address}, {"To Address": self.niche_payment_address}, {"NRVE Amount": nrve_amount})) return self.send_email("Successful Niche Payment", log) # connection is not autocommit by default. So you must commit to save your changes. connection.commit() except MySQLError as e: error_message = 'ERROR: event %s: {!r}, errno is {}'.format( event, e, e.args[0]) self.logger.error(error_message) self.send_email('Niche Payment Error', error_message) finally: connection.close() def handle_unknown_transaction(self, connection, event, from_address, nrve_amount, tx_hash): try: with connection.cursor() as cursor: sql = ( "select oid from `NrvePayment` where transactionId = %s;") params = tx_hash cursor.execute(sql, params) if cursor.rowcount == 0: # Send refund email subject = 'Failed identifying niche payment. ' + self.network_type + ' refund required!' self.logger.error(subject + ': %s', event) self.send_email( subject, self.format_error_message( {"Transaction Id": tx_hash}, {"From Address": from_address}, {"To Address": self.niche_payment_address}, {"NRVE Amount": nrve_amount})) return elif cursor.rowcount == 1: # Transaction is valid, no need to process any further. self.logger.info( "Transaction %s was already processed by a different thread.", tx_hash) return else: self.logger.error( "FATAL! Found %s records for transaction %s. ", cursor.rowcount, tx_hash) return except MySQLError as e: error_message = 'ERROR: event %s: {!r}, errno is {}'.format( event, e, e.args[0]) self.logger.error(error_message) self.send_email('Unable to verify Niche Payment status.', error_message) def send_email(self, subject, body): msg = MIMEText(body) msg['Subject'] = subject msg['From'] = 'Narrative ' + self.network_type + ' <' + self.smtp_config[ 'from_address'] + '>' msg['To'] = self.smtp_config['to_address'] # Send the message via our own SMTP server. # bl: production servers user port 587 s = smtplib.SMTP(self.smtp_config['host'], self.smtp_config['port']) if self.smtp_config['use_tls']: s.starttls() if self.smtp_config['username']: s.login(self.smtp_config['username'], self.smtp_config['password']) s.send_message(msg) s.quit() @staticmethod def format_error_message(*args): message = '' for eachDict in args: for key, value in eachDict.items(): if message != '': message += "\n" message += str(key) + ": " + str(value) return message
def __init__(self, contract_hash, wallet_path, wallet_pass): super(LootMarketsSmartContract, self).__init__() self.daemon = True self.contract_hash = contract_hash self.wallet_path = wallet_path self.wallet_pass = wallet_pass self.smart_contract = SmartContract(contract_hash) self.invoke_queue = Queue() # Setup redis cache. self.redis_cache = redis.StrictRedis(host='localhost', port=6379, db=0) self.calling_transaction = None self.tx_in_progress = None self.wallet = None settings.set_log_smart_contract_events(False) # Setup handler for smart contract Runtime.Notify event. # Here we listen to all notify events. @self.smart_contract.on_notify def sc_notify(event): """ This method catches Runtime.Notify calls, and updates the relevant cache. """ # Log the received smart contract event. logger.info("- SmartContract Event: %s", str(event)) event_name = event.event_payload[0].decode("utf-8") # ==== General Events ==== # Smart contract events that are not specific to a marketplace. # Event: balance_of if event_name == "balance_of": # Convert the given script hash to an address. script_hash = event.event_payload[1] sh = UInt160.UInt160(data=script_hash) address = Crypto.ToAddress(sh) balance = int.from_bytes(event.event_payload[2], 'little') # Save the balance to the cache. logger.info("- Balance of %s updated to %s LOOT", address, balance) self.redis_cache.set("balance:%s" % address, int(balance)) return # Event: get_marketplace_owner if event_name == "get_marketplace_owner": marketplace = event.event_payload[1].decode("utf-8") script_hash = event.event_payload[2] sh = UInt160.UInt160(data=script_hash) address = Crypto.ToAddress(sh) logger.info("- Owner of %s: %s", marketplace, address) self.redis_cache.set("owner:%s" % marketplace, address) return # ==== Marketplace Events ==== # Events that are specific to a marketplace. # Get the name of the marketplace, if it is none this is not a marketplace operation, return. marketplace = event.event_payload[1] if marketplace is not None: marketplace = marketplace.decode("utf-8") else: return # Ignore smart contract events that are not on our marketplace being used. if marketplace != self.marketplace: return # Event: get_inventory if event_name == "get_inventory": # Convert the script hash to an address. script_hash = event.event_payload[2] sh = UInt160.UInt160(data=script_hash) address = Crypto.ToAddress(sh) # After being converted from a byte to an int, append each element to the list. inventory = [] for i in event.event_payload[3]: item_id = int.from_bytes(i, 'little') inventory.append(item_id) # Update the inventory in the redis cache. logger.info("- Setting inventory of %s to %s", address, inventory) self.redis_cache.set("inventory:%s" % address, inventory) self.redis_cache.set("inventoryUpdatedAt:%s" % address, int(time.time())) # Event: get_all_offers if event_name == "get_all_offers": retrieved_offers = event.event_payload[2] # Decode all the offers given in the payload. offers = [] for i in retrieved_offers: # Offer is received like 'offer\x03' so we convert to 'offer3'. # We don't want to show the cached offers to the players. i = i.decode("utf-8") index = ord(i.split('offer')[1]) offer_id = 'offer' + str(index) if offer_id not in self.cached_offers: offers.append(offer_id) # Log the information and save to the cache. logger.info("-Setting offers in marketplace: %s", offers) self.redis_cache.set("offers", offers) self.redis_cache.set("timeOffersUpdated", str(datetime.now())) # Event: get_offer if event_name == "get_offer": print("Event: get_offer") # Get all the relevant information about the offer. offer = event.event_payload[2] address = offer[0] offer_id_encoded = offer[1] # If the offer is empty, return. if not offer: return # We receive the offer index sent from contract in format e.g. "offer\x03", convert to "offer3". index = ord(offer_id_encoded.decode().split('offer')[1]) offer_id = 'offer' + str(index) # Decode the bytes into integers. item_id = int.from_bytes(offer[2], 'little') price = int.from_bytes(offer[3], 'little') # Convert the script hash to an address. script_hash = address sh = UInt160.UInt160(data=script_hash) address = Crypto.ToAddress(sh) # Put the offer information in a list and save it to the redis cache with the offer id as the key. offer_information = [address, offer_id, item_id, price] logger.info("-Setting offer:%s to %s", offer_id, offer_information) self.redis_cache.set(offer_id, offer_information) # Event: Market/Item operation # The game/operator must know if these operations were successfully completed within the smart contract. # All of these notify events are sent in the same format. if event_name in ("cancel_offer", "buy_offer", "put_offer", "give_items", "remove_item"): # Convert the script hash to address. script_hash = event.event_payload[2] sh = UInt160.UInt160(data=script_hash) address = Crypto.ToAddress(sh) # Check if the operation was successfully completed within the smart contract. operation_successful = event.event_payload[3] # Save the address, and result to the cache with the event_name used as a key. self.redis_cache.set(event_name + "%s" % address, operation_successful) logger.info( "-" + event_name + " of address %s was completed: %s", address, operation_successful)
from neo.Implementations.Notifications.LevelDB.NotificationDB import NotificationDB from neo.Settings import settings from neo.Network.api.decorators import json_response, gen_authenticated_decorator, catch_exceptions from neo.Implementations.Wallets.peewee.UserWallet import UserWallet from neo.Prompt.Commands.Invoke import InvokeContract, TestInvokeContract, test_invoke from neo.contrib.smartcontract import SmartContract import urllib.request # setup the protocol to be used PROTOCOL_CONFIG = os.path.join(parent_dir, "protocol.privnet.json") # Setup the smart contract instance smart_contract = SmartContract("0b93cde1096433b2d1d9ddf74f85a7c6e266c4dc") @smart_contract.on_notify def sc_notify(event): logger.info("SmartContract Runtime.Notify event: %s", event) # Make sure that the event payload list has at least one element. if not len(event.event_payload): return # The event payload list has at least one element. As developer of the smart contract # you should know what data-type is in the bytes, and how to decode it. In this example, # it's just a string, so we decode it with utf-8: logger.info("- payload part 1: %s", event.event_payload[0].decode("utf-8"))
from socket import * from base58 import b58encode from threading import Thread from neo.Settings import settings from neocore.Cryptography.Crypto import Crypto from neo.Core.Blockchain import Blockchain from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain from neo.Implementations.Wallets.peewee.UserWallet import UserWallet from neo.Network.NodeLeader import NodeLeader from neo.Prompt.Commands.Invoke import InvokeContract, TestInvokeContract from neo.contrib.smartcontract import SmartContract from twisted.internet import reactor, task contract_address = "3c6a0ee4cecadfd6d3fd06fd7e7eedfa6d57dfe1" smart_contract = SmartContract(contract_address) @smart_contract.on_notify def sc_notify(event): global receiver if len(event.event_payload) != 3: return receiver.addGeo(event.event_payload) class NRCReceiver: def __init__(self, walletPath, walletPwd): self.open_wallet(walletPath, walletPwd)
from logzero import logger from twisted.internet import reactor, task from neo.contrib.smartcontract import SmartContract from neo.Network.NodeLeader import NodeLeader from neo.Core.Blockchain import Blockchain from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain from neo.Settings import settings from neocore.BigInteger import BigInteger # If you want the log messages to also be saved in a logfile, enable the # next line. This configures a logfile with max 10 MB and 3 rotations: # settings.set_logfile("/tmp/logfile.log", max_bytes=1e7, backup_count=3) # Setup the smart contract instance smart_contract = SmartContract("d5537fc7dea2150d250e9d5f0cd67b8b248b3fdf") # Register an event handler for Runtime.Notify events of the smart contract. @smart_contract.on_notify def sc_notify(event): logger.info("SmartContract Runtime.Notify event: %s", event) # Make sure that the event payload list has at least one element. if not len(event.event_payload): return # The event payload list has at least one element. As developer of the smart contract # you should know what data-type is in the bytes, and how to decode it. In this example, # it's just a string, so we decode it with utf-8: byte_array = event.event_payload[0]
class NichePaymentHandler(BlockchainMain): # Setup the smart contract instance smart_contract = None nrve_token_symbol = "NRVE" niche_payment_address = None niche_payment_storage_address = None db_config = None smtp_config = None wallet_needs_recovery = False transfers_to_process = [] transfer_tx_processing = None def __init__(self): with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'nrve-niche-config.json'), 'r') as f: config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'network-wallets.json'), 'r') as f: network_wallets_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'db-config.json'), 'r') as f: self.db_config = json.load(f) with open( os.path.join(os.path.abspath(os.path.dirname(__file__)), 'config', 'smtp-config.json'), 'r') as f: self.smtp_config = json.load(f) super().__init__(NetworkType[config['network']], 'nrve-niche-payment-handler') self.smart_contract = SmartContract(config['smart_contract']) self.niche_payment_address = config['niche_payment_address'] self.niche_payment_storage_address = config[ 'niche_payment_storage_address'] self.setup_wallet( network_wallets_config[config['network']]['wallet_path']) # decorate the event handler methods dynamically now that we have loaded the SC self.sc_notify = self.smart_contract.on_notify(self.sc_notify) def sc_notify(self, event): try: self.do_sc_notify(event) except Exception as e: print("Could not process notify event: %s" % e) traceback.print_stack() traceback.print_exc() raise e def do_sc_notify(self, event): # Make sure that the event payload list has at least one element. if not len(event.event_payload): self.logger.info( "[no event_payload] SmartContract Runtime.Notify event: %s", event) return if event.test_mode: self.logger.info( "[test_mode] SmartContract Runtime.Notify event: %s", event) return if not event.execution_success: self.logger.info( "[execution_success=false] SmartContract Runtime.Notify event: %s", event) return # The event payload list has at least one element. As developer of the smart contract # you should know what data-type is in the bytes, and how to decode it. In this example, # it's just a string, so we decode it with utf-8: event_type = event.event_payload[0].decode("utf-8") # only looking for transfer events, so ignore everything else if event_type != 'transfer': return # from, to, amount from_address = self.get_address(event.event_payload[1]) to_address = self.get_address(event.event_payload[2]) raw_nrve_amount = event.event_payload[3] # bl: there can be different data types returned in the amount payload for some reason, so detect which it is (BigInteger/int or bytes) if isinstance(raw_nrve_amount, int): nrve_amount = raw_nrve_amount else: nrve_amount = int.from_bytes(raw_nrve_amount, 'little') # bl: event.tx_hash is a UInt256, so convert it to a hex string tx_hash = event.tx_hash.ToString() # if this is an outbound NRVE transfer from our payment wallet, then it's a transfer! if from_address == self.niche_payment_address: # in order to move on to the next transfer, we just need to clear the tx, assuming it's the right one! if self.transfer_tx_processing and tx_hash == self.transfer_tx_processing.ToString( ): if to_address == self.niche_payment_storage_address: self.logger.info( "- payment storage %s: to %s: %s NRVE (tx: %s)", event_type, to_address, nrve_amount, tx_hash) else: self.logger.info("- refund %s: to %s: %s NRVE (tx: %s)", event_type, to_address, nrve_amount, tx_hash) self.transfer_tx_processing = None else: log = "%s: to %s: %s NRVE (tx: %s)" % (event_type, to_address, nrve_amount, tx_hash) self.logger.warn("- unexpected outbound transfer! %s", log) self.send_email("Unexpected Outbound Transfer", log) return # ignore transfers between other accounts. only care about payments to the niche payment address if to_address != self.niche_payment_address: return block_number = event.block_number timestamp = self.blockchain.GetHeaderByHeight(block_number).Timestamp # Connect to the database connection = pymysql.connect(host=self.db_config['host'], user=self.db_config['user'], password=self.db_config['password'], db=self.db_config['db'], charset='utf8mb4', cursorclass=pymysql.cursors.DictCursor) try: with connection.cursor() as cursor: log = "- payment %s: from %s: %s NRVE (tx: %s)" % ( event_type, from_address, nrve_amount, tx_hash) self.logger.info(log) sql = ("select oid from `NicheAuctionInvoicePayment`\n" "where fromNeoAddress = %s\n" "and nrveAmount = %s\n" "and paymentStatus = 0\n" "and transactionId is null\n" "for update;") args = (from_address, nrve_amount) cursor.execute(sql, args) if cursor.rowcount == 0: self.logger.error( 'Failed identifying payment. Returning to sender: %s', event) self.transfer_payment(from_address, nrve_amount) return elif cursor.rowcount > 1: self.logger.error( 'FATAL! Identified multiple payments by unique key. Should not be possible! %s', event) self.transfer_payment(from_address, nrve_amount) return # when a payment is outstanding, it will be recorded with the expected from address, the proper nrveAmount (in "neurons") # and a paymentStatus of 0 which indicates it's pending payment sql = ("update `NicheAuctionInvoicePayment`\n" "set transactionId = %s\n" ", transactionDate = from_unixtime(%s)\n" "where fromNeoAddress = %s\n" "and nrveAmount = %s\n" "and paymentStatus = 0\n" "and transactionId is null;") args = (tx_hash, timestamp, from_address, nrve_amount) # Create a new record cursor.execute(sql, args) if cursor.rowcount != 1: self.logger.error( 'Failed updating payment. Should not be possible since it was already locked for update: %s', event) return # send the NRVE to the payment storage address self.transfer_payment(self.niche_payment_storage_address, nrve_amount) self.send_email("Successful Niche Payment", log) # connection is not autocommit by default. So you must commit to save # your changes. connection.commit() except MySQLError as e: error_message = 'ERROR: event %s: {!r}, errno is {}'.format( event, e, e.args[0]) self.logger.error(error_message) self.send_email('Niche Payment Error', error_message) finally: connection.close() def send_email(self, subject, body): msg = MIMEText(body) msg['Subject'] = subject msg['From'] = self.smtp_config['from_address'] msg['To'] = self.smtp_config['to_address'] # Send the message via our own SMTP server. # bl: production servers user port 587 s = smtplib.SMTP(self.smtp_config['host'], self.smtp_config['port']) if self.smtp_config['use_tls']: s.starttls() s.send_message(msg) s.quit() def transfer_payment(self, from_address, nrve_amount): self.transfers_to_process.append([from_address, nrve_amount]) print('transfers_to_process %s', self.transfers_to_process) def custom_background_code(self): count = 0 while True: sleep(1) count += 1 if (count % 60) == 0: self.logger.info("Block %s / %s", str(Blockchain.Default().Height), str(Blockchain.Default().HeaderHeight)) count = 0 # already have a transfer that we are waiting to process? then just keep waiting until that transaction comes through if self.transfer_tx_processing: continue # no transfers? then keep waiting if not self.transfers_to_process: continue if self.wallet_needs_recovery: self.recover_wallet() self.wallet_needs_recovery = False else: self.wallet_sync() transfer = self.transfers_to_process[0] self.transfers_to_process = self.transfers_to_process[1:] if len(transfer) != 2: self.logger.error( 'ERROR! transfer must have exactly 2 args. skipping! %s', transfer) continue to_address = transfer[0] if to_address == self.niche_payment_storage_address: self.logger.debug('processing payment storage: %s', transfer) else: self.logger.debug('processing refund: %s', transfer) token = get_asset_id(self.wallet, self.nrve_token_symbol) print('found token %s', token) result = do_token_transfer(token, self.wallet, self.niche_payment_address, to_address, transfer[1], False) if not result: # transaction failed? wallet probably out-of-sync (insufficient funds) so reload it self.wallet_needs_recovery = True # we need to try to process this transfer again, so add it back in to the list self.transfers_to_process = [transfer ] + self.transfers_to_process else: # transaction successfully relayed? then let's set the tx Hash that we're waiting for self.transfer_tx_processing = result.Hash
from neo.Network.NodeLeader import NodeLeader from twisted.internet import reactor, task from neo.Core.Blockchain import Blockchain, Events from neo.SmartContract.StateReader import StateReader from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain from neo.Settings import settings from neocore.Cryptography.Crypto import Crypto from neocore.UInt160 import UInt160 import discord client = discord.Client() DISCORD_TOKEN = '' CHANNEL_ID = '' smart_contract = SmartContract('2d838efcda02e9b6bc42ce21ce34acad14b58923') @smart_contract.on_notify def sc_notify(event): if len(event.event_payload): #print("***** got new notify payload {}".format(event.event_payload[0])) if event.event_payload[0].decode("utf-8") == 'new_king': address = event.event_payload[1] bounty = int(event.event_payload[2]) newKingMessage = '' if len(event.event_payload[3]) > 0: name = event.event_payload[3].decode("utf-8", "ignore") newKingMessage = '{} is now king. Next bounty is {} TUT'.format( name, bounty / 100000000) else:
# If you want to enable logging to a file, set the filename here: LOGFILE = os.getenv("NEO_REST_LOGFILE", None) # Internal: if LOGFILE is set, file logging will be setup with max # 10 MB per file and 3 rotations: if LOGFILE: settings.set_logfile(LOGFILE, max_bytes=1e7, backup_count=3) # Internal: get the API token from an environment variable API_AUTH_TOKEN = os.getenv("NEO_REST_API_TOKEN", None) if not API_AUTH_TOKEN: raise Exception("No NEO_REST_API_TOKEN environment variable found!") # Internal: setup the smart contract instance smart_contract = SmartContract(SMART_CONTRACT_HASH) # Internal: setup the klein instance app = Klein() # Internal: generate the @authenticated decorator with valid tokens authenticated = gen_authenticated_decorator(API_AUTH_TOKEN) # # Smart contract event handler for Runtime.Notify events # @smart_contract.on_notify def sc_notify(event): logger.info("SmartContract Runtime.Notify event: %s", event)
from neo.contrib.smartcontract import SmartContract from neo.Network.NodeLeader import NodeLeader from neo.Core.Blockchain import Blockchain from neo.Implementations.Blockchains.LevelDB.LevelDBBlockchain import LevelDBBlockchain from neo.Settings import settings from neocore.Cryptography.Crypto import Crypto, UInt160 from dotenv import load_dotenv load_dotenv(os.path.abspath(os.path.join('.', '.env'))) # If you want the log messages to also be saved in a logfile, enable the # next line. This configures a logfile with max 10 MB and 3 rotations: # settings.set_logfile("/tmp/logfile.log", max_bytes=1e7, backup_count=3) # Setup the smart contract instance smart_contract = SmartContract("fdb94040d3578817cc9293f95b8ddae75d87ac57") # Register an event handler for Runtime.Notify events of the smart contract. @smart_contract.on_notify def sc_notify(event): logger.info("SmartContract Runtime.Notify event: %s", event) # Make sure that the event payload list has at least one element. if not len(event.event_payload): return # The event payload list has at least one element. As developer of the smart contract # you should know what data-type is in the bytes, and how to decode it. In this example, # it's just a string, so we decode it with utf-8: