Source code for intents.utils

from __future__ import annotations

from decimal import ROUND_DOWN, Decimal

from .models import TokenResponse


[docs] def to_atomic(amount: int | float | str | Decimal, decimals: int) -> str: """Convert a human-readable token amount to its atomic (smallest unit) string. Uses :class:`~decimal.Decimal` internally to avoid floating-point errors. Args: amount: Human-readable amount, e.g. ``0.1`` or ``"0.1"``. decimals: Number of decimals for the token (from :attr:`TokenResponse.decimals`). Returns: Atomic amount as a string, e.g. ``"100000000000000000"`` for 0.1 ETH. Raises: ValueError: If the amount is negative or has more decimal places than the token supports. Examples: >>> to_atomic("0.1", 18) # 0.1 ETH → wei '100000000000000000' >>> to_atomic(1, 24) # 1 NEAR → yoctoNEAR '1000000000000000000000000' """ d = Decimal(str(amount)) if d < 0: raise ValueError(f"Amount must be non-negative, got {amount!r}") scaled = d * Decimal(10) ** decimals if scaled != scaled.to_integral_value(): raise ValueError( f"{amount!r} has more decimal places than the token supports ({decimals})" ) return str(scaled.to_integral_value(rounding=ROUND_DOWN))
[docs] def from_atomic(amount: int | str, decimals: int) -> Decimal: """Convert an atomic (smallest unit) token amount to a human-readable :class:`~decimal.Decimal`. Args: amount: Atomic amount as returned by the API, e.g. ``"100000000000000000"``. decimals: Number of decimals for the token. Returns: Human-readable amount, e.g. ``Decimal("0.1")`` for 0.1 ETH. Examples: >>> from_atomic("100000000000000000", 18) Decimal('0.1') >>> from_atomic("1000000000000000000000000", 24) Decimal('1') """ return Decimal(str(amount)) / Decimal(10) ** decimals
[docs] def token_to_atomic(amount: int | float | str | Decimal, token: TokenResponse) -> str: """Convert a human-readable amount to atomic units using a :class:`~intents.models.TokenResponse`. Convenience wrapper around :func:`to_atomic` that reads ``decimals`` directly from the token object. Args: amount: Human-readable amount, e.g. ``0.1``. token: Token metadata returned by :meth:`~intents.client.IntentsClient.get_tokens`. Returns: Atomic amount string ready to pass as ``amount`` in a :class:`~intents.models.QuoteRequest`. Examples: >>> tokens = await client.get_tokens() >>> eth = find_token(tokens, "eth", "ETH") >>> token_to_atomic(0.1, eth) '100000000000000000' """ return to_atomic(amount, token.decimals)
[docs] def token_from_atomic(amount: int | str, token: TokenResponse) -> Decimal: """Convert an atomic amount to a human-readable :class:`~decimal.Decimal` using a :class:`~intents.models.TokenResponse`. Args: amount: Atomic amount string as returned by the API. token: Token metadata returned by :meth:`~intents.client.IntentsClient.get_tokens`. Returns: Human-readable :class:`~decimal.Decimal`. """ return from_atomic(amount, token.decimals)
[docs] def pct_to_bps(percent: int | float | str | Decimal) -> int: """Convert a percentage to basis points. Args: percent: Percentage value, e.g. ``1`` for 1% or ``0.5`` for 0.5%. Returns: Basis points as an integer, e.g. ``100`` for 1%. Examples: >>> pct_to_bps(1) 100 >>> pct_to_bps(0.5) 50 """ return int(Decimal(str(percent)) * 100)
[docs] def bps_to_pct(bps: int) -> Decimal: """Convert basis points to a percentage. Args: bps: Basis points, e.g. ``100`` for 1%. Returns: Percentage as a :class:`~decimal.Decimal`, e.g. ``Decimal("1")`` for 100 bps. Examples: >>> bps_to_pct(100) Decimal('1') >>> bps_to_pct(50) Decimal('0.50') """ return Decimal(bps) / 100
[docs] def find_token( tokens: list[TokenResponse], blockchain: str, symbol: str, ) -> TokenResponse: """Look up a token by blockchain and symbol from a token list. Args: tokens: Token list returned by :meth:`~intents.client.IntentsClient.get_tokens`. blockchain: The chain to search on, e.g. ``"eth"``, ``"arb"``, ``"sol"``. symbol: Token symbol, e.g. ``"ETH"``, ``"USDC"``. Case-insensitive. Returns: The matching :class:`~intents.models.TokenResponse`. Raises: LookupError: If no token matches the given blockchain and symbol. Examples: >>> tokens = await client.get_tokens() >>> eth = find_token(tokens, "eth", "ETH") >>> eth.asset_id 'nep141:eth.omft.near' """ match = next( (t for t in tokens if t.blockchain == blockchain and t.symbol.upper() == symbol.upper()), None, ) if match is None: raise LookupError(f"No token found for {symbol!r} on {blockchain!r}") return match