Skip to content

Commit

Permalink
Merge pull request #15 from multiversx/account-transactions-factory
Browse files Browse the repository at this point in the history
Implemented account transactions factory
  • Loading branch information
popenta authored Mar 6, 2024
2 parents e62a962 + 9ac6dbf commit 544988c
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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),
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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()
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 544988c

Please sign in to comment.