diff --git a/.gitignore b/.gitignore index b40f6d4..fbcecfa 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,6 @@ __pycache__ /deploy/*/upgrader_outputs /dump /abis -/wasm \ No newline at end of file +/wasm +*downloads +*state_dump \ No newline at end of file diff --git a/config.py b/config.py index 13265d5..615ed3e 100644 --- a/config.py +++ b/config.py @@ -62,6 +62,7 @@ ESCROW_BYTECODE_PATH = DEFAULT_WORKSPACE.absolute() / "wasm-v3" / "lkmex-transfer.wasm" LK_WRAP_BYTECODE_PATH = DEFAULT_WORKSPACE.absolute() / "wasm-v3" / "locked-token-wrapper.wasm" COMPOSABLE_TASKS_BYTECODE_PATH = "https://github.com/multiversx/mx-exchange-tools-sc/releases/download/v1.0.0-rc3/composable-tasks.wasm" +PERMISSIONS_HUBS_BYTECODE_PATH = DEFAULT_WORKSPACE.home() / "projects" / "dex" / "dex-v2" / "sc-dex-rs" / "output-docker" / "permissions-hub" / "permissions-hub.wasm" # ------------ Generic configuration below; Modify only in case of framework changes ------------ # @@ -113,6 +114,7 @@ ESCROWS = "escrows" LK_WRAPS = "lk_wraps" COMPOSABLE_TASKS = "composable_tasks" +PERMISSIONS_HUBS = "permissions_hubs" def get_default_tokens_file(): diff --git a/contracts/farm_contract.py b/contracts/farm_contract.py index 1cd1132..3c29bca 100644 --- a/contracts/farm_contract.py +++ b/contracts/farm_contract.py @@ -87,6 +87,27 @@ def enterFarm(self, network_provider: NetworkProviders, user: Account, event: En return multi_esdt_endpoint_call(function_purpose, network_provider.proxy, gas_limit, user, Address(self.address), enterFarmFn, sc_args) + + def enter_farm_on_behalf(self, network_provider: NetworkProviders, user: Account, event: EnterFarmEvent) -> str: + function_purpose = "enter farm on behalf" + logger.info(function_purpose) + logger.debug(f"Account: {user.address}") + + enterFarmFn = "enterFarmOnBehalf" + + logger.info(f"Calling {enterFarmFn} endpoint...") + + gas_limit = 50000000 + + tokens = [ESDTToken(event.farming_tk, event.farming_tk_nonce, event.farming_tk_amount)] + if event.farm_tk: + tokens.append(ESDTToken(event.farm_tk, event.farm_tk_nonce, event.farm_tk_amount)) + + sc_args = [tokens, + Address(event.on_behalf)] + + return multi_esdt_endpoint_call(function_purpose, network_provider.proxy, gas_limit, user, + Address(self.address), enterFarmFn, sc_args) def exitFarm(self, network_provider: NetworkProviders, user: Account, event: ExitFarmEvent) -> str: function_purpose = f"exit farm" @@ -124,6 +145,19 @@ def claim_boosted_rewards(self, network_provider: NetworkProviders, user: Accoun sc_args = [Address(event.user)] if event.user else [] return endpoint_call(network_provider.proxy, gas_limit, user, Address(self.address), claim_fn, sc_args) + + def claim_rewards_on_behalf(self, network_provider: NetworkProviders, user: Account, event: ClaimRewardsFarmEvent) -> str: + function_purpose = f"claimRewardsOnBehalf" + logger.info(function_purpose) + logger.debug(f"Account: {user.address}") + + gas_limit = 50000000 + tokens = [ESDTToken(self.farmToken, event.nonce, event.amount)] + sc_args = [ + tokens + ] + return multi_esdt_endpoint_call(function_purpose, network_provider.proxy, gas_limit, user, + Address(self.address), "claimRewardsOnBehalf", sc_args) def compoundRewards(self, network_provider: NetworkProviders, user: Account, event: CompoundRewardsFarmEvent) -> str: @@ -385,6 +419,17 @@ def update_owner_or_admin(self, deployer: Account, proxy: ProxyNetworkProvider, logger.debug(f"Arguments: {sc_args}") return endpoint_call(proxy, gas_limit, deployer, Address(self.address), "updateOwnerOrAdmin", sc_args) + def set_permissions_hub_address(self, deployer: Account, proxy: ProxyNetworkProvider, address: str): + """Only V3. + """ + function_purpose = "Set permissions hub address" + logger.info(function_purpose) + + gas_limit = 10000000 + sc_args = [address] + logger.debug(f"Arguments: {sc_args}") + return endpoint_call(proxy, gas_limit, deployer, Address(self.address), "setPermissionsHubAddress", sc_args) + def set_transfer_role_farm_token(self, deployer: Account, proxy: ProxyNetworkProvider, whitelisted_sc_address: str): """Only V2Boosted. """ diff --git a/contracts/permissions_hub_contract.py b/contracts/permissions_hub_contract.py new file mode 100644 index 0000000..2982776 --- /dev/null +++ b/contracts/permissions_hub_contract.py @@ -0,0 +1,124 @@ +from contracts.contract_identities import DEXContractInterface +from utils.contract_data_fetchers import PermissionsHubContractDataFetcher +from utils.logger import get_logger +from utils.utils_tx import endpoint_call, deploy +from utils.utils_chain import log_explorer_transaction +from utils.utils_generic import log_step_fail, log_step_pass, log_unexpected_args +from utils.utils_chain import Account, WrapperAddress as Address +from multiversx_sdk import CodeMetadata, ProxyNetworkProvider + + +logger = get_logger(__name__) + + +class PermissionsHubContract(DEXContractInterface): + def __init__(self, address: str = ""): + self.address = address + + def get_config_dict(self) -> dict: + output_dict = { + "address": self.address + } + return output_dict + + @classmethod + def load_config_dict(cls, config_dict: dict): + return PermissionsHubContract(address=config_dict['address']) + + @classmethod + def load_contract_by_address(cls, address: str): + return PermissionsHubContract(address) + + def contract_deploy(self, deployer: Account, proxy: ProxyNetworkProvider, bytecode_path, args: list): + """Expecting as args: + """ + function_purpose = f"Deploy permissions hub contract" + logger.info(function_purpose) + + metadata = CodeMetadata(upgradeable=True, payable_by_contract=True, readable=True) + gas_limit = 200000000 + + arguments = args + tx_hash, address = deploy(type(self).__name__, proxy, gas_limit, deployer, bytecode_path, metadata, arguments) + return tx_hash, address + + def add_to_whitelist(self, deployer: Account, proxy: ProxyNetworkProvider, args: list) -> str: + """Expecting as args: + - whitelisted_sc_addresses: list[address] + """ + function_purpose = "Add addresses to whitelist" + logger.info(function_purpose) + + if len(args) < 1: + log_unexpected_args(function_purpose, args) + return "" + + gas_limit = 30000000 + sc_args = args + logger.debug(f"Arguments: {sc_args}") + return endpoint_call(proxy, gas_limit, deployer, Address(self.address), "whitelist", sc_args) + + def remove_from_whitelist(self, deployer: Account, proxy: ProxyNetworkProvider, args: list) -> str: + """Expecting as args: + - whitelisted_sc_addresses: list[address] + """ + function_purpose = "Remove addresses to whitelist" + logger.info(function_purpose) + + if len(args) < 1: + log_unexpected_args(function_purpose, args) + return "" + + gas_limit = 30000000 + sc_args = args + logger.debug(f"Arguments: {sc_args}") + return endpoint_call(proxy, gas_limit, deployer, Address(self.address), "removeWhitelist", sc_args) + + def add_to_blacklist(self, deployer: Account, proxy: ProxyNetworkProvider, args: list) -> str: + """Expecting as args: + - blacklisted_sc_addresses: list[address] + """ + function_purpose = "Add addresses to blacklist" + logger.info(function_purpose) + + if len(args) < 1: + log_unexpected_args(function_purpose, args) + return "" + + gas_limit = 30000000 + sc_args = args + logger.debug(f"Arguments: {sc_args}") + return endpoint_call(proxy, gas_limit, deployer, Address(self.address), "blacklist", sc_args) + + def remove_from_blacklist(self, deployer: Account, proxy: ProxyNetworkProvider, args: list) -> str: + """Expecting as args: + - blacklisted_sc_addresses: list[address] + """ + function_purpose = "Remove addresses from blacklist" + logger.info(function_purpose) + + if len(args) < 1: + log_unexpected_args(function_purpose, args) + return "" + + gas_limit = 30000000 + sc_args = args + logger.debug(f"Arguments: {sc_args}") + return endpoint_call(proxy, gas_limit, deployer, Address(self.address), "removeBlacklist", sc_args) + + def is_whitelisted(self, user: str, address: str, proxy: ProxyNetworkProvider) -> bool: + data_fetcher = PermissionsHubContractDataFetcher(Address(self.address), proxy.url) + raw_results = data_fetcher.get_data('isWhitelisted', + [ + Address(address).get_public_key(), + Address(user).get_public_key() + ]) + if not raw_results: + return False + return bool(raw_results) + + def contract_start(self, deployer: Account, proxy: ProxyNetworkProvider, args: list = None): + pass + + def print_contract_info(self): + log_step_pass(f"Deployed permissions hub contract: {self.address}") diff --git a/deploy/configs-devnet/deploy_structure.json b/deploy/configs-devnet/deploy_structure.json index 910a91e..fee2646 100644 --- a/deploy/configs-devnet/deploy_structure.json +++ b/deploy/configs-devnet/deploy_structure.json @@ -257,6 +257,12 @@ } ], + "permissions_hubs": [ + { + "empty": 0 + } + ], + "farms_community": [ ], diff --git a/deploy/configs-devnet/deployed_farms_boosted.json b/deploy/configs-devnet/deployed_farms_boosted.json index 6062733..b93e3c7 100644 --- a/deploy/configs-devnet/deployed_farms_boosted.json +++ b/deploy/configs-devnet/deployed_farms_boosted.json @@ -1,9 +1,9 @@ [ { "farmingToken": "EGLDMEX-95c6d5", - "farmToken": "EGLDMEXFL-f0bc2e", + "farmToken": "EGLDMEXFL-2ed783", "farmedToken": "MEX-a659d0", - "address": "erd1qqqqqqqqqqqqqpgq3chrzjdg3gu40zt6kes6w62cyz8tes6k0n4ssk3hj4", + "address": "erd1qqqqqqqqqqqqqpgqqjpfmappkpzq6kdju3mkqepk3d2kcymu0n4srd45sa", "version": 4 }, { @@ -50,9 +50,9 @@ }, { "farmingToken": "HTMWEGLD-acd22a", - "farmToken": "HTMWEGLDFL-a7b3cc", + "farmToken": "HTMWEGLDFL-f9ec2b", "farmedToken": "MEX-a659d0", - "address": "erd1qqqqqqqqqqqqqpgq0qrv3nqsfv5sg936anqu2a56x99jt4370n4sp55uny", + "address": "erd1qqqqqqqqqqqqqpgqrrghrmzzq3vczqpx2quaza4yyke5fudh0n4sxdhv67", "version": 4 }, { diff --git a/deploy/configs-devnet/deployed_permissions_hubs.json b/deploy/configs-devnet/deployed_permissions_hubs.json new file mode 100644 index 0000000..28810f4 --- /dev/null +++ b/deploy/configs-devnet/deployed_permissions_hubs.json @@ -0,0 +1,5 @@ +[ + { + "address": "erd1qqqqqqqqqqqqqpgqwrntspndf3lqth9tgl9k4cde8prq3q4q0n4svee8fh" + } +] \ No newline at end of file diff --git a/deploy/dex_structure.py b/deploy/dex_structure.py index e203fa3..203c318 100644 --- a/deploy/dex_structure.py +++ b/deploy/dex_structure.py @@ -32,6 +32,7 @@ from contracts.dex_proxy_contract import DexProxyContract from contracts.governance_contract import GovernanceContract from contracts.composable_tasks_contract import ComposableTasksContract +from contracts.permissions_hub_contract import PermissionsHubContract from utils.utils_tx import NetworkProviders from utils.utils_chain import hex_to_string from utils.utils_chain import Account, WrapperAddress as Address @@ -144,6 +145,7 @@ def add_clean_contract_deploy_arguments(cls, parser: ArgumentParser): parser.add_argument("--escrows", action="store_true", help="Deploy clean escrows") parser.add_argument("--lk-wraps", action="store_true", help="Deploy clean lk token wrappers") parser.add_argument("--composable-tasks", action="store_true", help="Deploy clean composable tasks") + parser.add_argument("--permissions-hubs", action="store_true", help="Deploy clean permissions hubs") class DeployStructure: @@ -193,6 +195,9 @@ def __init__(self): config.PAIRS_VIEW: ContractStructure(config.PAIRS_VIEW, PairContract, config.PAIR_VIEW_BYTECODE_PATH, self.pool_view_deploy, False), + config.PERMISSIONS_HUBS: + ContractStructure(config.PERMISSIONS_HUBS, PermissionsHubContract, config.PERMISSIONS_HUBS_BYTECODE_PATH, + self.permissions_hub_deploy, False), config.FARMS_COMMUNITY: ContractStructure(config.FARMS_COMMUNITY, FarmContract, config.FARM_COMMUNITY_BYTECODE_PATH, self.farm_community_deploy, False), @@ -1081,6 +1086,25 @@ def pool_view_deploy(self, contracts_index: str, deployer_account: Account, netw deployed_contracts.append(dummy_pair_contract) self.contracts[contracts_index].deployed_contracts = deployed_contracts + def permissions_hub_deploy(self, contracts_index: str, deployer_account: Account, network_providers: NetworkProviders): + contract_structure = self.contracts[contracts_index] + deployed_contracts = [] + for _ in contract_structure.deploy_structure_list: + # deploy permissions hub contract + deployed_contract = PermissionsHubContract() + tx_hash, contract_address = deployed_contract.contract_deploy(deployer_account, + network_providers.proxy, + contract_structure.bytecode, []) + # check for deployment success and save the deployed address + if not network_providers.check_deploy_tx_status(tx_hash, contract_address, "permissions hub"): + return + + deployed_contract.address = contract_address + log_step_pass(f"Permissions hub contract address: {contract_address}") + + deployed_contracts.append(deployed_contract) + self.contracts[contracts_index].deployed_contracts = deployed_contracts + def farm_deploy(self, contracts_index: str, deployer_account: Account, network_providers: NetworkProviders): contract_structure = self.contracts[contracts_index] deployed_contracts = [] diff --git a/events/farm_events.py b/events/farm_events.py index 8782936..6121a6c 100644 --- a/events/farm_events.py +++ b/events/farm_events.py @@ -2,14 +2,16 @@ class EnterFarmEvent: def __init__(self, - farming_token: str, farming_nonce: int, farming_amount, - farm_token: str, farm_nonce: int, farm_amount): + farming_token: str, farming_nonce: int, farming_amount: int, + farm_token: str, farm_nonce: int, farm_amount: int, + on_behalf: str = ""): self.farming_tk = farming_token self.farming_tk_nonce = farming_nonce self.farming_tk_amount = farming_amount self.farm_tk = farm_token self.farm_tk_nonce = farm_nonce self.farm_tk_amount = farm_amount + self.on_behalf = on_behalf # address of the user on behalf of whom the operation is executed class ExitFarmEvent: diff --git a/tools/chain_simulator_connector.py b/tools/chain_simulator_connector.py index ad44e2f..1e46d9b 100644 --- a/tools/chain_simulator_connector.py +++ b/tools/chain_simulator_connector.py @@ -9,7 +9,7 @@ from utils.utils_chain import decode_merged_attributes, string_to_hex, dec_to_padded_hex from utils.utils_chain import WrapperAddress, Account from utils.utils_generic import log_step_fail, log_step_pass, log_warning -from tools.runners.account_state_runner import get_account_keys_online +from tools.runners.account_state_runner import get_account_keys_online, get_account_data_online from multiversx_sdk import ProxyNetworkProvider @@ -49,7 +49,7 @@ def get_contract_retrieval_labels(contracts: str) -> List[str]: return contracts.split(",") -def fetch_states(context: Context, args) -> json: +def fetch_contract_states(context: Context, args) -> json: proxy = ProxyNetworkProvider(args.gateway) contracts_shard = WrapperAddress(context.get_contracts(config.ROUTER)[0].address).get_shard() all_keys = list[dict] @@ -67,15 +67,24 @@ def fetch_states(context: Context, args) -> json: index = int(args.contract_index) if index >= len(contracts): log_step_fail(f"Contract index {index} is out of bounds for {label} contracts.") - return + return [] contracts = [contracts[index]] - # retrieve state for each contract + # retrieve keys and data for each contract for i, contract in enumerate(contracts): - file = f"{STATES_FOLDER}/{block_number}_{label}_{i}_state.json" - keys = get_account_keys_online(contract.address, proxy.url, block_number, file) - all_keys.append(keys) + keys_file = f"{STATES_FOLDER}/{block_number}_{label}_{i}_state.json" + data_file = f"{STATES_FOLDER}/{block_number}_{label}_{i}_data.json" + keys = get_account_keys_online(contract.address, proxy.url, block_number, keys_file) + data = get_account_data_online(contract.address, proxy.url, block_number, data_file) + account_state = {} + account_state.update(data) + account_state['keys'] = keys + + all_keys.append(account_state) + + return all_keys + # get system account state - done later since it's gigantic file = f"{STATES_FOLDER}/{block_number}_system_account_state.json" if args.system_account == "offline": diff --git a/tools/notebooks/boosted-farm.ipynb b/tools/notebooks/boosted-farm.ipynb index ebc76ea..2d28794 100644 --- a/tools/notebooks/boosted-farm.ipynb +++ b/tools/notebooks/boosted-farm.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -27,7 +27,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -42,7 +42,7 @@ "metadata": {}, "outputs": [], "source": [ - "farm_contract: FarmContract = context.deploy_structure.get_deployed_contract_by_index(config.FARMS_V2, 2)\n", + "farm_contract: FarmContract = context.deploy_structure.get_deployed_contract_by_index(config.FARMS_V2, -3)\n", "print(f\"Using {farm_contract.address} : {farm_contract.farmToken}\")" ] }, @@ -187,6 +187,23 @@ "farm_contract.resume(context.deployer_account, context.network_provider.proxy)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Basic user" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "user = Account(pem_file=config.DEFAULT_ADMIN)\n", + "user.sync_nonce(context.network_provider.proxy)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -204,7 +221,7 @@ "from multiversx_sdk import ApiNetworkProvider\n", "\n", "mainnet_api = ApiNetworkProvider(\"https://api.multiversx.com\")\n", - "fetched_users = collect_farm_contract_users(100, farm_contract.address, farm_contract.farmingToken, farm_contract.farmToken,\n", + "fetched_users = collect_farm_contract_users(20, farm_contract.address, farm_contract.farmingToken, farm_contract.farmToken,\n", " mainnet_api, context.network_provider.proxy)" ] }, @@ -224,6 +241,7 @@ "from utils.decoding_structures import FARM_TOKEN_ATTRIBUTES\n", "from utils.utils_chain import decode_merged_attributes, base64_to_hex, get_all_token_nonces_details_for_account\n", "from utils.utils_scenarios import FetchedUser\n", + "from typing import List\n", "\n", "index = 0\n", "users: List[FetchedUser] = fetched_users.get_users_with_farm_tokens()\n", @@ -290,10 +308,10 @@ "outputs": [], "source": [ "from utils.decoding_structures import FARM_TOKEN_ATTRIBUTES\n", - "from utils.utils_chain import decode_merged_attributes, base64_to_hex\n", + "from utils.utils_chain import decode_merged_attributes, base64_to_hex, WrapperAddress\n", "\n", "user = Account(pem_file=config.DEFAULT_ACCOUNTS)\n", - "user.address = WrapperAddress(\"erd15u0ulntwpjl32m7afnnatz48kp8sec6euj64vl97vxhynxcmt9jq0n350m\")\n", + "user.address = WrapperAddress(\"\")\n", "user.sync_nonce(context.network_provider.proxy)\n", "\n", "tokens_in_account = get_all_token_nonces_details_for_account(farm_contract.farmingToken, user.address.bech32(), context.network_provider.proxy)\n", @@ -322,17 +340,19 @@ "outputs": [], "source": [ "from utils.utils_tx import multi_esdt_transfer, ESDTToken\n", - "from utils.utils_chain import Account\n", + "from utils.utils_chain import Account, WrapperAddress\n", "fetched_user = users[0]\n", "user_account = Account(pem_file=config.DEFAULT_ACCOUNTS)\n", "user_account.address = fetched_user.address\n", "user_account.sync_nonce(context.network_provider.proxy)\n", "\n", + "destination = WrapperAddress(\"\")\n", + "\n", "full_token_id = fetched_user.farm_tokens[0].get('tokenIdentifier')\n", "token_id = '-'.join(full_token_id.split('-')[:-1])\n", "token = ESDTToken(token_id, fetched_user.farm_tokens[0].get('nonce'), int(fetched_user.farm_tokens[0].get('balance'))//2)\n", "print(f'Sending: {token.get_token_data()} from {fetched_user.address.bech32()}')\n", - "multi_esdt_transfer(context.network_provider.proxy, 1000000, user_account, users[1].address, [token])" + "multi_esdt_transfer(context.network_provider.proxy, 1000000, user_account, destination, [token])" ] }, { @@ -353,9 +373,12 @@ "\n", "# user = fetched_users[randint(0, len(fetched_users.get_users_with_farming_tokens()) - 1)].address\n", "# user = fetched_users.get_users_with_both_tokens()[0].address\n", - "user_account = Account(pem_file=config.DEFAULT_ACCOUNTS)\n", - "user_account.address = user\n", - "user_account.sync_nonce(context.network_provider.proxy)\n", + "\n", + "# user_account = Account(pem_file=config.DEFAULT_ACCOUNTS)\n", + "# user_account.address = user\n", + "# user_account.sync_nonce(context.network_provider.proxy)\n", + "\n", + "user_account = user\n", "\n", "farming_tk_balance = 0\n", "tokens_in_account = get_all_token_nonces_details_for_account(farm_contract.farmingToken, user_account.address.bech32(), context.network_provider.proxy)\n", @@ -412,6 +435,8 @@ "\n", "# user = fetched_users[randint(0, len(fetched_users.get_users_with_farming_tokens()) - 1)].address\n", "# user = fetched_users.get_users_with_both_tokens()[0].address\n", + "user_account = Account(pem_file=config.DEFAULT_ACCOUNTS)\n", + "user_account.address = user\n", "user_account.sync_nonce(context.network_provider.proxy)\n", "\n", "farm_tk_balance, farm_tk_nonce = 0, 0\n", @@ -449,6 +474,67 @@ "tx_hash = farm_contract.claim_boosted_rewards(context.network_provider, user_account, event)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Claim on behalf" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from contracts.permissions_hub_contract import PermissionsHubContract\n", + "\n", + "permissions_hub_contract: PermissionsHubContract\n", + "permissions_hub_contract = context.get_contracts(config.PERMISSIONS_HUBS)[0]\n", + "print(permissions_hub_contract.address)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "permissions_hub_contract.add_to_whitelist(user_account, context.network_provider.proxy, [\"erd1rf4hv70arudgzus0ymnnsnc4pml0jkywg2xjvzslg0mz4nn2tg7q7k0t6p\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from utils.utils_chain import Account, WrapperAddress as Address, get_all_token_nonces_details_for_account\n", + "from contracts.farm_contract import ClaimRewardsFarmEvent\n", + "\n", + "# user = fetched_users[randint(0, len(fetched_users.get_users_with_farming_tokens()) - 1)].address\n", + "# user = fetched_users.get_users_with_both_tokens()[0].address\n", + "claim_account = Account(pem_file=config.DEFAULT_ACCOUNTS)\n", + "claim_account.address = WrapperAddress(\"\")\n", + "claim_account.sync_nonce(context.network_provider.proxy)\n", + "\n", + "farm_tk_balance, farm_tk_nonce = 0, 0\n", + "tokens_in_account = get_all_token_nonces_details_for_account(farm_contract.farmToken, claim_account.address.bech32(), context.network_provider.proxy)\n", + "print(f'Found {len(tokens_in_account)} positions of {farm_contract.farmToken} in account')\n", + "for token in tokens_in_account:\n", + " if int(token['balance']) > farm_tk_balance:\n", + " farm_tk_balance = int(token['balance'])\n", + " farm_tk_nonce = token['nonce']\n", + " break\n", + "\n", + "if not farm_tk_nonce:\n", + " raise Exception(\"Not enough farm token balance\")\n", + "\n", + "event = ClaimRewardsFarmEvent(farm_tk_balance, farm_tk_nonce, '')\n", + "\n", + "tx_hash = farm_contract.claim_rewards_on_behalf(context.network_provider, claim_account, event)" + ] + }, { "cell_type": "markdown", "metadata": {}, diff --git a/tools/runners/account_state_runner.py b/tools/runners/account_state_runner.py index abd05a9..420138b 100644 --- a/tools/runners/account_state_runner.py +++ b/tools/runners/account_state_runner.py @@ -55,6 +55,36 @@ def get_account_keys_online(address: str, proxy_url: str, block_number: int = 0, return keys +def get_account_data_online(address: str, proxy_url: str, block_number: int = 0, with_save_in: str = "") -> Dict[str, Any]: + """Get account data from chain""" + + if block_number == 0: + resource_url = f"address/{address}" + else: + resource_url = f"address/{address}?blockNonce={block_number}" + + proxy = ProxyNetworkProvider(proxy_url) + response = {} + + try: + response = proxy.do_get_generic(resource_url) + except (requests.exceptions.RequestException, errors.GenericError) as e: + log_step_fail(f"Exception occurred while retrieving data: {e}") + + data = response.get("account", {}) + + if data and with_save_in: + dir_path = os.path.dirname(with_save_in) + if not os.path.exists(dir_path): + os.mkdir(dir_path) + + with open(with_save_in, 'w', encoding="UTF-8") as state_writer: + json.dump(data, state_writer, indent=4) + logger.info(f'Dumped the retrieved contact state in: {with_save_in}') + + return data + + def compare_keys(left_state: dict, right_state: dict) -> Tuple[bool, dict, dict, dict, dict]: """ Returns: diff --git a/utils/contract_data_fetchers.py b/utils/contract_data_fetchers.py index 2fd4c48..15ad05c 100644 --- a/utils/contract_data_fetchers.py +++ b/utils/contract_data_fetchers.py @@ -348,6 +348,14 @@ def __init__(self, contract_address: Address, proxy_url: str): "isSCAddressWhitelisted": self._get_int_view } +class PermissionsHubContractDataFetcher(DataFetcher): + def __init__(self, contract_address: Address, proxy_url: str): + super().__init__(contract_address, proxy_url) + self.view_handler_map = { + "isWhitelisted": self._get_int_view, + "getBlacklistedAddresses": self._get_hex_list_view, + } + class LkWrapContractDataFetcher(DataFetcher): def __init__(self, contract_address: Address, proxy_url: str):