createAndSign method

Future<Tx> createAndSign(
  1. Wallet wallet,
  2. List<GeneratedMessage> msgs, {
  3. TxConfig? config,
  4. String? memo,
  5. Fee? fee,
})

Creates a new Tx object containing the given msgs and signs it using the provided wallet. Optional TxConfig, memo, gas and fees can be supplied as well.

Implementation

Future<Tx> createAndSign(
  Wallet wallet,
  List<GeneratedMessage> msgs, {
  TxConfig? config,
  String? memo,
  Fee? fee,
}) async {
  // Set the config to the default value if not given
  config ??= DefaultTxConfig.create();
  final signMode = config.defaultSignMode();

  // Set the default fees
  fee ??= Fee()..gasLimit = 200000.toInt64();
  if (!fee.hasGasLimit()) {
    throw Exception('Invalid fees: invalid gas amount specified');
  }

  // Get the account data and node info from the network
  final account = await _authQuerier.getAccountData(wallet.bech32Address);
  if (account == null) {
    throw Exception(
      'Account ${wallet.bech32Address} does not exist on chain',
    );
  }

  // Get the node info data
  final nodeInfo = await _nodeQuerier.getNodeInfo();

  // Get the public key from the account, or generate it if the
  // chain does not have it yet
  var pubKey = account.pubKey;
  if (pubKey.value.isNotEmpty != true) {
    final secp256Key = secp256.PubKey.create()..key = wallet.publicKey;
    pubKey = Codec.serialize(secp256Key);
  }

  // For SIGN_MODE_DIRECT, calling SetSignatures calls setSignerInfos on
  // TxBuilder under the hood, and SignerInfos is needed to generated the
  // sign bytes. This is the reason for setting SetSignatures here, with a
  // nil signature.
  //
  // Note: this line is not needed for SIGN_MODE_LEGACY_AMINO, but putting it
  // also doesn't affect its generated sign bytes, so for code's simplicity
  // sake, we put it here.
  var sigData = SingleSignatureData(signMode: signMode, signature: null);

  // Set SignatureV2 with empty signatures, to set correct signer infos.
  var sig = SignatureV2(
    pubKey: pubKey,
    data: sigData,
    sequence: account.sequence,
  );

  // Create the transaction builder
  final tx = config.newTxBuilder()
    ..setMsgs(msgs)
    ..setSignatures([sig])
    ..setMemo(memo)
    ..setFeeAmount(fee.amount)
    ..setFeePayer(fee.payer)
    ..setFeeGranter(fee.granter)
    ..setGasLimit(fee.gasLimit);

  // Generate the bytes to be signed.
  final handler = config.signModeHandler();
  final signerData = SignerData(
    chainId: nodeInfo.network,
    accountNumber: account.accountNumber,
    sequence: account.sequence,
  );
  final bytesToSign = handler.getSignBytes(signMode, signerData, tx.getTx());

  // Sign those bytes
  final sigBytes = wallet.sign(Uint8List.fromList(bytesToSign));

  // Construct the SignatureV2 struct
  sigData = SingleSignatureData(signMode: signMode, signature: sigBytes);
  sig = SignatureV2(
    pubKey: pubKey,
    data: sigData,
    sequence: account.sequence,
  );
  tx.setSignatures([sig]);

  // Return the signed transaction
  return tx.getTx();
}