#include "wallet/lite_wallet_state_mapper.h" #include namespace dragonx::wallet { namespace { LiteWalletAppAddressModel mapAddress(const std::string& address, LiteWalletAppAddressKind kind) { LiteWalletAppAddressModel model; model.address = address; model.kind = kind; return model; } LiteWalletAppSpendableOutputModel mapSpendableOutput(const LiteSpendableOutput& output) { LiteWalletAppSpendableOutputModel model; model.kind = output.kind; model.address = output.address; model.createdInTxid = output.createdInTxid; model.createdInBlock = output.createdInBlock; model.valueZatoshis = output.value; model.spent = output.spent; model.unconfirmedSpent = output.unconfirmedSpent; model.pending = output.pending; model.spendable = output.spendable; return model; } LiteWalletAppTransactionKind mapTransactionKind(LiteTransactionDirection direction) { switch (direction) { case LiteTransactionDirection::Send: return LiteWalletAppTransactionKind::Send; case LiteTransactionDirection::Receive: return LiteWalletAppTransactionKind::Receive; case LiteTransactionDirection::Unknown: return LiteWalletAppTransactionKind::Unknown; } return LiteWalletAppTransactionKind::Unknown; } LiteWalletAppTransactionOutputModel mapTransactionOutput(const LiteTransactionOutput& output) { LiteWalletAppTransactionOutputModel model; model.address = output.address; model.valueZatoshis = output.value; model.memo = output.memo; return model; } LiteWalletAppTransactionModel mapTransaction(const LiteTransactionRecord& transaction) { LiteWalletAppTransactionModel model; model.txid = transaction.txid; model.kind = mapTransactionKind(transaction.direction); model.timestamp = transaction.datetime; model.blockHeight = transaction.blockHeight; model.unconfirmed = transaction.unconfirmed; model.address = transaction.address; model.amountZatoshis = transaction.amount; model.signedAmountZatoshis = model.kind == LiteWalletAppTransactionKind::Send ? -transaction.amount : transaction.amount; model.memo = transaction.memo; model.position = transaction.position; model.outgoingOutputs.reserve(transaction.outgoingMetadata.size()); for (const auto& output : transaction.outgoingMetadata) { model.outgoingOutputs.push_back(mapTransactionOutput(output)); } return model; } bool modelHasAnyMappedField(const LiteWalletAppRefreshModel& model) { return model.hasChainInfo || model.hasHeight || model.hasBalance || model.hasAddresses || model.hasSpendableOutputs || model.hasTransactions || model.hasSyncStatus; } void addIssue(LiteWalletStateMapResult& result, LiteWalletStateMapIssue issue, std::string message) { result.issues.push_back(LiteWalletStateMapIssueInfo{issue, std::move(message)}); } } // namespace const char* liteWalletAppAddressKindName(LiteWalletAppAddressKind kind) { switch (kind) { case LiteWalletAppAddressKind::Shielded: return "shielded"; case LiteWalletAppAddressKind::Transparent: return "transparent"; } return "unknown"; } const char* liteWalletAppTransactionKindName(LiteWalletAppTransactionKind kind) { switch (kind) { case LiteWalletAppTransactionKind::Unknown: return "unknown"; case LiteWalletAppTransactionKind::Send: return "send"; case LiteWalletAppTransactionKind::Receive: return "receive"; } return "unknown"; } const char* liteWalletStateMapIssueName(LiteWalletStateMapIssue issue) { switch (issue) { case LiteWalletStateMapIssue::EmptyBundle: return "EmptyBundle"; case LiteWalletStateMapIssue::IncompleteBundle: return "IncompleteBundle"; } return "Unknown"; } LiteWalletStateMapResult mapLiteWalletRefreshBundle(const LiteWalletRefreshBundle& bundle) { LiteWalletStateMapResult result; result.stateMutationAllowed = false; result.model.complete = bundle.complete; result.model.successfulCommandCount = bundle.successfulCommandCount; if (bundle.hasInfo) { result.model.hasChainInfo = true; result.model.chain.chainName = bundle.info.chainName; result.model.chain.version = bundle.info.version; result.model.chain.vendor = bundle.info.vendor; result.model.chain.latestBlockHeight = bundle.info.latestBlockHeight; result.model.chain.difficulty = bundle.info.difficulty; result.model.chain.longestChain = bundle.info.longestChain; result.model.chain.notarized = bundle.info.notarized; } if (bundle.hasHeight) { result.model.hasHeight = true; result.model.height.height = bundle.height.height; } if (bundle.hasBalance) { result.model.hasBalance = true; result.model.balance.transparentZatoshis = bundle.balance.transparentBalance; result.model.balance.shieldedZatoshis = bundle.balance.shieldedBalance; result.model.balance.unconfirmedZatoshis = bundle.balance.unconfirmedBalance; result.model.balance.verifiedShieldedZatoshis = bundle.balance.verifiedShieldedBalance; result.model.balance.spendableShieldedZatoshis = bundle.balance.spendableShieldedBalance; result.model.balance.totalZatoshis = bundle.balance.transparentBalance + bundle.balance.shieldedBalance; } if (bundle.hasAddresses) { result.model.hasAddresses = true; result.model.addresses.reserve(bundle.addresses.zAddresses.size() + bundle.addresses.tAddresses.size()); for (const auto& address : bundle.addresses.zAddresses) { result.model.addresses.push_back(mapAddress(address, LiteWalletAppAddressKind::Shielded)); } for (const auto& address : bundle.addresses.tAddresses) { result.model.addresses.push_back(mapAddress(address, LiteWalletAppAddressKind::Transparent)); } } if (bundle.hasNotes) { result.model.hasSpendableOutputs = true; const auto outputCount = bundle.notes.unspentNotes.size() + bundle.notes.utxos.size() + bundle.notes.pendingNotes.size() + bundle.notes.pendingUtxos.size(); result.model.spendableOutputs.reserve(outputCount); for (const auto& output : bundle.notes.unspentNotes) { result.model.spendableOutputs.push_back(mapSpendableOutput(output)); } for (const auto& output : bundle.notes.utxos) { result.model.spendableOutputs.push_back(mapSpendableOutput(output)); } for (const auto& output : bundle.notes.pendingNotes) { result.model.spendableOutputs.push_back(mapSpendableOutput(output)); } for (const auto& output : bundle.notes.pendingUtxos) { result.model.spendableOutputs.push_back(mapSpendableOutput(output)); } } if (bundle.hasTransactions) { result.model.hasTransactions = true; result.model.transactions.reserve(bundle.transactions.transactions.size()); for (const auto& transaction : bundle.transactions.transactions) { result.model.transactions.push_back(mapTransaction(transaction)); } } if (bundle.hasSyncStatus) { result.model.hasSyncStatus = true; result.model.sync.walletHeight = bundle.syncStatus.syncedBlocks; result.model.sync.chainHeight = bundle.syncStatus.totalBlocks; result.model.sync.progress = bundle.syncStatus.progress; result.model.sync.complete = bundle.syncStatus.complete; } if (!modelHasAnyMappedField(result.model)) { addIssue(result, LiteWalletStateMapIssue::EmptyBundle, "lite refresh bundle has no mappable fields"); result.error = result.issues.back().message; return result; } if (!bundle.complete) { addIssue(result, LiteWalletStateMapIssue::IncompleteBundle, "lite refresh bundle is partial"); } result.ok = true; return result; } LiteWalletStateMapResult mapLiteWalletRefreshResult(const LiteWalletRefreshResult& result) { return mapLiteWalletRefreshBundle(result.bundle); } } // namespace dragonx::wallet