Exemplo n.º 1
0
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
Exemplo n.º 2
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)
Exemplo n.º 3
0
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
Exemplo n.º 4
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)
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
    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)
Exemplo n.º 8
0
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
Exemplo n.º 9
0
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]}
            )