Source code for erc7730.convert.resolved.v2.parameters

from typing import Any, assert_never, cast

from erc7730.common.abi import ABIDataType
from erc7730.common.output import OutputAdder
from erc7730.convert.resolved.v2.constants import ConstantProvider
from erc7730.convert.resolved.v2.enums import get_enum, get_enum_id
from erc7730.convert.resolved.v2.values import (
    resolve_map_reference_value,
    resolve_path_constant_or_map_value,
)
from erc7730.model.input.path import DescriptorPathStr
from erc7730.model.input.v2.display import (
    InputAddressNameParameters,
    InputCallDataParameters,
    InputDateParameters,
    InputEncryptionParameters,
    InputEnumParameters,
    InputFieldParameters,
    InputInteroperableAddressNameParameters,
    InputMapReference,
    InputNftNameParameters,
    InputTokenAmountParameters,
    InputTokenTickerParameters,
    InputUnitParameters,
)
from erc7730.model.paths import ContainerPath, DataPath, DescriptorPath
from erc7730.model.paths.path_ops import data_or_container_path_concat
from erc7730.model.resolved.display import ResolvedValueConstant
from erc7730.model.resolved.metadata import EnumDefinition
from erc7730.model.resolved.v2.display import (
    ResolvedAddressNameParameters,
    ResolvedCallDataParameters,
    ResolvedDateParameters,
    ResolvedEncryptionParameters,
    ResolvedEnumParameters,
    ResolvedFieldParameters,
    ResolvedInteroperableAddressNameParameters,
    ResolvedNftNameParameters,
    ResolvedTokenAmountParameters,
    ResolvedTokenTickerParameters,
    ResolvedUnitParameters,
    ResolvedValueMap,
    ResolvedValueOrMap,
)
from erc7730.model.types import Address, HexStr, Id, MixedCaseAddress, ScalarType


[docs] def resolve_field_parameters( prefix: DataPath, params: InputFieldParameters | None, enums: dict[Id, EnumDefinition], constants: ConstantProvider, out: OutputAdder, ) -> ResolvedFieldParameters | None: match params: case None: return None case InputAddressNameParameters(): return resolve_address_name_parameters(prefix, params, constants, out) case InputInteroperableAddressNameParameters(): return resolve_interoperable_address_name_parameters(prefix, params, constants, out) case InputCallDataParameters(): return resolve_calldata_parameters(prefix, params, constants, out) case InputTokenAmountParameters(): return resolve_token_amount_parameters(prefix, params, constants, out) case InputTokenTickerParameters(): return resolve_token_ticker_parameters(prefix, params, constants, out) case InputNftNameParameters(): return resolve_nft_parameters(prefix, params, constants, out) case InputDateParameters(): return resolve_date_parameters(prefix, params, constants, out) case InputUnitParameters(): return resolve_unit_parameters(prefix, params, constants, out) case InputEnumParameters(): return resolve_enum_parameters(prefix, params, enums, constants, out) case _: assert_never(params)
[docs] def resolve_address_name_parameters( prefix: DataPath, params: InputAddressNameParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedAddressNameParameters | None: sender_address = _resolve_sender_address(prefix, params.senderAddress, constants, out) if params.senderAddress is not None and sender_address is None: return None return ResolvedAddressNameParameters( types=constants.resolve_or_none(params.types, out), sources=constants.resolve_or_none(params.sources, out), senderAddress=sender_address, )
[docs] def resolve_interoperable_address_name_parameters( prefix: DataPath, params: InputInteroperableAddressNameParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedInteroperableAddressNameParameters | None: sender_address = _resolve_sender_address(prefix, params.senderAddress, constants, out) if params.senderAddress is not None and sender_address is None: return None return ResolvedInteroperableAddressNameParameters( types=constants.resolve_or_none(params.types, out), sources=constants.resolve_or_none(params.sources, out), senderAddress=sender_address, )
def _resolve_sender_address( prefix: DataPath, sender_addr_input: MixedCaseAddress | list[MixedCaseAddress] | DescriptorPath | InputMapReference | None, constants: ConstantProvider, out: OutputAdder, ) -> list[Address] | ResolvedValueMap | None: if sender_addr_input is None: return None if isinstance(sender_addr_input, InputMapReference): match resolved_map := resolve_map_reference_value( prefix, sender_addr_input, ABIDataType.ADDRESS, constants, out ): case ResolvedValueConstant(): return [Address(str(resolved_map.value))] case _: return resolved_map resolved_sender = constants.resolve_or_none(sender_addr_input, out) if resolved_sender is None: return None if isinstance(resolved_sender, str): return [Address(resolved_sender)] if isinstance(resolved_sender, list): return [Address(addr) for addr in resolved_sender] raise Exception("Invalid senderAddress type")
[docs] def resolve_calldata_parameters( prefix: DataPath, params: InputCallDataParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedCallDataParameters | None: def resolve( input_path: DescriptorPath | DataPath | ContainerPath | None, input_value: DescriptorPath | ScalarType | InputMapReference | None, abi_type: ABIDataType, ) -> ResolvedValueOrMap | None: return resolve_path_constant_or_map_value(prefix, input_path, input_value, abi_type, constants, out) # callee is mandatory, other parameters are optional: each can be a path, a constant or a map reference if params.callee is None and params.calleePath is None: return out.error( title="Missing callee", message='Calldata parameters must set either "callee" or "calleePath".', ) if (callee_resolved := resolve(params.calleePath, params.callee, ABIDataType.ADDRESS)) is None: return None selector_resolved = resolve(params.selectorPath, params.selector, ABIDataType.STRING) amount_resolved = resolve(params.amountPath, params.amount, ABIDataType.UINT) spender_resolved = resolve(params.spenderPath, params.spender, ABIDataType.ADDRESS) # an optional parameter that is set but fails to resolve must reject the field, not be omitted if ( (selector_resolved is None and (params.selector is not None or params.selectorPath is not None)) or (amount_resolved is None and (params.amount is not None or params.amountPath is not None)) or (spender_resolved is None and (params.spender is not None or params.spenderPath is not None)) ): return None return ResolvedCallDataParameters( callee=callee_resolved, selector=selector_resolved, amount=amount_resolved, spender=spender_resolved, )
[docs] def resolve_token_amount_parameters( prefix: DataPath, params: InputTokenAmountParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedTokenAmountParameters | None: # Resolve token into a ResolvedValue (path or constant) like v1, or into a map lookup. token_resolved = resolve_path_constant_or_map_value( prefix=prefix, input_path=params.tokenPath, input_value=params.token, abi_type=ABIDataType.ADDRESS, constants=constants, out=out, ) if token_resolved is None and (params.token is not None or params.tokenPath is not None): return None input_addresses = cast( list[DescriptorPathStr | MixedCaseAddress] | MixedCaseAddress | None, constants.resolve_or_none(params.nativeCurrencyAddress, out), ) resolved_addresses: list[Address] | None if input_addresses is None: resolved_addresses = None elif isinstance(input_addresses, list): resolved_addresses = [] for input_address in input_addresses: if (resolved_address := constants.resolve(input_address, out)) is None: return None resolved_addresses.append(Address(resolved_address)) elif isinstance(input_addresses, str): resolved_addresses = [Address(input_addresses)] else: raise Exception("Invalid nativeCurrencyAddress type") input_threshold = cast(HexStr | int | None, constants.resolve_or_none(params.threshold, out)) resolved_threshold: HexStr | None if input_threshold is not None: if isinstance(input_threshold, int): resolved_threshold = "0x" + input_threshold.to_bytes(byteorder="big", signed=False).hex() else: resolved_threshold = input_threshold else: resolved_threshold = None if (resolved_chain_id := _resolve_chain_id(prefix, params.chainId, constants, out)) is None and ( params.chainId is not None ): return None return ResolvedTokenAmountParameters( token=token_resolved, nativeCurrencyAddress=resolved_addresses, threshold=resolved_threshold, message=constants.resolve_or_none(params.message, out), chainId=resolved_chain_id, chainIdPath=params.chainIdPath, )
[docs] def resolve_token_ticker_parameters( prefix: DataPath, params: InputTokenTickerParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedTokenTickerParameters | None: if (resolved_chain_id := _resolve_chain_id(prefix, params.chainId, constants, out)) is None and ( params.chainId is not None ): return None # Resolve and normalize chainIdPath using constants and the current prefix resolved_chain_id_path: DataPath | ContainerPath | None = None if params.chainIdPath is not None: relative_chain_id_path = constants.resolve_path(params.chainIdPath, out) if relative_chain_id_path is not None: resolved_chain_id_path = data_or_container_path_concat(prefix, relative_chain_id_path) return ResolvedTokenTickerParameters( chainId=resolved_chain_id, chainIdPath=resolved_chain_id_path, )
def _resolve_chain_id( prefix: DataPath, chain_id_input: int | DescriptorPath | InputMapReference | None, constants: ConstantProvider, out: OutputAdder, ) -> int | ResolvedValueMap | None: if chain_id_input is None: return None if isinstance(chain_id_input, InputMapReference): match resolved_map := resolve_map_reference_value(prefix, chain_id_input, ABIDataType.UINT, constants, out): case ResolvedValueConstant(): if not isinstance(resolved_map.value, int): return out.error( title="Invalid chain id", message=f"""Chain id "{resolved_map.value}" from map {chain_id_input.map} is not an integer.""", ) return resolved_map.value case _: return resolved_map if isinstance(chain_id_input, int): return chain_id_input # Descriptor path resolved_value: Any = constants.resolve(chain_id_input, out) if resolved_value is None: return None if not isinstance(resolved_value, int): return out.error( title="Invalid chain id", message=f"""Chain id "{resolved_value}" from {chain_id_input} is not an integer.""", ) return resolved_value
[docs] def resolve_nft_parameters( prefix: DataPath, params: InputNftNameParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedNftNameParameters | None: # Resolve collection - can be path, constant, or map reference if ( collection_resolved := resolve_path_constant_or_map_value( prefix=prefix, input_path=params.collectionPath, input_value=params.collection, abi_type=ABIDataType.ADDRESS, constants=constants, out=out, ) ) is None: return None return ResolvedNftNameParameters(collection=collection_resolved)
[docs] def resolve_date_parameters( prefix: DataPath, params: InputDateParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedDateParameters | None: return ResolvedDateParameters(encoding=constants.resolve(params.encoding, out))
[docs] def resolve_unit_parameters( prefix: DataPath, params: InputUnitParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedUnitParameters | None: return ResolvedUnitParameters( base=constants.resolve(params.base, out), decimals=constants.resolve_or_none(params.decimals, out), prefix=constants.resolve_or_none(params.prefix, out), )
[docs] def resolve_enum_parameters( prefix: DataPath, params: InputEnumParameters, enums: dict[Id, EnumDefinition], constants: ConstantProvider, out: OutputAdder, ) -> ResolvedEnumParameters | None: if get_enum_id(params.ref, out) is None: return None if get_enum(params.ref, enums, out) is None: return None return ResolvedEnumParameters.model_validate({"$ref": str(params.ref)})
[docs] def resolve_encryption_parameters( prefix: DataPath, params: InputEncryptionParameters, constants: ConstantProvider, out: OutputAdder ) -> ResolvedEncryptionParameters | None: # Encryption parameters are passed through as-is return ResolvedEncryptionParameters( scheme=params.scheme, plaintextType=params.plaintextType, fallbackLabel=params.fallbackLabel, )