transactionEnvelopeXdrBase64FromTxRep static method
returns a base64 encoded transaction envelope xdr by parsing txRep
.
Implementation
static String transactionEnvelopeXdrBase64FromTxRep(String txRep) {
List<String> lines = txRep.split('\n'); //TODO: handle newline within string
Map<String, String> map = Map<String, String>();
for (String line in lines) {
var parts = line.split(':');
if (parts.length > 1) {
String key = parts[0].trim();
String value = parts.sublist(1).join(':').trim();
map.addAll({key: value});
}
}
String prefix = 'tx.';
bool isFeeBump = _removeComment(map['type']) == 'ENVELOPE_TYPE_TX_FEE_BUMP';
int? feeBumpFee;
String? feeBumpSource = _removeComment(map['feeBump.tx.feeSource']);
if (isFeeBump) {
prefix = 'feeBump.tx.innerTx.tx.';
String? feeBumpFeeStr = _removeComment(map['feeBump.tx.fee']);
if (feeBumpFeeStr == null) {
throw Exception('missing feeBump.tx.fee');
}
try {
feeBumpFee = int.tryParse(feeBumpFeeStr);
} catch (e) {
throw Exception('invalid feeBump.tx.fee');
}
if (feeBumpFee == null) {
throw Exception('invalid feeBump.tx.fee');
}
if (feeBumpSource == null) {
throw Exception('missing feeBump.tx.feeSource');
}
KeyPair? feeBumpSourceKeyPair;
try {
feeBumpSourceKeyPair = KeyPair.fromAccountId(feeBumpSource);
} catch (e) {
throw Exception('invalid feeBump.tx.feeSource');
}
}
String? sourceAccountId = _removeComment(map['${prefix}sourceAccount']);
if (sourceAccountId == null) {
throw Exception('missing ${prefix}sourceAccount');
}
String? feeStr = _removeComment(map['${prefix}fee']);
int? fee;
if (feeStr == null) {
throw Exception('missing ${prefix}fee');
} else {
try {
fee = int.tryParse(feeStr);
} catch (e) {
throw Exception('invalid ${prefix}fee');
}
}
if (fee == null) {
throw Exception('invalid ${prefix}fee');
}
String? seqNr = _removeComment(map['${prefix}seqNum']);
int? sequenceNumber;
if (seqNr == null) {
throw Exception('missing ${prefix}seqNum');
} else {
try {
sequenceNumber = int.tryParse(seqNr);
} catch (e) {
throw Exception('invalid ${prefix}seqNum');
}
}
if (sequenceNumber == null) {
throw Exception('invalid ${prefix}seqNum');
}
try {
KeyPair.fromAccountId(sourceAccountId);
} catch (e) {
throw Exception('invalid ${prefix}sourceAccount');
}
MuxedAccount? mux = MuxedAccount.fromAccountId(sourceAccountId);
Account sourceAccount = Account(mux!.ed25519AccountId, sequenceNumber - 1,
muxedAccountMed25519Id: mux.id);
TransactionBuilder txBuilder = TransactionBuilder(sourceAccount);
txBuilder.addPreconditions(_getPreconditions(map, prefix));
// Memo
String? memoType = _removeComment(map['${prefix}memo.type']);
if (memoType == null) {
throw Exception('missing ${prefix}memo.type');
}
try {
if (memoType == 'MEMO_TEXT' && map['${prefix}memo.text'] != null) {
txBuilder.addMemo(MemoText(
_removeComment(map['${prefix}memo.text'])!.replaceAll('"', '')));
} else if (memoType == 'MEMO_ID' && map['${prefix}memo.id'] != null) {
txBuilder.addMemo(
MemoId(int.tryParse(_removeComment(map['${prefix}memo.id'])!)!));
} else if (memoType == 'MEMO_HASH' && map['${prefix}memo.hash'] != null) {
txBuilder.addMemo(MemoHash(
Util.hexToBytes(_removeComment(map['${prefix}memo.hash'])!)));
} else if (memoType == 'MEMO_RETURN' &&
map['${prefix}memo.return'] != null) {
txBuilder.addMemo(MemoReturnHash.string(
_removeComment(map['${prefix}memo.return'])!));
} else {
txBuilder.addMemo(MemoNone());
}
} catch (e) {
throw Exception('invalid ${prefix}memo');
}
// Operations
String? operationsLen = _removeComment(map['${prefix}operations.len']);
if (operationsLen == null) {
throw Exception('missing ${prefix}operations.len');
}
int nrOfOperations;
try {
nrOfOperations = int.parse(operationsLen);
} catch (e) {
throw Exception('invalid ${prefix}operations.len');
}
if (nrOfOperations > 100) {
throw Exception('invalid ${prefix}operations.len - greater than 100');
}
for (int i = 0; i < nrOfOperations; i++) {
Operation? operation = _getOperation(i, map, prefix);
if (operation != null) {
txBuilder.addOperation(operation);
}
}
int maxOperationFee = (fee.toDouble() / nrOfOperations.toDouble()).round();
txBuilder.setMaxOperationFee(maxOperationFee);
// AbstractTransaction transaction = txBuilder.build();
Transaction transaction = txBuilder.build();
// Signatures
prefix = isFeeBump ? 'feeBump.tx.innerTx.' : "";
String? signaturesLen = _removeComment(map['${prefix}signatures.len']);
if (signaturesLen != null) {
int nrOfSignatures;
try {
nrOfSignatures = int.parse(signaturesLen);
} catch (e) {
throw Exception('invalid ${prefix}signatures.len');
}
if (nrOfSignatures > 20) {
throw Exception('invalid ${prefix}signatures.len - greater than 20');
}
List<XdrDecoratedSignature> signatures =
List<XdrDecoratedSignature>.empty(growable: true);
for (int i = 0; i < nrOfSignatures; i++) {
XdrDecoratedSignature? signature = _getSignature(i, map, prefix);
if (signature != null) {
signatures.add(signature);
}
}
transaction.signatures = signatures;
}
if (isFeeBump) {
FeeBumpTransactionBuilder builder =
FeeBumpTransactionBuilder(transaction);
int baseFee =
(feeBumpFee!.toDouble() / (nrOfOperations + 1).toDouble()).round();
builder.setBaseFee(baseFee);
builder.setMuxedFeeAccount(MuxedAccount.fromAccountId(feeBumpSource!)!);
FeeBumpTransaction feeBumpTransaction = builder.build();
String? fbSignaturesLen = _removeComment(map['feeBump.signatures.len']);
if (fbSignaturesLen != null) {
int nrOfFbSignatures;
try {
nrOfFbSignatures = int.parse(fbSignaturesLen);
} catch (e) {
throw Exception('invalid feeBump.signatures.len');
}
if (nrOfFbSignatures > 20) {
throw Exception('invalid feeBump.signatures.len - greater than 20');
}
List<XdrDecoratedSignature> fbSignatures =
List<XdrDecoratedSignature>.empty(growable: true);
for (int i = 0; i < nrOfFbSignatures; i++) {
XdrDecoratedSignature? fbSignature =
_getSignature(i, map, 'feeBump.');
if (fbSignature != null) {
fbSignatures.add(fbSignature);
}
}
feeBumpTransaction.signatures = fbSignatures;
return feeBumpTransaction.toEnvelopeXdrBase64();
}
}
return transaction.toEnvelopeXdrBase64();
}