diff --git a/hathor/nanocontracts/runner/index_records.py b/hathor/nanocontracts/runner/index_records.py index 14d5c7929..2e0123358 100644 --- a/hathor/nanocontracts/runner/index_records.py +++ b/hathor/nanocontracts/runner/index_records.py @@ -83,10 +83,12 @@ def to_json(self) -> dict[str, Any]: @classmethod def from_json(cls, json_dict: dict[str, Any]) -> Self: + token_version = TokenVersion(json_dict['token_version']) + assert token_version in (TokenVersion.DEPOSIT, TokenVersion.FEE) return cls( token_uid=TokenUid(VertexId(bytes.fromhex(json_dict['token_uid']))), amount=json_dict['amount'], - token_version=json_dict['token_version'], + token_version=token_version, # type: ignore[arg-type] token_name=json_dict['token_name'], token_symbol=json_dict['token_symbol'], ) diff --git a/hathor/nanocontracts/runner/runner.py b/hathor/nanocontracts/runner/runner.py index e35313dc4..a04903320 100644 --- a/hathor/nanocontracts/runner/runner.py +++ b/hathor/nanocontracts/runner/runner.py @@ -55,9 +55,9 @@ UpdateAuthoritiesRecord, UpdateTokenBalanceRecord, ) +from hathor.nanocontracts.runner.token_fees import calculate_melt_fee, calculate_mint_fee from hathor.nanocontracts.storage import NCBlockStorage, NCChangesTracker, NCContractStorage, NCStorageFactory from hathor.nanocontracts.storage.contract_storage import Balance -from hathor.nanocontracts.syscall_token_balance_rules import TokenSyscallBalanceRules from hathor.nanocontracts.types import ( NC_ALLOW_REENTRANCY, NC_ALLOWED_ACTIONS_ATTR, @@ -475,9 +475,9 @@ def _unsafe_call_another_contract_public_method( # execution, the verification of the tokens and amounts will be done after it for fee in fees: assert fee.amount > 0 - self._update_tokens_amount([ - UpdateTokenBalanceRecord(token_uid=fee.token_uid, amount=-fee.amount) - ]) + self._update_tokens_amount( + fee=UpdateTokenBalanceRecord(token_uid=fee.token_uid, amount=-fee.amount), + ) self._register_paid_fee(fee.token_uid, fee.amount) ctx_actions = Context.__group_actions__(actions) @@ -1033,14 +1033,19 @@ def syscall_mint_tokens( if not balance.can_mint: raise NCInvalidSyscall(f'contract {call_record.contract_id.hex()} cannot mint {token_uid.hex()} tokens') - fee_payment_token_info = self._get_token(fee_payment_token) token_info = self._get_token(token_uid) + fee_amount = calculate_mint_fee( + settings=self._settings, + token_version=token_info.token_version, + amount=amount, + fee_payment_token=self._get_token(fee_payment_token), + ) - syscall_rules = TokenSyscallBalanceRules.get_rules(token_uid, token_info.token_version, self._settings) - syscall_balance = syscall_rules.mint(amount, fee_payment_token=fee_payment_token_info) - records = syscall_rules.get_syscall_update_token_records(syscall_balance) - - self._update_tokens_amount(records) + assert amount > 0 and fee_amount < 0 + self._update_tokens_amount( + operation=UpdateTokenBalanceRecord(token_uid=token_uid, amount=amount), + fee=UpdateTokenBalanceRecord(token_uid=fee_payment_token, amount=fee_amount), + ) @_forbid_syscall_from_view('melt_tokens') def syscall_melt_tokens( @@ -1068,13 +1073,28 @@ def syscall_melt_tokens( raise NCInvalidSyscall(f'contract {call_record.contract_id.hex()} cannot melt {token_uid.hex()} tokens') token_info = self._get_token(token_uid) - fee_payment_token_info = self._get_token(fee_payment_token) - - syscall_rules = TokenSyscallBalanceRules.get_rules(token_uid, token_info.token_version, self._settings) - syscall_balance = syscall_rules.melt(amount, fee_payment_token=fee_payment_token_info) - records = syscall_rules.get_syscall_update_token_records(syscall_balance) + fee_amount = calculate_melt_fee( + settings=self._settings, + token_version=token_info.token_version, + amount=amount, + fee_payment_token=self._get_token(fee_payment_token), + ) - self._update_tokens_amount(records) + assert amount > 0 + match token_info.token_version: + case TokenVersion.NATIVE: + raise AssertionError + case TokenVersion.DEPOSIT: + assert fee_amount > 0 + case TokenVersion.FEE: + assert fee_amount < 0 + case _: # pragma: no cover + assert_never(token_info.token_version) + + self._update_tokens_amount( + operation=UpdateTokenBalanceRecord(token_uid=token_uid, amount=-amount), + fee=UpdateTokenBalanceRecord(token_uid=fee_payment_token, amount=fee_amount), + ) def _validate_context(self, ctx: Context) -> None: """Check whether the context is valid.""" @@ -1154,17 +1174,14 @@ def syscall_create_child_deposit_token( grant_melt=melt_authority, ) - syscall_rules = TokenSyscallBalanceRules.get_rules(token_id, token_version, self._settings) - syscall_balance = syscall_rules.create_token( + self._create_token( + token_version=token_version, token_uid=token_id, - token_symbol=token_symbol, - token_name=token_name, amount=amount, - fee_payment_token=self._get_token(TokenUid(HATHOR_TOKEN_UID)) + fee_payment_token=self._get_token(TokenUid(HATHOR_TOKEN_UID)), + token_name=token_name, + token_symbol=token_symbol, ) - records = syscall_rules.get_syscall_update_token_records(syscall_balance) - - self._update_tokens_amount(records) return token_id @@ -1194,7 +1211,6 @@ def syscall_create_child_fee_token( parent_id = call_record.contract_id cleaned_token_symbol = clean_token_string(token_symbol) - fee_payment_token_info = self._get_token(fee_payment_token) token_id = derive_child_token_id(parent_id, cleaned_token_symbol, salt=salt) token_version = TokenVersion.FEE @@ -1210,17 +1226,15 @@ def syscall_create_child_fee_token( grant_mint=mint_authority, grant_melt=melt_authority, ) - syscall_rules = TokenSyscallBalanceRules.get_rules(token_id, token_version, self._settings) - syscall_balance = syscall_rules.create_token( + + self._create_token( + token_version=token_version, token_uid=token_id, + amount=amount, + fee_payment_token=self._get_token(fee_payment_token), token_symbol=token_symbol, token_name=token_name, - amount=amount, - fee_payment_token=fee_payment_token_info ) - records = syscall_rules.get_syscall_update_token_records(syscall_balance) - - self._update_tokens_amount(records) return token_id @@ -1289,7 +1303,45 @@ def _get_token(self, token_uid: TokenUid) -> TokenDescription: token_id=token_creation_tx.hash ) - def _update_tokens_amount(self, records: list[UpdateTokenBalanceRecord | CreateTokenRecord]) -> None: + def _create_token( + self, + *, + token_version: TokenVersion, + token_uid: TokenUid, + amount: int, + fee_payment_token: TokenDescription, + token_symbol: str, + token_name: str, + ) -> None: + """Create a new token.""" + assert token_version in (TokenVersion.DEPOSIT, TokenVersion.FEE) + fee_amount = calculate_mint_fee( + settings=self._settings, + token_version=token_version, + amount=amount, + fee_payment_token=fee_payment_token, + ) + assert amount > 0 and fee_amount < 0 + self._update_tokens_amount( + operation=CreateTokenRecord( + token_uid=token_uid, + amount=amount, + token_version=token_version, # type: ignore[arg-type] + token_symbol=token_symbol, + token_name=token_name, + ), + fee=UpdateTokenBalanceRecord( + token_uid=TokenUid(fee_payment_token.token_id), + amount=fee_amount, + ), + ) + + def _update_tokens_amount( + self, + *, + operation: UpdateTokenBalanceRecord | CreateTokenRecord | None = None, + fee: UpdateTokenBalanceRecord | None = None, + ) -> None: """ Update token balances and create index records for a token operation. @@ -1297,20 +1349,16 @@ def _update_tokens_amount(self, records: list[UpdateTokenBalanceRecord | CreateT 1. Updates the contract's token balances in the changes tracker 2. Updates the global token totals 3. Appends the syscall records to call_record.index_updates - - Args: - records: List of syscall update records (typically main token + fee payment) - - Raises: - AssertionError: If call_record.index_updates is None """ call_record = self.get_current_call_record() changes_tracker = self.get_current_changes_tracker() - + assert operation or fee assert changes_tracker.nc_id == call_record.contract_id assert call_record.index_updates is not None - for record in records: + for record in (operation, fee): + if record is None: + continue changes_tracker.add_balance(record.token_uid, record.amount) self._updated_tokens_totals[record.token_uid] += record.amount call_record.index_updates.append(record) diff --git a/hathor/nanocontracts/runner/token_fees.py b/hathor/nanocontracts/runner/token_fees.py new file mode 100644 index 000000000..896fd76e9 --- /dev/null +++ b/hathor/nanocontracts/runner/token_fees.py @@ -0,0 +1,88 @@ +# Copyright 2025 Hathor Labs +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing_extensions import assert_never + +from hathor.conf.settings import HathorSettings +from hathor.nanocontracts.exception import NCInvalidFeePaymentToken +from hathor.transaction.token_info import TokenDescription, TokenVersion +from hathor.transaction.util import get_deposit_token_deposit_amount, get_deposit_token_withdraw_amount + + +def calculate_mint_fee( + *, + settings: HathorSettings, + token_version: TokenVersion, + amount: int, + fee_payment_token: TokenDescription, +) -> int: + """Calculate the fee for a mint operation.""" + match token_version: + case TokenVersion.NATIVE: + raise AssertionError + case TokenVersion.DEPOSIT: + _validate_deposit_based_payment_token(fee_payment_token) + return -get_deposit_token_deposit_amount(settings, amount) + case TokenVersion.FEE: + _validate_fee_based_payment_token(fee_payment_token) + return -_calculate_unit_fee_token_fee(settings, fee_payment_token) + case _: # pragma: no cover + assert_never(token_version) + + +def calculate_melt_fee( + *, + settings: HathorSettings, + token_version: TokenVersion, + amount: int, + fee_payment_token: TokenDescription, +) -> int: + """Calculate the fee for a melt operation.""" + match token_version: + case TokenVersion.NATIVE: + raise AssertionError + case TokenVersion.DEPOSIT: + _validate_deposit_based_payment_token(fee_payment_token) + return +get_deposit_token_withdraw_amount(settings, amount) + case TokenVersion.FEE: + _validate_fee_based_payment_token(fee_payment_token) + return -_calculate_unit_fee_token_fee(settings, fee_payment_token) + case _: # pragma: no cover + assert_never(token_version) + + +def _validate_deposit_based_payment_token(fee_payment_token: TokenDescription) -> None: + """Validate the token used to pay the fee of a deposit-based token operation.""" + from hathor import HATHOR_TOKEN_UID + if fee_payment_token.token_id != HATHOR_TOKEN_UID: + raise NCInvalidFeePaymentToken('Only HTR is allowed to be used with deposit based token syscalls') + + +def _validate_fee_based_payment_token(fee_payment_token: TokenDescription) -> None: + """Validate the token used to pay the fee of a fee-based token operation.""" + match fee_payment_token.token_version: + case TokenVersion.FEE: + raise NCInvalidFeePaymentToken("fee-based tokens aren't allowed for paying fees") + case TokenVersion.DEPOSIT | TokenVersion.NATIVE: + pass + case _: # pragma: no cover + assert_never(fee_payment_token.token_version) + + +def _calculate_unit_fee_token_fee(settings: HathorSettings, fee_payment_token: TokenDescription) -> int: + """Calculate the fee for handling a fee-based token""" + from hathor import HATHOR_TOKEN_UID + if fee_payment_token.token_id == HATHOR_TOKEN_UID: + return settings.FEE_PER_OUTPUT + return int(settings.FEE_PER_OUTPUT / settings.TOKEN_DEPOSIT_PERCENTAGE) diff --git a/hathor/nanocontracts/syscall_token_balance_rules.py b/hathor/nanocontracts/syscall_token_balance_rules.py deleted file mode 100644 index 582182512..000000000 --- a/hathor/nanocontracts/syscall_token_balance_rules.py +++ /dev/null @@ -1,357 +0,0 @@ -# Copyright 2025 Hathor Labs -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import annotations - -from abc import ABC, abstractmethod -from dataclasses import dataclass -from enum import StrEnum, auto, unique - -from typing_extensions import assert_never - -from hathor.conf.settings import HATHOR_TOKEN_UID, HathorSettings -from hathor.nanocontracts.exception import NCInvalidFeePaymentToken -from hathor.nanocontracts.runner.index_records import CreateTokenRecord, UpdateTokenBalanceRecord -from hathor.nanocontracts.types import TokenUid -from hathor.transaction.token_info import TokenDescription, TokenVersion -from hathor.transaction.util import get_deposit_token_deposit_amount, get_deposit_token_withdraw_amount - - -@unique -class TokenOperationType(StrEnum): - """Types of token operations for syscalls.""" - CREATE = auto() - MINT = auto() - MELT = auto() - - -@dataclass(slots=True, kw_only=True) -class TokenSyscallBalanceEntry: - token_uid: TokenUid - amount: int - - -@dataclass(slots=True, kw_only=True) -class TokenSyscallBalance: - type: TokenOperationType - token: TokenSyscallBalanceEntry - fee_payment: TokenSyscallBalanceEntry - # create token syscall - token_version: TokenVersion | None = None - token_symbol: str | None = None - token_name: str | None = None - - def to_syscall_records(self) -> list[UpdateTokenBalanceRecord | CreateTokenRecord]: - """ - Convert TokenSyscallBalance to a list of UpdateTokenBalanceRecord or CreateTokenRecord. - - Each operation generates two records: - 1. Main token operation (mint/melt/create) - 2. Fee payment token operation - - Returns: - A list with two index record instances. - """ - records: list[UpdateTokenBalanceRecord | CreateTokenRecord] = [] - - # First record: main token operation - if self.token_version is not None: - assert self.token_symbol is not None - assert self.token_name is not None - assert self.token_version != TokenVersion.NATIVE - records.append(CreateTokenRecord( - token_uid=self.token.token_uid, - amount=self.token.amount, - token_version=self.token_version, - token_symbol=self.token_symbol, - token_name=self.token_name, - )) - else: - assert self.token_symbol is None - assert self.token_name is None - records.append(UpdateTokenBalanceRecord(token_uid=self.token.token_uid, amount=self.token.amount)) - - # Second record: fee payment token - records.append(UpdateTokenBalanceRecord( - token_uid=self.fee_payment.token_uid, - amount=self.fee_payment.amount, - )) - - return records - - -class TokenSyscallBalanceRules(ABC): - """ - An abstract base class that unifies token balance rules for syscalls. - - Requires definitions for create tokens, mint, and melt syscalls. - """ - - __slots__ = ('_settings', 'token_version', 'token_uid') - - def __init__( - self, - settings: HathorSettings, - token_uid: TokenUid, - token_version: TokenVersion - ) -> None: - self._settings = settings - self.token_version = token_version - self.token_uid = token_uid - - assert token_uid != TokenUid(HATHOR_TOKEN_UID) - assert token_version is not TokenVersion.NATIVE - - @abstractmethod - def create_token( - self, - *, - token_uid: TokenUid, - token_symbol: str, - token_name: str, - amount: int, - fee_payment_token: TokenDescription - ) -> TokenSyscallBalance: - """ - Calculate and return the token amounts needed for token creation syscalls. - - Returns: - `TokenSyscallBalance` with the token data and the amounts - """ - raise NotImplementedError - - @abstractmethod - def mint(self, amount: int, *, fee_payment_token: TokenDescription) -> TokenSyscallBalance: - """ - Calculate and return the token amounts needed for minting operations. - - Args: - amount: The amount to be minted. - fee_payment_token: The token that will be used to pay fees - - Returns: - TokenSyscallBalance: A data class with the current syscall record type, token UIDs, and - their respective amounts that will be used by the Runner class for balance updates during token minting. - """ - raise NotImplementedError - - @abstractmethod - def melt(self, amount: int, *, fee_payment_token: TokenDescription) -> TokenSyscallBalance: - """ - Calculate and return the token amounts needed for melting operations. - - Args: - amount: The amount to be melted. - fee_payment_token: The token that will be used to pay fees - - Returns: - TokenSyscallBalance: A data class with the current syscall record type, token UIDs, and - their respective amounts that will be used by the Runner class for balance updates during token melting. - """ - raise NotImplementedError - - @abstractmethod - def get_syscall_update_token_records( - self, - syscall_balance: TokenSyscallBalance - ) -> list[UpdateTokenBalanceRecord | CreateTokenRecord]: - """ - Create syscall update records for the given token operation. - - This method transforms a TokenSyscallBalance into a list of index records - that will be appended to the call record's index_updates for tracking token operations. - - Args: - syscall_balance: The token balance operation containing operation type, - token amounts, and payment details. - - Returns: - A list of syscall update records (main token + fee payment). - """ - raise NotImplementedError - - @staticmethod - def get_rules( - token_uid: TokenUid, - token_version: TokenVersion, - settings: HathorSettings - ) -> TokenSyscallBalanceRules: - """Get the balance rules instance for the provided token version.""" - match token_version: - case TokenVersion.DEPOSIT: - return _DepositTokenRules( - settings, - token_uid, - token_version, - ) - case TokenVersion.FEE: - return _FeeTokenRules( - settings, - token_uid, - token_version, - ) - case TokenVersion.NATIVE: - raise AssertionError(f"NATIVE token version is not supported for token {token_uid.hex()}") - case _: - assert_never(token_version) - - -class _DepositTokenRules(TokenSyscallBalanceRules): - - def create_token( - self, - *, - token_uid: TokenUid, - token_symbol: str, - token_name: str, - amount: int, - fee_payment_token: TokenDescription - ) -> TokenSyscallBalance: - assert amount > 0 - self._validate_payment_token(fee_payment_token) - htr_amount = -get_deposit_token_deposit_amount(self._settings, amount) - - return TokenSyscallBalance( - type=TokenOperationType.CREATE, - token_version=TokenVersion.DEPOSIT, - token_name=token_name, - token_symbol=token_symbol, - token=TokenSyscallBalanceEntry(token_uid=self.token_uid, amount=amount), - fee_payment=TokenSyscallBalanceEntry(token_uid=TokenUid(fee_payment_token.token_id), amount=htr_amount) - ) - - def mint(self, amount: int, *, fee_payment_token: TokenDescription) -> TokenSyscallBalance: - assert amount > 0 - self._validate_payment_token(fee_payment_token) - htr_amount = -get_deposit_token_deposit_amount(self._settings, amount) - - return TokenSyscallBalance( - type=TokenOperationType.MINT, - token=TokenSyscallBalanceEntry(token_uid=self.token_uid, amount=amount), - fee_payment=TokenSyscallBalanceEntry(token_uid=TokenUid(fee_payment_token.token_id), amount=htr_amount) - ) - - def melt(self, amount: int, *, fee_payment_token: TokenDescription) -> TokenSyscallBalance: - assert amount > 0 - self._validate_payment_token(fee_payment_token) - htr_amount = +get_deposit_token_withdraw_amount(self._settings, amount) - - return TokenSyscallBalance( - type=TokenOperationType.MELT, - token=TokenSyscallBalanceEntry(token_uid=self.token_uid, amount=-amount), - fee_payment=TokenSyscallBalanceEntry(token_uid=TokenUid(fee_payment_token.token_id), amount=htr_amount) - ) - - def get_syscall_update_token_records( - self, - operation: TokenSyscallBalance, - ) -> list[UpdateTokenBalanceRecord | CreateTokenRecord]: - match operation.type: - case TokenOperationType.MINT | TokenOperationType.CREATE: - assert operation.token.amount > 0 and operation.fee_payment.amount < 0 - case TokenOperationType.MELT: - assert operation.token.amount < 0 and operation.fee_payment.amount > 0 - case _: - assert_never(operation.type) - - return operation.to_syscall_records() - - def _validate_payment_token(self, token: TokenDescription) -> bool: - if token.token_id == TokenUid(HATHOR_TOKEN_UID): - return True - raise NCInvalidFeePaymentToken("Only HTR is allowed to be used with deposit based token syscalls") - - -class _FeeTokenRules(TokenSyscallBalanceRules): - - def _get_fee_amount(self, fee_payment_token: TokenUid) -> int: - # For fee tokens, we only need to pay the transaction fee, not deposit HTR - if fee_payment_token == TokenUid(HATHOR_TOKEN_UID): - fee_amount = -self._settings.FEE_PER_OUTPUT - else: - fee_amount = -int(self._settings.FEE_PER_OUTPUT / self._settings.TOKEN_DEPOSIT_PERCENTAGE) - - assert fee_amount < 0 - return fee_amount - - def create_token( - self, - *, - token_uid: TokenUid, - token_symbol: str, - token_name: str, - amount: int, - fee_payment_token: TokenDescription - ) -> TokenSyscallBalance: - assert amount > 0 - self._validate_payment_token(fee_payment_token) - # For fee tokens, we only need to pay the transaction fee, not deposit HTR - fee_amount = self._get_fee_amount(TokenUid(fee_payment_token.token_id)) - - return TokenSyscallBalance( - type=TokenOperationType.CREATE, - token_version=TokenVersion.FEE, - token_name=token_name, - token_symbol=token_symbol, - token=TokenSyscallBalanceEntry(token_uid=self.token_uid, amount=amount), - fee_payment=TokenSyscallBalanceEntry(token_uid=TokenUid(fee_payment_token.token_id), amount=fee_amount) - ) - - def mint(self, amount: int, *, fee_payment_token: TokenDescription) -> TokenSyscallBalance: - assert amount > 0 - self._validate_payment_token(fee_payment_token) - fee_amount = self._get_fee_amount(TokenUid(fee_payment_token.token_id)) - return TokenSyscallBalance( - type=TokenOperationType.MINT, - token=TokenSyscallBalanceEntry(token_uid=self.token_uid, amount=amount), - fee_payment=TokenSyscallBalanceEntry(token_uid=TokenUid(fee_payment_token.token_id), amount=fee_amount) - ) - - def melt(self, amount: int, *, fee_payment_token: TokenDescription) -> TokenSyscallBalance: - assert amount > 0 - self._validate_payment_token(fee_payment_token) - fee_amount = self._get_fee_amount(TokenUid(fee_payment_token.token_id)) - - return TokenSyscallBalance( - type=TokenOperationType.MELT, - token=TokenSyscallBalanceEntry(token_uid=self.token_uid, amount=-amount), - fee_payment=TokenSyscallBalanceEntry(token_uid=TokenUid(fee_payment_token.token_id), amount=fee_amount) - ) - - def get_syscall_update_token_records( - self, - operation: TokenSyscallBalance, - ) -> list[UpdateTokenBalanceRecord | CreateTokenRecord]: - assert operation.fee_payment.amount < 0 - - match operation.type: - case TokenOperationType.MINT | TokenOperationType.CREATE: - assert operation.token.amount > 0 - case TokenOperationType.MELT: - assert operation.token.amount < 0 - case _: - assert_never(operation.type) - - return operation.to_syscall_records() - - def _validate_payment_token(self, token_info: TokenDescription) -> None: - match token_info.token_version: - case TokenVersion.FEE: - raise NCInvalidFeePaymentToken("fee-based tokens aren't allowed for paying fees") - case TokenVersion.DEPOSIT: - pass - case TokenVersion.NATIVE: - pass - case _: - assert_never(token_info.token_version) diff --git a/tests/nanocontracts/test_syscalls.py b/tests/nanocontracts/test_syscalls.py index 12414305c..0a3e90f97 100644 --- a/tests/nanocontracts/test_syscalls.py +++ b/tests/nanocontracts/test_syscalls.py @@ -371,7 +371,7 @@ def test_fee_token_creation(self) -> None: fbt2_balance_key: Balance(value=1000000, can_mint=True, can_melt=True), } - # Try to create fee tokens without enough dbt balances + # Try to create fee tokens without enough dbt balance msg = f'negative balance for contract {nc_id.hex()}' with pytest.raises(NCInsufficientFunds, match=msg): self.runner.call_public_method( @@ -409,7 +409,7 @@ def test_fee_token_creation(self) -> None: TokenUid(HATHOR_TOKEN_UID) ) - # created fee token paying with deposit token + # Balance should remain unchanged after failed melt attempt assert storage.get_all_balances() == { htr_balance_key: Balance(value=0, can_mint=False, can_melt=False), fbt_balance_key: Balance(value=1000000, can_mint=True, can_melt=True), @@ -456,7 +456,7 @@ def test_fee_token_melt(self) -> None: # Successfully melt some tokens - don't deposit, melt from existing balance using deposit token self.runner.call_public_method(nc_id, 'melt', self.create_context(), token_uid, 500000, dbt_token_uid) - # Balance should decrease by melted amount, HTR consumed for fee + # Balance should decrease by melted amount, DBT consumed for fee assert storage.get_all_balances() == { htr_balance_key: Balance(value=0, can_mint=False, can_melt=False), fbt_balance_key: Balance(value=500000, can_mint=True, can_melt=True),