コード例 #1
0
    async def get_collectibles(self, address, contract_address=None):

        if not validate_address(address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_address', 'message': 'Invalid Address'})
        if contract_address is not None and not validate_address(contract_address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_contract_address', 'message': 'Invalid Contract Address'})

        if contract_address is None:
            async with self.db:
                collectibles = await self.db.fetch(COLLECTIBLE_UNION_QUERY, address)

            return {"collectibles": [{
                "contract_address": c['contract_address'],
                "value": hex(c['value']),
                "balance": hex(c['value']),
                "name": c['name'],
                "url": c["url"],
                "icon": c['icon']
            } for c in collectibles]}
        else:
            async with self.db:
                collectible = await self.db.fetchrow(
                    "SELECT * FROM collectibles WHERE contract_address = $1 AND ready = true",
                    contract_address)
                if collectible is None:
                    return None
                if collectible['type'] == 2:
                    token_results = await self.db.fetch(
                        "SELECT c.contract_address AS token_id, c.name, c.image, c.creator_address, c.description, c.token_uri, b.balance "
                        "FROM fungible_collectible_balances b "
                        "JOIN fungible_collectibles c ON b.contract_address = c.contract_address "
                        "WHERE c.collectible_address = $1 AND b.balance != '0x0' AND owner_address = $2",
                        contract_address, address)
                    tokens = []
                    for t in token_results:
                        token = dict(t)
                        if token['description'] is None:
                            token['description'] = "Created by: {}, balance: {}".format(token.pop('creator_address'), parse_int(token.pop('balance')))
                        tokens.append(token)
                else:
                    tokens = await self.db.fetch(
                        "SELECT token_id, name, image, description, token_uri FROM collectible_tokens "
                        "WHERE contract_address = $1 AND owner_address = $2",
                        contract_address, address)
                    tokens = [dict(t) for t in tokens]

            if collectible is None:
                return None
            return {
                "contract_address": collectible["contract_address"],
                "name": collectible["name"],
                "icon": collectible["icon"],
                "url": collectible["url"],
                "value": hex(len(tokens)),
                "balance": hex(len(tokens)),
                "tokens": tokens
            }
コード例 #2
0
    async def get_collectibles(self, address, contract_address=None):

        if not validate_address(address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_address',
                'message': 'Invalid Address'
            })
        if contract_address is not None and not validate_address(
                contract_address):
            raise JsonRPCInvalidParamsError(
                data={
                    'id': 'invalid_contract_address',
                    'message': 'Invalid Contract Address'
                })

        if contract_address is None:
            async with self.db:
                collectibles = await self.db.fetch(
                    "SELECT t.contract_address, COUNT(t.token_id) AS value, c.name, c.icon, c.url "
                    "FROM collectible_tokens t "
                    "JOIN collectibles c ON c.contract_address = t.contract_address "
                    "WHERE t.owner_address = $1 AND c.ready = true "
                    "GROUP BY t.contract_address, c.name, c.icon, c.url",
                    address)

            return {
                "collectibles": [{
                    "contract_address": c['contract_address'],
                    "value": hex(c['value']),
                    "name": c['name'],
                    "url": c["url"],
                    "icon": c['icon']
                } for c in collectibles]
            }
        else:
            async with self.db:
                collectible = await self.db.fetchrow(
                    "SELECT * FROM collectibles WHERE contract_address = $1 AND ready = true",
                    contract_address)
                tokens = await self.db.fetch(
                    "SELECT * FROM collectible_tokens "
                    "WHERE contract_address = $1 AND owner_address = $2",
                    contract_address, address)

            if collectible is None:
                return None
            return {
                "contract_address": collectible["contract_address"],
                "name": collectible["name"],
                "icon": collectible["icon"],
                "url": collectible["url"],
                "value": hex(len(tokens)),
                "tokens": [dict(t) for t in tokens]
            }
コード例 #3
0
    async def get_transaction_count(self, address):

        if not validate_address(address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_address',
                'message': 'Invalid Address'
            })

        # get the network nonce
        nw_nonce = await self.eth.eth_getTransactionCount(address)

        # check the database for queued txs
        async with self.db:
            nonce = await self.db.fetchval(
                "SELECT nonce FROM transactions "
                "WHERE from_address = $1 "
                "AND (status is NULL OR status = 'queued' OR status = 'unconfirmed') "
                "ORDER BY nonce DESC", address)

        #nonce = nonce[0]['nonce'] if nonce else None
        if nonce is not None:
            # return the next usable nonce
            nonce = nonce + 1
            if nonce < nw_nonce:
                return nw_nonce
            return nonce
        else:
            return nw_nonce
コード例 #4
0
    async def unsubscribe(self, *addresses):
        for address in addresses:
            if not validate_address(address):
                raise JsonRPCInvalidParamsError(data={'id': 'bad_arguments', 'message': 'Bad Arguments'})

        await self.request_handler.unsubscribe(addresses)

        return True
コード例 #5
0
    async def get_balance(self, address):

        if not validate_address(address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_address', 'message': 'Invalid Address'})

        confirmed, unconfirmed, _, _ = await self.get_balances(address)

        return {
            "confirmed_balance": hex(confirmed),
            "unconfirmed_balance": hex(unconfirmed)
        }
コード例 #6
0
ファイル: handlers.py プロジェクト: wd1/app_ethereum_service
    async def post(self):

        toshi_id = self.verify_request()
        payload = self.json

        if 'addresses' not in payload or len(payload['addresses']) == 0:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        addresses = payload['addresses']

        for address in addresses:
            if not validate_address(address):
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id': 'bad_arguments',
                                            'message': 'Bad Arguments'
                                        }]
                                    })

        async with self.db:

            # see if this toshi_id is already registered, listening to it's own toshi_id
            rows = await self.db.fetch(
                "SELECT * FROM notification_registrations "
                "WHERE toshi_id = $1 AND eth_address = $1 AND service != 'ws'",
                toshi_id)
            if rows:
                if len(rows) > 1:
                    log.warning(
                        "LEGACY REGISTRATION FOR '{}' HAS MORE THAN ONE DEVICE OR SERVICE"
                        .format(toshi_id))
                registration_id = rows[0]['registration_id']
                service = rows[0]['service']
            else:
                service = 'LEGACY'
                registration_id = 'LEGACY'

            # simply store all the entered addresses with no service/registrations id
            for address in addresses:
                await self.db.execute(
                    "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) "
                    "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING",
                    toshi_id, service, registration_id, address)

            await self.db.commit()

        self.set_status(204)
コード例 #7
0
    async def filter(self, *, address=None, topic=None):
        if address is not None:
            if not validate_address(address):
                raise JsonRPCInvalidParamsError(data={'id': 'bad_arguments', 'message': 'Invalid Adddress'})
        if topic is not None:
            try:
                topic_id, topic = encode_topic(topic)
            except ValueError:
                raise JsonRPCInvalidParamsError(data={'id': 'bad_arguments', 'message': 'Invalid Topic'})

        filter_id = await self.request_handler.filter(address, topic_id, topic)
        return filter_id
コード例 #8
0
    async def post(self, service):

        toshi_id = self.verify_request()
        payload = self.json

        if 'registration_id' not in payload:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        # eth address verification (if none is supplied, delete all the matching addresses)
        if 'address' in payload:
            eth_addresses = [payload['address']]
        elif 'addresses' in payload:
            eth_addresses = payload['addresses']
            if not type(eth_addresses) == list:
                raise JSONHTTPError(400,
                                    data={
                                        'id': 'bad_arguments',
                                        'message': '`addresses` must be a list'
                                    })
        else:
            eth_addresses = []

        if not all(
                validate_address(eth_address)
                for eth_address in eth_addresses):
            raise JSONHTTPError(400,
                                data={
                                    'id': 'bad_arguments',
                                    'message': 'Bad Arguments'
                                })

        async with self.db:
            if eth_addresses:
                await self.db.executemany(
                    "DELETE FROM notification_registrations WHERE toshi_id = $1 AND service = $2 AND registration_id = $3 and eth_address = $4",
                    [(toshi_id, service, payload['registration_id'],
                      eth_address) for eth_address in eth_addresses])
            else:
                await self.db.execute(
                    "DELETE FROM notification_registrations WHERE toshi_id = $1 AND service = $2 AND registration_id = $3",
                    toshi_id, service, payload['registration_id'])

            await self.db.commit()

        request_to_migrate(toshi_id)
        self.set_status(204)
        self.track(toshi_id, "Deregistered ETH notifications")
コード例 #9
0
    async def post(self, service):
        toshi_id = self.verify_request()
        payload = self.json

        if not all(arg in payload for arg in ['registration_id']):
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        # eth address verification (default to toshi_id if eth_address is not supplied)
        if 'address' in payload:
            eth_addresses = [payload['address']]
        elif 'addresses' in payload:
            eth_addresses = payload['addresses']
            if not type(eth_addresses) == list:
                raise JSONHTTPError(400,
                                    data={
                                        'id': 'bad_arguments',
                                        'message': '`addresses` must be a list'
                                    })
        else:
            raise JSONHTTPError(400,
                                data={
                                    'id': 'bad_arguments',
                                    'message': 'Bad Arguments'
                                })

        if not all(
                validate_address(eth_address)
                for eth_address in eth_addresses):
            raise JSONHTTPError(400,
                                data={
                                    'id': 'bad_arguments',
                                    'message': 'Bad Arguments'
                                })

        async with self.db:

            await self.db.executemany(
                "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) "
                "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING",
                [(toshi_id, service, payload['registration_id'], eth_address)
                 for eth_address in eth_addresses])

            await self.db.commit()

        request_to_migrate(toshi_id)
        self.set_status(204)
コード例 #10
0
    async def remove_token(self, *, contract_address):

        if not self.user_toshi_id:
            raise JsonRPCInvalidParamsError(data={'id': 'bad_arguments', 'message': "Missing authorisation"})

        if not validate_address(contract_address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_address', 'message': 'Invalid Contract Address'})
        contract_address = contract_address.lower()

        async with self.db:
            await self.db.execute("UPDATE token_balances SET visibility = 0 "
                                  "WHERE eth_address = $1 AND contract_address = $2",
                                  self.user_toshi_id, contract_address)
            await self.db.commit()
コード例 #11
0
    def test_validate_address(self):

        self.assertTrue(
            utils.validate_address(
                "0x056db290f8ba3250ca64a45d16284d04bc6f5fbf"))
        self.assertTrue(
            utils.validate_address(
                u"0x056db290f8ba3250ca64a45d16284d04bc6f5fbf"))
        self.assertFalse(utils.validate_address("hello"))
        self.assertFalse(utils.validate_address("0x12345"))
        self.assertFalse(utils.validate_address(None))
        self.assertFalse(utils.validate_address({}))
        self.assertFalse(
            utils.validate_address(0x056db290f8ba3250ca64a45d16284d04bc6f5fbf))
        self.assertFalse(
            utils.validate_address(
                "0x114655db4898a6580f0abfc53fc0c0a88110724abf8d41f2abf206c69d7d4c821ed2cdf6939484ef6aebc39ce5662363b82140106bbc374a0f1381b6948214b001"
            ))
コード例 #12
0
    async def list_users(self, *, toshi_ids=None, payment_addresses=None):

        sql = "SELECT u.* FROM users u JOIN (VALUES "
        values = []
        if toshi_ids is not None:
            column = 'toshi_id'
            addresses = toshi_ids
        elif payment_addresses is not None:
            column = 'payment_address'
            addresses = payment_addresses
        else:
            raise Exception(
                "list_users called without toshi_ids or payment_addresses")
        for i, address in enumerate(addresses):
            if not validate_address(address):
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id': 'bad_arguments',
                                            'message': 'Bad Arguments'
                                        }]
                                    })
            values.append("('{}', {}) ".format(address, i))
            # in case this request is made with a lot of ids
            # make sure we yield to other processes rather
            # than taking up all resources on this
            if i > 0 and i % 100 == 0:
                await asyncio.sleep(0.001)
        sql += ", ".join(values)
        sql += ") AS v ({column}, ordering) ON u.{column} = v.{column} ".format(
            column=column)
        sql += "ORDER BY v.ordering"

        async with self.db:
            rows = await self.db.fetch(sql)

        self.write({
            'limit': len(rows),
            'total': len(rows),
            'offset': 0,
            'query': '',
            'results': [user_row_for_json(row) for row in rows]
        })
コード例 #13
0
ファイル: handlers.py プロジェクト: wd1/app_ethereum_service
    async def post(self):

        toshi_id = self.verify_request()
        payload = self.json

        if 'addresses' not in payload or len(payload['addresses']) == 0:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        addresses = payload['addresses']

        for address in addresses:
            if not validate_address(address):
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id': 'bad_arguments',
                                            'message': 'Bad Arguments'
                                        }]
                                    })

        async with self.db:

            await self.db.execute(
                "DELETE FROM notification_registrations WHERE service != 'ws' AND toshi_id = $1 AND ({})"
                .format(' OR '.join('eth_address = ${}'.format(i + 2)
                                    for i, _ in enumerate(addresses))),
                toshi_id, *addresses)

            await self.db.commit()

        self.set_status(204)
        self.track(toshi_id, "Deregistered ETH notifications")
コード例 #14
0
ファイル: handlers.py プロジェクト: wd1/app_ethereum_service
    async def post(self, service):

        toshi_id = self.verify_request()
        payload = self.json

        if 'registration_id' not in payload:
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        # TODO: registration id verification

        # eth address verification (if none is supplied, delete all the matching addresses)
        eth_address = payload.get('address', None)
        if eth_address and not validate_address(eth_address):
            raise JSONHTTPError(data={
                'id': 'bad_arguments',
                'message': 'Bad Arguments'
            })

        async with self.db:

            args = [toshi_id, service, payload['registration_id']]
            if eth_address:
                args.append(eth_address)
            await self.db.execute(
                "DELETE FROM notification_registrations WHERE toshi_id = $1 AND service = $2 AND registration_id = $3{}"
                .format("AND eth_address = $4" if eth_address else ""), *args)

            await self.db.commit()

        self.set_status(204)
        self.track(toshi_id, "Deregistered ETH notifications")
コード例 #15
0
    async def get_token_balances(self,
                                 eth_address,
                                 token_address=None,
                                 force_update=None):
        if not validate_address(eth_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_address',
                'message': 'Invalid Address'
            })
        if token_address is not None and not validate_address(token_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_token_address',
                'message': 'Invalid Token Address'
            })

        # get token balances
        async with self.db:
            result = await self.db.execute(
                "UPDATE token_registrations SET last_queried = (now() AT TIME ZONE 'utc') WHERE eth_address = $1",
                eth_address)
            await self.db.commit()
        registered = result == "UPDATE 1"

        if not registered or force_update:
            erc20_dispatcher.update_token_cache("*", eth_address)
            async with self.db:
                await self.db.execute(
                    "INSERT INTO token_registrations (eth_address) VALUES ($1) ON CONFLICT (eth_address) DO NOTHING",
                    eth_address)
                await self.db.commit()

        if token_address:
            async with self.db:
                token = await self.db.fetchrow(
                    "SELECT symbol, name, decimals, format, custom "
                    "FROM tokens WHERE contract_address = $1", token_address)
                if token is None:
                    return None
                if token['custom']:
                    custom_token = await self.db.fetchrow(
                        "SELECT name, symbol, decimals FROM token_balances "
                        "WHERE contract_address = $1 AND eth_address = $2",
                        token_address, eth_address)
                    if custom_token:
                        token = {
                            'name': custom_token['name'],
                            'symbol': custom_token['symbol'],
                            'decimals': custom_token['decimals'],
                            'format': token['format']
                        }
                balance = await self.db.fetchval(
                    "SELECT balance "
                    "FROM token_balances "
                    "WHERE eth_address = $1 AND contract_address = $2",
                    eth_address, token_address)
            if balance is None:
                balance = "0x0"
            details = {
                "symbol": token['symbol'],
                "name": token['name'],
                "decimals": token['decimals'],
                "value":
                balance,  # NOTE: 'value' left in for backwards compatibility
                "balance": balance,
                "contract_address": token_address
            }
            if token['format'] is not None:
                details["icon"] = "{}://{}/token/{}.{}".format(
                    self.request.protocol, self.request.host, token_address,
                    token['format'])
            else:
                details['icon'] = None
            return details
        else:
            async with self.db:
                balances = await self.db.fetch(
                    "SELECT COALESCE(b.symbol, t.symbol) AS symbol, COALESCE(b.name, t.name) AS name, COALESCE(b.decimals, t.decimals) AS decimals, b.balance, b.contract_address, t.format "
                    "FROM token_balances b "
                    "JOIN tokens t "
                    "ON t.contract_address = b.contract_address "
                    "WHERE b.eth_address = $1 AND "
                    "(b.visibility = 2 OR (b.visibility = 1 AND b.balance != '0x0')) "
                    "ORDER BY t.symbol", eth_address)

            tokens = []
            for b in balances:
                details = {
                    "symbol": b['symbol'],
                    "name": b['name'],
                    "decimals": b['decimals'],
                    "value":
                    b['balance'],  # NOTE: 'value' left in for backwards compatibility
                    "balance": b['balance'],
                    "contract_address": b['contract_address']
                }
                if b['format'] is not None:
                    details["icon"] = "{}://{}/token/{}.{}".format(
                        self.request.protocol, self.request.host,
                        b['contract_address'], b['format'])
                else:
                    details['icon'] = None

                tokens.append(details)

            return tokens
コード例 #16
0
    def do_POST(self):

        # TODO: figure out why read is blocking here
        data = self.rfile.read(len(self.rfile.peek()))
        data = data.decode('utf-8')
        data = json.loads(data)

        if self.path == "/v1/tx/skel":

            gas_price = parse_int(
                data['gas_price']) if 'gas_price' in data else DEFAULT_GASPRICE
            gas = parse_int(data['gas']) if 'gas' in data else DEFAULT_STARTGAS
            nonce = parse_int(data['nonce']) if 'nonce' in data else 0

            if 'value' not in data or 'from' not in data or 'to' not in data:
                self.write_data(
                    400, {
                        'errors': [{
                            'id': 'bad_arguments',
                            'message': 'Bad Arguments'
                        }]
                    })
                return
            value = parse_int(data['value'])
            to_address = data['to']
            from_address = data['from']

            if not validate_address(to_address):
                self.write_data(
                    400, {
                        'errors': [{
                            'id': 'invalid_to_address',
                            'message': 'Invalid To Address'
                        }]
                    })
                return
            if not validate_address(from_address):
                self.write_data(
                    400, {
                        'errors': [{
                            'id': 'invalid_from_address',
                            'message': 'Invalid From Address'
                        }]
                    })
                return

            tx = create_transaction(nonce=nonce,
                                    gasprice=gas_price,
                                    startgas=gas,
                                    to=to_address,
                                    value=value)

            transaction = encode_transaction(tx)

            self.write_data(
                200, {
                    "tx_data": {
                        "nonce": hex(nonce),
                        "from": from_address,
                        "to": to_address,
                        "value": hex(value),
                        "startGas": hex(gas),
                        "gasPrice": hex(gas_price)
                    },
                    "tx": transaction
                })

        elif self.path == "/v1/tx":

            tx = decode_transaction(data['tx'])

            if 'signature' in data:

                sig = data_decoder(data['signature'])

                add_signature_to_transaction(tx, sig)

            self.write_data(200, {"tx_hash": data_encoder(tx.hash)})

        else:

            self.write_data(404)
コード例 #17
0
ファイル: handlers.py プロジェクト: wd1/app_ethereum_service
    async def post(self, service):
        toshi_id = self.verify_request()
        payload = self.json

        if not all(arg in payload for arg in ['registration_id']):
            raise JSONHTTPError(400,
                                body={
                                    'errors': [{
                                        'id': 'bad_arguments',
                                        'message': 'Bad Arguments'
                                    }]
                                })

        # TODO: registration id verification

        # XXX: BACKWARDS COMPAT FOR OLD PN REGISTARTION
        # remove when no longer needed
        if 'address' not in payload:
            async with self.db:
                legacy = await self.db.fetch(
                    "SELECT eth_address FROM notification_registrations "
                    "WHERE toshi_id = $1 AND service = 'LEGACY' AND registration_id = 'LEGACY'",
                    toshi_id)
        else:
            legacy = False

        if legacy:

            async with self.db:

                for row in legacy:
                    eth_address = row['eth_address']
                    await self.db.execute(
                        "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) "
                        "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING",
                        toshi_id, service, payload['registration_id'],
                        eth_address)
                await self.db.execute(
                    "DELETE FROM notification_registrations "
                    "WHERE toshi_id = $1 AND service = 'LEGACY' AND registration_id = 'LEGACY'",
                    toshi_id)
                await self.db.commit()

        else:

            # eth address verification (default to toshi_id if eth_address is not supplied)
            eth_address = payload[
                'address'] if 'address' in payload else toshi_id
            if not validate_address(eth_address):
                raise JSONHTTPError(data={
                    'id': 'bad_arguments',
                    'message': 'Bad Arguments'
                })

            async with self.db:

                await self.db.execute(
                    "INSERT INTO notification_registrations (toshi_id, service, registration_id, eth_address) "
                    "VALUES ($1, $2, $3, $4) ON CONFLICT (toshi_id, service, registration_id, eth_address) DO NOTHING",
                    toshi_id, service, payload['registration_id'], eth_address)

                # XXX: temporary fix for old ios versions sending their payment address as toshi_id
                # should be removed after enough time has passed that most people should be using the fixed version
                if eth_address != toshi_id:
                    # remove any apn registrations where toshi_id == eth_address for this eth_address
                    await self.db.execute(
                        "DELETE FROM notification_registrations "
                        "WHERE toshi_id = $1 AND eth_address = $1 AND service = 'apn'",
                        eth_address)

                await self.db.commit()

        self.set_status(204)
コード例 #18
0
ファイル: handlers.py プロジェクト: wd1/app_ethereum_service
    async def get(self, address):

        self.set_header("Access-Control-Allow-Origin", "*")
        self.set_header("Access-Control-Allow-Headers", "x-requested-with")
        self.set_header('Access-Control-Allow-Methods', 'GET')

        offset = parse_int(self.get_argument('offset', '0'))
        limit = parse_int(self.get_argument('limit', '10'))
        status = set([s.lower() for s in self.get_arguments('status')])
        direction = set([d.lower() for d in self.get_arguments('direction')])
        order = self.get_argument('order', 'desc').upper()

        if not validate_address(address) or \
           offset is None or \
           limit is None or \
           (status and not status.issubset(['confirmed', 'unconfirmed', 'queued', 'error'])) or \
           (direction and not direction.issubset(['in', 'out'])) or \
           (order not in ['DESC', 'ASC']):
            raise JSONHTTPError(400,
                                body={
                                    'id': 'bad_arguments',
                                    'message': 'Bad Arguments'
                                })

        query = "SELECT * FROM transactions WHERE "
        args = [address, offset, limit]

        if len(direction) == 0 or len(direction) == 2:
            query += "(from_address = $1 OR to_address = $1) "
        elif 'in' in direction:
            query += "to_address = $1 "
        elif 'out' in direction:
            query += "from_address = $1 "

        if len(status) == 0:
            query += "AND (status != $4 OR status IS NULL) "
            args.append('error')
        else:
            status_query = []
            for s in status:
                if s == 'queued':
                    status_query.extend([
                        "status = ${}".format(len(args) + 1), "status IS NULL"
                    ])
                else:
                    status_query.append("status = ${}".format(len(args) + 1))
                args.append(s)
            query += "AND (" + " OR ".join(status_query) + ") "

        query += "ORDER BY created {} OFFSET $2 LIMIT $3".format(order)

        async with self.db:
            rows = await self.db.fetch(query, *args)

        transactions = []
        for row in rows:
            transactions.append({
                "hash":
                row['hash'],
                "to":
                row['to_address'],
                "from":
                row['from_address'],
                "nonce":
                hex(row['nonce']),
                "value":
                row['value'],
                "gas":
                row['gas'],
                "gas_price":
                row['gas_price'],
                "created_data":
                row['created'].isoformat(),
                "confirmed_data":
                row['updated'].isoformat() if row['blocknumber'] else None,
                "status":
                row['status'] if row['status'] is not None else 'queued',
                "data":
                row['data']
            })
        resp = {
            "transactions": transactions,
            "offset": offset,
            "limit": limit,
            "order": order
        }
        if len(direction) == 1:
            resp['direction'] = direction.pop()
        if status:
            resp['status'] = "&".join(status)
        self.write(resp)
コード例 #19
0
    async def create_transaction_skeleton(self, *, to_address, from_address, value=0, nonce=None, gas=None, gas_price=None, data=None):

        if not validate_address(from_address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_from_address', 'message': 'Invalid From Address'})

        if to_address is not None and not validate_address(to_address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_to_address', 'message': 'Invalid To Address'})

        if from_address != from_address.lower() and not checksum_validate_address(from_address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_from_address', 'message': 'Invalid From Address Checksum'})

        if to_address is not None and to_address != to_address.lower() and not checksum_validate_address(to_address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_to_address', 'message': 'Invalid To Address Checksum'})

        if value:
            value = parse_int(value)
            if value is None or value < 0:
                raise JsonRPCInvalidParamsError(data={'id': 'invalid_value', 'message': 'Invalid Value'})

        # check optional arguments

        # check if we should ignore the given gasprice
        # NOTE: only meant to be here while cryptokitty fever is pushing
        # up gas prices... this shouldn't be perminant
        # anytime the nonce is also set, use the provided gas (this is to
        # support easier overwriting of transactions)
        if gas_price is not None and nonce is None:
            async with self.db:
                whitelisted = await self.db.fetchrow("SELECT 1 FROM from_address_gas_price_whitelist WHERE address = $1", from_address)
                if not whitelisted:
                    whitelisted = await self.db.fetchrow("SELECT 1 FROM to_address_gas_price_whitelist WHERE address = $1", to_address)
            if not whitelisted:
                gas_price = None

        if nonce is None:
            # check cache for nonce
            nonce = await self.get_transaction_count(from_address)
        else:
            nonce = parse_int(nonce)
            if nonce is None:
                raise JsonRPCInvalidParamsError(data={'id': 'invalid_nonce', 'message': 'Invalid Nonce'})

        if data is not None:
            if isinstance(data, int):
                data = hex(data)
            if isinstance(data, str):
                try:
                    data = data_decoder(data)
                except binascii.Error:
                    pass
            if not isinstance(data, bytes):
                raise JsonRPCInvalidParamsError(data={'id': 'invalid_data', 'message': 'Invalid Data field'})
        else:
            data = b''

        if gas is None:
            try:
                gas = await self.eth.eth_estimateGas(from_address, to_address, data=data, value=value)
            except JsonRPCError:
                # this can occur if sending a transaction to a contract that doesn't match a valid method
                # and the contract has no default method implemented
                raise JsonRPCInvalidParamsError(data={'id': 'invalid_data', 'message': 'Unable to estimate gas for contract call'})
        else:
            gas = parse_int(gas)
            if gas is None:
                raise JsonRPCInvalidParamsError(data={'id': 'invalid_gas', 'message': 'Invalid Gas'})

        if gas_price is None:
            # try and use cached gas station gas price
            gas_station_gas_price = self.redis.get('gas_station_standard_gas_price')
            if gas_station_gas_price:
                gas_price = parse_int(gas_station_gas_price)
            if gas_price is None:
                gas_price = self.application.config['ethereum'].getint('default_gasprice', DEFAULT_GASPRICE)
        else:
            gas_price = parse_int(gas_price)
            if gas_price is None:
                raise JsonRPCInvalidParamsError(data={'id': 'invalid_gas_price', 'message': 'Invalid Gas Price'})

        try:
            tx = create_transaction(nonce=nonce, gasprice=gas_price, startgas=gas,
                                    to=to_address, value=value, data=data,
                                    network_id=self.network_id)
        except InvalidTransaction as e:
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_transaction', 'message': str(e)})

        if tx.intrinsic_gas_used > gas:
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_transaction',
                'message': 'Transaction gas is too low. There is not enough gas to cover minimal cost of the transaction (minimal: {}, got: {}). Try increasing supplied gas.'.format(
                    tx.intrinsic_gas_used, gas)})

        transaction = encode_transaction(tx)

        return {"tx": transaction, "gas": hex(gas), "gas_price": hex(gas_price), "nonce": hex(nonce), "value": hex(value)}
コード例 #20
0
    async def get_token(self, contract_address):

        if not validate_address(contract_address):
            raise JsonRPCInvalidParamsError(data={'id': 'invalid_address', 'message': 'Invalid Contract Address'})
        if contract_address != contract_address.lower():
            if not checksum_validate_address(contract_address):
                raise JsonRPCInvalidParamsError(data={'id': 'invalid_address', 'message': 'Invalid Contract Address Checksum'})
            contract_address = contract_address.lower()

        async with self.db:
            row = await self.db.fetchrow(
                "SELECT symbol, name, contract_address, decimals, icon_url, format FROM tokens WHERE contract_address = $1",
                contract_address)

        if row:
            token = {
                'symbol': row['symbol'],
                'name': row['name'],
                'contract_address': row['contract_address'],
                'decimals': row['decimals']
            }
            if self.user_toshi_id:
                async with self.db:
                    balance = await self.db.fetchval(
                        "SELECT balance FROM token_balances WHERE contract_address = $1 and eth_address = $2",
                        contract_address, self.user_toshi_id)
                if balance is None:
                    try:
                        balance = await self.eth.eth_call(to_address=contract_address, data="{}000000000000000000000000{}".format(
                            ERC20_BALANCEOF_CALL_DATA, self.user_toshi_id[2:]))
                    except:
                        log.exception("Unable to get balance of erc20 token {} for address {}".format(contract_address,
                                                                                                      self.user_toshi_id))
                        return None
                    if balance == "0x":
                        balance = "0x0"
                    else:
                        # strip 0 padding
                        balance = hex(int(balance, 16))
                token['balance'] = balance
            if row['icon_url'] is not None:
                token['icon'] = row['icon_url']
            elif row['format'] is not None:
                token['icon'] = "{}://{}/token/{}.{}".format(self.request.protocol, self.request.host,
                                                             token['contract_address'], row['format'])
            else:
                token['icon'] = None
            return token

        balance, name, symbol, decimals = await self._get_token_details(contract_address)

        if balance is None:
            return None

        async with self.db:
            await self.db.execute("INSERT INTO tokens (contract_address, name, symbol, decimals, custom, ready) "
                                  "VALUES ($1, $2, $3, $4, $5, $6) "
                                  "ON CONFLICT (contract_address) DO NOTHING",
                                  contract_address, name, symbol, decimals, True, True)
            await self.db.commit()

        rval = {
            'symbol': symbol,
            'name': name,
            'contract_address': contract_address,
            'decimals': decimals
        }
        if self.user_toshi_id:
            rval['balance'] = hex(balance)
        return rval
コード例 #21
0
    async def create_transaction_skeleton(self,
                                          *,
                                          to_address,
                                          from_address,
                                          value=0,
                                          nonce=None,
                                          gas=None,
                                          gas_price=None,
                                          data=None,
                                          token_address=None):

        if not validate_address(from_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_from_address',
                'message': 'Invalid From Address'
            })

        if to_address is not None and not validate_address(to_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_to_address',
                'message': 'Invalid To Address'
            })

        if from_address != from_address.lower(
        ) and not checksum_validate_address(from_address):
            raise JsonRPCInvalidParamsError(
                data={
                    'id': 'invalid_from_address',
                    'message': 'Invalid From Address Checksum'
                })

        if to_address is not None and to_address != to_address.lower(
        ) and not checksum_validate_address(to_address):
            raise JsonRPCInvalidParamsError(
                data={
                    'id': 'invalid_to_address',
                    'message': 'Invalid To Address Checksum'
                })

        # check if we should ignore the given gasprice
        # NOTE: only meant to be here while cryptokitty fever is pushing
        # up gas prices... this shouldn't be perminant
        # anytime the nonce is also set, use the provided gas (this is to
        # support easier overwriting of transactions)
        if gas_price is not None and nonce is None:
            async with self.db:
                whitelisted = await self.db.fetchrow(
                    "SELECT 1 FROM from_address_gas_price_whitelist WHERE address = $1",
                    from_address)
                if not whitelisted:
                    whitelisted = await self.db.fetchrow(
                        "SELECT 1 FROM to_address_gas_price_whitelist WHERE address = $1",
                        to_address)
            if not whitelisted:
                gas_price = None

        if gas_price is None:
            # try and use cached gas station gas price
            gas_station_gas_price = await self.redis.get(
                'gas_station_standard_gas_price')
            if gas_station_gas_price:
                gas_price = parse_int(gas_station_gas_price)
            if gas_price is None:
                gas_price = config['ethereum'].getint('default_gasprice',
                                                      DEFAULT_GASPRICE)
        else:
            gas_price = parse_int(gas_price)
            if gas_price is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_gas_price',
                    'message': 'Invalid Gas Price'
                })

        if gas is not None:
            gas = parse_int(gas)
            if gas is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_gas',
                    'message': 'Invalid Gas'
                })

        if nonce is None:
            # check cache for nonce
            nonce = await self.get_transaction_count(from_address)
        else:
            nonce = parse_int(nonce)
            if nonce is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_nonce',
                    'message': 'Invalid Nonce'
                })

        if data is not None:
            if isinstance(data, int):
                data = hex(data)
            if isinstance(data, str):
                try:
                    data = data_decoder(data)
                except binascii.Error:
                    pass
            if not isinstance(data, bytes):
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_data',
                    'message': 'Invalid Data field'
                })
        else:
            data = b''

        # flag to force arguments into an erc20 token transfer
        if token_address is not None:
            if not validate_address(token_address):
                raise JsonRPCInvalidParamsError(
                    data={
                        'id': 'invalid_token_address',
                        'message': 'Invalid Token Address'
                    })
            if data != b'':
                raise JsonRPCInvalidParamsError(
                    data={
                        'id': 'bad_arguments',
                        'message': 'Cannot include both data and token_address'
                    })

            if isinstance(value, str) and value.lower() == "max":
                # get the balance in the database
                async with self.db:
                    value = await self.db.fetchval(
                        "SELECT value FROM token_balances "
                        "WHERE contract_address = $1 AND eth_address = $2",
                        token_address, from_address)
                if value is None:
                    # get the value from the ethereum node
                    data = "0x70a08231000000000000000000000000" + from_address[
                        2:].lower()
                    value = await self.eth.eth_call(to_address=token_address,
                                                    data=data)

            value = parse_int(value)
            if value is None or value < 0:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_value',
                    'message': 'Invalid Value'
                })
            data = data_decoder(
                "0xa9059cbb000000000000000000000000{}{:064x}".format(
                    to_address[2:].lower(), value))
            token_value = value
            value = 0
            to_address = token_address

        elif value:

            if value == "max":
                network_balance, balance, _, _ = await self.get_balances(
                    from_address)
                if gas is None:
                    code = await self.eth.eth_getCode(to_address)
                    if code:
                        # we might have to do some work
                        try:
                            gas = await self.eth.eth_estimateGas(from_address,
                                                                 to_address,
                                                                 data=data,
                                                                 value=0)
                        except JsonRPCError:
                            # no fallback function implemented in the contract means no ether can be sent to it
                            raise JsonRPCInvalidParamsError(
                                data={
                                    'id':
                                    'invalid_to_address',
                                    'message':
                                    'Cannot send payments to that address'
                                })
                        attempts = 0
                        # because the default function could do different things based on the eth sent, we make sure
                        # the value is suitable. if we get different values 3 times abort
                        while True:
                            if attempts > 2:
                                log.warning(
                                    "Hit max attempts trying to get max value to send to contract '{}'"
                                    .format(to_address))
                                raise JsonRPCInvalidParamsError(
                                    data={
                                        'id':
                                        'invalid_to_address',
                                        'message':
                                        'Cannot send payments to that address'
                                    })
                            value = balance - (gas_price * gas)
                            try:
                                gas_with_value = await self.eth.eth_estimateGas(
                                    from_address,
                                    to_address,
                                    data=data,
                                    value=value)
                            except JsonRPCError:
                                # no fallback function implemented in the contract means no ether can be sent to it
                                raise JsonRPCInvalidParamsError(
                                    data={
                                        'id':
                                        'invalid_to_address',
                                        'message':
                                        'Cannot send payments to that address'
                                    })
                            if gas_with_value != gas:
                                gas = gas_with_value
                                attempts += 1
                                continue
                            else:
                                break
                    else:
                        # normal address, 21000 gas per transaction
                        gas = 21000
                        value = balance - (gas_price * gas)
                else:
                    # preset gas, run with it!
                    value = balance - (gas_price * gas)
            else:
                value = parse_int(value)
                if value is None or value < 0:
                    raise JsonRPCInvalidParamsError(data={
                        'id': 'invalid_value',
                        'message': 'Invalid Value'
                    })

        if gas is None:
            try:
                gas = await self.eth.eth_estimateGas(from_address,
                                                     to_address,
                                                     data=data,
                                                     value=value)
            except JsonRPCError:
                # this can occur if sending a transaction to a contract that doesn't match a valid method
                # and the contract has no default method implemented.
                # this can also happen if the current state of the blockchain means that submitting the
                # transaction would fail (abort).
                if token_address is not None:
                    # when dealing with erc20, this usually means the user's balance for that token isn't
                    # high enough, check that and throw an error if it's the case, and if not fall
                    # back to the standard invalid_data error
                    async with self.db:
                        bal = await self.db.fetchval(
                            "SELECT value FROM token_balances "
                            "WHERE contract_address = $1 AND eth_address = $2",
                            token_address, from_address)
                    if bal is not None:
                        bal = parse_int(bal)
                        if bal < token_value:
                            raise JsonRPCInsufficientFundsError(
                                data={
                                    'id': 'insufficient_funds',
                                    'message': 'Insufficient Funds'
                                })
                raise JsonRPCInvalidParamsError(
                    data={
                        'id': 'invalid_data',
                        'message': 'Unable to estimate gas for contract call'
                    })
            # if data is present, buffer gas estimate by 20%
            if len(data) > 0:
                gas = int(gas * 1.2)

        try:
            tx = create_transaction(nonce=nonce,
                                    gasprice=gas_price,
                                    startgas=gas,
                                    to=to_address,
                                    value=value,
                                    data=data,
                                    network_id=self.network_id)
        except InvalidTransaction as e:
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_transaction',
                'message': str(e)
            })

        if tx.intrinsic_gas_used > gas:
            raise JsonRPCInvalidParamsError(
                data={
                    'id':
                    'invalid_transaction',
                    'message':
                    'Transaction gas is too low. There is not enough gas to cover minimal cost of the transaction (minimal: {}, got: {}). Try increasing supplied gas.'
                    .format(tx.intrinsic_gas_used, gas)
                })

        transaction = encode_transaction(tx)

        return {
            "tx": transaction,
            "gas": hex(gas),
            "gas_price": hex(gas_price),
            "nonce": hex(nonce),
            "value": hex(token_value) if token_address else hex(value)
        }
コード例 #22
0
        def verify_request(self):
            """Verifies that the signature and the payload match the expected address
            raising a JSONHTTPError (400) if something is wrong with the request"""

            if TOSHI_ID_ADDRESS_HEADER in self.request.headers:
                expected_address = self.request.headers[
                    TOSHI_ID_ADDRESS_HEADER]
            elif self.get_argument(TOSHI_ID_ADDRESS_QUERY_ARG, None):
                expected_address = self.get_argument(
                    TOSHI_ID_ADDRESS_QUERY_ARG)
            elif TOKEN_ID_ADDRESS_HEADER in self.request.headers:
                expected_address = self.request.headers[
                    TOKEN_ID_ADDRESS_HEADER]
            elif self.get_argument(TOKEN_ID_ADDRESS_QUERY_ARG, None):
                expected_address = self.get_argument(
                    TOKEN_ID_ADDRESS_QUERY_ARG)
            else:
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'bad_arguments',
                                            'message':
                                            'Missing Toshi-ID-Address'
                                        }]
                                    })

            if TOSHI_SIGNATURE_HEADER in self.request.headers:
                signature = self.request.headers[TOSHI_SIGNATURE_HEADER]
            elif self.get_argument(TOSHI_SIGNATURE_QUERY_ARG, None):
                signature = self.get_argument(TOSHI_SIGNATURE_QUERY_ARG)
            elif TOKEN_SIGNATURE_HEADER in self.request.headers:
                signature = self.request.headers[TOKEN_SIGNATURE_HEADER]
            elif self.get_argument(TOKEN_SIGNATURE_QUERY_ARG, None):
                signature = self.get_argument(TOKEN_SIGNATURE_QUERY_ARG)
            else:
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'bad_arguments',
                                            'message':
                                            'Missing Toshi-Signature'
                                        }]
                                    })

            if TOSHI_TIMESTAMP_HEADER in self.request.headers:
                timestamp = self.request.headers[TOSHI_TIMESTAMP_HEADER]
            elif self.get_argument(TOSHI_TIMESTAMP_QUERY_ARG, None):
                timestamp = self.get_argument(TOSHI_TIMESTAMP_QUERY_ARG)
            elif TOKEN_TIMESTAMP_HEADER in self.request.headers:
                timestamp = self.request.headers[TOKEN_TIMESTAMP_HEADER]
            elif self.get_argument(TOKEN_TIMESTAMP_QUERY_ARG, None):
                timestamp = self.get_argument(TOKEN_TIMESTAMP_QUERY_ARG)
            else:
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'bad_arguments',
                                            'message':
                                            'Missing Toshi-Timestamp'
                                        }]
                                    })

            timestamp = parse_int(timestamp)
            if timestamp is None:
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'invalid_timestamp',
                                            'message':
                                            'Given Toshi-Timestamp is invalid'
                                        }]
                                    })

            if not validate_address(expected_address):
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'invalid_id_address',
                                            'message':
                                            'Invalid Toshi-ID-Address'
                                        }]
                                    })

            if not validate_signature(signature):
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'invalid_signature',
                                            'message':
                                            'Invalid Toshi-Signature'
                                        }]
                                    })

            try:
                signature = data_decoder(signature)
            except Exception:
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'invalid_signature',
                                            'message':
                                            'Invalid Toshi-Signature'
                                        }]
                                    })

            verb = self.request.method
            uri = self.request.path

            if self.request.body:
                datahash = self.request.body
            else:
                datahash = ""

            data_string = generate_request_signature_data_string(
                verb, uri, timestamp, datahash)

            if not ecrecover(data_string, signature, expected_address):
                raise JSONHTTPError(400,
                                    body={
                                        'errors': [{
                                            'id':
                                            'invalid_signature',
                                            'message':
                                            'Invalid Toshi-Signature'
                                        }]
                                    })

            if abs(int(time.time()) - timestamp) > TIMESTAMP_EXPIRY:
                raise JSONHTTPError(
                    400,
                    body={
                        'errors': [{
                            'id':
                            'invalid_timestamp',
                            'message':
                            'The difference between the timestamp and the current time is too large'
                        }]
                    })

            return expected_address
コード例 #23
0
    async def create_transaction_skeleton(self,
                                          *,
                                          to_address,
                                          from_address,
                                          value=0,
                                          nonce=None,
                                          gas=None,
                                          gas_price=None,
                                          data=None):

        if not validate_address(from_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_from_address',
                'message': 'Invalid From Address'
            })

        if to_address is not None and not validate_address(to_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_to_address',
                'message': 'Invalid To Address'
            })

        if value:
            value = parse_int(value)
            if value is None or value < 0:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_value',
                    'message': 'Invalid Value'
                })

        # check optional arguments

        if nonce is None:
            # check cache for nonce
            nonce = await self.get_transaction_count(from_address)
        else:
            nonce = parse_int(nonce)
            if nonce is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_nonce',
                    'message': 'Invalid Nonce'
                })

        if data is not None:
            if isinstance(data, int):
                data = hex(data)
            if isinstance(data, str):
                try:
                    data = data_decoder(data)
                except binascii.Error:
                    pass
            if not isinstance(data, bytes):
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_data',
                    'message': 'Invalid Data field'
                })
        else:
            data = b''

        if gas is None:
            try:
                gas = await self.eth.eth_estimateGas(from_address,
                                                     to_address,
                                                     data=data,
                                                     value=value)
            except JsonRPCError:
                # this can occur if sending a transaction to a contract that doesn't match a valid method
                # and the contract has no default method implemented
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_data',
                    'message': 'Invalid Data field'
                })
        else:
            gas = parse_int(gas)
            if gas is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_gas',
                    'message': 'Invalid Gas'
                })

        if gas_price is None:
            gas_price = self.application.config['ethereum'].getint(
                'default_gasprice', DEFAULT_GASPRICE)
        else:
            gas_price = parse_int(gas_price)
            if gas_price is None:
                raise JsonRPCInvalidParamsError(data={
                    'id': 'invalid_gas_price',
                    'message': 'Invalid Gas Price'
                })

        try:
            tx = create_transaction(nonce=nonce,
                                    gasprice=gas_price,
                                    startgas=gas,
                                    to=to_address,
                                    value=value,
                                    data=data,
                                    network_id=self.network_id)
        except InvalidTransaction as e:
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_transaction',
                'message': str(e)
            })

        if tx.intrinsic_gas_used > gas:
            raise JsonRPCInvalidParamsError(
                data={
                    'id':
                    'invalid_transaction',
                    'message':
                    'Transaction gas is too low. There is not enough gas to cover minimal cost of the transaction (minimal: {}, got: {}). Try increasing supplied gas.'
                    .format(tx.intrinsic_gas_used, gas)
                })

        transaction = encode_transaction(tx)

        return transaction
コード例 #24
0
    async def get_token_balances(self, eth_address, token_address=None):
        if not validate_address(eth_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_address',
                'message': 'Invalid Address'
            })
        if token_address is not None and not validate_address(token_address):
            raise JsonRPCInvalidParamsError(data={
                'id': 'invalid_token_address',
                'message': 'Invalid Token Address'
            })

        # get token balances
        while True:
            async with self.db:
                result = await self.db.execute(
                    "UPDATE token_registrations SET last_queried = (now() AT TIME ZONE 'utc') WHERE eth_address = $1",
                    eth_address)
                await self.db.commit()
            registered = result == "UPDATE 1"

            if not registered:
                try:
                    async with RedisLock(
                            "token_balance_update:{}".format(eth_address)):
                        try:
                            await erc20_dispatcher.update_token_cache(
                                "*", eth_address)
                        except:
                            log.exception("Error updating token cache")
                            raise
                        async with self.db:
                            await self.db.execute(
                                "INSERT INTO token_registrations (eth_address) VALUES ($1)",
                                eth_address)
                            await self.db.commit()
                    break
                except RedisLockException:
                    # wait until the previous task is done and try again
                    await asyncio.sleep(0.1)
                    continue
            else:
                break

        if token_address:
            async with self.db:
                token = await self.db.fetchrow(
                    "SELECT symbol, name, decimals, format "
                    "FROM tokens WHERE contract_address = $1", token_address)
                if token is None:
                    return None
                balance = await self.db.fetchval(
                    "SELECT value "
                    "FROM token_balances "
                    "WHERE eth_address = $1 AND contract_address = $2",
                    eth_address, token_address)
            if balance is None:
                balance = "0x0"
            details = {
                "symbol": token['symbol'],
                "name": token['name'],
                "decimals": token['decimals'],
                "value": balance,
                "contract_address": token_address
            }
            if token['format'] is not None:
                details["icon"] = "{}://{}/token/{}.{}".format(
                    self.request.protocol, self.request.host, token_address,
                    token['format'])
            else:
                details['icon'] = None
            return details
        else:
            async with self.db:
                balances = await self.db.fetch(
                    "SELECT t.symbol, t.name, t.decimals, b.value, b.contract_address, t.format "
                    "FROM token_balances b "
                    "JOIN tokens t "
                    "ON t.contract_address = b.contract_address "
                    "WHERE eth_address = $1 ORDER BY t.symbol", eth_address)

            tokens = []
            for b in balances:
                details = {
                    "symbol": b['symbol'],
                    "name": b['name'],
                    "decimals": b['decimals'],
                    "value": b['value'],
                    "contract_address": b['contract_address']
                }
                if b['format'] is not None:
                    details["icon"] = "{}://{}/token/{}.{}".format(
                        self.request.protocol, self.request.host,
                        b['contract_address'], b['format'])
                else:
                    details['icon'] = None

                tokens.append(details)

            return tokens