diff --git a/multiversx_sdk/abi/abi.py b/multiversx_sdk/abi/abi.py index 34f2a03e..a020b5c9 100644 --- a/multiversx_sdk/abi/abi.py +++ b/multiversx_sdk/abi/abi.py @@ -1,10 +1,12 @@ from copy import deepcopy from pathlib import Path +from types import SimpleNamespace from typing import Any, Dict, List, cast from multiversx_sdk.abi.abi_definition import (AbiDefinition, EndpointDefinition, - EnumDefinition, + EnumDefinition, EventDefinition, + EventTopicDefinition, ParameterDefinition, StructDefinition) from multiversx_sdk.abi.address_value import AddressValue @@ -39,6 +41,7 @@ def __init__(self, definition: AbiDefinition) -> None: self.definition = definition self.custom_types_prototypes_by_name: Dict[str, Any] = {} self.endpoints_prototypes_by_name: Dict[str, EndpointPrototype] = {} + self.events_prototypes_by_name: Dict[str, EventPrototype] = {} for name in definition.types.enums: self.custom_types_prototypes_by_name[name] = self._create_custom_type_prototype(name) @@ -67,6 +70,15 @@ def __init__(self, definition: AbiDefinition) -> None: self.endpoints_prototypes_by_name[endpoint.name] = endpoint_prototype + for event in definition.events: + prototype = self._create_event_input_prototypes(event) + + event_prototype = EventPrototype( + fields=prototype + ) + + self.events_prototypes_by_name[event.identifier] = event_prototype + def _create_custom_type_prototype(self, name: str) -> Any: if name in self.definition.types.enums: definition = self.definition.types.enums[name] @@ -126,10 +138,23 @@ def _create_endpoint_output_prototypes(self, endpoint: EndpointDefinition) -> Li return prototypes + def _create_event_input_prototypes(self, event: EventDefinition) -> List[Any]: + prototypes: List[Any] = [] + + for topic in event.inputs: + event_field_prototype = EventField(name=topic.name, value=self._create_event_field_prototype(topic)) + prototypes.append(event_field_prototype) + + return prototypes + def _create_parameter_prototype(self, parameter: ParameterDefinition) -> Any: type_formula = self._type_formula_parser.parse_expression(parameter.type) return self._create_prototype(type_formula) + def _create_event_field_prototype(self, parameter: EventTopicDefinition) -> Any: + type_formula = self._type_formula_parser.parse_expression(parameter.type) + return self._create_prototype(type_formula) + def encode_constructor_input_parameters(self, values: List[Any]) -> List[bytes]: return self._do_encode_endpoint_input_parameters("constructor", self.constructor_prototype, values) @@ -163,6 +188,39 @@ def decode_endpoint_output_parameters(self, endpoint_name: str, encoded_values: output_native_values = [value.get_payload() for value in output_values_as_native_object_holders] return output_native_values + def decode_event(self, event_name: str, topics: List[bytes], data_items: List[bytes]) -> SimpleNamespace: + result = SimpleNamespace() + event_definition = self.definition.get_event_definition(event_name) + event_prototype = self._get_event_prototype(event_name) + + indexed_inputs = [input for input in event_definition.inputs if input.indexed] + indexed_inputs_names = [item.name for item in indexed_inputs] + + fields = deepcopy(event_prototype.fields) + + output_values = [field.value for field in fields if field.name in indexed_inputs_names] + self._serializer.deserialize_parts(topics, output_values) + + output_values_as_native_object_holders = cast(List[IPayloadHolder], output_values) + output_native_values = [value.get_payload() for value in output_values_as_native_object_holders] + + for i in range(len(indexed_inputs)): + setattr(result, indexed_inputs[i].name, output_native_values[i]) + + non_indexed_inputs = [input for input in event_definition.inputs if not input.indexed] + non_indexed_inputs_names = [item.name for item in non_indexed_inputs] + + output_values = [field.value for field in fields if field.name in non_indexed_inputs_names] + self._serializer.deserialize_parts(data_items, output_values) + + output_values_as_native_object_holders = cast(List[IPayloadHolder], output_values) + output_native_values = [value.get_payload() for value in output_values_as_native_object_holders] + + for i in range(len(non_indexed_inputs)): + setattr(result, non_indexed_inputs[i].name, output_native_values[i]) + + return result + def _get_custom_type_prototype(self, type_name: str) -> Any: type_prototype = self.custom_types_prototypes_by_name.get(type_name) @@ -179,6 +237,14 @@ def _get_endpoint_prototype(self, endpoint_name: str) -> 'EndpointPrototype': return endpoint_prototype + def _get_event_prototype(self, event_name: str) -> 'EventPrototype': + event_prototype = self.events_prototypes_by_name.get(event_name) + + if not event_prototype: + raise ValueError(f"event '{event_name}' not found") + + return event_prototype + def _create_prototype(self, type_formula: TypeFormula) -> Any: name = type_formula.name @@ -248,3 +314,14 @@ class EndpointPrototype: def __init__(self, input_parameters: List[Any], output_parameters: List[Any]) -> None: self.input_parameters = input_parameters self.output_parameters = output_parameters + + +class EventField: + def __init__(self, name: str, value: Any) -> None: + self.name = name + self.value = value + + +class EventPrototype: + def __init__(self, fields: List[EventField]) -> None: + self.fields = fields diff --git a/multiversx_sdk/abi/abi_definition.py b/multiversx_sdk/abi/abi_definition.py index a18c2a31..83d05950 100644 --- a/multiversx_sdk/abi/abi_definition.py +++ b/multiversx_sdk/abi/abi_definition.py @@ -8,11 +8,13 @@ def __init__(self, constructor: "EndpointDefinition", upgrade_constructor: "EndpointDefinition", endpoints: List["EndpointDefinition"], - types: "TypesDefinitions") -> None: + types: "TypesDefinitions", + events: List["EventDefinition"]) -> None: self.constructor = constructor self.upgrade_constructor = upgrade_constructor self.endpoints = endpoints self.types = types + self.events = events @classmethod def from_dict(cls, data: Dict[str, Any]) -> "AbiDefinition": @@ -25,11 +27,14 @@ def from_dict(cls, data: Dict[str, Any]) -> "AbiDefinition": endpoints = [EndpointDefinition.from_dict(item) for item in data["endpoints"]] if "endpoints" in data else [] types = TypesDefinitions.from_dict(data.get("types", {})) + events = [EventDefinition.from_dict(item) for item in data["events"]] if "events" in data else [] + return cls( constructor=constructor, upgrade_constructor=upgrade_constructor, endpoints=endpoints, - types=types + types=types, + events=events ) @classmethod @@ -45,7 +50,8 @@ def _get_definition_for_upgrade(cls, data: Dict[str, Any]) -> "EndpointDefinitio return EndpointDefinition.from_dict(data["upgradeConstructor"]) # Fallback for contracts written using a not-old, but not-new Rust framework: - if "upgrade" in data["endpoints"]: + endpoints = data.get("endpoints", []) + if "upgrade" in endpoints: return EndpointDefinition.from_dict(data["endpoints"]["upgrade"]) # Fallback for contracts written using an old Rust framework: @@ -60,6 +66,17 @@ def load(cls, path: Path) -> "AbiDefinition": data = json.loads(content) return cls.from_dict(data) + def get_event_definition(self, name: str) -> "EventDefinition": + event = [event for event in self.events if event.identifier == name] + + if not len(event): + raise Exception(f"event [{name}] not found") + + if len(event) > 1: + raise Exception(f"more than one event found: [{event}]") + + return event[0] + class EndpointDefinition: def __init__(self, @@ -282,7 +299,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "EventTopicDefinition": return cls( name=data["name"], type=data["type"], - indexed=data["indexed"] + indexed=data.get("indexed", False) ) def __repr__(self): diff --git a/multiversx_sdk/abi/abi_test.py b/multiversx_sdk/abi/abi_test.py index 415168fe..f51774d1 100644 --- a/multiversx_sdk/abi/abi_test.py +++ b/multiversx_sdk/abi/abi_test.py @@ -31,6 +31,8 @@ def test_abi(): assert abi.definition.upgrade_constructor.inputs == [ParameterDefinition("initial_value", "BigUint")] assert abi.definition.upgrade_constructor.outputs == [] + assert abi.definition.events == [] + assert abi.definition.endpoints[0].name == "getSum" assert abi.definition.endpoints[0].inputs == [] assert abi.definition.endpoints[0].outputs == [ParameterDefinition("", "BigUint")] @@ -54,6 +56,13 @@ def test_abi(): assert abi.endpoints_prototypes_by_name["add"].output_parameters == [] +def test_abi_events(): + abi = Abi.load(testdata / "artificial.abi.json") + + assert len(abi.definition.events) == 1 + assert abi.events_prototypes_by_name["firstEvent"].fields[0].value == BigUIntValue() + + def test_encode_endpoint_input_parameters_artificial_contract(): abi = Abi.load(testdata / "artificial.abi.json") diff --git a/multiversx_sdk/core/transactions_outcome_parsers/__init__.py b/multiversx_sdk/core/transactions_outcome_parsers/__init__.py index 815ab2ea..94d8ef62 100644 --- a/multiversx_sdk/core/transactions_outcome_parsers/__init__.py +++ b/multiversx_sdk/core/transactions_outcome_parsers/__init__.py @@ -2,7 +2,7 @@ DelegationTransactionsOutcomeParser from multiversx_sdk.core.transactions_outcome_parsers.resources import ( SmartContractResult, TransactionEvent, TransactionLogs, TransactionOutcome, - find_events_by_identifier) + find_events_by_first_topic, find_events_by_identifier) from multiversx_sdk.core.transactions_outcome_parsers.smart_contract_transactions_outcome_parser import \ SmartContractTransactionsOutcomeParser from multiversx_sdk.core.transactions_outcome_parsers.token_management_transactions_outcome_parser import \ @@ -11,5 +11,5 @@ __all__ = [ "TokenManagementTransactionsOutcomeParser", "SmartContractResult", "TransactionEvent", "TransactionLogs", "TransactionOutcome", "find_events_by_identifier", "DelegationTransactionsOutcomeParser", - "SmartContractTransactionsOutcomeParser" + "SmartContractTransactionsOutcomeParser", "find_events_by_first_topic" ] diff --git a/multiversx_sdk/core/transactions_outcome_parsers/resources.py b/multiversx_sdk/core/transactions_outcome_parsers/resources.py index b491fba7..4b73439c 100644 --- a/multiversx_sdk/core/transactions_outcome_parsers/resources.py +++ b/multiversx_sdk/core/transactions_outcome_parsers/resources.py @@ -59,6 +59,10 @@ def find_events_by_identifier(transaction_outcome: TransactionOutcome, identifie return find_events_by_predicate(transaction_outcome, lambda event: event.identifier == identifier) +def find_events_by_first_topic(transaction_outcome: TransactionOutcome, topic: str) -> List[TransactionEvent]: + return find_events_by_predicate(transaction_outcome, lambda event: event.topics[0].decode() == topic if len(event.topics) else False) + + def find_events_by_predicate( transaction_outcome: TransactionOutcome, predicate: Callable[[TransactionEvent], bool] diff --git a/multiversx_sdk/core/transactions_outcome_parsers/transaction_events_parser.py b/multiversx_sdk/core/transactions_outcome_parsers/transaction_events_parser.py new file mode 100644 index 00000000..62307e75 --- /dev/null +++ b/multiversx_sdk/core/transactions_outcome_parsers/transaction_events_parser.py @@ -0,0 +1,39 @@ +from types import SimpleNamespace +from typing import List, Protocol + +from multiversx_sdk.core.transactions_outcome_parsers.resources import \ + TransactionEvent + + +class IAbi(Protocol): + def decode_event(self, event_name: str, topics: List[bytes], data_items: List[bytes]) -> SimpleNamespace: + ... + + +class TransactionEventsParser: + def __init__(self, abi: IAbi, first_topic_as_identifier: bool = True) -> None: + self.abi = abi + + # By default, we consider that the first topic is the event identifier. + # This is true for log entries emitted by smart contracts: + # https://github.com/multiversx/mx-chain-vm-go/blob/v1.5.27/vmhost/contexts/output.go#L270 + # https://github.com/multiversx/mx-chain-vm-go/blob/v1.5.27/vmhost/contexts/output.go#L283 + self.first_topic_as_identifier = first_topic_as_identifier + + def parse_events(self, events: List[TransactionEvent]) -> List[SimpleNamespace]: + return [self.parse_event(event) for event in events] + + def parse_event(self, event: TransactionEvent) -> SimpleNamespace: + first_topic = event.topics[0].decode() if len(event.topics) else "" + abi_identifier = first_topic if first_topic and self.first_topic_as_identifier else event.identifier + + topics = event.topics + + if self.first_topic_as_identifier: + topics = topics[1:] + + return self.abi.decode_event( + event_name=abi_identifier, + topics=topics, + data_items=event.data_items, + ) diff --git a/multiversx_sdk/core/transactions_outcome_parsers/transaction_events_parser_test.py b/multiversx_sdk/core/transactions_outcome_parsers/transaction_events_parser_test.py new file mode 100644 index 00000000..0e18474d --- /dev/null +++ b/multiversx_sdk/core/transactions_outcome_parsers/transaction_events_parser_test.py @@ -0,0 +1,215 @@ +from pathlib import Path +from types import SimpleNamespace + +from multiversx_sdk.abi.abi import Abi +from multiversx_sdk.abi.abi_definition import AbiDefinition +from multiversx_sdk.core.address import Address +from multiversx_sdk.core.transactions_outcome_parsers.resources import ( + SmartContractCallOutcome, SmartContractResult, TransactionEvent, + TransactionLogs, TransactionOutcome, find_events_by_first_topic, + find_events_by_identifier) +from multiversx_sdk.core.transactions_outcome_parsers.transaction_events_parser import \ + TransactionEventsParser + +testdata = Path(__file__).parent.parent.parent / "testutils" / "testdata" + + +def test_parse_events_minimalistic(): + abi = Abi.load(testdata / "esdt-safe.abi.json") + parser = TransactionEventsParser(abi=abi) + + values = parser.parse_events( + events=[ + TransactionEvent( + identifier="transferOverMaxAmount", + topics=["transferOverMaxAmount".encode(), bytes([0x2a]), bytes([0x2b])] + ) + ] + ) + + assert len(values) == 1 + assert values[0] == SimpleNamespace( + batch_id=42, + tx_id=43 + ) + + +def test_parse_esdt_safe_deposit_event(): + abi = Abi.load(testdata / "esdt-safe.abi.json") + parser = TransactionEventsParser(abi=abi) + + transaction_outcome = TransactionOutcome() + + logs = TransactionLogs( + events=[ + TransactionEvent( + topics=[ + bytes.fromhex("6465706f736974"), + bytes.fromhex("726cc2d4b46dd6bd74a4c84d02715bf85cae76318cab81bc09e7c261d4149a67"), + bytes.fromhex("0000000c5745474c442d30316534396400000000000000000000000164") + ], + data_items=[bytes.fromhex("00000000000003db000000")] + ) + ] + ) + + transaction_outcome.direct_smart_contract_call = SmartContractCallOutcome(return_code="ok", return_message="ok") + transaction_outcome.transaction_results = [ + SmartContractResult(data=bytes.fromhex("4036663662"), logs=logs) + ] + + events = find_events_by_first_topic(transaction_outcome, "deposit") + parsed = parser.parse_events(events) + + assert len(parsed) == 1 + assert parsed[0] == SimpleNamespace( + dest_address=Address.new_from_bech32("erd1wfkv9495dhtt6a9yepxsyu2mlpw2ua333j4cr0qfulpxr4q5nfnshgyqun").get_public_key(), + tokens=[SimpleNamespace( + token_identifier="WEGLD-01e49d", + token_nonce=0, + amount=100 + )], + event_data=SimpleNamespace( + tx_nonce=987, + opt_function=None, + opt_arguments=None, + opt_gas_limit=None, + ) + ) + + +def test_parse_multisig_start_perform_action(): + abi = Abi.load(testdata / "multisig-full.abi.json") + parser = TransactionEventsParser(abi=abi) + + transaction_outcome = TransactionOutcome( + direct_smart_contract_call_outcome=SmartContractCallOutcome(return_code="ok", return_message="ok"), + transaction_results=[SmartContractResult(data=bytes.fromhex("4036663662"))], + transaction_logs=TransactionLogs(events=[TransactionEvent( + identifier="performAction", + topics=[bytes.fromhex("7374617274506572666f726d416374696f6e")], + data_items=[bytes.fromhex("00000001000000000500000000000000000500d006f73c4221216fa679bc559005584c4f1160e569e1000000000000000003616464000000010000000107000000010139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1")] + )]) + ) + + events = find_events_by_first_topic(transaction_outcome, "startPerformAction") + parsed = parser.parse_events(events) + data = parsed[0].data + + assert data == SimpleNamespace( + action_id=1, + group_id=0, + action_data=SimpleNamespace( + **{ + "0": SimpleNamespace( + to=Address.new_from_bech32("erd1qqqqqqqqqqqqqpgq6qr0w0zzyysklfneh32eqp2cf383zc89d8sstnkl60").get_public_key(), + egld_amount=0, + opt_gas_limit=None, + endpoint_name=b'add', + arguments=[bytes.fromhex("07")] + ), + '__discriminant__': 5 + } + ), + signers=[Address.new_from_bech32("erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th").get_public_key()] + ) + + +def test_parse_event_with_multi_values(): + abi_definition = AbiDefinition.from_dict( + { + "events": [ + { + "identifier": "doFoobar", + "inputs": [ + { + "name": "a", + "type": "multi", + "indexed": True, + }, + { + "name": "b", + "type": "multi", + "indexed": True, + }, + { + "name": "c", + "type": "u8", + "indexed": False, + }, + ], + }, + ] + } + ) + + abi = Abi(abi_definition) + parser = TransactionEventsParser(abi=abi) + value = 42 + + parsed = parser.parse_event( + TransactionEvent( + identifier="foobar", + topics=[ + "doFoobar".encode(), + value.to_bytes(), + "test".encode(), + (value + 1).to_bytes(), + "test".encode(), + "test".encode(), + (value + 2).to_bytes() + ], + data_items=[value.to_bytes()] + ) + ) + + assert parsed == SimpleNamespace( + a=[42, "test", 43, "test"], + b=["test", 44], + c=42 + ) + + +def test_parse_esdt_safe_deposit_event_without_first_topic(): + abi = Abi.load(testdata / "esdt-safe.abi.json") + parser = TransactionEventsParser(abi=abi) + + transaction_outcome = TransactionOutcome() + + logs = TransactionLogs( + events=[ + TransactionEvent( + identifier="deposit", + topics=[ + bytes.fromhex(""), + bytes.fromhex("726cc2d4b46dd6bd74a4c84d02715bf85cae76318cab81bc09e7c261d4149a67"), + bytes.fromhex("0000000c5745474c442d30316534396400000000000000000000000164") + ], + data_items=[bytes.fromhex("00000000000003db000000")] + ) + ] + ) + + transaction_outcome.direct_smart_contract_call = SmartContractCallOutcome(return_code="ok", return_message="ok") + transaction_outcome.transaction_results = [ + SmartContractResult(data=bytes.fromhex("4036663662"), logs=logs) + ] + + events = find_events_by_identifier(transaction_outcome, "deposit") + parsed = parser.parse_events(events) + + assert len(parsed) == 1 + assert parsed[0] == SimpleNamespace( + dest_address=Address.new_from_bech32("erd1wfkv9495dhtt6a9yepxsyu2mlpw2ua333j4cr0qfulpxr4q5nfnshgyqun").get_public_key(), + tokens=[SimpleNamespace( + token_identifier="WEGLD-01e49d", + token_nonce=0, + amount=100 + )], + event_data=SimpleNamespace( + tx_nonce=987, + opt_function=None, + opt_arguments=None, + opt_gas_limit=None, + ) + ) diff --git a/multiversx_sdk/network_providers/contract_results.py b/multiversx_sdk/network_providers/contract_results.py index e00eefc4..82e41af7 100644 --- a/multiversx_sdk/network_providers/contract_results.py +++ b/multiversx_sdk/network_providers/contract_results.py @@ -55,6 +55,7 @@ def to_dictionary(self) -> Dict[str, Any]: "callType": self.call_type, "returnMessage": self.return_message, "isRefund": self.is_refund, + "logs": self.logs.to_dictionary() } @staticmethod diff --git a/multiversx_sdk/testutils/testdata/artificial.abi.json b/multiversx_sdk/testutils/testdata/artificial.abi.json index fae5d546..d8b0dbfe 100644 --- a/multiversx_sdk/testutils/testdata/artificial.abi.json +++ b/multiversx_sdk/testutils/testdata/artificial.abi.json @@ -41,5 +41,17 @@ "outputs": [] } ], - "types": {} + "types": {}, + "events": [ + { + "identifier": "firstEvent", + "inputs": [ + { + "name": "result", + "type": "BigUint", + "indexed": true + } + ] + } + ] } diff --git a/multiversx_sdk/testutils/testdata/esdt-safe.abi.json b/multiversx_sdk/testutils/testdata/esdt-safe.abi.json new file mode 100644 index 00000000..ee17c3f3 --- /dev/null +++ b/multiversx_sdk/testutils/testdata/esdt-safe.abi.json @@ -0,0 +1,725 @@ +{ + "buildInfo": { + "rustc": { + "version": "1.75.0-nightly", + "commitHash": "42b1224e9eb37177f608d3f6a6f2be2ee13902e4", + "commitDate": "2023-10-15", + "channel": "Nightly", + "short": "rustc 1.75.0-nightly (42b1224e9 2023-10-15)" + }, + "contractCrate": { + "name": "esdt-safe", + "version": "0.0.0" + }, + "framework": { + "name": "multiversx-sc", + "version": "0.43.5" + } + }, + "name": "EsdtSafe", + "constructor": { + "inputs": [ + { + "name": "min_valid_signers", + "type": "u32" + }, + { + "name": "signers", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + "endpoints": [ + { + "name": "upgrade", + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "docs": [ + "Create an Elrond -> Sovereign transaction." + ], + "name": "deposit", + "mutability": "mutable", + "payableInTokens": [ + "*" + ], + "inputs": [ + { + "name": "to", + "type": "Address" + }, + { + "name": "opt_transfer_data", + "type": "optional", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "docs": [ + "Claim funds for failed Elrond -> Sovereign transactions.", + "These are not sent automatically to prevent the contract getting stuck.", + "For example, if the receiver is a SC, a frozen account, etc." + ], + "name": "claimRefund", + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "List" + } + ] + }, + { + "docs": [ + "Sets the statuses for the transactions, after they were executed on the Sovereign side.", + "", + "Only TransactionStatus::Executed (3) and TransactionStatus::Rejected (4) values are allowed.", + "Number of provided statuses must be equal to number of transactions in the batch." + ], + "name": "setTransactionBatchStatus", + "mutability": "mutable", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + }, + { + "name": "signature", + "type": "array48" + }, + { + "name": "tx_statuses", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setMinValidSigners", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_value", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "addSigners", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "signers", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "removeSigners", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "signers", + "type": "variadic
", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "registerToken", + "mutability": "mutable", + "payableInTokens": [ + "EGLD" + ], + "inputs": [ + { + "name": "sov_token_id", + "type": "TokenIdentifier" + }, + { + "name": "token_type", + "type": "EsdtTokenType" + }, + { + "name": "token_display_name", + "type": "bytes" + }, + { + "name": "token_ticker", + "type": "bytes" + }, + { + "name": "num_decimals", + "type": "u32" + }, + { + "name": "bls_multisig", + "type": "array48" + } + ], + "outputs": [] + }, + { + "name": "clearRegisteredToken", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "sov_token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [] + }, + { + "name": "batchTransferEsdtToken", + "mutability": "mutable", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + }, + { + "name": "signature", + "type": "array48" + }, + { + "name": "transfers", + "type": "variadic", + "multi_arg": true + } + ], + "outputs": [] + }, + { + "name": "setMaxTxBatchSize", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_size", + "type": "u32" + } + ], + "outputs": [] + }, + { + "name": "setMaxTxBatchBlockDuration", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "new_max_tx_batch_block_duration", + "type": "u64" + } + ], + "outputs": [] + }, + { + "name": "getCurrentTxBatch", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional,List,Option>>>>", + "multi_result": true + } + ] + }, + { + "name": "getFirstBatchAnyStatus", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "optional,List,Option>>>>", + "multi_result": true + } + ] + }, + { + "name": "getBatch", + "mutability": "readonly", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [ + { + "type": "optional,List,Option>>>>", + "multi_result": true + } + ] + }, + { + "name": "getBatchStatus", + "mutability": "readonly", + "inputs": [ + { + "name": "batch_id", + "type": "u64" + } + ], + "outputs": [ + { + "type": "BatchStatus" + } + ] + }, + { + "name": "getFirstBatchId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "getLastBatchId", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "u64" + } + ] + }, + { + "name": "setMaxBridgedAmount", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + }, + { + "name": "max_amount", + "type": "BigUint" + } + ], + "outputs": [] + }, + { + "name": "getMaxBridgedAmount", + "mutability": "readonly", + "inputs": [ + { + "name": "token_id", + "type": "TokenIdentifier" + } + ], + "outputs": [ + { + "type": "BigUint" + } + ] + }, + { + "name": "pause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "unpause", + "onlyOwner": true, + "mutability": "mutable", + "inputs": [], + "outputs": [] + }, + { + "name": "isPaused", + "mutability": "readonly", + "inputs": [], + "outputs": [ + { + "type": "bool" + } + ] + } + ], + "promisesCallbackNames": [ + "transfer_callback" + ], + "events": [ + { + "identifier": "deposit", + "inputs": [ + { + "name": "dest_address", + "type": "Address", + "indexed": true + }, + { + "name": "tokens", + "type": "List", + "indexed": true + }, + { + "name": "event_data", + "type": "DepositEvent" + } + ] + }, + { + "identifier": "setStatusEvent", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_status", + "type": "TransactionStatus", + "indexed": true + } + ] + }, + { + "identifier": "addRefundTransactionEvent", + "inputs": [ + { + "name": "tx_id", + "type": "u64", + "indexed": true + }, + { + "name": "original_tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferPerformedEvent", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx", + "type": "Transaction" + } + ] + }, + { + "identifier": "transferFailedInvalidToken", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferFailedFrozenDestinationAccount", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferOverMaxAmount", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + }, + { + "identifier": "transferFailedExecutionFailed", + "inputs": [ + { + "name": "batch_id", + "type": "u64", + "indexed": true + }, + { + "name": "tx_id", + "type": "u64", + "indexed": true + } + ] + } + ], + "hasCallback": true, + "types": { + "BatchStatus": { + "type": "enum", + "variants": [ + { + "name": "AlreadyProcessed", + "discriminant": 0 + }, + { + "name": "Empty", + "discriminant": 1 + }, + { + "name": "PartiallyFull", + "discriminant": 2, + "fields": [ + { + "name": "end_block_nonce", + "type": "u64" + }, + { + "name": "tx_ids", + "type": "List" + } + ] + }, + { + "name": "Full", + "discriminant": 3 + }, + { + "name": "WaitingForSignatures", + "discriminant": 4 + } + ] + }, + "DepositEvent": { + "type": "struct", + "fields": [ + { + "name": "tx_nonce", + "type": "u64" + }, + { + "name": "opt_function", + "type": "Option" + }, + { + "name": "opt_arguments", + "type": "Option>" + }, + { + "name": "opt_gas_limit", + "type": "Option" + } + ] + }, + "EsdtTokenPayment": { + "type": "struct", + "fields": [ + { + "name": "token_identifier", + "type": "TokenIdentifier" + }, + { + "name": "token_nonce", + "type": "u64" + }, + { + "name": "amount", + "type": "BigUint" + } + ] + }, + "EsdtTokenType": { + "type": "enum", + "variants": [ + { + "name": "Fungible", + "discriminant": 0 + }, + { + "name": "NonFungible", + "discriminant": 1 + }, + { + "name": "SemiFungible", + "discriminant": 2 + }, + { + "name": "Meta", + "discriminant": 3 + }, + { + "name": "Invalid", + "discriminant": 4 + } + ] + }, + "StolenFromFrameworkEsdtTokenData": { + "type": "struct", + "fields": [ + { + "name": "token_type", + "type": "EsdtTokenType" + }, + { + "name": "amount", + "type": "BigUint" + }, + { + "name": "frozen", + "type": "bool" + }, + { + "name": "hash", + "type": "bytes" + }, + { + "name": "name", + "type": "bytes" + }, + { + "name": "attributes", + "type": "bytes" + }, + { + "name": "creator", + "type": "Address" + }, + { + "name": "royalties", + "type": "BigUint" + }, + { + "name": "uris", + "type": "List" + } + ] + }, + "Transaction": { + "type": "struct", + "fields": [ + { + "name": "block_nonce", + "type": "u64" + }, + { + "name": "nonce", + "type": "u64" + }, + { + "name": "from", + "type": "Address" + }, + { + "name": "to", + "type": "Address" + }, + { + "name": "tokens", + "type": "List" + }, + { + "name": "token_data", + "type": "List" + }, + { + "name": "opt_transfer_data", + "type": "Option" + }, + { + "name": "is_refund_tx", + "type": "bool" + } + ] + }, + "TransactionStatus": { + "type": "enum", + "variants": [ + { + "name": "None", + "discriminant": 0 + }, + { + "name": "Pending", + "discriminant": 1 + }, + { + "name": "InProgress", + "discriminant": 2 + }, + { + "name": "Executed", + "discriminant": 3 + }, + { + "name": "Rejected", + "discriminant": 4 + } + ] + }, + "TransferData": { + "type": "struct", + "fields": [ + { + "name": "gas_limit", + "type": "u64" + }, + { + "name": "function", + "type": "bytes" + }, + { + "name": "args", + "type": "List" + } + ] + } + } +}