From ce8dca9e79646d4804d6d760d6d956d77240c71a Mon Sep 17 00:00:00 2001 From: fgandila Date: Tue, 4 Jun 2024 09:42:25 +0300 Subject: [PATCH 1/2] FeesCollector & EnergyFactory tests updated --- contracts/fees_collector_contract.py | 160 ++++++++++- tools/notebooks/energy-factory.ipynb | 222 +++++++++++++-- tools/notebooks/fees_colector.ipynb | 386 +++++++++++++++++++++++++++ utils/contract_data_fetchers.py | 5 +- utils/decoding_structures.py | 8 - 5 files changed, 735 insertions(+), 46 deletions(-) create mode 100644 tools/notebooks/fees_colector.ipynb diff --git a/contracts/fees_collector_contract.py b/contracts/fees_collector_contract.py index 34f255a..c60851d 100644 --- a/contracts/fees_collector_contract.py +++ b/contracts/fees_collector_contract.py @@ -1,15 +1,33 @@ -from contracts.base_contracts import BaseBoostedContract +from copy import copy, deepcopy +from typing import Any, cast +from multiversx_sdk import ApiNetworkProvider, CodeMetadata, ProxyNetworkProvider, AddressComputer, SmartContractQueryResponse +from multiversx_sdk.network_providers.transaction_decoder import TransactionDecoder +from contracts.contract_identities import DEXContractInterface +from contracts.farm_contract import FarmContract +from contracts.metastaking_contract import MetaStakingContract +from contracts.staking_contract import StakingContract +from utils import decoding_structures +from utils.contract_data_fetchers import FeeCollectorContractDataFetcher from utils.logger import get_logger -from utils.utils_tx import deploy, endpoint_call, upgrade_call -from utils.utils_generic import log_step_pass, log_unexpected_args -from utils.utils_chain import Account, WrapperAddress as Address -from multiversx_sdk import CodeMetadata, ProxyNetworkProvider - +from utils.utils_tx import prepare_contract_call_tx, send_contract_call_tx, deploy, endpoint_call, upgrade_call +from utils.utils_generic import log_step_fail, log_step_pass, log_warning, log_unexpected_args +from utils.utils_chain import Account, WrapperAddress as Address, decode_merged_attributes, hex_to_string, log_explorer_transaction, dec_to_padded_hex, string_to_base64 +from multiversx_sdk.abi.serializer import Serializer +import re +from multiversx_sdk.abi.biguint_value import BigUIntValue +from multiversx_sdk.abi.small_int_values import * +from multiversx_sdk.abi.string_value import StringValue +from multiversx_sdk.abi.struct_value import StructValue +from multiversx_sdk.abi.values_multi import * +from multiversx_sdk.abi.field import Field +from multiversx_sdk.abi.list_value import ListValue logger = get_logger(__name__) -class FeesCollectorContract(BaseBoostedContract): +class FeesCollectorContract(DEXContractInterface): + transaction_decoder = TransactionDecoder() + def __init__(self, address: str = ""): self.address = address @@ -55,7 +73,7 @@ def contract_upgrade(self, deployer: Account, proxy: ProxyNetworkProvider, bytec metadata = CodeMetadata(upgradeable=True, payable_by_contract=True, readable=True) gas_limit = 200000000 - + if no_init: arguments = [] else: @@ -188,17 +206,141 @@ def claim_rewards(self, user: Account, proxy: ProxyNetworkProvider): gas_limit = 80000000 sc_args = [] return endpoint_call(proxy, gas_limit, user, Address(self.address), "claimRewards", sc_args) + + def claim_rewards_original_caller(self, user: Account, proxy: ProxyNetworkProvider, opt_original_caller: Account): + function_purpose = f"Claim rewards from fees collector" + logger.info(function_purpose) + + gas_limit = 80000000 + opt_original_caller = user.address.bech32() + sc_args = [ + opt_original_caller + ] + return endpoint_call(proxy, gas_limit, user, Address(self.address), "claimBoostedRewards", sc_args) def claim_boosted_rewards(self, user: Account, proxy: ProxyNetworkProvider): - function_purpose = f"Claim rewards from fees collector" + function_purpose = f"Claim boosted rewards from fees collector" logger.info(function_purpose) gas_limit = 80000000 sc_args = [] return endpoint_call(proxy, gas_limit, user, Address(self.address), "claimBoostedRewards", sc_args) + def contract_start(self, deployer: Account, proxy: ProxyNetworkProvider, args: list = None): pass def print_contract_info(self): log_step_pass(f"Deployed fees collector contract: {self.address}") + + def allow_external_claim(self, user: Account, proxy: ProxyNetworkProvider, allow_external_claim: bool): + fn = 'setAllowExternalClaimRewards' + sc_args = [ + allow_external_claim + ] + logger.info(f"{fn}") + # logger.debug(f"Account: {user.address}") + + gas_limit = 80000000 + + return endpoint_call(proxy, gas_limit ,user, Address(self.address), "setAllowExternalClaimRewards", sc_args) + + + def get_user_energy_for_week(self, user: str, proxy: ProxyNetworkProvider, week: int) -> dict: + data_fetcher = FeeCollectorContractDataFetcher(Address(self.address), proxy.url) + raw_results = data_fetcher.get_data('getUserEnergyForWeek', [Address(user).get_public_key(), week]) + print(raw_results) + if not raw_results: + return {} + decoder = TransactionDecoder() + user_energy_for_week = decoder.hex_to_number(raw_results) + + return user_energy_for_week + + def get_current_week(self, proxy: ProxyNetworkProvider) -> int: + data_fetcher = FeeCollectorContractDataFetcher(Address(self.address), proxy.url) + raw_results = data_fetcher.get_data('getCurrentWeek') + if not raw_results: + return 0 + current_week = int(raw_results) + + return current_week + + def get_total_energy_for_week(self, proxy: ProxyNetworkProvider, week: int) -> int: + data_fetcher = FeeCollectorContractDataFetcher(Address(self.address), proxy.url) + raw_results = data_fetcher.get_data('getTotalEnergyForWeek', [week]) + if not raw_results: + return 0 + return int(raw_results) + + def get_total_rewards_for_week(self, proxy: ProxyNetworkProvider, week: int): + serializer = Serializer("@") + + data_fetcher = FeeCollectorContractDataFetcher(Address(self.address), proxy.url) + raw_results = data_fetcher.get_data('getTotalRewardsForWeek', [week]) + + if not raw_results: + return {} + + b = bytes.fromhex(raw_results) + + data = b + + def create_esdt_token_payment() -> StructValue: + return StructValue([ + Field("token_identifier", StringValue()), + Field("token_nonce", U64Value()), + Field("amount", BigUIntValue()), + ]) + + destination = ListValue( + item_creator=create_esdt_token_payment + ) + + s = serializer.deserialize_parts([data], [destination]) + + tokens = [] + + for i, item in enumerate(destination.items): + fields = cast(StructValue, item).fields + for field in fields: + fvalue: Any = field.value + tokens.append([field.name, fvalue.value]) + + tokens_list = [tokens[i:i+3] for i in range(0, len(tokens), 3)] + + return tokens_list + + def get_all_stats(self, proxy: ProxyNetworkProvider, user: str, week: int): + fees_collector_stats = { + "current_week": self.get_current_week(proxy), + "total_rewards_for_week": self.get_total_rewards_for_week(proxy, week), + "total_energy_for_week": self.get_total_energy_for_week(proxy, week) + } + + return fees_collector_stats + + + def get_tx_op(self, tx_hash: str, operation: dict, api: ApiNetworkProvider) -> dict: + used_api = api + # Get the transaction details + tx = used_api.get_transaction(tx_hash) + # Get and check transaction operations + ops = tx.raw_response['operations'] + + + tx_decoded = tx + new = self.transaction_decoder.get_transaction_metadata(tx_decoded) + + print(new.function_name) + print(new.function_args) + + # Take each op in ops and match it with operation. Try to match only the fields expected in operation dictionary. + # TX Operations are unordered. If any of the operations match, return it. + for op in ops: + # print(f'Matching with {operation}') + if all(op.get(key) == operation.get(key) for key in operation.keys()): + return op + + def wrap(self, source: str, width: int): + return [source[i:i + width] for i in range(0, len(source), width)] \ No newline at end of file diff --git a/tools/notebooks/energy-factory.ipynb b/tools/notebooks/energy-factory.ipynb index 569d207..1cfbbde 100644 --- a/tools/notebooks/energy-factory.ipynb +++ b/tools/notebooks/energy-factory.ipynb @@ -8,7 +8,32 @@ "source": [ "import sys\n", "from pathlib import Path\n", - "sys.path.append(str(Path.cwd().parent.parent.absolute()))" + "sys.path.append(str(Path.cwd().parent.parent.absolute()))\n", + "import config\n", + "\n", + "from context import Context\n", + "from utils.utils_chain import Account, Address\n", + "from contracts.simple_lock_contract import SimpleLockContract\n", + "from contracts.simple_lock_energy_contract import SimpleLockEnergyContract\n", + "from contracts.dex_proxy_contract import DexProxyContract\n", + "context = Context()\n", + "IMPERSONATE_ADDRESS = \"\"\n", + "SIMPLE_LOCK_ENERGY_ADDRESS = \"\"\n", + "LOCKED_TOKEN = \"MEX-455c57\"\n", + "LOCKED_LP_TOKEN = \"XMEX-fda355\"\n", + "LOCKED_FARM_TOKEN = \"LKMEX-aab910\"\n", + "\n", + "user = Account(pem_file=config.DEFAULT_ACCOUNTS)\n", + "# contract = SimpleLockEnergyContract(LOCKED_TOKEN, LOCKED_LP_TOKEN, LOCKED_FARM_TOKEN, SIMPLE_LOCK_ENERGY_ADDRESS)\n", + "if \"shadowfork\" in context.network_provider.proxy.url and IMPERSONATE_ADDRESS:\n", + " user.address = Address.from_bech32(IMPERSONATE_ADDRESS)\n", + " user.sync_nonce(context.network_provider.proxy)\n", + "\n", + "\n", + "proxy_contract: DexProxyContract\n", + "proxy_contract = context.get_contracts(config.PROXIES_V2)[0]\n", + "energy_contract: SimpleLockEnergyContract\n", + "energy_contract = context.get_contracts(config.SIMPLE_LOCKS_ENERGY)[0]" ] }, { @@ -25,31 +50,6 @@ "proxy = ProxyNetworkProvider(env.PROXY_URL)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "contract_address = env.ENERGY_CONTRACT\n", - "XMEX = env.XMEX\n", - "LKMEX = env.LKMEX\n", - "WXMEX = env.WXMEX" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from multiversx_sdk import AccountNonceHolder, UserSigner\n", - "from pathlib import Path\n", - "\n", - "user = env.USER1\n", - "user_signer = UserSigner.from_pem_file(Path(env.USER1_PEM))" - ] - }, { "attachments": {}, "cell_type": "markdown", @@ -104,6 +104,174 @@ "tx.signature = user_signer.sign(tx_computer.compute_bytes_for_signing(tx))\n", "proxy.send_transaction(tx)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from contracts.simple_lock_energy_contract import SimpleLockEnergyContract\n", + "from utils.utils_chain import Account, WrapperAddress\n", + "from utils.utils_tx import ESDTToken\n", + "from multiversx_sdk import ProxyNetworkProvider\n", + "\n", + "energy_contract: SimpleLockEnergyContract\n", + "energy_contract = context.get_contracts(config.SIMPLE_LOCKS_ENERGY)[0]\n", + "\n", + "user = Account(pem_file=config.DEFAULT_OWNER)\n", + "user.address = WrapperAddress(\"erd14sp4unwsgyae0hae8cgnm0tp6yaj3emyt02mxlhfsr48ymqz52lqheyh9s\")\n", + "user.sync_nonce(context.network_provider.proxy)\n", + "\n", + "proxy2 = ProxyNetworkProvider(\"https://proxy-shadowfork-one.elrond.ro\")\n", + "\n", + "hash = energy_contract.lock_tokens(user, context.network_provider.proxy, [[ESDTToken(\"MEX-455c57\", 0, 10000)], 720])\n", + "user.sync_nonce(proxy2)\n", + "hash2 = energy_contract.lock_tokens(user, proxy2, [[ESDTToken(\"MEX-455c57\", 0, 10000)], 720])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "LOCK TOKENS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from contracts.simple_lock_energy_contract import SimpleLockEnergyContract\n", + "from utils.utils_chain import get_token_details_for_address\n", + "from utils.utils_tx import ESDTToken\n", + "\n", + "_, mex_amount, _ = get_token_details_for_address(proxy_contract.token, user.address.bech32(), context.network_provider.proxy)\n", + "\n", + "lockable_tokens = ESDTToken(proxy_contract.token, 0, mex_amount)\n", + "\n", + "txhash = energy_contract.lock_tokens(user, context.network_provider.proxy, [[lockable_tokens], 720])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "UNLOCK TOKENS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from contracts.simple_lock_energy_contract import SimpleLockEnergyContract\n", + "from utils.utils_chain import get_token_details_for_address\n", + "from utils.utils_tx import ESDTToken\n", + "\n", + "txhash = energy_contract.unlock_tokens(user, context.network_provider.proxy, [[ESDTToken(\"XMEX-fda355\", 62, 10)]])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "EXTEND LOCK" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from contracts.simple_lock_energy_contract import SimpleLockEnergyContract\n", + "from utils.utils_tx import ESDTToken\n", + "\n", + "# contract = SimpleLockEnergyContract(LOCKED_TOKEN, LOCKED_TOKEN, LOCKED_LP_TOKEN, LOCKED_FARM_TOKEN, SIMPLE_LOCK_ENERGY_ADDRESS)\n", + "tx = energy_contract.extend_lock(user, context.network_provider.proxy, [[ESDTToken(\"XMEX-fda355\", 61, 10)], 30])\n", + "\n", + "print(\"Transaction hash:\", tx)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "import random\n", + "import time\n", + "from ported_arrows.stress.shared import get_shard_of_address\n", + "from utils.contract_data_fetchers import SimpleLockEnergyContractDataFetcher\n", + "from utils.utils_chain import nominated_amount\n", + "\n", + "\n", + "deployer_shard = get_shard_of_address(context.deployer_account.address)\n", + "# contract: SimpleLockEnergyContract\n", + "contract = context.get_contracts(config.SIMPLE_LOCKS_ENERGY)[0]\n", + "\n", + "\n", + "simple_lock_energy_data_fetcher = SimpleLockEnergyContractDataFetcher(Address.new_from_bech32(contract.address),\n", + " context.network_provider.proxy.url)\n", + "lock_options = simple_lock_energy_data_fetcher.get_data('getLockOptions')\n", + "sleep_time = config.CROSS_SHARD_DELAY if get_shard_of_address(user.address) is not deployer_shard \\\n", + " else 6\n", + "\n", + "print(lock_options)\n", + " # lock tokens\n", + "amount = random.randint(1, 10)\n", + "lock_period = random.choice(lock_options)\n", + "token = ESDTToken(LOCKED_TOKEN, 0, nominated_amount(amount))\n", + "args = [[token], lock_period]\n", + "_ = contract.lock_tokens(user, context.network_provider.proxy, args)\n", + "\n", + "print(f\"User: {user.address.bech32()}\")\n", + "print(f\"Locked {amount} tokens for {lock_period} epochs.\")\n", + "time.sleep(sleep_time)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "GET ENERGY FOR USER" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from utils.contract_data_fetchers import SimpleLockEnergyContractDataFetcher\n", + "\n", + "\n", + "simple_lock_energy_data_fetcher = SimpleLockEnergyContractDataFetcher(Address.new_from_bech32(energy_contract.address),\n", + " context.network_provider.proxy.url)\n", + "\n", + "user_energy = simple_lock_energy_data_fetcher.get_data('getEnergyEntryForUser', [user.address])\n", + "print(user_energy)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from utils.contract_data_fetchers import SimpleLockEnergyContractDataFetcher\n", + "\n", + "\n", + "simple_lock_energy_data_fetcher = SimpleLockEnergyContractDataFetcher(Address.new_from_bech32(energy_contract.address),\n", + " context.network_provider.proxy.url)\n", + "\n", + "user_energy = simple_lock_energy_data_fetcher.get_data('getEnergyAmountForUser', [user.address])\n", + "print(user_energy)" + ] } ], "metadata": { @@ -122,7 +290,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10 (default, Nov 14 2022, 12:59:47) \n[GCC 9.4.0]" + "version": "3.11.6" }, "orig_nbformat": 4, "vscode": { diff --git a/tools/notebooks/fees_colector.ipynb b/tools/notebooks/fees_colector.ipynb new file mode 100644 index 0000000..88d6b67 --- /dev/null +++ b/tools/notebooks/fees_colector.ipynb @@ -0,0 +1,386 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#INIT\n", + "import sys\n", + "from pathlib import Path\n", + "sys.path.append(str(Path.cwd().parent.parent.absolute()))\n", + "import config\n", + "\n", + "from context import Context\n", + "from utils.utils_chain import WrapperAddress as Address, Account\n", + "from utils.utils_chain import nominated_amount\n", + "from utils.contract_retrievers import retrieve_farm_by_address\n", + "from utils.utils_chain import get_current_tokens_for_address, get_token_details_for_address, get_all_token_nonces_details_for_account, dec_to_padded_hex\n", + "from utils.utils_tx import ESDTToken, NetworkProviders\n", + "from contracts.fees_collector_contract import FeesCollectorContract\n", + "from contracts.dex_proxy_contract import DexProxyContract, DexProxyClaimRewardsEvent, DexProxyEnterFarmEvent, DexProxyExitFarmEvent, DexProxyRemoveLiquidityEvent, DexProxyAddLiquidityEvent\n", + "\n", + "context = Context()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#ADDRESS SETUP\n", + "user = Account(pem_file=\"~/Documents/mx-sdk-py-exchange/wallets/C1.pem\")\n", + "user.address = Address(\"\")\n", + "user2 = Account(pem_file=\"\")\n", + "user2.address = \"\"\n", + "sc_owner = Account(pem_file=\"~/Documents/mx-sdk-py-exchange/wallets/C10.pem\")\n", + "sc_owner.address = Address(\"erd1ss6u80ruas2phpmr82r42xnkd6rxy40g9jl69frppl4qez9w2jpsqj8x97\")\n", + "user.sync_nonce(context.network_provider.proxy)\n", + "# opt_original_caller.sync_nonce(context.network_provider.proxy)\n", + "sc_owner.sync_nonce(context.network_provider.proxy)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#CONTRACTS INIT\n", + "proxy_contract: DexProxyContract\n", + "proxy_contract = context.get_contracts(config.PROXIES_V2)[0]\n", + "fees_collector_contract: FeesCollectorContract\n", + "fees_collector_contract = context.get_contracts(config.FEES_COLLECTORS)[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#CLAIM REWARDS\n", + "txhash = fees_collector_contract.claim_rewards(user, context.network_provider.proxy)\n", + "used_api = context.network_provider.api" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "CLAIM ORIGINAL CALLER" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "txhash = fees_collector_contract.claim_rewards_original_caller(user, context.network_provider.proxy, user)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "CLAIM BOOSTED REWARDS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "txhash = fees_collector_contract.claim_boosted_rewards(user, context.network_provider.proxy)\n", + "used_api = context.network_provider.api\n", + "\n", + "tokens_list = {}\n", + "tokens_list = get_current_tokens_for_address(user.address, context.network_provider.proxy)\n", + "if tokens_list.keys == 0:\n", + " print(\"No tokens\")\n", + " raise\n", + "print(tokens_list.items())\n", + "\n", + "# Get the transaction details\n", + "tx = used_api.get_transaction(txhash)\n", + "sender = tx.sender.bech32()\n", + "\n", + "ops = tx.raw_response['data']\n", + "print(ops)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "CHECK ENERGY AFTER CLAIM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current_week = fees_collector_contract.get_current_week(context.network_provider.proxy)\n", + "\n", + "get_total_energy_for_week_hex = fees_collector_contract.get_total_energy_for_week(context.network_provider.proxy , current_week)\n", + "print(get_total_energy_for_week_hex)\n", + "get_user_energy_hex = fees_collector_contract.get_user_energy_for_week(user.address.to_bech32(), context.network_provider.proxy ,current_week)\n", + "print(get_user_energy_hex)\n", + "user_energy_before, staking_stats_before = fees_collector_contract.get_all_stats(context.network_provider.proxy, user.address.to_bech32(), current_week)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "CHECK CLAIM" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "op_to_look_for = {\n", + " \"action\": \"MultiESDTNFTTransfer\",\n", + " \"sender\": user.address,\n", + " \"receiver\": user.address,\n", + " \"value\": 0\n", + " }\n", + "api = context.network_provider.api\n", + "op = fees_collector_contract.get_tx_op(txhash, op_to_look_for, api)\n", + "print(op)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "CHECK SHADOWFORK SCENARIO" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "from typing import cast\n", + "from multiversx_sdk import ApiNetworkProvider, ProxyNetworkProvider\n", + "from utils.utils_generic import TestStepConditions\n", + "\n", + "\n", + "SECONDSDELAY = 5\n", + "\n", + "user.sync_nonce(context.network_provider.proxy)\n", + "proxy1 = ProxyNetworkProvider(\"https://proxy-shadowfork-one.elrond.ro\")\n", + "api1 = ApiNetworkProvider(\"https://express-api-shadowfork-one.elrond.ro\")\n", + "sf1 = NetworkProviders(api1, proxy1)\n", + "\n", + "txhash = fees_collector_contract.claim_boosted_rewards(user, context.network_provider.proxy)\n", + "print(txhash)\n", + "user.sync_nonce(proxy2)\n", + "txhash2 = fees_collector_contract.claim_rewards(user, proxy2)\n", + "print(txhash2)\n", + "\n", + "time.sleep(SECONDSDELAY)\n", + "\n", + "current_week = fees_collector_contract.get_current_week(context.network_provider.proxy)\n", + "get_total_rewards_hex1 = fees_collector_contract.get_total_rewards_for_week(context.network_provider.proxy , current_week)\n", + "get_total_rewards_hex2 = fees_collector_contract.get_total_rewards_for_week(context.network_provider.proxy , current_week)\n", + "\n", + "token1 = get_total_rewards_hex1[0]\n", + "token2 = get_total_rewards_hex2[0]\n", + "print(token1)\n", + "\n", + "expected_token1 = ESDTToken(token1[0][1], int((token1[1][1])), token1[2][1]) \n", + "expected_token2 = ESDTToken(token2[0][1], int((token2[1][1])), token2[2][1]) \n", + "\n", + "operations = context.network_provider.get_tx_operations(txhash)\n", + "operations2 = sf1.get_tx_operations(txhash2)\n", + "print(operations)\n", + "print(operations2)\n", + "\n", + "teststep = TestStepConditions()\n", + "teststep.add_condition(context.network_provider.check_simple_tx_status(txhash),\"\"),\n", + "teststep.add_condition(context.network_provider.check_for_transfer_operation(txhash, expected_token1,\n", + " sender=fees_collector_contract.address,\n", + " destination=user.address),\n", + " \"claimed tokens\")\n", + "context.network_provider.check_complex_tx_status\n", + "teststep.assert_conditions()\n", + "\n", + "teststep2 = TestStepConditions()\n", + "teststep2.add_condition(sf1.check_simple_tx_status(txhash2),\"\"),\n", + "teststep2.add_condition(sf1.check_for_transfer_operation(txhash2, expected_token2,\n", + " destination=user.address.bech32()),\n", + " \"claimed tokens\")\n", + "sf1.check_complex_tx_status\n", + "teststep2.assert_conditions()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "allowExternalClaim" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "txhash = fees_collector_contract.allow_external_claim(user, context.network_provider.proxy, True)\n", + "tx = used_api.get_transaction(txhash)\n", + "data = tx.raw_response['data']\n", + "print(data)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "External Claim" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# opt_original_caller.address\n", + "txhash = fees_collector_contract.claim_for_user(user, user2.address, context.network_provider.proxy )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#UPGRADE CONTRACT\n", + "from contracts.fees_collector_contract import FeesCollectorContract\n", + "\n", + "fees_collector_contract: FeesCollectorContract\n", + "fees_collector_contract = context.get_contracts(config.FEES_COLLECTORS)[0]\n", + "\n", + "# get_account_keys_online(router_contract.address, context.network_provider.proxy.url, with_save_in=\"state_dump/pre_upgrade_router.json\")\n", + "fees_collector_contract.contract_upgrade(context.deployer_account, context.network_provider.proxy,\n", + " config.FEES_COLLECTOR_BYTECODE_PATH_V3, [], True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "============ VIEWS =================" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#GET CURRENT WEEK\n", + "current_week = fees_collector_contract.get_current_week(context.network_provider.proxy)\n", + "print(current_week)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#GET USER ENERGY FOR WEEK\n", + "get_user_energy_hex = fees_collector_contract.get_user_energy_for_week(user.address.bech32(), context.network_provider.proxy , current_week)\n", + "print(get_user_energy_hex)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#GET TOTAL ENERGY FOR WEEK\n", + "get_total_energy_for_week_hex = fees_collector_contract.get_total_energy_for_week(context.network_provider.proxy , current_week)\n", + "print(get_total_energy_for_week_hex)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "#GET TOTAL REWARDS FOR WEEK\n", + "current_week = fees_collector_contract.get_current_week(context.network_provider.proxy)\n", + "\n", + "get_total_rewards_hex = fees_collector_contract.get_total_rewards_for_week(context.network_provider.proxy , current_week-2)\n", + "print(get_total_rewards_hex)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "GET ALL STATS" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "current_week = fees_collector_contract.get_current_week(context.network_provider.proxy)\n", + "get_user_energy_hex = fees_collector_contract.get_user_energy_for_week(user.address.bech32(), context.network_provider.proxy , current_week)\n", + "\n", + "all_stats = fees_collector_contract.get_all_stats(context.network_provider.proxy, user.address.to_bech32, current_week)\n", + "print(all_stats)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/utils/contract_data_fetchers.py b/utils/contract_data_fetchers.py index 551280e..503f51f 100644 --- a/utils/contract_data_fetchers.py +++ b/utils/contract_data_fetchers.py @@ -231,10 +231,11 @@ def __init__(self, contract_address: Address, proxy_url: str): "updateAndGetSafePrice": self._get_hex_view, "getLpTokenIdentifier": self._get_hex_view, "getTotalRewardsForWeek": self._get_hex_view, + "getTotalEnergyForWeek": self._get_int_view, + "getUserEnergyForWeek": self._get_hex_view, "getTokensForGivenPosition": self._get_int_list_view, "getReservesAndTotalSupply": self._get_int_list_view, - "getUserEnergyForWeek": self._get_hex_view, - "getTotalEnergyForWeek": self._get_int_view, + "getCurrentWeek": self._get_int_view } def get_token_reserve(self, token_ticker: str) -> int: diff --git a/utils/decoding_structures.py b/utils/decoding_structures.py index 878e9fb..fc62b43 100644 --- a/utils/decoding_structures.py +++ b/utils/decoding_structures.py @@ -103,12 +103,4 @@ 'proxy_token_id': 'string', 'proxy_token_nonce': 'u64', 'proxy_token_amount': 'biguint' -} - -TOTAL_REWARDS_FOR_WEEK = { - 'rewards_for_week_list': { - 'token_id': 'string', - 'nonce': 'u64', - 'amount': 'biguint' - } } \ No newline at end of file From c41da795e93c77d98f0594b5a0c1653c02472fed Mon Sep 17 00:00:00 2001 From: fgandila Date: Thu, 27 Jun 2024 09:11:15 +0300 Subject: [PATCH 2/2] modified as per review --- contracts/fees_collector_contract.py | 32 ++----------------------- tools/notebooks/fees_colector.ipynb | 35 ++++++++++++++-------------- utils/utils_tx.py | 15 ++++++++++++ 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/contracts/fees_collector_contract.py b/contracts/fees_collector_contract.py index c60851d..16a6908 100644 --- a/contracts/fees_collector_contract.py +++ b/contracts/fees_collector_contract.py @@ -13,13 +13,11 @@ from utils.utils_generic import log_step_fail, log_step_pass, log_warning, log_unexpected_args from utils.utils_chain import Account, WrapperAddress as Address, decode_merged_attributes, hex_to_string, log_explorer_transaction, dec_to_padded_hex, string_to_base64 from multiversx_sdk.abi.serializer import Serializer -import re from multiversx_sdk.abi.biguint_value import BigUIntValue from multiversx_sdk.abi.small_int_values import * from multiversx_sdk.abi.string_value import StringValue from multiversx_sdk.abi.struct_value import StructValue -from multiversx_sdk.abi.values_multi import * -from multiversx_sdk.abi.field import Field +from multiversx_sdk.abi.fields import Field from multiversx_sdk.abi.list_value import ListValue logger = get_logger(__name__) @@ -245,7 +243,6 @@ def allow_external_claim(self, user: Account, proxy: ProxyNetworkProvider, allow return endpoint_call(proxy, gas_limit ,user, Address(self.address), "setAllowExternalClaimRewards", sc_args) - def get_user_energy_for_week(self, user: str, proxy: ProxyNetworkProvider, week: int) -> dict: data_fetcher = FeeCollectorContractDataFetcher(Address(self.address), proxy.url) raw_results = data_fetcher.get_data('getUserEnergyForWeek', [Address(user).get_public_key(), week]) @@ -318,29 +315,4 @@ def get_all_stats(self, proxy: ProxyNetworkProvider, user: str, week: int): "total_energy_for_week": self.get_total_energy_for_week(proxy, week) } - return fees_collector_stats - - - def get_tx_op(self, tx_hash: str, operation: dict, api: ApiNetworkProvider) -> dict: - used_api = api - # Get the transaction details - tx = used_api.get_transaction(tx_hash) - # Get and check transaction operations - ops = tx.raw_response['operations'] - - - tx_decoded = tx - new = self.transaction_decoder.get_transaction_metadata(tx_decoded) - - print(new.function_name) - print(new.function_args) - - # Take each op in ops and match it with operation. Try to match only the fields expected in operation dictionary. - # TX Operations are unordered. If any of the operations match, return it. - for op in ops: - # print(f'Matching with {operation}') - if all(op.get(key) == operation.get(key) for key in operation.keys()): - return op - - def wrap(self, source: str, width: int): - return [source[i:i + width] for i in range(0, len(source), width)] \ No newline at end of file + return fees_collector_stats \ No newline at end of file diff --git a/tools/notebooks/fees_colector.ipynb b/tools/notebooks/fees_colector.ipynb index 88d6b67..ebf1bbe 100644 --- a/tools/notebooks/fees_colector.ipynb +++ b/tools/notebooks/fees_colector.ipynb @@ -32,9 +32,7 @@ "source": [ "#ADDRESS SETUP\n", "user = Account(pem_file=\"~/Documents/mx-sdk-py-exchange/wallets/C1.pem\")\n", - "user.address = Address(\"\")\n", - "user2 = Account(pem_file=\"\")\n", - "user2.address = \"\"\n", + "user.address = Address(\"erd1lvjeyuhupghd64qmppnrsvtqhqp6catp6aje6g72clgnrn0c84xsntan3x\")\n", "sc_owner = Account(pem_file=\"~/Documents/mx-sdk-py-exchange/wallets/C10.pem\")\n", "sc_owner.address = Address(\"erd1ss6u80ruas2phpmr82r42xnkd6rxy40g9jl69frppl4qez9w2jpsqj8x97\")\n", "user.sync_nonce(context.network_provider.proxy)\n", @@ -107,7 +105,7 @@ "\n", "# Get the transaction details\n", "tx = used_api.get_transaction(txhash)\n", - "sender = tx.sender.bech32()\n", + "sender = tx.sender.to_bech32()\n", "\n", "ops = tx.raw_response['data']\n", "print(ops)\n" @@ -148,15 +146,23 @@ "metadata": {}, "outputs": [], "source": [ + "sender = fees_collector_contract.address\n", + "receiver = user.address.to_bech32()\n", + "\n", "op_to_look_for = {\n", - " \"action\": \"MultiESDTNFTTransfer\",\n", - " \"sender\": user.address,\n", - " \"receiver\": user.address,\n", - " \"value\": 0\n", + " \"id\": txhash,\n", + " \"action\": \"transfer\",\n", " }\n", - "api = context.network_provider.api\n", - "op = fees_collector_contract.get_tx_op(txhash, op_to_look_for, api)\n", - "print(op)" + "\n", + "network = context.network_provider\n", + "api = network.api\n", + "tx = api.get_transaction(txhash)\n", + "ops = tx.raw_response['operations']\n", + "op = network.get_tx_specific_op(txhash, op_to_look_for,api)\n", + "\n", + "# print(ops)\n", + "for operation in op:\n", + " print(operation)\n" ] }, { @@ -353,13 +359,6 @@ "all_stats = fees_collector_contract.get_all_stats(context.network_provider.proxy, user.address.to_bech32, current_week)\n", "print(all_stats)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/utils/utils_tx.py b/utils/utils_tx.py index 602aa5a..8431400 100644 --- a/utils/utils_tx.py +++ b/utils/utils_tx.py @@ -18,6 +18,7 @@ from multiversx_sdk.network_providers.transaction_status import \ TransactionStatus from multiversx_sdk.network_providers.transactions import TransactionOnNetwork +from multiversx_sdk.network_providers.transaction_decoder import TransactionDecoder from utils.logger import get_logger from utils.utils_chain import Account, WrapperAddress, log_explorer_transaction @@ -178,6 +179,20 @@ def get_tx_operations(self, tx_hash: str) -> list: return [] + def get_tx_specific_op(self, tx_hash: str, operation: dict, api: ApiNetworkProvider) -> dict: + # Get the transaction details + tx = api.get_transaction(tx_hash) + # Get and check transaction operations + ops = tx.raw_response['operations'] + # Take each op in ops and match it with operation. Try to match only the fields expected in operation dictionary. + # TX Operations are unordered. If any of the operations match, return it. + operations = [] + for op in ops: + # print(f'Matching with {operation}') + if all(op.get(key) == operation.get(key) for key in operation.keys()): + operations.append(op.copy()) + return operations + def check_for_burn_operation(self, tx_hash: str, token: ESDTToken) -> bool: operations = self.get_tx_operations(tx_hash) if not operations: