Skip to main content

Outcome Parsers

Parse transaction outcomes to extract structured results from completed transactions.

Overview

After a transaction is processed, outcome parsers extract meaningful data from transaction logs and events:

ParserUse Case
SmartContractOutcomeParserContract deployments and calls
TokenManagementOutcomeParserToken issuance, roles, NFT operations
DelegationOutcomeParserDelegation contract creation
GovernanceOutcomeParserProposals, votes, proposal closing
Where the outcome data is found

Parsers do not stop at transaction.logs.

TokenManagementOutcomeParser searches the transaction's own logs and the logs attached to every one of its smart contract results, in transaction-first order. This matters for cross-shard operations: several token-management endpoints -- role assignment, freeze/unfreeze/wipe, the local mint/burn pair -- run as the system contract forwarding a built-in call to the target address. That call executes on the target's shard, so its event is reported on the resulting smart contract result rather than on the original transaction. A parser that only read transaction.logs would silently return an empty list.

SmartContractOutcomeParser.parseExecute falls back to the smart contract results themselves when the transaction's own logs carry no direct outcome, reading returnCode and returnData off the result addressed back to the original sender. parseDeploy reads transaction.logs only, since SCDeploy is emitted there.

DelegationOutcomeParser also reads transaction.logs only, which is sufficient for the SCDeploy event it needs.

SmartContractOutcomeParser

Parse smart contract deployment and execution results.

Setup

import 'package:abidock_mvx/abidock_mvx.dart';

// Without ABI (returns raw buffers)
final parser = SmartContractOutcomeParser();

// With ABI (returns decoded values)
final parserWithAbi = SmartContractOutcomeParser(abi: myAbi);

Parse Deployment

Extract deployed contract addresses:

final hash = await provider.sendTransaction(deployTx);

// Wait for transaction to complete
final txOnNetwork = await provider.getTransaction(hash);

final outcome = parser.parseDeploy(txOnNetwork);

print('Return code: ${outcome.returnCode}');
print('Contracts deployed: ${outcome.contracts.length}');

for (final contract in outcome.contracts) {
print('Address: ${contract.address.bech32}');
print('Owner: ${contract.ownerAddress.bech32}');
print('Code hash: ${HexUtils.bytesToHex(contract.codeHash)}');
}

Parse Execution

Extract return values from contract calls:

// Send transaction
final hash = await provider.sendTransaction(callTx);
final txOnNetwork = await provider.getTransaction(hash);

// Parse with ABI for decoded values
final parserWithAbi = SmartContractOutcomeParser(abi: myAbi);
final outcome = parserWithAbi.parseExecute(
txOnNetwork,
function: 'getBalance', // Function name for ABI lookup
);

print('Return code: ${outcome.returnCode}');
print('Return message: ${outcome.returnMessage}');

// Values are decoded according to ABI
for (final value in outcome.values) {
print('Value: $value (${value.runtimeType})');
}

Without ABI

When no ABI is provided, raw buffers are returned:

final parser = SmartContractOutcomeParser();
final outcome = parser.parseExecute(txOnNetwork);

// Values are Uint8List buffers
for (final buffer in outcome.values) {
print('Raw bytes: ${buffer as Uint8List}');
}

Result Types

SmartContractDeployOutcome -- returned by parseDeploy:

FieldTypeDescription
returnCodeString'ok', or the code taken from the error event
returnMessageStringHuman-readable message; empty when successful
contractsList<DeployedContract>One entry per SCDeploy event

DeployedContract -- one deployed contract:

FieldTypeDescription
addressAddressContract address
ownerAddressAddressDeployer address
codeHashUint8ListContract code hash

ParsedSmartContractCallOutcome -- returned by parseExecute:

FieldTypeDescription
returnCodeStringReturn code reported by the VM
returnMessageStringError message, or the return code when there is none
valuesList<dynamic>Decoded values when an ABI is supplied, raw Uint8List buffers otherwise

When the transaction has no logs at all, parseDeploy returns an outcome with empty returnCode/returnMessage and no contracts rather than throwing.

TokenManagementOutcomeParser

Parse token management operation results.

Setup

final parser = TokenManagementOutcomeParser();

Parse Token Issuance

// After issuing a fungible token
final txOnNetwork = await provider.getTransaction(issueHash);
final results = parser.parseIssueFungible(txOnNetwork);

for (final result in results) {
print('Token identifier: ${result.tokenIdentifier}');
// e.g., "MTK-abc123"
}

Parse NFT Collection

// After issuing NFT collection
final results = parser.parseIssueNonFungible(txOnNetwork);
print('Collection: ${results.first.tokenIdentifier}');

// After issuing SFT collection
final sftResults = parser.parseIssueSemiFungible(txOnNetwork);
print('SFT Collection: ${sftResults.first.tokenIdentifier}');

// After registering Meta-ESDT
final metaResults = parser.parseRegisterMetaEsdt(txOnNetwork);
print('Meta-ESDT: ${metaResults.first.tokenIdentifier}');

Parse Role Assignment

final results = parser.parseSetSpecialRole(txOnNetwork);

for (final result in results) {
print('User: ${result.userAddress.bech32}');
print('Token: ${result.tokenIdentifier}');
print('Roles: ${result.roles.join(", ")}');
}

Parse NFT Creation

final results = parser.parseNftCreate(txOnNetwork);

for (final result in results) {
print('Token: ${result.tokenIdentifier}');
print('Nonce: ${result.nonce}'); // NFT ID within collection
print('Quantity: ${result.initialQuantity}');
}

Parse Mint/Burn

// Local mint
final mintResults = parser.parseLocalMint(txOnNetwork);
print('Minted: ${mintResults.first.mintedSupply}');

// Local burn
final burnResults = parser.parseLocalBurn(txOnNetwork);
print('Burned: ${burnResults.first.burntSupply}');

Parse Control Operations

// Pause
final pauseResults = parser.parsePause(txOnNetwork);
print('Paused: ${pauseResults.first.tokenIdentifier}');

// Unpause
final unpauseResults = parser.parseUnpause(txOnNetwork);
print('Unpaused: ${unpauseResults.first.tokenIdentifier}');

// Freeze
final freezeResults = parser.parseFreeze(txOnNetwork);
print('Frozen user: ${freezeResults.first.userAddress}');

// Wipe
final wipeResults = parser.parseWipe(txOnNetwork);
print('Wiped balance: ${wipeResults.first.balance}');

Parse Advanced Token Operations

// Modify royalties
final royaltiesResults = parser.parseModifyRoyalties(txOnNetwork);
print('New royalties: ${royaltiesResults.first.royalties}'); // basis points

// Set new URIs
final urisResults = parser.parseSetNewUris(txOnNetwork);
print('New URIs: ${urisResults.first.uris}');

// Modify creator
final creatorResults = parser.parseModifyCreator(txOnNetwork);
print('Creator modified for: ${creatorResults.first.tokenIdentifier}');

// Update metadata
final metadataResults = parser.parseUpdateMetadata(txOnNetwork);
print('Metadata updated: ${metadataResults.first.metadata.length} bytes');

// Metadata recreate
final recreateResults = parser.parseMetadataRecreate(txOnNetwork);
print('Metadata recreated for nonce: ${recreateResults.first.nonce}');

// Change token to dynamic
final dynamicResults = parser.parseChangeTokenToDynamic(txOnNetwork);
print('Token type: ${dynamicResults.first.tokenType}');

// Register dynamic token
final registerResults = parser.parseRegisterDynamicToken(txOnNetwork);
print('Registered: ${registerResults.first.tokenIdentifier}');

// Register dynamic token with roles
final registerRolesResults = parser.parseRegisterDynamicTokenAndSettingRoles(txOnNetwork);
print('Registered with roles: ${registerRolesResults.first.tokenIdentifier}');

Parse Global Burn Role

// These methods validate that the operation succeeded but don't return data
parser.parseSetBurnRoleGlobally(txOnNetwork); // throws on error
parser.parseUnsetBurnRoleGlobally(txOnNetwork); // throws on error

All Token Management Results

MethodResult TypeFields
parseIssueFungibleIssueFungibleResulttokenIdentifier
parseIssueNonFungibleIssueNonFungibleResulttokenIdentifier
parseIssueSemiFungibleIssueSemiFungibleResulttokenIdentifier
parseRegisterMetaEsdtRegisterMetaEsdtResulttokenIdentifier
parseRegisterAndSetAllRolesRegisterAndSetAllRolesResulttokenIdentifier, roles
parseSetSpecialRoleSetSpecialRoleResultuserAddress, tokenIdentifier, roles
parseSetBurnRoleGloballyvoid-
parseUnsetBurnRoleGloballyvoid-
parseNftCreateNftCreateResulttokenIdentifier, nonce, initialQuantity
parseLocalMintLocalMintResultuserAddress, tokenIdentifier, nonce, mintedSupply
parseLocalBurnLocalBurnResultuserAddress, tokenIdentifier, nonce, burntSupply
parsePausePauseResulttokenIdentifier
parseUnpauseUnpauseResulttokenIdentifier
parseFreezeFreezeResultuserAddress, tokenIdentifier, nonce, balance
parseUnfreezeUnfreezeResultuserAddress, tokenIdentifier, nonce, balance
parseWipeWipeResultuserAddress, tokenIdentifier, nonce, balance
parseUpdateAttributesUpdateAttributesResulttokenIdentifier, nonce, attributes
parseAddQuantityAddQuantityResulttokenIdentifier, nonce, addedQuantity
parseBurnQuantityBurnQuantityResulttokenIdentifier, nonce, burntQuantity
parseModifyRoyaltiesModifyRoyaltiesResulttokenIdentifier, nonce, royalties
parseSetNewUrisSetNewUrisResulttokenIdentifier, nonce, uris
parseModifyCreatorModifyCreatorResulttokenIdentifier, nonce
parseUpdateMetadataUpdateMetadataResulttokenIdentifier, nonce, metadata
parseMetadataRecreateMetadataRecreateResulttokenIdentifier, nonce, metadata
parseChangeTokenToDynamicChangeToDynamicResulttokenIdentifier, tokenName, tickerName, tokenType
parseRegisterDynamicTokenRegisterDynamicResulttokenIdentifier, tokenName, tokenTicker, tokenType, numOfDecimals
parseRegisterDynamicTokenAndSettingRolesRegisterDynamicResulttokenIdentifier, tokenName, tokenTicker, tokenType, numOfDecimals

DelegationOutcomeParser

Parse delegation contract creation results.

Setup

final parser = DelegationOutcomeParser();

Parse Contract Creation

final txOnNetwork = await provider.getTransaction(delegationHash);
final results = parser.parseCreateNewDelegationContract(txOnNetwork);

for (final result in results) {
print('Delegation contract: ${result.contractAddress}');
}

Error Handling

Parsers throw exceptions when transactions contain errors:

try {
final results = parser.parseIssueFungible(txOnNetwork);
print('Success: ${results.first.tokenIdentifier}');
} on TokenManagementParseException catch (e) {
print('Token operation failed: ${e.message}');
if (e.cause != null) {
print('Caused by: ${e.cause}');
}
} on SmartContractParseException catch (e) {
print('Contract operation failed: ${e.message}');
} on DelegationParseException catch (e) {
print('Delegation operation failed: ${e.message}');
}

Complete Example

import 'package:abidock_mvx/abidock_mvx.dart';

void main() async {
final provider = GatewayNetworkProvider.devnet();
final account = await Account.fromMnemonic('your mnemonic...');

final controller = TokenManagementController(
chainId: const ChainId.devnet(),
gasLimitEstimator: GasEstimator(networkProvider: provider),
);

final accountInfo = await provider.getAccount(account.address);

// 1. Issue token
final input = IssueFungibleInput(
tokenName: 'MyToken',
tokenTicker: 'MTK',
initialSupply: BigInt.from(1000000000000000000),
numDecimals: BigInt.from(18),
canFreeze: true,
canWipe: true,
canPause: true,
canChangeOwner: true,
canUpgrade: true,
canAddSpecialRoles: true,
);

final tx = await controller.createTransactionForIssuingFungible(
account,
accountInfo.nonce,
input,
const BaseControllerInput(),
);

final hash = await provider.sendTransaction(tx);
print('Sent: $hash');

// 2. Wait for completion
final watcher = TransactionWatcher(networkProvider: provider);
await watcher.awaitCompleted(hash);

// 3. Parse outcome
final txOnNetwork = await provider.getTransaction(hash);
final parser = TokenManagementOutcomeParser();

try {
final results = parser.parseIssueFungible(txOnNetwork);
final tokenId = results.first.tokenIdentifier;
print('Token created: $tokenId');

// Save token identifier for future use
// e.g., store in database or config

} on TokenManagementParseException catch (e) {
print('Failed to issue token: ${e.message}');
}
}

Best Practices

  1. Always wait for finalization - Parse only after transaction is finalized
  2. Handle errors - Wrap parsing in try-catch for graceful error handling
  3. Use ABI when available - Provides typed return values instead of raw bytes
  4. Store identifiers - Save token identifiers and contract addresses from outcomes
  5. Check return codes - Verify returnCode == 'ok' before using results