normalizeInputs method

Future<void> normalizeInputs(
  1. BuildOptions options
)

Implementation

Future<void> normalizeInputs(BuildOptions options) async {
  final inputs = _blockData.inputs;
  final commands = _blockData.commands;
  final moveCallsToResolve = [];
  final moveFunctionsToResolve = <String>{};

  for (var command in commands) {
    if (command["MoveCall"] != null) {
      // Determine if any of the arguments require encoding.
      // - If they don't, then this is good to go.
      // - If they do, then we need to fetch the normalized move module.

      // If we already know the argument types, we don't need to resolve them again
      if (command["MoveCall"]?["_argumentTypes"] != null) {
        continue;
      }

      final inputs = (command["MoveCall"]["arguments"] as Iterable).map((arg) {
        if (arg is Map && arg["Input"] != null) {
          return _blockData.inputs[arg["Input"]];
        }
        return null;
      }).toList();
      final needsResolution = inputs.firstWhere(
        (input) => input?["UnresolvedPure"] != null || input?["UnresolvedObject"] != null,
        orElse: () => null
      );

      if (needsResolution != null) {
        final functionName = "${command["MoveCall"]["package"]}::${command["MoveCall"]["module"]}::${command["MoveCall"]["function"]}";
        moveFunctionsToResolve.add(functionName);
        moveCallsToResolve.add(command["MoveCall"]);
      }
    }

    // Special handling for values that where previously encoded using the wellKnownEncoding pattern.
    // This should only happen when transaction data was hydrated from an old version of the SDK
    if (command["SplitCoins"] != null) {
      command["SplitCoins"]["amounts"].forEach((amount) {
        normalizeRawArgument(amount, SuiBcs.U64);
      });
    } else if (command["TransferObjects"] != null) {
      normalizeRawArgument(command["TransferObjects"]["address"], SuiBcs.Address);
    }
  }

  final moveFunctionParameters = <String, List<dynamic>>{};
  if (moveFunctionsToResolve.isNotEmpty) {
    final client = expectClient(options);
    await Future.wait(
      [...moveFunctionsToResolve].map((functionName) async {
        final [packageId, moduleId, functionId] = functionName.split('::');
        final def = await client.getNormalizedMoveFunction(
          packageId,
          moduleId,
          functionId,
        );

        moveFunctionParameters[functionName] = def.parameters.map((param) => normalizedTypeToMoveTypeSignature(param)).toList();
      }),
    );
  }

  if (moveCallsToResolve.isNotEmpty) {
    moveCallsToResolve.forEach((moveCall) {
      final parameters = moveFunctionParameters["${moveCall["package"]}::${moveCall["module"]}::${moveCall["function"]}"];
      if (parameters != null && parameters.isNotEmpty) {
        // Entry functions can have a mutable reference to an instance of the TxContext
        // struct defined in the TxContext module as the last parameter. The caller of
        // the function does not need to pass it in as an argument.
        final hasTxContext = isTxContext(parameters.last!);
        final params = hasTxContext ? parameters.sublist(0, parameters.length - 1) : parameters;

        moveCall["_argumentTypes"] = params;
      }
    });
  }

  for (var command in commands) {
    if (command["MoveCall"] == null) {
      continue;
    }

    final moveCall = command["MoveCall"];
    final fnName = "${moveCall["package"]}::${moveCall["module"]}::${moveCall["function"]}";
    final params = moveCall["_argumentTypes"];

    if (params == null) {
      continue;
    }

    if (params.length != command["MoveCall"]["arguments"].length) {
      throw ArgumentError("Incorrect number of arguments for $fnName");
    }

    final callArgs = moveCall["arguments"].toList();
    for (var i = 0; i < params.length; i++) {
      final param = params[i];
      final arg = callArgs[i];
      if (arg["Input"] == null) continue;
      final input = inputs[arg["Input"]];

      // Skip if the input is already resolved
      if (input["UnresolvedPure"] == null && input["UnresolvedObject"] == null) {
        continue;
      }

      final inputValue = input["UnresolvedPure"]?["value"] ?? input["UnresolvedObject"]?["objectId"];

      final schema = getPureBcsSchema(param["body"]);
      if (schema != null) {
        arg["type"] = 'pure';
        inputs[inputs.indexOf(input)] = Inputs.pure(schema.serialize(inputValue));
        continue;
      }

      if (inputValue is! String) {
        throw ArgumentError("Expect the argument to be an object id string, got $inputValue");
      }

      arg["type"] = 'object';
      final unresolvedObject = input["UnresolvedPure"] != null
        ? {
            "UnresolvedObject": {
              "objectId": inputValue,
            },
          }
        : input;

      inputs[arg["Input"]] = unresolvedObject;
    }
  }
}