From 5c4318dab6828197fdf48958d24474ceab8cc32f Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Fri, 1 Apr 2022 13:00:28 +0300 Subject: [PATCH 1/8] add flake8 rules --- .flake8 | 2 ++ .pre-commit-config.yaml | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 .flake8 create mode 100644 .pre-commit-config.yaml diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..b651ebd --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +ignore = E501,F403,F405,E126,E121,W503 \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c46302b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: +- repo: https://github.com/pycqa/flake8 + rev: '3.9.2' # pick a git hash / tag to point to + hooks: + - id: flake8 + args: ['--ignore=E501,F403,F405,E126,E121,W503'] + exclude: ^(env|venv) From e34817a1b38778469d999ff87b518db6dac5a1fa Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Fri, 1 Apr 2022 13:00:56 +0300 Subject: [PATCH 2/8] fix code formatting --- tinyman/v1/staking/__init__.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tinyman/v1/staking/__init__.py b/tinyman/v1/staking/__init__.py index c30ad78..2f5372e 100644 --- a/tinyman/v1/staking/__init__.py +++ b/tinyman/v1/staking/__init__.py @@ -38,7 +38,7 @@ def prepare_update_transaction(app_id: int, sender, suggested_params): return TransactionGroup([txn]) -def prepare_commit_transaction(app_id: int, program_id: int, program_account: str, pool_asset_id: int, amount:int, reward_asset_id:int , sender, suggested_params): +def prepare_commit_transaction(app_id: int, program_id: int, program_account: str, pool_asset_id: int, amount: int, reward_asset_id: int, sender, suggested_params): txn = ApplicationNoOpTxn( index=app_id, sender=sender, @@ -65,11 +65,11 @@ def parse_commit_transaction(txn, app_id: int): result['pooler'] = txn['sender'] result['program_address'] = app_call['accounts'][0] result['pool_asset_id'] = app_call['foreign-assets'][0] - result['program_id'] = int.from_bytes(b64decode(note)[19:19+8], 'big') + result['program_id'] = int.from_bytes(b64decode(note)[19:19 + 8], 'big') result['amount'] = int.from_bytes(b64decode(app_call['application-args'][1]), 'big') result['balance'] = int.from_bytes(b64decode(txn['logs'][0])[8:], 'big') return result - except Exception as e: + except Exception: return return @@ -100,7 +100,7 @@ def parse_program_update_transaction(txn, app_id: int): state = apply_delta({}, local_delta) result = parse_program_state(txn['sender'], state) return result - except Exception as e: + except Exception: return return @@ -181,7 +181,7 @@ def prepare_update_rewards_transaction(app_id: int, reward_amounts_dict: dict, s amounts = reward_amounts_dict[start_time] r[i][0] = start_time for j, x in enumerate(amounts): - r[i][j+1] = x + r[i][j + 1] = x txn = ApplicationNoOpTxn( index=app_id, @@ -218,7 +218,7 @@ def prepare_payment_transaction(staker_address: str, reward_asset_id: int, amoun ) return txn else: - raise NotImplemented() + raise NotImplementedError() def prepare_reward_metadata_for_payment(distribution_date: str, cycles_rewards: List[List], pool_address: int, pool_asset_id: int, pool_name: str): @@ -333,5 +333,3 @@ def parse_reward_payment_transaction(txn): "rewards": rewards, } return result - - From 833fb6881e8d24675fc9a6bc42292570902fc226 Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Fri, 1 Apr 2022 16:41:35 +0300 Subject: [PATCH 3/8] update reward payment note format --- tinyman/v1/staking/__init__.py | 120 ++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/tinyman/v1/staking/__init__.py b/tinyman/v1/staking/__init__.py index 2f5372e..3ef5ce2 100644 --- a/tinyman/v1/staking/__init__.py +++ b/tinyman/v1/staking/__init__.py @@ -1,4 +1,5 @@ import json +import re from base64 import b64decode, b64encode from datetime import datetime from hashlib import sha256 @@ -221,33 +222,50 @@ def prepare_payment_transaction(staker_address: str, reward_asset_id: int, amoun raise NotImplementedError() -def prepare_reward_metadata_for_payment(distribution_date: str, cycles_rewards: List[List], pool_address: int, pool_asset_id: int, pool_name: str): +def prepare_reward_metadata_for_payment(distribution_date: str, program_id: str, pool_address: str, pool_asset_id: int, pool_name: str, first_cycle: str, last_cycle: str): data = { "rewards": { - "distribution": distribution_date + '_' + pool_address, + "distribution": f"{pool_asset_id}_{program_id}_{distribution_date}", "pool_address": pool_address, + "distribution_date": distribution_date, "pool_asset_id": pool_asset_id, + "program_id": program_id, "pool_name": pool_name, - "rewards": [[str(cycle), str(amount)] for (cycle, amount) in cycles_rewards], + "first_cycle": first_cycle, + "last_cycle": last_cycle, }, } return data def generate_note_from_metadata(metadata): - note = b'tinymanStaking/v1:j' + json.dumps(metadata, sort_keys=True).encode() + note = b'tinymanStaking/v2:j' + json.dumps(metadata, sort_keys=True).encode() return note def get_note_prefix_for_distribution(distribution_date, pool_address): - metadata = prepare_reward_metadata_for_payment(distribution_date, cycles_rewards=[], pool_address=pool_address, pool_asset_id=None, pool_name=None) + metadata = prepare_reward_metadata_for_payment(distribution_date, program_id=None, pool_address=pool_address, pool_asset_id=None, pool_name=None, first_cycle=None, last_cycle=None) note = generate_note_from_metadata(metadata) - prefix = note.split(b', "pool_address"')[0] + prefix = note.split(b', "distribution_date"')[0] return prefix +def get_note_version(note): + assert isinstance(note, (bytes, str)) + + if isinstance(note, bytes): + note = note.decode() + + m = re.match(r"^tinymanStaking/v(?P\w+):j.*", note) + if m is None: + raise ValueError("Couldn't determine the version.") + + version = m.groupdict()["version"] + assert version in ["1", "2"] + return version + + def parse_reward_payment_transaction(txn): - prefix = "tinymanStaking/v1:j" date_format = "%Y%m%d" if "note" not in txn: @@ -265,15 +283,18 @@ def parse_reward_payment_transaction(txn): return note = b64decode(txn['note']) + note_version = get_note_version(note) + note_prefix = f"tinymanStaking/v{note_version}:j" + try: note = note.decode() except UnicodeDecodeError: return - if not note.startswith(prefix): + if not note.startswith(note_prefix): return - data = json.loads(note.lstrip(prefix)) + data = json.loads(note.lstrip(note_prefix)) if "rewards" not in data: return @@ -281,6 +302,28 @@ def parse_reward_payment_transaction(txn): if not isinstance(payment_data, dict): return + if note_version == "1": + return _parse_reward_payment_transaction_v1( + payment_data=payment_data, + txn=txn, + reward_asset_id=reward_asset_id, + transfer_amount=transfer_amount, + staker_address=staker_address, + date_format=date_format + ) + + if note_version == "2": + return _parse_reward_payment_transaction_v2( + payment_data=payment_data, + txn=txn, + reward_asset_id=reward_asset_id, + transfer_amount=transfer_amount, + staker_address=staker_address, + date_format=date_format + ) + + +def _parse_reward_payment_transaction_v1(*, payment_data, txn, reward_asset_id, transfer_amount, staker_address, date_format): if not {"distribution", "pool_address", "pool_name", "pool_asset_id", "rewards"} <= set(payment_data): return @@ -323,6 +366,8 @@ def parse_reward_payment_transaction(txn): return result = { + "version": "1", + "distribution": payment_data["distribution"], "distribution_date": distribution_date, "program_address": txn["sender"], "staker_address": staker_address, @@ -330,6 +375,63 @@ def parse_reward_payment_transaction(txn): "pool_name": payment_data["pool_name"], "pool_asset_id": pool_asset_id, "reward_asset_id": reward_asset_id, + "total_amount": transfer_amount, "rewards": rewards, } return result + + +def _parse_reward_payment_transaction_v2(*, payment_data, txn, reward_asset_id, transfer_amount, staker_address, date_format): + if not {"distribution", "pool_address", "pool_name", "pool_asset_id", "program_id", "distribution_date", "first_cycle", "last_cycle"} <= set(payment_data): + return + + try: + pool_asset_id, program_id, distribution_date = payment_data["distribution"].split("_") + except ValueError: + return + + if pool_asset_id != payment_data["pool_asset_id"]: + return + + if program_id != payment_data["program_id"]: + return + + if distribution_date != payment_data["distribution_date"]: + return + + try: + pool_asset_id = int(pool_asset_id) + except ValueError: + return + + try: + program_id = int(program_id) + except ValueError: + return + + try: + distribution_date = datetime.strptime(distribution_date, date_format).date() + first_cycle = datetime.strptime(payment_data["first_cycle"], date_format).date() + last_cycle = datetime.strptime(payment_data["last_cycle"], date_format).date() + except ValueError: + return + + if not is_valid_address(payment_data["pool_address"]): + return + + result = { + "version": "2", + "distribution": payment_data["distribution"], + "distribution_date": distribution_date, + "program_id": program_id, + "program_address": txn["sender"], + "staker_address": staker_address, + "pool_address": payment_data["pool_address"], + "pool_name": payment_data["pool_name"], + "pool_asset_id": pool_asset_id, + "reward_asset_id": reward_asset_id, + "total_amount": transfer_amount, + "first_cycle": first_cycle, + "last_cycle": last_cycle, + } + return result From 9b04b6e13d99388702259a0aa8bc77315f2e6c3f Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Fri, 1 Apr 2022 18:19:55 +0300 Subject: [PATCH 4/8] add get_reward_metadata_from_note function --- tinyman/v1/staking/__init__.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/tinyman/v1/staking/__init__.py b/tinyman/v1/staking/__init__.py index 3ef5ce2..3c9ecb1 100644 --- a/tinyman/v1/staking/__init__.py +++ b/tinyman/v1/staking/__init__.py @@ -258,13 +258,28 @@ def get_note_version(note): m = re.match(r"^tinymanStaking/v(?P\w+):j.*", note) if m is None: - raise ValueError("Couldn't determine the version.") + raise ValueError("Invalid note.") - version = m.groupdict()["version"] + version = m.group("version") assert version in ["1", "2"] return version +def get_reward_metadata_from_note(note: str): + assert isinstance(note, (bytes, str)) + + if isinstance(note, bytes): + note = note.decode() + + m = re.match(r"^tinymanStaking/v(?P\w+):j(?P.*)", note) + if m is None: + raise ValueError("Invalid note.") + + metadata = m.group("metadata") + metadata = json.loads(metadata) + return metadata + + def parse_reward_payment_transaction(txn): date_format = "%Y%m%d" From 466eb94e0dde9b71abc6f8a9d95a7233695c7717 Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Fri, 1 Apr 2022 18:23:21 +0300 Subject: [PATCH 5/8] add DATE_FORMAT constant for staking --- tinyman/v1/staking/__init__.py | 19 ++++++++----------- tinyman/v1/staking/constants.py | 1 + 2 files changed, 9 insertions(+), 11 deletions(-) create mode 100644 tinyman/v1/staking/constants.py diff --git a/tinyman/v1/staking/__init__.py b/tinyman/v1/staking/__init__.py index 3c9ecb1..4c86fa8 100644 --- a/tinyman/v1/staking/__init__.py +++ b/tinyman/v1/staking/__init__.py @@ -10,6 +10,7 @@ from algosdk.future.transaction import ApplicationClearStateTxn, ApplicationCreateTxn, ApplicationOptInTxn, OnComplete, PaymentTxn, StateSchema, ApplicationUpdateTxn, ApplicationNoOpTxn from tinyman.utils import TransactionGroup, apply_delta, bytes_to_int_list, int_list_to_bytes, int_to_bytes, timestamp_to_date_str +from tinyman.v1.staking.constants import DATE_FORMAT def prepare_create_transaction(args, sender, suggested_params): @@ -281,8 +282,6 @@ def get_reward_metadata_from_note(note: str): def parse_reward_payment_transaction(txn): - date_format = "%Y%m%d" - if "note" not in txn: return @@ -324,7 +323,6 @@ def parse_reward_payment_transaction(txn): reward_asset_id=reward_asset_id, transfer_amount=transfer_amount, staker_address=staker_address, - date_format=date_format ) if note_version == "2": @@ -334,11 +332,10 @@ def parse_reward_payment_transaction(txn): reward_asset_id=reward_asset_id, transfer_amount=transfer_amount, staker_address=staker_address, - date_format=date_format ) -def _parse_reward_payment_transaction_v1(*, payment_data, txn, reward_asset_id, transfer_amount, staker_address, date_format): +def _parse_reward_payment_transaction_v1(*, payment_data, txn, reward_asset_id, transfer_amount, staker_address): if not {"distribution", "pool_address", "pool_name", "pool_asset_id", "rewards"} <= set(payment_data): return @@ -350,7 +347,7 @@ def _parse_reward_payment_transaction_v1(*, payment_data, txn, reward_asset_id, try: distribution_date, pool_address = payment_data["distribution"].split("_") - distribution_date = datetime.strptime(distribution_date, date_format).date() + distribution_date = datetime.strptime(distribution_date, DATE_FORMAT).date() except ValueError: return @@ -369,7 +366,7 @@ def _parse_reward_payment_transaction_v1(*, payment_data, txn, reward_asset_id, try: for cycle, reward_amount in payment_data["rewards"]: rewards.append({ - "cycle": datetime.strptime(cycle, date_format).date(), + "cycle": datetime.strptime(cycle, DATE_FORMAT).date(), "amount": int(reward_amount) }) except ValueError: @@ -396,7 +393,7 @@ def _parse_reward_payment_transaction_v1(*, payment_data, txn, reward_asset_id, return result -def _parse_reward_payment_transaction_v2(*, payment_data, txn, reward_asset_id, transfer_amount, staker_address, date_format): +def _parse_reward_payment_transaction_v2(*, payment_data, txn, reward_asset_id, transfer_amount, staker_address): if not {"distribution", "pool_address", "pool_name", "pool_asset_id", "program_id", "distribution_date", "first_cycle", "last_cycle"} <= set(payment_data): return @@ -425,9 +422,9 @@ def _parse_reward_payment_transaction_v2(*, payment_data, txn, reward_asset_id, return try: - distribution_date = datetime.strptime(distribution_date, date_format).date() - first_cycle = datetime.strptime(payment_data["first_cycle"], date_format).date() - last_cycle = datetime.strptime(payment_data["last_cycle"], date_format).date() + distribution_date = datetime.strptime(distribution_date, DATE_FORMAT).date() + first_cycle = datetime.strptime(payment_data["first_cycle"], DATE_FORMAT).date() + last_cycle = datetime.strptime(payment_data["last_cycle"], DATE_FORMAT).date() except ValueError: return diff --git a/tinyman/v1/staking/constants.py b/tinyman/v1/staking/constants.py new file mode 100644 index 0000000..64fa003 --- /dev/null +++ b/tinyman/v1/staking/constants.py @@ -0,0 +1 @@ +DATE_FORMAT = "%Y%m%d" From 80a9eb6532300e144f44eb74ba5495b8b7eb0b4d Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Mon, 4 Apr 2022 15:31:35 +0300 Subject: [PATCH 6/8] fix reward payment parser --- tinyman/v1/staking/__init__.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tinyman/v1/staking/__init__.py b/tinyman/v1/staking/__init__.py index 4c86fa8..7ffd85a 100644 --- a/tinyman/v1/staking/__init__.py +++ b/tinyman/v1/staking/__init__.py @@ -399,6 +399,8 @@ def _parse_reward_payment_transaction_v2(*, payment_data, txn, reward_asset_id, try: pool_asset_id, program_id, distribution_date = payment_data["distribution"].split("_") + pool_asset_id = int(pool_asset_id) + program_id = int(program_id) except ValueError: return @@ -411,16 +413,6 @@ def _parse_reward_payment_transaction_v2(*, payment_data, txn, reward_asset_id, if distribution_date != payment_data["distribution_date"]: return - try: - pool_asset_id = int(pool_asset_id) - except ValueError: - return - - try: - program_id = int(program_id) - except ValueError: - return - try: distribution_date = datetime.strptime(distribution_date, DATE_FORMAT).date() first_cycle = datetime.strptime(payment_data["first_cycle"], DATE_FORMAT).date() @@ -436,7 +428,7 @@ def _parse_reward_payment_transaction_v2(*, payment_data, txn, reward_asset_id, "distribution": payment_data["distribution"], "distribution_date": distribution_date, "program_id": program_id, - "program_address": txn["sender"], + "program_distribution_address": txn["sender"], "staker_address": staker_address, "pool_address": payment_data["pool_address"], "pool_name": payment_data["pool_name"], From bb388393f5af7583af9d305af123598414dd81bd Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Tue, 5 Apr 2022 16:58:51 +0300 Subject: [PATCH 7/8] handle invalid reward payment note --- tinyman/v1/staking/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tinyman/v1/staking/__init__.py b/tinyman/v1/staking/__init__.py index 7ffd85a..daa85d5 100644 --- a/tinyman/v1/staking/__init__.py +++ b/tinyman/v1/staking/__init__.py @@ -297,7 +297,10 @@ def parse_reward_payment_transaction(txn): return note = b64decode(txn['note']) - note_version = get_note_version(note) + try: + note_version = get_note_version(note) + except ValueError: + return note_prefix = f"tinymanStaking/v{note_version}:j" try: From 64705e8c094c5325d5c39233493796c6b30c5b79 Mon Sep 17 00:00:00 2001 From: Goksel Coban Date: Thu, 7 Apr 2022 13:16:29 +0300 Subject: [PATCH 8/8] fix reward payment metadata --- tinyman/v1/staking/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tinyman/v1/staking/__init__.py b/tinyman/v1/staking/__init__.py index daa85d5..85b4164 100644 --- a/tinyman/v1/staking/__init__.py +++ b/tinyman/v1/staking/__init__.py @@ -223,14 +223,14 @@ def prepare_payment_transaction(staker_address: str, reward_asset_id: int, amoun raise NotImplementedError() -def prepare_reward_metadata_for_payment(distribution_date: str, program_id: str, pool_address: str, pool_asset_id: int, pool_name: str, first_cycle: str, last_cycle: str): +def prepare_reward_metadata_for_payment(distribution_date: str, program_id: int, pool_address: str, pool_asset_id: int, pool_name: str, first_cycle: str, last_cycle: str): data = { "rewards": { "distribution": f"{pool_asset_id}_{program_id}_{distribution_date}", "pool_address": pool_address, "distribution_date": distribution_date, - "pool_asset_id": pool_asset_id, - "program_id": program_id, + "pool_asset_id": int(pool_asset_id), + "program_id": int(program_id), "pool_name": pool_name, "first_cycle": first_cycle, "last_cycle": last_cycle,