Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change get_account to accept bytes, address or hex #55

Merged
merged 1 commit into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 27 additions & 25 deletions examples/get_account_examples.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ async def run(self, ctx: Config):
host=ctx.access_node_host, port=ctx.access_node_port
) as client:
account = await client.get_account(
address=ctx.service_account_address.bytes
# pass address as cadence.Address object
address=ctx.service_account_address
)
self.log.info(f"Account Address: {account.address.hex()}")
self.log.info(f"Account Balance: {account.balance}")
Expand All @@ -42,7 +43,8 @@ async def run(self, ctx: Config):
) as client:
_, _, _ = await random_account(client=client, ctx=ctx)
account = await client.get_account_at_latest_block(
address=ctx.service_account_address.bytes
# pass address as hex string
address=ctx.service_account_address.hex()
)
self.log.info(f"Account Address: {account.address.hex()}")
self.log.info(f"Account Balance: {account.balance}")
Expand All @@ -53,26 +55,26 @@ async def run(self, ctx: Config):
# -------------------------------------------------------------------------
# Get an account by address at the given block height.
# -------------------------------------------------------------------------
# SKIP DUE TO EMULATOR BUG
# class GetAccountAtBlockHeightExample(Example):
# def __init__(self) -> None:
# super().__init__(
# tag="GA.3.", name="GetAccountAtBlockHeightExample", sort_order=903
# )
#
# async def run(self, ctx: Config):
# # First Step : Create a client to connect to the flow blockchain
# # flow_client function creates a client using the host and port
# async with flow_client(
# host=ctx.access_node_host, port=ctx.access_node_port
# ) as client:
# latest_block = await client.get_latest_block()
# _, _, _ = await random_account(client=client, ctx=ctx)
# account = await client.get_account_at_block_height(
# address=ctx.service_account_address.bytes,
# block_height=latest_block.height,
# )
# print("Account Address: {}".format(account.address.hex()))
# print("Account Balance: {}".format(account.balance))
# print("Account Contracts: {}".format(len(account.contracts)))
# print("Account Keys: {}".format(len(account.keys)))
class GetAccountAtBlockHeightExample(Example):
def __init__(self) -> None:
super().__init__(
tag="GA.3.", name="GetAccountAtBlockHeightExample", sort_order=903
)

async def run(self, ctx: Config):
# First Step : Create a client to connect to the flow blockchain
# flow_client function creates a client using the host and port
async with flow_client(
host=ctx.access_node_host, port=ctx.access_node_port
) as client:
latest_block = await client.get_latest_block()
_, _, _ = await random_account(client=client, ctx=ctx)
account = await client.get_account_at_block_height(
# pass address as bytes
address=ctx.service_account_address.bytes,
block_height=latest_block.height,
)
self.log.info(f"Account Address: {account.address.hex()}")
self.log.info(f"Account Balance: {account.balance}")
self.log.info(f"Account Contracts: {len(account.contracts)}")
self.log.info(f"Account Keys: {len(account.keys)}")
31 changes: 30 additions & 1 deletion flow_py_sdk/cadence/address.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from __future__ import annotations

from flow_py_sdk.cadence.value import Value
import flow_py_sdk.cadence.constants as c

from flow_py_sdk.exceptions import NotAddressError


class Address(Value):
address_length = 8
Expand Down Expand Up @@ -35,9 +39,34 @@ def encode_value(self) -> dict:
def decode(cls, value) -> "Address":
addr = str(value[c.valueKey])
if addr[:2] != "0x":
raise Exception() # TODO
raise NotAddressError.from_value(addr) # TODO
return Address.from_hex(addr)

@classmethod
def type_str(cls) -> str:
return c.addressTypeStr

@classmethod
def convert_to_bytes(cls, address: bytes | Address | str) -> bytes:
"""
Converts an address to bytes if it is not already bytes.
Parameters
----------
address : bytes | Address | str
The address to convert to bytes
Can be bytes, Address, or hex string

Returns
-------
bytes
The address as bytes

"""
if isinstance(address, bytes):
return address
elif isinstance(address, Address):
return address.bytes
elif isinstance(address, str):
return Address.from_hex(address).bytes
else:
raise NotAddressError.from_value(address)
21 changes: 15 additions & 6 deletions flow_py_sdk/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from grpclib.encoding.base import CodecBase, StatusDetailsCodecBase
from grpclib.metadata import Deadline

from flow_py_sdk import cadence
from flow_py_sdk.cadence import Value, cadence_object_hook, encode_arguments
from flow_py_sdk.client import entities
from flow_py_sdk.proto.flow.access import (
Expand Down Expand Up @@ -194,54 +195,61 @@ async def get_transaction(self, *, id: bytes = b"") -> entities.Transaction:
response = await super().get_transaction(id=id)
return entities.Transaction.from_proto(response.transaction)

async def get_account(self, *, address: bytes = b"") -> entities.Account:
async def get_account(
self, *, address: bytes | cadence.Address | str = b""
) -> entities.Account:
"""
Get an account using its address.

Parameters
----------
address : bytes
address : bytes | cadence.Address | str
Address of requested account.
Can be a bytes, cadence.Address or hex str.

Returns
-------
entities.Account
Return requested account.

"""
address = cadence.Address.convert_to_bytes(address)
response = await super().get_account(address=address)
return entities.Account.from_proto(response.account)

async def get_account_at_latest_block(
self, *, address: bytes = b""
self, *, address: bytes | cadence.Address | str = b""
) -> entities.Account:
"""
Get an account by address at the latest sealed block.

Parameters
----------
address : bytes
address : bytes | cadence.Address | str
Address of requested account.
Can be a bytes, cadence.Address or hex str.

Returns
-------
entities.Account
Return requested account.

"""
address = cadence.Address.convert_to_bytes(address)
response = await super().get_account_at_latest_block(address=address)
return entities.Account.from_proto(response.account)

async def get_account_at_block_height(
self, *, address: bytes = b"", block_height: int = 0
self, *, address: bytes | cadence.Address | str = b"", block_height: int = 0
) -> entities.Account:
"""
Get an account by address at the given block height.

Parameters
----------
address : bytes
address : bytes | cadence.Address | str
Address of requested account.
Can be a bytes, cadence.Address or hex str.
block_height : int
Desired block height.

Expand All @@ -251,6 +259,7 @@ async def get_account_at_block_height(
Return requested account.

"""
address = cadence.Address.convert_to_bytes(address)
response = await super().get_account_at_block_height(
address=address, block_height=block_height
)
Expand Down
6 changes: 6 additions & 0 deletions flow_py_sdk/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ class CadenceEncodingError(PySDKError):

class CadenceIncorrectTypeError(PySDKError):
pass


class NotAddressError(PySDKError):
@classmethod
def from_value(cls, value) -> "NotAddressError":
return NotAddressError(f"Value {value} is not a cadence address.")
23 changes: 23 additions & 0 deletions tests/test_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from unittest import TestCase

from flow_py_sdk.cadence import Address
from flow_py_sdk.exceptions import NotAddressError


class TestAddress(TestCase):
def test_convert_to_bytes(self):
with self.subTest(msg="Pass address as bytes"):
address = Address.from_hex("0x01")
self.assertEqual(address.bytes, Address.convert_to_bytes(address.bytes))

with self.subTest(msg="Pass address as Address"):
address = Address.from_hex("0x01")
self.assertEqual(address.bytes, Address.convert_to_bytes(address))

with self.subTest(msg="Pass address as hex string"):
address = Address.from_hex("0x01")
self.assertEqual(address.bytes, Address.convert_to_bytes(address.hex()))

with self.subTest(msg="Pass address as unknown"):
with self.assertRaises(NotAddressError):
Address.convert_to_bytes(1)