diff --git a/multiversx_sdk/core/transactions_factories/account_transactions_factory.py b/multiversx_sdk/core/transactions_factories/account_transactions_factory.py new file mode 100644 index 00000000..df1a8d4d --- /dev/null +++ b/multiversx_sdk/core/transactions_factories/account_transactions_factory.py @@ -0,0 +1,115 @@ +from typing import Dict, List, Protocol + +from multiversx_sdk.core.interfaces import IAddress +from multiversx_sdk.core.transaction import Transaction +from multiversx_sdk.core.transactions_factories.transaction_builder import \ + TransactionBuilder + + +class IConfig(Protocol): + chain_id: str + min_gas_limit: int + gas_limit_per_byte: int + gas_limit_save_key_value: int + gas_limit_persist_per_byte: int + gas_limit_store_per_byte: int + gas_limit_set_guardian: int + gas_limit_guard_account: int + gas_limit_unguard_account: int + extra_gas_limit_for_guarded_transaction: int + + +class AccountTransactionsFactory: + def __init__(self, config: IConfig) -> None: + self.config = config + + def create_transaction_for_saving_key_value( + self, + sender: IAddress, + key_value_pairs: Dict[bytes, bytes] + ) -> Transaction: + function = "SaveKeyValue" + + extra_gas = self._compute_extra_gas_for_saving_key_value(key_value_pairs) + data_parts = self._compute_data_parts_for_saving_key_value(key_value_pairs) + + data_parts.insert(0, function) + + return TransactionBuilder( + config=self.config, + sender=sender, + receiver=sender, + data_parts=data_parts, + gas_limit=extra_gas, + add_data_movement_gas=True + ).build() + + def create_transaction_for_setting_guardian( + self, + sender: IAddress, + guardian_address: IAddress, + service_id: str + ) -> Transaction: + data_parts = [ + "SetGuardian", + guardian_address.to_hex(), + service_id.encode().hex() + ] + + gas_limit = self.config.gas_limit_set_guardian + self.config.extra_gas_limit_for_guarded_transaction + + return TransactionBuilder( + config=self.config, + sender=sender, + receiver=sender, + data_parts=data_parts, + gas_limit=gas_limit, + add_data_movement_gas=True + ).build() + + def create_transaction_for_guarding_account(self, sender: IAddress) -> Transaction: + data_parts = ["GuardAccount"] + + gas_limit = self.config.gas_limit_guard_account + self.config.extra_gas_limit_for_guarded_transaction + + return TransactionBuilder( + config=self.config, + sender=sender, + receiver=sender, + data_parts=data_parts, + gas_limit=gas_limit, + add_data_movement_gas=True + ).build() + + def create_transaction_for_unguarding_account(self, sender: IAddress) -> Transaction: + data_parts = ["UnGuardAccount"] + + gas_limit = self.config.gas_limit_unguard_account + self.config.extra_gas_limit_for_guarded_transaction + + transaction = TransactionBuilder( + config=self.config, + sender=sender, + receiver=sender, + data_parts=data_parts, + gas_limit=gas_limit, + add_data_movement_gas=True + ).build() + transaction.options = 2 + + return transaction + + def _compute_data_parts_for_saving_key_value(self, key_value_pairs: Dict[bytes, bytes]) -> List[str]: + data_parts: List[str] = [] + + for key, value in key_value_pairs.items(): + data_parts.extend([key.hex(), value.hex()]) + + return data_parts + + def _compute_extra_gas_for_saving_key_value(self, key_value_pairs: Dict[bytes, bytes]) -> int: + extra_gas = 0 + + for key, value in key_value_pairs.items(): + extra_gas += self.config.gas_limit_persist_per_byte * (len(key) + len(value)) + self.config.gas_limit_store_per_byte * len(value) + + return extra_gas + self.config.gas_limit_save_key_value diff --git a/multiversx_sdk/core/transactions_factories/account_transactions_factory_test.py b/multiversx_sdk/core/transactions_factories/account_transactions_factory_test.py new file mode 100644 index 00000000..bfc825a1 --- /dev/null +++ b/multiversx_sdk/core/transactions_factories/account_transactions_factory_test.py @@ -0,0 +1,63 @@ +from typing import Dict + +from multiversx_sdk.core.address import Address +from multiversx_sdk.core.transactions_factories.account_transactions_factory import \ + AccountTransactionsFactory +from multiversx_sdk.core.transactions_factories.transactions_factory_config import \ + TransactionsFactoryConfig + + +class TestAccountTransactionsFactory: + config = TransactionsFactoryConfig("D") + factory = AccountTransactionsFactory(config) + + def test_save_key_value(self): + sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") + pairs: Dict[bytes, bytes] = {} + key = bytes.fromhex("6b657930") + value = bytes.fromhex("76616c756530") + pairs[key] = value + + tx = self.factory.create_transaction_for_saving_key_value( + sender=sender, + key_value_pairs=pairs + ) + + assert tx.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.receiver == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.data.decode() == "SaveKeyValue@6b657930@76616c756530" + assert tx.gas_limit == 271000 + + def test_set_guardian(self): + sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") + guardian = Address.new_from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx") + service_id = "MultiversXTCSService" + + tx = self.factory.create_transaction_for_setting_guardian( + sender=sender, + guardian_address=guardian, + service_id=service_id + ) + + assert tx.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.receiver == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.data.decode() == "SetGuardian@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8@4d756c7469766572735854435353657276696365" + assert tx.gas_limit == 525500 + + def test_guard_account(self): + sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") + tx = self.factory.create_transaction_for_guarding_account(sender) + + assert tx.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.receiver == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.data.decode() == "GuardAccount" + assert tx.gas_limit == 368000 + + def test_unguard_account(self): + sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") + tx = self.factory.create_transaction_for_unguarding_account(sender) + + assert tx.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.receiver == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert tx.data.decode() == "UnGuardAccount" + assert tx.gas_limit == 371000 diff --git a/multiversx_sdk/core/transactions_factories/smart_contract_transaction_factory_test.py b/multiversx_sdk/core/transactions_factories/smart_contract_transaction_factory_test.py index 0b8a4f3a..d3f3a37b 100644 --- a/multiversx_sdk/core/transactions_factories/smart_contract_transaction_factory_test.py +++ b/multiversx_sdk/core/transactions_factories/smart_contract_transaction_factory_test.py @@ -207,3 +207,33 @@ def test_create_transaction_for_upgrade(self): assert transaction.data.decode().startswith("upgradeContract@") assert transaction.gas_limit == gas_limit assert transaction.value == 0 + + def test_create_transaction_for_claiming_developer_rewards(self): + sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") + contract_address = Address.new_from_bech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4") + + transaction = self.factory.create_transaction_for_claiming_developer_rewards( + sender=sender, + contract=contract_address + ) + + assert transaction.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction.receiver == "erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4" + assert transaction.data.decode() == "ClaimDeveloperRewards" + assert transaction.gas_limit == 6_000_000 + + def test_create_transaction_for_changing_owner_address(self): + sender = Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th") + contract_address = Address.new_from_bech32("erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4") + new_owner = Address.from_bech32("erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx") + + transaction = self.factory.create_transaction_for_changing_owner_address( + sender=sender, + contract=contract_address, + new_owner=new_owner + ) + + assert transaction.sender == "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th" + assert transaction.receiver == "erd1qqqqqqqqqqqqqpgqhy6nl6zq07rnzry8uyh6rtyq0uzgtk3e69fqgtz9l4" + assert transaction.data.decode() == "ChangeOwnerAddress@8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8" + assert transaction.gas_limit == 6_000_000 diff --git a/multiversx_sdk/core/transactions_factories/smart_contract_transactions_factory.py b/multiversx_sdk/core/transactions_factories/smart_contract_transactions_factory.py index df1a627c..e57e446d 100644 --- a/multiversx_sdk/core/transactions_factories/smart_contract_transactions_factory.py +++ b/multiversx_sdk/core/transactions_factories/smart_contract_transactions_factory.py @@ -19,6 +19,8 @@ class IConfig(Protocol): chain_id: str min_gas_limit: int gas_limit_per_byte: int + gas_limit_claim_developer_rewards: int + gas_limit_change_owner_address: int class ITokenComputer(Protocol): @@ -58,7 +60,7 @@ def create_transaction_for_deploy(self, parts += args_to_strings(arguments) - transaction = TransactionBuilder( + return TransactionBuilder( config=self.config, sender=sender, receiver=Address.new_from_bech32(CONTRACT_DEPLOY_ADDRESS), @@ -68,8 +70,6 @@ def create_transaction_for_deploy(self, amount=native_transfer_amount ).build() - return transaction - def create_transaction_for_execute(self, sender: IAddress, contract: IAddress, @@ -103,7 +103,7 @@ def create_transaction_for_execute(self, data_parts.append(function) if not data_parts else data_parts.append(arg_to_string(function)) data_parts.extend(args_to_strings(arguments)) - transaction = TransactionBuilder( + return TransactionBuilder( config=self.config, sender=sender, receiver=receiver, @@ -113,8 +113,6 @@ def create_transaction_for_execute(self, amount=native_transfer_amount ).build() - return transaction - def create_transaction_for_upgrade(self, sender: IAddress, contract: IAddress, @@ -139,7 +137,7 @@ def create_transaction_for_upgrade(self, parts += args_to_strings(arguments) - intent = TransactionBuilder( + return TransactionBuilder( config=self.config, sender=sender, receiver=contract, @@ -149,4 +147,31 @@ def create_transaction_for_upgrade(self, amount=native_transfer_amount ).build() - return intent + def create_transaction_for_claiming_developer_rewards(self, + sender: IAddress, + contract: IAddress) -> Transaction: + data_parts = ["ClaimDeveloperRewards"] + + return TransactionBuilder( + config=self.config, + sender=sender, + receiver=contract, + data_parts=data_parts, + gas_limit=self.config.gas_limit_claim_developer_rewards, + add_data_movement_gas=False + ).build() + + def create_transaction_for_changing_owner_address(self, + sender: IAddress, + contract: IAddress, + new_owner: IAddress) -> Transaction: + data_parts = ["ChangeOwnerAddress", new_owner.to_hex()] + + return TransactionBuilder( + config=self.config, + sender=sender, + receiver=contract, + data_parts=data_parts, + gas_limit=self.config.gas_limit_change_owner_address, + add_data_movement_gas=False, + ).build() diff --git a/multiversx_sdk/core/transactions_factories/transactions_factory_config.py b/multiversx_sdk/core/transactions_factories/transactions_factory_config.py index 356f1b40..5e437b56 100644 --- a/multiversx_sdk/core/transactions_factories/transactions_factory_config.py +++ b/multiversx_sdk/core/transactions_factories/transactions_factory_config.py @@ -41,3 +41,16 @@ def __init__(self, chain_id: str) -> None: self.gas_limit_esdt_transfer = 200_000 self.gas_limit_esdt_nft_transfer = 200_000 self.gas_limit_multi_esdt_nft_transfer = 200_000 + + # Configuration for account operations + self.gas_limit_save_key_value = 100_000 + self.gas_limit_persist_per_byte = 1_000 + self.gas_limit_store_per_byte = 10_000 + self.gas_limit_set_guardian = 250_000 + self.gas_limit_guard_account = 250_000 + self.gas_limit_unguard_account = 250_000 + self.extra_gas_limit_for_guarded_transaction = 50_000 + + # Configuration for smart contract operations + self.gas_limit_claim_developer_rewards = 6_000_000 + self.gas_limit_change_owner_address = 6_000_000