mirror of
https://github.com/mollyim/monero-wallet-sdk.git
synced 2024-12-25 23:49:28 -05:00
lib: add per-account balance and account listing to Ledger
This commit is contained in:
parent
6cc43d2502
commit
ada39f13e8
@ -6,5 +6,5 @@ import im.molly.monero.internal.TxInfo;
|
|||||||
oneway interface IBalanceListener {
|
oneway interface IBalanceListener {
|
||||||
void onBalanceChanged(in List<TxInfo> txHistory, in String[] subAddresses, in BlockchainTime blockchainTime);
|
void onBalanceChanged(in List<TxInfo> txHistory, in String[] subAddresses, in BlockchainTime blockchainTime);
|
||||||
void onRefresh(in BlockchainTime blockchainTime);
|
void onRefresh(in BlockchainTime blockchainTime);
|
||||||
void onAddressCreated(String subAddress);
|
void onSubAddressListUpdated(in String[] subAddresses);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ interface IWallet {
|
|||||||
oneway void addDetachedSubAddress(int accountIndex, int subAddressIndex, in IWalletCallbacks callback);
|
oneway void addDetachedSubAddress(int accountIndex, int subAddressIndex, in IWalletCallbacks callback);
|
||||||
oneway void createAccount(in IWalletCallbacks callback);
|
oneway void createAccount(in IWalletCallbacks callback);
|
||||||
oneway void createSubAddressForAccount(int accountIndex, in IWalletCallbacks callback);
|
oneway void createSubAddressForAccount(int accountIndex, in IWalletCallbacks callback);
|
||||||
|
oneway void getAddressesForAccount(int accountIndex, in IWalletCallbacks callback);
|
||||||
oneway void getAllAddresses(in IWalletCallbacks callback);
|
oneway void getAllAddresses(in IWalletCallbacks callback);
|
||||||
oneway void resumeRefresh(boolean skipCoinbase, in IWalletCallbacks callback);
|
oneway void resumeRefresh(boolean skipCoinbase, in IWalletCallbacks callback);
|
||||||
oneway void cancelRefresh();
|
oneway void cancelRefresh();
|
||||||
|
@ -3,8 +3,9 @@ package im.molly.monero;
|
|||||||
import im.molly.monero.BlockchainTime;
|
import im.molly.monero.BlockchainTime;
|
||||||
|
|
||||||
oneway interface IWalletCallbacks {
|
oneway interface IWalletCallbacks {
|
||||||
void onAddressReady(in String[] subAddresses);
|
|
||||||
void onRefreshResult(in BlockchainTime blockchainTime, int status);
|
void onRefreshResult(in BlockchainTime blockchainTime, int status);
|
||||||
void onCommitResult(boolean success);
|
void onCommitResult(boolean success);
|
||||||
|
void onSubAddressReady(String subAddress);
|
||||||
|
void onSubAddressListReceived(in String[] subAddresses);
|
||||||
void onFeesReceived(in long[] fees);
|
void onFeesReceived(in long[] fees);
|
||||||
}
|
}
|
||||||
|
@ -220,15 +220,17 @@ std::string Wallet::public_address() const {
|
|||||||
return account.get_public_address_str(m_wallet.nettype());
|
return account.get_public_address_str(m_wallet.nettype());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> Wallet::formatted_subaddresses() {
|
std::vector<std::string> Wallet::formatted_subaddresses(uint32_t index_major) {
|
||||||
std::lock_guard<std::mutex> lock(m_subaddresses_mutex);
|
std::lock_guard<std::mutex> lock(m_subaddresses_mutex);
|
||||||
|
|
||||||
std::vector<std::string> ret;
|
std::vector<std::string> ret;
|
||||||
ret.reserve(m_subaddresses.size());
|
ret.reserve(m_subaddresses.size());
|
||||||
|
|
||||||
for (const auto& entry: m_subaddresses) {
|
for (const auto& entry: m_subaddresses) {
|
||||||
|
if (index_major == -1 || index_major == entry.first.major) {
|
||||||
ret.push_back(FormatAccountAddress(entry));
|
ret.push_back(FormatAccountAddress(entry));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -745,9 +747,15 @@ JNIEXPORT jobjectArray JNICALL
|
|||||||
Java_im_molly_monero_WalletNative_nativeGetSubAddresses(
|
Java_im_molly_monero_WalletNative_nativeGetSubAddresses(
|
||||||
JNIEnv* env,
|
JNIEnv* env,
|
||||||
jobject thiz,
|
jobject thiz,
|
||||||
|
jint sub_address_major,
|
||||||
jlong handle) {
|
jlong handle) {
|
||||||
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
auto* wallet = reinterpret_cast<Wallet*>(handle);
|
||||||
return NativeToJavaStringArray(env, wallet->formatted_subaddresses());
|
try {
|
||||||
|
auto subaddresses = wallet->formatted_subaddresses(sub_address_major);
|
||||||
|
return NativeToJavaStringArray(env, subaddresses);
|
||||||
|
} catch (error::account_index_outofbound& e) {
|
||||||
|
return NativeToJavaStringArray(env, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
@ -112,7 +112,7 @@ class Wallet : i_wallet2_callback {
|
|||||||
std::vector<uint64_t> fetchBaseFeeEstimate();
|
std::vector<uint64_t> fetchBaseFeeEstimate();
|
||||||
|
|
||||||
std::string public_address() const;
|
std::string public_address() const;
|
||||||
std::vector<std::string> formatted_subaddresses();
|
std::vector<std::string> formatted_subaddresses(uint32_t index_major = -1);
|
||||||
|
|
||||||
uint32_t current_blockchain_height() const { return static_cast<uint32_t>(m_last_block_height); }
|
uint32_t current_blockchain_height() const { return static_cast<uint32_t>(m_last_block_height); }
|
||||||
uint64_t current_blockchain_timestamp() const { return m_last_block_timestamp; }
|
uint64_t current_blockchain_timestamp() const { return m_last_block_timestamp; }
|
||||||
|
@ -49,10 +49,3 @@ data class AccountAddress(
|
|||||||
|
|
||||||
override fun toString(): String = "$accountIndex/$subAddressIndex/$publicAddress"
|
override fun toString(): String = "$accountIndex/$subAddressIndex/$publicAddress"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Iterable<AccountAddress>.findByIndexes(
|
|
||||||
accountIndex: Int,
|
|
||||||
subAddressIndex: Int,
|
|
||||||
): AccountAddress? {
|
|
||||||
return find { it.accountIndex == accountIndex && it.subAddressIndex == subAddressIndex }
|
|
||||||
}
|
|
||||||
|
@ -1,29 +1,17 @@
|
|||||||
package im.molly.monero
|
package im.molly.monero
|
||||||
|
|
||||||
//import im.molly.monero.proto.LedgerProto
|
|
||||||
|
|
||||||
data class Ledger(
|
data class Ledger(
|
||||||
val publicAddress: PublicAddress,
|
val publicAddress: PublicAddress,
|
||||||
val accountAddresses: Set<AccountAddress>,
|
val indexedAccounts: List<WalletAccount>,
|
||||||
val transactionById: Map<String, Transaction>,
|
val transactionById: Map<String, Transaction>,
|
||||||
val enotes: Set<TimeLocked<Enote>>,
|
val enoteSet: Set<TimeLocked<Enote>>,
|
||||||
val checkedAt: BlockchainTime,
|
val checkedAt: BlockchainTime,
|
||||||
) {
|
) {
|
||||||
val transactions get() = transactionById.values
|
val transactions: Collection<Transaction>
|
||||||
|
get() = transactionById.values
|
||||||
|
|
||||||
val balance: Balance = enotes.calculateBalance()
|
fun getBalance(): Balance = enoteSet.calculateBalance()
|
||||||
|
|
||||||
// companion object {
|
fun getBalanceForAccount(accountIndex: Int): Balance =
|
||||||
// fun fromProto(proto: LedgerProto) = Ledger(
|
enoteSet.calculateBalance { it.accountIndex == accountIndex }
|
||||||
// publicAddress = PublicAddress.base58(proto.publicAddress),
|
|
||||||
// txOuts = proto.ownedTxOutsList.map { OwnedTxOut.fromProto(it) },
|
|
||||||
// checkedAtBlockHeight = proto.blockHeight,
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun proto(): LedgerProto = LedgerProto.newBuilder()
|
|
||||||
// .setPublicAddress(publicAddress.base58)
|
|
||||||
// .addAllOwnedTxOuts(txOuts.map { it.proto() })
|
|
||||||
// .setBlockHeight(checkedAtBlockHeight)
|
|
||||||
// .build()
|
|
||||||
}
|
}
|
||||||
|
@ -27,54 +27,90 @@ class MoneroWallet internal constructor(
|
|||||||
|
|
||||||
var dataStore by storageAdapter::dataStore
|
var dataStore by storageAdapter::dataStore
|
||||||
|
|
||||||
suspend fun addDetachedSubAddress(accountIndex: Int, subAddressIndex: Int): AccountAddress =
|
// suspend fun addDetachedSubAddress(accountIndex: Int, subAddressIndex: Int): AccountAddress =
|
||||||
suspendCancellableCoroutine { continuation ->
|
// suspendCancellableCoroutine { continuation ->
|
||||||
wallet.addDetachedSubAddress(
|
// wallet.addDetachedSubAddress(
|
||||||
accountIndex,
|
// accountIndex,
|
||||||
subAddressIndex,
|
// subAddressIndex,
|
||||||
object : BaseWalletCallbacks() {
|
// object : BaseWalletCallbacks() {
|
||||||
override fun onAddressReady(subAddresses: Array<String>) {
|
// override fun onSubAddressReady(subAddress: String) {
|
||||||
val accountAddress = AccountAddress.parseWithIndexes(subAddresses[0])
|
// continuation.resume(AccountAddress.parseWithIndexes(subAddress)) {}
|
||||||
continuation.resume(accountAddress) {}
|
// }
|
||||||
}
|
// })
|
||||||
})
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun createAccount(): AccountAddress =
|
suspend fun createAccount(): WalletAccount =
|
||||||
suspendCancellableCoroutine { continuation ->
|
suspendCancellableCoroutine { continuation ->
|
||||||
wallet.createAccount(object : BaseWalletCallbacks() {
|
wallet.createAccount(object : BaseWalletCallbacks() {
|
||||||
override fun onAddressReady(subAddresses: Array<String>) {
|
override fun onSubAddressReady(subAddress: String) {
|
||||||
val accountAddress = AccountAddress.parseWithIndexes(subAddresses[0])
|
val primaryAddress = AccountAddress.parseWithIndexes(subAddress)
|
||||||
continuation.resume(accountAddress) {}
|
continuation.resume(
|
||||||
|
WalletAccount(
|
||||||
|
addresses = listOf(primaryAddress),
|
||||||
|
accountIndex = primaryAddress.accountIndex,
|
||||||
|
)
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NoSuchAccountException
|
||||||
|
*/
|
||||||
suspend fun createSubAddressForAccount(accountIndex: Int = 0): AccountAddress =
|
suspend fun createSubAddressForAccount(accountIndex: Int = 0): AccountAddress =
|
||||||
suspendCancellableCoroutine { continuation ->
|
suspendCancellableCoroutine { continuation ->
|
||||||
wallet.createSubAddressForAccount(accountIndex, object : BaseWalletCallbacks() {
|
wallet.createSubAddressForAccount(accountIndex, object : BaseWalletCallbacks() {
|
||||||
override fun onAddressReady(subAddresses: Array<String>) {
|
override fun onSubAddressReady(subAddress: String) {
|
||||||
if (subAddresses.isEmpty()) {
|
continuation.resume(AccountAddress.parseWithIndexes(subAddress)) {}
|
||||||
throw NoSuchAccountException(accountIndex)
|
|
||||||
}
|
|
||||||
val accountAddress = AccountAddress.parseWithIndexes(subAddresses[0])
|
|
||||||
continuation.resume(accountAddress) {}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getAllAddresses(): Set<AccountAddress> =
|
/**
|
||||||
|
* @throws NoSuchAccountException
|
||||||
|
*/
|
||||||
|
suspend fun findUnusedSubAddress(accountIndex: Int = 0): AccountAddress? {
|
||||||
|
val ledger = ledger().first()
|
||||||
|
val transactions = ledger.transactions
|
||||||
|
val account = ledger.indexedAccounts.getOrNull(accountIndex)
|
||||||
|
?: throw NoSuchAccountException(accountIndex)
|
||||||
|
|
||||||
|
return account.addresses.firstOrNull { !it.isAddressUsed(transactions) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NoSuchAccountException
|
||||||
|
*/
|
||||||
|
suspend fun getAccount(accountIndex: Int = 0): WalletAccount =
|
||||||
|
suspendCancellableCoroutine { continuation ->
|
||||||
|
wallet.getAddressesForAccount(accountIndex, object : BaseWalletCallbacks() {
|
||||||
|
override fun onSubAddressListReceived(subAddresses: Array<String>) {
|
||||||
|
val accounts = parseAndAggregateAddresses(subAddresses)
|
||||||
|
continuation.resume(accounts.single()) {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getAllAccounts(): List<WalletAccount> =
|
||||||
suspendCancellableCoroutine { continuation ->
|
suspendCancellableCoroutine { continuation ->
|
||||||
wallet.getAllAddresses(object : BaseWalletCallbacks() {
|
wallet.getAllAddresses(object : BaseWalletCallbacks() {
|
||||||
override fun onAddressReady(subAddresses: Array<String>) {
|
override fun onSubAddressListReceived(subAddresses: Array<String>) {
|
||||||
continuation.resume(subAddresses.toAccountAddresses()) {}
|
val accounts = parseAndAggregateAddresses(subAddresses)
|
||||||
|
continuation.resume(accounts) {}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Array<String>.toAccountAddresses(): Set<AccountAddress> {
|
private fun parseAndAggregateAddresses(subAddresses: Array<String>): List<WalletAccount> =
|
||||||
return map { AccountAddress.parseWithIndexes(it) }.toSet()
|
subAddresses.map { AccountAddress.parseWithIndexes(it) }
|
||||||
|
.groupBy { it.accountIndex }
|
||||||
|
.map { (index, addresses) ->
|
||||||
|
WalletAccount(
|
||||||
|
addresses = addresses,
|
||||||
|
accountIndex = index,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
.sortedBy { it.accountIndex }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [Flow] of ledger changes.
|
* A [Flow] of ledger changes.
|
||||||
@ -88,16 +124,16 @@ class MoneroWallet internal constructor(
|
|||||||
subAddresses: Array<String>,
|
subAddresses: Array<String>,
|
||||||
blockchainTime: BlockchainTime,
|
blockchainTime: BlockchainTime,
|
||||||
) {
|
) {
|
||||||
val accountAddresses = subAddresses.toAccountAddresses()
|
val indexedAccounts = parseAndAggregateAddresses(subAddresses)
|
||||||
val (txById, enotes) = txHistory.consolidateTransactions(
|
val (txById, enotes) = txHistory.consolidateTransactions(
|
||||||
accountAddresses = accountAddresses,
|
accounts = indexedAccounts,
|
||||||
blockchainContext = blockchainTime,
|
blockchainContext = blockchainTime,
|
||||||
)
|
)
|
||||||
val ledger = Ledger(
|
val ledger = Ledger(
|
||||||
publicAddress = publicAddress,
|
publicAddress = publicAddress,
|
||||||
accountAddresses = accountAddresses,
|
indexedAccounts = indexedAccounts,
|
||||||
transactionById = txById,
|
transactionById = txById,
|
||||||
enotes = enotes,
|
enoteSet = enotes,
|
||||||
checkedAt = blockchainTime,
|
checkedAt = blockchainTime,
|
||||||
)
|
)
|
||||||
sendLedger(ledger)
|
sendLedger(ledger)
|
||||||
@ -107,11 +143,10 @@ class MoneroWallet internal constructor(
|
|||||||
sendLedger(lastKnownLedger.copy(checkedAt = blockchainTime))
|
sendLedger(lastKnownLedger.copy(checkedAt = blockchainTime))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAddressCreated(subAddress: String) {
|
override fun onSubAddressListUpdated(subAddresses: Array<String>) {
|
||||||
val addressSet = lastKnownLedger.accountAddresses.toMutableSet()
|
val accountsUpdated = parseAndAggregateAddresses(subAddresses)
|
||||||
val accountAddress = AccountAddress.parseWithIndexes(subAddress)
|
if (lastKnownLedger.indexedAccounts != accountsUpdated) {
|
||||||
if (addressSet.add(accountAddress)) {
|
sendLedger(lastKnownLedger.copy(indexedAccounts = accountsUpdated))
|
||||||
sendLedger(lastKnownLedger.copy(accountAddresses = addressSet))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,12 +240,14 @@ class NoSuchAccountException(private val accountIndex: Int) : NoSuchElementExcep
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract class BaseWalletCallbacks : IWalletCallbacks.Stub() {
|
private abstract class BaseWalletCallbacks : IWalletCallbacks.Stub() {
|
||||||
override fun onAddressReady(subAddresses: Array<String>) = Unit
|
|
||||||
|
|
||||||
override fun onRefreshResult(blockchainTime: BlockchainTime, status: Int) = Unit
|
override fun onRefreshResult(blockchainTime: BlockchainTime, status: Int) = Unit
|
||||||
|
|
||||||
override fun onCommitResult(success: Boolean) = Unit
|
override fun onCommitResult(success: Boolean) = Unit
|
||||||
|
|
||||||
|
override fun onSubAddressReady(subAddress: String) = Unit
|
||||||
|
|
||||||
|
override fun onSubAddressListReceived(subAddresses: Array<String>) = Unit
|
||||||
|
|
||||||
override fun onFeesReceived(fees: LongArray?) = Unit
|
override fun onFeesReceived(fees: LongArray?) = Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ sealed interface PublicAddress : Parcelable {
|
|||||||
fun isSubAddress(): Boolean
|
fun isSubAddress(): Boolean
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
/**
|
||||||
|
* @throws InvalidAddress
|
||||||
|
*/
|
||||||
fun parse(addressString: String): PublicAddress {
|
fun parse(addressString: String): PublicAddress {
|
||||||
val decoded = try {
|
val decoded = try {
|
||||||
addressString.decodeBase58()
|
addressString.decodeBase58()
|
||||||
@ -39,7 +42,9 @@ sealed interface PublicAddress : Parcelable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidAddress(message: String, cause: Throwable? = null) : Exception(message, cause)
|
// TODO: Extend ParseException
|
||||||
|
class InvalidAddress(message: String, cause: Throwable? = null) :
|
||||||
|
IllegalArgumentException(message, cause)
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class StandardAddress(
|
data class StandardAddress(
|
||||||
|
15
lib/android/src/main/kotlin/im/molly/monero/WalletAccount.kt
Normal file
15
lib/android/src/main/kotlin/im/molly/monero/WalletAccount.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package im.molly.monero
|
||||||
|
|
||||||
|
data class WalletAccount(
|
||||||
|
val addresses: List<AccountAddress>,
|
||||||
|
val accountIndex: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun Iterable<WalletAccount>.findAddressByIndex(
|
||||||
|
accountIndex: Int,
|
||||||
|
subAddressIndex: Int = 0,
|
||||||
|
): AccountAddress? {
|
||||||
|
return flatMap { it.addresses }.find {
|
||||||
|
it.accountIndex == accountIndex && it.subAddressIndex == subAddressIndex
|
||||||
|
}
|
||||||
|
}
|
@ -122,10 +122,13 @@ internal class WalletNative private constructor(
|
|||||||
val currentBalance: Balance
|
val currentBalance: Balance
|
||||||
get() = TODO() // txHistorySnapshot().consolidateTransactions().second.balance()
|
get() = TODO() // txHistorySnapshot().consolidateTransactions().second.balance()
|
||||||
|
|
||||||
val subAddresses: Array<String>
|
private fun getSubAddresses(accountIndex: Int? = null): Array<String> {
|
||||||
get() = nativeGetSubAddresses(handle)
|
return nativeGetSubAddresses(accountIndex ?: -1, handle)
|
||||||
|
}
|
||||||
|
|
||||||
private fun txHistorySnapshot(): List<TxInfo> = nativeGetTxHistory(handle).toList()
|
private fun getTxHistorySnapshot(): List<TxInfo> {
|
||||||
|
return nativeGetTxHistory(handle).toList()
|
||||||
|
}
|
||||||
|
|
||||||
@GuardedBy("listenersLock")
|
@GuardedBy("listenersLock")
|
||||||
private val balanceListeners = mutableSetOf<IBalanceListener>()
|
private val balanceListeners = mutableSetOf<IBalanceListener>()
|
||||||
@ -224,9 +227,12 @@ internal class WalletNative private constructor(
|
|||||||
* Also replays the last known balance whenever a new listener registers.
|
* Also replays the last known balance whenever a new listener registers.
|
||||||
*/
|
*/
|
||||||
override fun addBalanceListener(listener: IBalanceListener) {
|
override fun addBalanceListener(listener: IBalanceListener) {
|
||||||
|
val txHistory = getTxHistorySnapshot()
|
||||||
|
val subAddresses = getSubAddresses()
|
||||||
|
|
||||||
balanceListenersLock.withLock {
|
balanceListenersLock.withLock {
|
||||||
balanceListeners.add(listener)
|
balanceListeners.add(listener)
|
||||||
listener.onBalanceChanged(txHistorySnapshot(), subAddresses, currentBlockchainTime)
|
listener.onBalanceChanged(txHistory, subAddresses, currentBlockchainTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,8 +255,8 @@ internal class WalletNative private constructor(
|
|||||||
|
|
||||||
override fun createAccount(callback: IWalletCallbacks?) {
|
override fun createAccount(callback: IWalletCallbacks?) {
|
||||||
scope.launch(ioDispatcher) {
|
scope.launch(ioDispatcher) {
|
||||||
val subAddress = nativeCreateSubAddressAccount(handle)
|
val primaryAddress = nativeCreateSubAddressAccount(handle)
|
||||||
notifyAddressCreation(subAddress, callback)
|
notifyAddressCreation(primaryAddress, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,23 +266,37 @@ internal class WalletNative private constructor(
|
|||||||
if (subAddress != null) {
|
if (subAddress != null) {
|
||||||
notifyAddressCreation(subAddress, callback)
|
notifyAddressCreation(subAddress, callback)
|
||||||
} else {
|
} else {
|
||||||
callback?.onAddressReady(emptyArray())
|
TODO()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notifyAddressCreation(subAddress: String, callback: IWalletCallbacks?) {
|
private fun notifyAddressCreation(subAddress: String, callback: IWalletCallbacks?) {
|
||||||
balanceListenersLock.withLock {
|
balanceListenersLock.withLock {
|
||||||
|
if (balanceListeners.isNotEmpty()) {
|
||||||
|
val subAddresses = getSubAddresses()
|
||||||
balanceListeners.forEach { listener ->
|
balanceListeners.forEach { listener ->
|
||||||
listener.onAddressCreated(subAddress)
|
listener.onSubAddressListUpdated(subAddresses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback?.onSubAddressReady(subAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAddressesForAccount(accountIndex: Int, callback: IWalletCallbacks) {
|
||||||
|
scope.launch(ioDispatcher) {
|
||||||
|
val accountSubAddresses = getSubAddresses(accountIndex)
|
||||||
|
if (accountSubAddresses.isNotEmpty()) {
|
||||||
|
callback.onSubAddressListReceived(accountSubAddresses)
|
||||||
|
} else {
|
||||||
|
TODO()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
callback?.onAddressReady(arrayOf(subAddress))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getAllAddresses(callback: IWalletCallbacks) {
|
override fun getAllAddresses(callback: IWalletCallbacks) {
|
||||||
scope.launch(ioDispatcher) {
|
scope.launch(ioDispatcher) {
|
||||||
callback.onAddressReady(subAddresses)
|
callback.onSubAddressListReceived(getSubAddresses())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,7 +306,8 @@ internal class WalletNative private constructor(
|
|||||||
if (balanceListeners.isNotEmpty()) {
|
if (balanceListeners.isNotEmpty()) {
|
||||||
val blockchainTime = network.blockchainTime(height, timestamp)
|
val blockchainTime = network.blockchainTime(height, timestamp)
|
||||||
val call = if (balanceChanged) {
|
val call = if (balanceChanged) {
|
||||||
val txHistory = txHistorySnapshot()
|
val txHistory = getTxHistorySnapshot()
|
||||||
|
val subAddresses = getSubAddresses()
|
||||||
fun(listener: IBalanceListener) {
|
fun(listener: IBalanceListener) {
|
||||||
listener.onBalanceChanged(txHistory, subAddresses, blockchainTime)
|
listener.onBalanceChanged(txHistory, subAddresses, blockchainTime)
|
||||||
}
|
}
|
||||||
@ -396,7 +417,7 @@ internal class WalletNative private constructor(
|
|||||||
private external fun nativeGetPublicAddress(handle: Long): String
|
private external fun nativeGetPublicAddress(handle: Long): String
|
||||||
private external fun nativeGetCurrentBlockchainHeight(handle: Long): Int
|
private external fun nativeGetCurrentBlockchainHeight(handle: Long): Int
|
||||||
private external fun nativeGetCurrentBlockchainTimestamp(handle: Long): Long
|
private external fun nativeGetCurrentBlockchainTimestamp(handle: Long): Long
|
||||||
private external fun nativeGetSubAddresses(handle: Long): Array<String>
|
private external fun nativeGetSubAddresses(subAddressMajor: Int, handle: Long): Array<String>
|
||||||
private external fun nativeGetTxHistory(handle: Long): Array<TxInfo>
|
private external fun nativeGetTxHistory(handle: Long): Array<TxInfo>
|
||||||
private external fun nativeFetchBaseFeeEstimate(handle: Long): LongArray
|
private external fun nativeFetchBaseFeeEstimate(handle: Long): LongArray
|
||||||
private external fun nativeLoad(handle: Long, fd: Int): Boolean
|
private external fun nativeLoad(handle: Long, fd: Int): Boolean
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package im.molly.monero.internal
|
package im.molly.monero.internal
|
||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import im.molly.monero.AccountAddress
|
|
||||||
import im.molly.monero.BlockHeader
|
import im.molly.monero.BlockHeader
|
||||||
import im.molly.monero.BlockchainTime
|
import im.molly.monero.BlockchainTime
|
||||||
import im.molly.monero.CalledByNative
|
import im.molly.monero.CalledByNative
|
||||||
import im.molly.monero.Enote
|
import im.molly.monero.Enote
|
||||||
import im.molly.monero.HashDigest
|
import im.molly.monero.HashDigest
|
||||||
|
import im.molly.monero.WalletAccount
|
||||||
import im.molly.monero.MoneroAmount
|
import im.molly.monero.MoneroAmount
|
||||||
import im.molly.monero.PaymentDetail
|
import im.molly.monero.PaymentDetail
|
||||||
import im.molly.monero.PublicAddress
|
import im.molly.monero.PublicAddress
|
||||||
@ -15,7 +15,7 @@ import im.molly.monero.TimeLocked
|
|||||||
import im.molly.monero.Transaction
|
import im.molly.monero.Transaction
|
||||||
import im.molly.monero.TxState
|
import im.molly.monero.TxState
|
||||||
import im.molly.monero.UnlockTime
|
import im.molly.monero.UnlockTime
|
||||||
import im.molly.monero.findByIndexes
|
import im.molly.monero.findAddressByIndex
|
||||||
import im.molly.monero.isBlockHeightInRange
|
import im.molly.monero.isBlockHeightInRange
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
@ -62,12 +62,12 @@ internal data class TxInfo @CalledByNative constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun List<TxInfo>.consolidateTransactions(
|
internal fun List<TxInfo>.consolidateTransactions(
|
||||||
accountAddresses: Set<AccountAddress>,
|
accounts: List<WalletAccount>,
|
||||||
blockchainContext: BlockchainTime,
|
blockchainContext: BlockchainTime,
|
||||||
): Pair<Map<String, Transaction>, Set<TimeLocked<Enote>>> {
|
): Pair<Map<String, Transaction>, Set<TimeLocked<Enote>>> {
|
||||||
// Extract enotes from incoming transactions
|
// Extract enotes from incoming transactions
|
||||||
val allEnotes =
|
val allEnotes =
|
||||||
filter { it.incoming }.map { it.toEnote(blockchainContext.height, accountAddresses) }
|
filter { it.incoming }.map { it.toEnote(blockchainContext.height, accounts) }
|
||||||
|
|
||||||
val enoteByTxId = allEnotes.groupBy { enote -> enote.sourceTxId!! }
|
val enoteByTxId = allEnotes.groupBy { enote -> enote.sourceTxId!! }
|
||||||
|
|
||||||
@ -152,9 +152,9 @@ private fun List<TxInfo>.determineTxState(): TxState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TxInfo.toEnote(blockchainHeight: Int, accountAddresses: Set<AccountAddress>): Enote {
|
private fun TxInfo.toEnote(blockchainHeight: Int, accounts: List<WalletAccount>): Enote {
|
||||||
val ownerAddress = accountAddresses.findByIndexes(subAddressMajor, subAddressMinor)
|
val ownerAddress = accounts.findAddressByIndex(subAddressMajor, subAddressMinor)
|
||||||
?: error("Failed to find account address for: $subAddressMajor/$subAddressMinor")
|
?: error("Failed to find subaddress: $subAddressMajor/$subAddressMinor")
|
||||||
val calculatedAge = if (height > 0) (blockchainHeight - height + 1) else 0
|
val calculatedAge = if (height > 0) (blockchainHeight - height + 1) else 0
|
||||||
|
|
||||||
return Enote(
|
return Enote(
|
||||||
|
Loading…
Reference in New Issue
Block a user