def test_factory(w3, exchange_template, HAY_token, factory, pad_bytes32, exchange_abi, assert_fail): a0, a1 = w3.eth.accounts[:2] # Can't call initializeFactory on factory twice with raises(TransactionFailed): factory.initializeFactory(HAY_token.address) # Factory initial state assert factory.exchangeTemplate() == exchange_template.address assert factory.getExchange(HAY_token.address) == None # Create Exchange for MRS Token factory.createExchange(HAY_token.address, transact={}) HAY_exchange_address = factory.getExchange(HAY_token.address) assert HAY_exchange_address != None HAY_exchange = ConciseContract( w3.eth.contract(address=HAY_exchange_address, abi=exchange_abi)) assert factory.getToken(HAY_exchange.address) == HAY_token.address assert factory.tokenCount() == 1 assert factory.getTokenWithId(1) == HAY_token.address # Exchange already exists with raises(TransactionFailed): factory.createExchange(HAY_token.address) # Can't call setup on exchange assert_fail(lambda: HAY_exchange.setup(factory.address)) # Exchange initial state assert HAY_exchange.name() == pad_bytes32('Marswap V1') assert HAY_exchange.symbol() == pad_bytes32('MRS-V1') assert HAY_exchange.decimals() == 18 assert HAY_exchange.totalSupply() == 0 assert HAY_exchange.tokenAddress() == HAY_token.address assert HAY_exchange.factoryAddress() == factory.address assert w3.eth.getBalance(HAY_exchange.address) == 0 assert HAY_token.balanceOf(HAY_exchange.address) == 0
def get_transaction(cls, tx_hash): tx = web3.eth.getTransaction(tx_hash) contract_instance = cls.erc20_contracts.get(tx.to, None) if contract_instance == None: raise Exception('Not supported contract address for swap:%s' % tx.to) contract = ConciseContract(contract_instance) func, paras = contract_instance.decode_function_input(tx.input) if str(func) != '<Function transfer(address,uint256)>': raise Exception('Unexpect contract method for %s' % tx_hash) if paras['_to'].lower() != config.ETHEREUM_SCAN_ADDRESS.lower(): raise Exception('Unexpect reveive address for %s' % tx_hash) amount, decimals = cls.Wei2Satoshi(paras['_value'], contract.decimals()) totalSupply, _ = cls.Wei2Satoshi(contract.totalSupply(), contract.decimals()) if totalSupply > 0xFFFFFFFFFFFFFFFF: totalSupply = 0xFFFFFFFFFFFFFFFF return contract.symbol(), tx['from'], (amount, totalSupply, decimals)
def test_factory(w3, exchange_template, HAY_token, exchange_factory, pad_bytes32, exchange_abi): a0, a1 = w3.eth.accounts[:2] # Factory initial state assert exchange_factory.exchangeTemplate() == exchange_template.address assert exchange_factory.getExchange(HAY_token.address) == None # Create Exchange for UNI Token exchange_factory.createExchange(HAY_token.address, transact={}) HAY_exchange_address = exchange_factory.getExchange(HAY_token.address) assert HAY_exchange_address != None HAY_exchange = ConciseContract(w3.eth.contract(address=HAY_exchange_address, abi=exchange_abi)) assert exchange_factory.getToken(HAY_exchange.address) == HAY_token.address assert exchange_factory.tokenCount() == 1 assert exchange_factory.getTokenWithId(1) == HAY_token.address # # Can't call initializeFactory on factory twice # assert_tx_failed(lambda: exchange_factory.initializeFactory(HAY_token.address)) # # Exchange already exists # assert_tx_failed(lambda: exchange_factory.createExchange(HAY_token.address)) # # Can't call setup on exchange # assert_tx_failed(lambda: HAY_exchange.setup(exchange_factory.address)) # # Exchange initial state assert HAY_exchange.name() == pad_bytes32('Uniswap V1') assert HAY_exchange.symbol() == pad_bytes32('UNI-V1') assert HAY_exchange.decimals() == 18 assert HAY_exchange.totalSupply() == 0 assert HAY_exchange.tokenAddress() == HAY_token.address assert HAY_exchange.factoryAddress() == exchange_factory.address assert w3.eth.getBalance(HAY_exchange.address) == 0 assert HAY_token.balanceOf(HAY_exchange.address) == 0
def get_token_from_token_contract(self, token_address: str) -> Optional[Token]: """ Getting information from token contract and creating Token. Smart contract is taken based on provided address """ token_address = self.unify_address(token_address) token_contract = self.web3.eth.contract(address=token_address, abi=ERC20_BASIC_ABI) concise = ConciseContract(token_contract) try: name = concise.name() symbol = concise.symbol() decimals = concise.decimals() logger.debug(f'Token get from contract with success') except (OverflowError, BadFunctionCallOutput): logger.warning(f'Unable to take token from address: {token_address}') return return Token(name, symbol, token_address, decimals)
def test_factory_for_eth(w3, eth_flash_template, factory, pad_bytes32, eth_flash_abi, assert_fail): assert factory.getEthFlash(INTEREST_FACTOR) != None assert_fail(lambda: factory.getEthFlash(MAX_INTEREST_FACTOR + 1)) assert_fail(lambda: eth_flash.setup(INTEREST_FACTOR)) eth_flash_address = factory.getEthFlash(INTEREST_FACTOR) assert eth_flash_address != None eth_flash = ConciseContract( w3.eth.contract(address=eth_flash_address, abi=eth_flash_abi)) assert w3.eth.getBalance(eth_flash.address) == 0 assert eth_flash.factoryAddress() == factory.address assert eth_flash.name() == pad_bytes32('Uniflash for ETH V1') assert eth_flash.symbol() == pad_bytes32('UFO-V1') assert eth_flash.totalSupply() == 0 assert eth_flash.interestFactor() == INTEREST_FACTOR
def test_factory_for_erc20(w3, erc20_flash_template, HAY_token, factory, pad_bytes32, erc20_flash_abi, assert_fail): assert factory.getErc20Flash(HAY_token.address, INTEREST_FACTOR) == None factory.createErc20Flash(HAY_token.address, transact={}) assert_fail( lambda: factory.createErc20Flash(HAY_token.address, transact={})) assert_fail(lambda: factory.getErc20Flash(MAX_INTEREST_FACTOR + 1)) assert_fail(lambda: eth_flash.setup(INTEREST_FACTOR)) erc20_flash_address = factory.getErc20Flash(HAY_token.address, INTEREST_FACTOR) assert erc20_flash_address != None erc20_flash = ConciseContract( w3.eth.contract(address=erc20_flash_address, abi=erc20_flash_abi)) assert w3.eth.getBalance(erc20_flash.address) == 0 assert erc20_flash.factoryAddress() == factory.address assert erc20_flash.token() == HAY_token.address assert erc20_flash.name() == pad_bytes32('Uniflash for ETH V1') assert erc20_flash.symbol() == pad_bytes32('UFO-V1') assert erc20_flash.totalSupply() == 0 assert erc20_flash.interestFactor() == INTEREST_FACTOR
def get_token_from_token_contract(self, token_address: str) -> Optional[Token]: ''' Getting information from token contract (remote) Args: token_address (str): address of the token contract Returns: Token, None: Ethereum Token namedtuple or None if there is something goes wrong Raises: RuntimeError: if name or symbol of the token is not defined. Example: >>> from clove.network import EthereumTestnet >>> network = EthereumTestnet() >>> network.get_token_from_token_contract('0x2c76B98079Bb5520FF4BDBC1bf5012AC3E87ddF6') Token(name='PrettyGoodToken', symbol='PGT', address='0x2c76B98079Bb5520FF4BDBC1bf5012AC3E87ddF6', decimals=18) # noqa: E501 ''' token_address = self.unify_address(token_address) token_contract = self.web3.eth.contract(address=token_address, abi=ERC20_BASIC_ABI) concise = ConciseContract(token_contract) try: name = concise.name() symbol = concise.symbol() decimals = concise.decimals() if name == '' or symbol == '': raise RuntimeError( 'Unable to extract token details from token contract') logger.debug(f'Token get from contract with success') except (OverflowError, BadFunctionCallOutput): logger.warning( f'Unable to take token from address: {token_address}') return return Token(name, symbol, token_address, decimals)
class ContractInterface: ''' This python interface mimics the solidity smart contract interface deployed on the blockchain and its methods. This way, it's much simpler and easier on the eyes the interaction with the smart contract functions from the client side ''' CONTRACT_PATH = path.dirname( path.abspath(__file__)) + "/../contracts/token.sol" COMPILED_CONTRACT_PATH = path.dirname( path.abspath(__file__)) + "/../contracts/contract_compiled.pickle" # Client number is only for tests def __init__(self, account=None, ip_address=None, mac_address=None, w3=None, client_number: int = None): ''' Default constructor of the interface class, with parameters that make the code much cleaner in our client API . For now the only parameter is the client number which identifies which of the default accounts provided by ganache-cli the client sets as its default one {1 - 9} since 0 is for the server and owner of the contract ''' # In case there's no w3 given we initialize to the default server if w3 is None: self.w3 = Web3(HTTPProvider("http://localhost:8545")) #self.w3 = Web3(HTTPProvider("http://192.168.0.29:8545")) else: self.w3 = w3 # Regular register (not from proxy) if ip_address is None and mac_address is None: self.IP = utils.getIP() self.MAC = utils.getMAC() else: self.IP = ip_address self.MAC = mac_address self.contract = self._getContract(self.w3) #self.contract = self._load_contract(self.w3) # The ConciseContract class is much better for reaidng variables straight away. # If our goal is not transact something, we'll use the Concise version of the contract self.contractConcise = ConciseContract(self.contract) # set the default account for the client, if it's None we'll register and we'll get a new one # Only for tests if client_number is not None: self.w3.eth.defaultAccount = self.w3.eth.accounts[client_number] self.account = self.w3.eth.defaultAccount else: # Usamos el w3 del proxy en ese caso # The client's own address is in the account variable self.account = account # The default account is however the one of the server # DATA FOR LEASES: # The dictionary is of the shape { grant_id : amount } so for the total memory we # just add the values self.remoteStorage = {} # The dictionary is of the shape { grant_id : amount } where amount is a number from # 0 to 100. 100 would mean that the amount is equal to all the available storage on # the remote server self.remoteCPU = {} # Load the reservations self._load_reservations() def register(self): ''' Registers the client in the server's contract, and sends the useful information so that the server can recognize each client ''' tx_hash = None print('Registering with', self.account) # Regular register tx_hash = self.contract.functions.register(self.account, self.IP, self.MAC).transact() self.w3.eth.waitForTransactionReceipt(tx_hash) # build a listener so we can get the server's response server_answer_filter = self.contract.events.RegisterResponse.createFilter( fromBlock=0, toBlock='latest', argument_filters={'account': self.account}) print('Created filter to wait for server register response') while True: response = server_answer_filter.get_new_entries() if len(response) != 0: for grant in response: args = grant['args'] if args['accepted']: print('Accepted request') # If we don't have an account we stablish it if self.account is None: self.account = args['account'] self.w3.eth.defaultAccount = self.account self._pickle_address() print('Address pickled') else: print('Grant rejected') break else: print('No answer yet') time.sleep(1) print('Balance after register:', self.contractConcise.balanceOf(self.account), self.contractConcise.symbol()) del server_answer_filter def transfer(self, to: str, amount: int): ''' Transfers to the specified ethereum account (expressed as a hexadecimal string) the specified amount of IoT (tokens) ''' print('Balances before: {}, {}'.format( self.contractConcise.balanceOf(self.account), self.contractConcise.balanceOf(to))) tx_hash = self.contract.functions.transfer(self.account, to, amount).transact() self.w3.eth.waitForTransactionReceipt(tx_hash) print('Balances after: {}, {}'.format( self.contractConcise.balanceOf(self.account), self.contractConcise.balanceOf(to))) def request_storage(self, amount: int): ''' Requests storage from the server ''' print('Asking for {} for account {}'.format(amount, self.account)) tx_hash = self.contract.functions.getStorage(self.account, amount).transact() self.w3.eth.waitForTransactionReceipt(tx_hash) # Wait for the storage grant and filter just the ones for us petitionFilter = self.contract.events.StorageResponse.createFilter( fromBlock=0, toBlock='latest', argument_filters={'account': self.account}) print('Created filter for storage grants') while True: response = petitionFilter.get_new_entries() if len(response) != 0: for grant in response: args = grant['args'] if args['accepted']: print('Accepted request') self.remoteStorage[args['grantID']] = int( args['amount']) print('new dict:', self.remoteStorage) else: print('Grant rejected') break else: print('No answer yet') time.sleep(1) # We no longer need the filter del petitionFilter self._update_reservations() def free_storage(self, id): ''' Free the storage acquired from the server ''' print('Removing item with id=', id) tx_hash = self.contract.functions.freeStorage(self.account, id).transact() self.w3.eth.waitForTransactionReceipt(tx_hash) print('freed storage') # Pop the item self.remoteStorage.pop(id) self._update_reservations() def request_computing_power(self, amount: int): """ Request computing power from the server """ print('Asking for {}% of cpu for account {}'.format( amount, self.account)) # Transact and wait for the transaction receipt tx_hash = self.contract.functions.getComputingPower( self.account, amount).transact() self.w3.eth.waitForTransactionReceipt(tx_hash) # Filter the answers to get if our request was granted petition_filter = self.contract.events.CPUResponse.createFilter( fromBlock=0, toBlock='latest', argument_filters={'account': self.account}) print('Created computing power filter for account', self.account) # Wait for our response while True: response = petition_filter.get_new_entries() if len(response) != 0: for answer in response: args = answer['args'] if args['accepted']: print('Our computing power request was granted') self.remoteCPU[args['grantID']] = int(args['amount']) print('New dict: ', self.remoteCPU) else: print('Grant rejected') break else: print('No answer to cpu request yet') time.sleep(1) del petition_filter self._update_reservations() def free_computing_power(self, id): ''' Free the cpu reservation ''' print('Removing cpu reservation with id', id) tx_hash = self.contract.functions.freeComputingPower(self.account, id).transact() self.w3.eth.waitForTransactionReceipt(tx_hash) print('Freed cpu storage') # Pop item self.remoteCPU.pop(id) self._update_reservations() def force_error(self): """ Method to test whether the onlyOwner modifier works properly """ print('Trying to force an error') tx_hash = self.contract.functions._freeStorage(self.account, 500).transact() self.w3.eth.waitForTransactionReceipt(tx_hash) print('Received transaction hash') def _pickle_address(self): """ Saves the device's address in a pickle file """ with open(ADDRESS_PICKLE_FILE, 'wb') as pickle_file: # dump the address pickle.dump(self.account, pickle_file) def _unpickle_address(self): """ Gets this device's address from the file """ if os.path.exists(ADDRESS_PICKLE_FILE): with open(ADDRESS_PICKLE_FILE, 'rb') as pickle_file: self.account = pickle.load(pickle_file) self.w3.eth.defaultAccount = self.account #self.w3.eth.defaultAccount = self.w3.eth.accounts[client_number] #self.account = self.w3.eth.defaultAccount def _update_reservations(self): """ Updates the reservations in the pickle file after a reservation is made or freed """ with open(RESERVATIONS_PICKLE_FILE, 'wb') as pickle_file: # save the dictionaries pickle.dump(self.remoteStorage, pickle_file) pickle.dump(self.remoteCPU, pickle_file) def _load_reservations(self): """ Use pickle to load the remote storage and computing power reservations. This way we make them persistent in case of a reboot of the service. The variables inside the pickle file keep the same name """ if os.path.exists(RESERVATIONS_PICKLE_FILE): with open(RESERVATIONS_PICKLE_FILE, 'rb') as pickle_file: # restore the dictionaries self.remoteStorage = pickle.load(pickle_file) self.remoteCPU = pickle.load(pickle_file) print('Restored storage reservations:\n', self.remoteStorage) print('Restored cpu reservations:\n', self.remoteCPU) def _load_contract(self, w3: Web3): with open(self.COMPILED_CONTRACT_PATH, 'rb') as pickle_file: interface = pickle.load(pickle_file) contract = w3.eth.contract(abi=interface['abi'], address=CONTRACT_ADDRESS) return contract def _getContract(self, w3: Web3): with open(self.CONTRACT_PATH, "r") as contract_file: global CONTRACT_ADDRESS source_code = contract_file.read() compiled = compile_source(source_code) interface = compiled['<stdin>:IoToken'] contract = w3.eth.contract(abi=interface['abi'], address=CONTRACT_ADDRESS) return contract
def test_deploy( pack_deploy, instantiate, accounts, assert_tx_failed, pack_types, pack_prices, get_receipt, get_logs_for_event, w3, #Parameters bundle_sizes, bundle_caps): BLOCK_GAS_LIMIT = 8e6 # Well over double this now, but a possible concern. # Deploy an oracle and a Link Token (pack, processor, referrals, cards, vault, pack_r, processor_r, referrals_r, cards_r, vault_r) = pack_deploy() receipts = { "pack": (pack, pack_r), "processor": (processor, processor_r), "referrals": (referrals, referrals_r), "cards": (cards, cards_r), "vault": (vault, vault_r) } if DEBUG: print("") # new line for name, r in receipts.items(): if DEBUG: print("Gas used to deploy {}:\t{}".format(name, r[1]['gasUsed'])) assert r[1]['gasUsed'] < BLOCK_GAS_LIMIT, "{} uses more gas than block_gas_limit!".format(name) assert r[0].address != 0, "Address returned not expected for {}".format(name) addPack = cards.functions.addPack(pack.address) assert_tx_failed(addPack, {'from': accounts[1]}) addPack.transact({'from': accounts[0]}) tokens = {} # Now each of the bundle tokens need to be deployed # For each, the pack has to be set to point to the bundle and stipulate it's price and size. for i, (rarity, rarity_value) in enumerate(pack_types.items()): setPack = pack.functions.setPack( rarity_value, pack_prices[rarity], "Genesis {} Bundle".format(rarity), "BND{}".format(rarity[0]), bundle_sizes[i], bundle_caps[i], ) assert_tx_failed(setPack, {'from': accounts[1]}) tx_hash = setPack.transact({'from': accounts[0]}) rcpt = get_receipt(tx_hash) assert len(rcpt['logs']) == 1, "Should have one event emitted." assert rcpt['gasUsed'] < BLOCK_GAS_LIMIT, "Too much gas for block!" logs = get_logs_for_event(pack.events.PackAdded, tx_hash) tokens[rarity] = instantiate(logs[0]['args']['bundle'], abi=None, contract="Bundle") processor.functions.setCanSell(tokens[rarity].address, True).transact({'from': accounts[0]}) if DEBUG: print("Created {} Bundles: Size: {}, Cap: {}, Gas: {}".format( rarity, bundle_sizes[i], bundle_caps[i], rcpt['gasUsed']) ) print("Approval Statuses:") for address in accounts[:5]: print("{}: {}".format(address, processor.functions.approvedSellers(accounts[0]).call())) # For each of the tokens, check their variables and try to purchase a bunch of them, # making sure we can't purchase too many for i, (rarity, token) in enumerate(tokens.items()): c = ConciseContract(token) assert c.totalSupply() == 0, "{} tokens should start with supply of zero".format(rarity) assert c.balanceOf(accounts[0]) == 0, "No user should have balance before mint." assert c.balanceOf(accounts[1]) == 0, "No user should have balance before mint." assert c.name() == "Genesis {} Bundle".format(rarity), "Incorrect token name" assert c.symbol() == "BND{}".format(rarity[0]), "Incorrect token symbol" assert c.decimals() == 0, "Incorrect token decimals" assert c.cap() == bundle_caps[i], "Incorrect cap for token" assert c.packType() == pack_types[rarity], "Incorrect packType for token" BUNDLES_TO_BUY = 6 for _ in range(bundle_caps[i] if bundle_caps[i] > 0 else BUNDLES_TO_BUY): # Shouldn't be able to buy if we send no money assert_tx_failed( token.functions.purchaseFor(accounts[0], 1, accounts[1]), {'from': accounts[0]} ) assert processor.functions.approvedSellers(token.address).call(), "Bundle should be approved to sell." tx_hash = token.functions.purchaseFor(accounts[0], 1, accounts[1])\ .transact({'from': accounts[0], 'value': pack_prices[rarity] * bundle_sizes[i]}) rcpt = get_receipt(tx_hash) assert rcpt['gasUsed'] < BLOCK_GAS_LIMIT, "Too much gas for block!" if DEBUG: print("Account0 {} tokens: {}, vault: {} ({})".format( rarity, token.functions.balanceOf(accounts[0]).call(), (vault.functions.total().call())/10**18, w3.eth.getBalance(vault.address)/10**18)) # Now there shouldn't be any more bundles available to buy if bundle_caps[i] > 0: assert_tx_failed( token.functions.purchaseFor(accounts[0], 1, accounts[1]), {'from': accounts[0]} )