Skip to content

Commit

Permalink
Merge branch 'epic/pera-798-common-dev-update' into PERA-1353-bottoms…
Browse files Browse the repository at this point in the history
…heet-hd-wallet
  • Loading branch information
michaeltchuang authored Feb 28, 2025
2 parents 29d7bed + 178eb79 commit f504aa2
Show file tree
Hide file tree
Showing 23 changed files with 349 additions and 54 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/android-app-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,32 @@ jobs:
path: ./**/build/reports/**
overwrite: true

android-test-coverage:
name: "Android common-sdk Test Coverage"
runs-on: ubuntu-latest
steps:
- name: "Checkout"
uses: actions/checkout@v4
- name: "Install JDK 21"
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "21"
cache: "gradle"
- name: "Update packages"
run: sudo apt-get update
- name: "Install xmlstarlet"
run: sudo apt-get install xmlstarlet
- name: "Generate Report and Validate Coverage"
run: ./common-sdk/test-coverage/testCoverage.sh
- name: "Archive Test Coverage Results"
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: "test-coverage-result"
path: ./common-sdk/build/reports/kover/**
overwrite: true

android-bundle-publish-test:
name: "Android App Bundle Publish Test"
runs-on: ubuntu-latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
package com.algorand.android.modules.accounts.di

import com.algorand.android.modules.accounts.domain.usecase.GetAuthAddressOfAnAccount
import com.algorand.android.modules.accounts.domain.usecase.IsSenderRekeyedToAnotherAccount
import com.algorand.android.usecase.AccountDetailUseCase
import dagger.Module
import dagger.Provides
Expand All @@ -25,12 +24,6 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object AccountsModule {

@Provides
@Singleton
fun provideIsSenderRekeyedToAnotherAccount(
useCase: AccountDetailUseCase
): IsSenderRekeyedToAnotherAccount = IsSenderRekeyedToAnotherAccount(useCase::isAccountRekeyed)

@Provides
@Singleton
fun provideGetAuthAddressOfAnAccount(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,24 @@ import com.algorand.android.models.TransactionParams
import com.algorand.android.modules.assetinbox.detail.receivedetail.domain.model.Arc59ClaimTransactionPayload
import com.algorand.android.modules.assetinbox.detail.receivedetail.domain.model.BaseArc59ClaimRejectTransaction.Arc59ClaimTransaction
import com.algorand.android.repository.TransactionsRepository
import com.algorand.android.usecase.AccountDetailUseCase
import com.algorand.android.usecase.IsOnTestnetUseCase
import com.algorand.android.utils.toSuggestedParams
import com.algorand.wallet.account.detail.domain.usecase.IsAccountRekeyedToAnotherAccount
import com.algorand.wallet.account.info.domain.usecase.IsAssetOwnedByAccount
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAdminAddress
import javax.inject.Inject

class CreateArc59ClaimTransactionUseCase @Inject constructor(
private val accountDetailUseCase: AccountDetailUseCase,
private val transactionsRepository: TransactionsRepository,
private val isOnTestnetUseCase: IsOnTestnetUseCase
private val isOnTestnetUseCase: IsOnTestnetUseCase,
private val getAccountRekeyAdminAddress: GetAccountRekeyAdminAddress,
private val isAccountRekeyedToAnotherAccount: IsAccountRekeyedToAnotherAccount,
private val isAssetOwnedByAccount: IsAssetOwnedByAccount
) : CreateArc59ClaimTransaction {

override suspend fun invoke(payload: Arc59ClaimTransactionPayload): Result<List<Arc59ClaimTransaction>> {
val isAccountRekeyed = accountDetailUseCase.isAccountRekeyed(payload.receiverAddress)
val authAddress = accountDetailUseCase.getAuthAddress(payload.receiverAddress)
val authAddress = getAccountRekeyAdminAddress(payload.receiverAddress)
val isAccountRekeyed = isAccountRekeyedToAnotherAccount(payload.receiverAddress)
return transactionsRepository.getTransactionParams().map { transactionParams ->
createTransactions(payload, transactionParams).map { transactionByteArray ->
Arc59ClaimTransaction(
Expand All @@ -46,11 +50,7 @@ class CreateArc59ClaimTransactionUseCase @Inject constructor(
}
}

private fun isReceiverOptedInToAsset(address: String, assetId: Long): Boolean {
return accountDetailUseCase.getCachedAccountDetail(address)?.data?.accountInformation?.hasAsset(assetId) == true
}

private fun createTransactions(
private suspend fun createTransactions(
payload: Arc59ClaimTransactionPayload,
transactionParams: TransactionParams
): List<ByteArray> {
Expand All @@ -63,7 +63,7 @@ class CreateArc59ClaimTransactionUseCase @Inject constructor(
appID,
assetId,
transactionParams.toSuggestedParams(),
isReceiverOptedInToAsset(payload.receiverAddress, assetId),
isAssetOwnedByAccount(payload.receiverAddress, assetId),
payload.isClaimingAlgo
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,22 @@ import com.algorand.android.models.TransactionParams
import com.algorand.android.modules.assetinbox.detail.receivedetail.domain.model.Arc59RejectTransactionPayload
import com.algorand.android.modules.assetinbox.detail.receivedetail.domain.model.BaseArc59ClaimRejectTransaction.Arc59RejectTransaction
import com.algorand.android.repository.TransactionsRepository
import com.algorand.android.usecase.AccountDetailUseCase
import com.algorand.android.usecase.IsOnTestnetUseCase
import com.algorand.android.utils.toSuggestedParams
import com.algorand.wallet.account.detail.domain.usecase.IsAccountRekeyedToAnotherAccount
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAdminAddress
import javax.inject.Inject

class CreateArc59RejectTransactionUseCase @Inject constructor(
private val accountDetailUseCase: AccountDetailUseCase,
private val transactionsRepository: TransactionsRepository,
private val isOnTestnetUseCase: IsOnTestnetUseCase
private val isOnTestnetUseCase: IsOnTestnetUseCase,
private val isAccountRekeyedToAnotherAccount: IsAccountRekeyedToAnotherAccount,
private val getAccountRekeyAdminAddress: GetAccountRekeyAdminAddress
) : CreateArc59RejectTransaction {

override suspend fun invoke(payload: Arc59RejectTransactionPayload): Result<List<Arc59RejectTransaction>> {
val isAccountRekeyed = accountDetailUseCase.isAccountRekeyed(payload.receiverAddress)
val authAddress = accountDetailUseCase.getAuthAddress(payload.receiverAddress)
val isAccountRekeyed = isAccountRekeyedToAnotherAccount(payload.receiverAddress)
val authAddress = getAccountRekeyAdminAddress(payload.receiverAddress)
return transactionsRepository.getTransactionParams().map { transactionParams ->
createTransactions(payload, transactionParams).map { transactionByteArray ->
Arc59RejectTransaction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.algorand.android.modules.assetinbox.send.domain.model.Arc59SendTransa
import com.algorand.android.modules.assetinbox.send.domain.model.Arc59TransactionPayload

interface CreateArc59SendTransaction {
operator fun invoke(
suspend operator fun invoke(
txnParams: TransactionParams,
payload: Arc59TransactionPayload
): List<Arc59SendTransaction>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,25 @@ import com.algorand.android.BuildConfig
import com.algorand.android.models.TransactionParams
import com.algorand.android.modules.assetinbox.send.domain.model.Arc59SendTransaction
import com.algorand.android.modules.assetinbox.send.domain.model.Arc59TransactionPayload
import com.algorand.android.usecase.AccountDetailUseCase
import com.algorand.android.usecase.IsOnTestnetUseCase
import com.algorand.android.utils.toSuggestedParams
import com.algorand.android.utils.toUint64
import com.algorand.wallet.account.detail.domain.usecase.IsAccountRekeyedToAnotherAccount
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAdminAddress
import javax.inject.Inject

class CreateArc59SendTransactionUseCase @Inject constructor(
private val accountDetailUseCase: AccountDetailUseCase,
private val isOnTestnetUseCase: IsOnTestnetUseCase
private val isOnTestnetUseCase: IsOnTestnetUseCase,
private val isAccountRekeyedToAnotherAccount: IsAccountRekeyedToAnotherAccount,
private val getAccountRekeyAdminAddress: GetAccountRekeyAdminAddress
) : CreateArc59SendTransaction {

override fun invoke(txnParams: TransactionParams, payload: Arc59TransactionPayload): List<Arc59SendTransaction> {
val senderAuthAddress = accountDetailUseCase.getAuthAddress(payload.senderAddress)
val isSenderRekeyedToAnotherAccount = accountDetailUseCase.isAccountRekeyed(payload.senderAddress)
override suspend fun invoke(
txnParams: TransactionParams,
payload: Arc59TransactionPayload
): List<Arc59SendTransaction> {
val senderAuthAddress = getAccountRekeyAdminAddress(payload.senderAddress)
val isSenderRekeyedToAnotherAccount = isAccountRekeyedToAnotherAccount(payload.senderAddress)
val transactions = txnParams.createTransactions(payload)
return transactions.map { transactionByteArray ->
Arc59SendTransaction(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class CreateArc59TransactionsUseCase @Inject constructor(
)
}

private fun TransactionParams.createArc59Transactions(
private suspend fun TransactionParams.createArc59Transactions(
payload: Arc59TransactionPayload
): Result<List<Arc59SendTransaction>> {
val sendTransactions = createArc59SendTransaction(this, payload) ?: emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ import com.algorand.android.models.Result.Error
import com.algorand.android.models.Result.Success
import com.algorand.android.models.TransactionParams
import com.algorand.android.modules.accounts.domain.usecase.GetAuthAddressOfAnAccount
import com.algorand.android.modules.accounts.domain.usecase.IsSenderRekeyedToAnotherAccount
import com.algorand.android.modules.algosdk.domain.model.OfflineKeyRegTransactionPayload
import com.algorand.android.modules.algosdk.domain.model.OnlineKeyRegTransactionPayload
import com.algorand.android.modules.algosdk.domain.usecase.BuildKeyRegOfflineTransaction
import com.algorand.android.modules.algosdk.domain.usecase.BuildKeyRegOnlineTransaction
import com.algorand.android.modules.keyreg.domain.model.KeyRegTransaction
import com.algorand.android.modules.keyreg.ui.model.KeyRegTransactionDetail
import com.algorand.android.modules.transaction.domain.GetTransactionParams
import com.algorand.wallet.account.detail.domain.usecase.IsAccountRekeyedToAnotherAccount
import javax.inject.Inject

fun interface CreateKeyRegTransaction {
suspend operator fun invoke(txnDetail: KeyRegTransactionDetail): Result<KeyRegTransaction>
}

internal class CreateKeyRegTransactionUseCase @Inject constructor(
private val isSenderRekeyedToAnotherAccount: IsSenderRekeyedToAnotherAccount,
private val isAccountRekeyedToAnotherAccount: IsAccountRekeyedToAnotherAccount,
private val getAuthAddressOfAnAccount: GetAuthAddressOfAnAccount,
private val getTransactionParams: GetTransactionParams,
private val buildKeyRegOfflineTransaction: BuildKeyRegOfflineTransaction,
Expand Down Expand Up @@ -76,15 +76,15 @@ internal class CreateKeyRegTransactionUseCase @Inject constructor(
}
}

private fun createKeyRegTransactionResult(
private suspend fun createKeyRegTransactionResult(
txnDetail: KeyRegTransactionDetail,
txnByteArray: ByteArray
): KeyRegTransaction {
return KeyRegTransaction(
transactionByteArray = txnByteArray,
accountAddress = txnDetail.address,
accountAuthAddress = getAuthAddressOfAnAccount(txnDetail.address),
isRekeyedToAnotherAccount = isSenderRekeyedToAnotherAccount(txnDetail.address)
isRekeyedToAnotherAccount = isAccountRekeyedToAnotherAccount(txnDetail.address)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.algorand.android.modules.swap.confirmswap.domain.model.UnsignedSwapSi
import com.algorand.android.usecase.NetworkSlugUseCase
import com.algorand.android.utils.DataResource
import com.algorand.android.utils.decodeBase64
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAuthAddress
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAdminAddress
import javax.inject.Inject
import javax.inject.Named
import kotlinx.coroutines.flow.collectLatest
Expand All @@ -37,7 +37,7 @@ class CreateSwapQuoteTransactionsUseCase @Inject constructor(
private val swapTransactionItemFactory: SwapTransactionItemFactory,
private val networkSlugUseCase: NetworkSlugUseCase,
private val parseTransactionMsgPackUseCase: ParseTransactionMsgPackUseCase,
private val getAccountRekeyAuthAddress: GetAccountRekeyAuthAddress
private val getAccountRekeyAdminAddress: GetAccountRekeyAdminAddress
) {

suspend fun createQuoteTransactions(
Expand Down Expand Up @@ -111,7 +111,7 @@ class CreateSwapQuoteTransactionsUseCase @Inject constructor(
transactionListIndex = index,
transactionMsgPack = unsignedTransaction,
accountAddress = accountAddress,
accountAuthAddress = getAccountRekeyAuthAddress(accountAddress),
accountAuthAddress = getAccountRekeyAdminAddress(accountAddress),
rawTransaction = unsignedTransaction?.decodeBase64()
?.run { parseTransactionMsgPackUseCase.parse(this) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import com.algorand.android.models.AccountDetail
import com.algorand.android.repository.AccountRepository
import com.algorand.android.utils.CacheResult
import com.algorand.android.utils.exceptions.AccountNotFoundException
import com.algorand.android.utils.isRekeyedToAnotherAccount
import com.algorand.android.utils.recordException
import com.algorand.android.utils.toShortenedAddress
import java.math.BigInteger
Expand All @@ -46,7 +45,7 @@ class AccountDetailUseCase @Inject constructor(
.distinctUntilChanged()
}

fun getCachedAccountDetails() = getAccountDetailCacheFlow().value.values
private fun getCachedAccountDetails() = getAccountDetailCacheFlow().value.values

fun getCachedAccountDetail(publicKey: String): CacheResult<AccountDetail>? {
return accountRepository.getCachedAccountDetail(publicKey)
Expand Down Expand Up @@ -101,7 +100,7 @@ class AccountDetailUseCase @Inject constructor(
}
}

fun isAuthAccountInDevice(accountAddress: String): Boolean {
private fun isAuthAccountInDevice(accountAddress: String): Boolean {
val accountAuthAddress = getAuthAddress(accountAddress) ?: return false
val authAccountDetail = getCachedAccountDetail(accountAuthAddress)?.data ?: return false
return canAccountSignTransaction(authAccountDetail.account.address)
Expand All @@ -127,14 +126,6 @@ class AccountDetailUseCase @Inject constructor(
return accountInformation?.rekeyAdminAddress
}

fun isAccountRekeyed(publicKey: String): Boolean {
val authAddress = accountRepository.getCachedAccountDetail(publicKey)
?.data
?.accountInformation
?.rekeyAdminAddress
return isRekeyedToAnotherAccount(authAddress, publicKey)
}

fun isThereAnyAccountWithPublicKey(publicKey: String): Boolean {
return accountManager.isThereAnyAccountWithPublicKey(publicKey)
}
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ buildscript {
classpath(libs.ksp.gradle.plugin)
classpath(libs.navigation.safe.args.gradle.plugin)
classpath(libs.firebase.perf.plugin)
classpath(libs.kover.plugin)

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down
2 changes: 2 additions & 0 deletions common-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ plugins {
id("dagger.hilt.android.plugin")
}

apply(from = "./test-coverage/kover.gradle")

android {
namespace = "com.algorand.wallet"
compileSdk = libs.versions.android.compileSdk.get().toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import com.algorand.wallet.account.detail.domain.usecase.GetAccountsDetails
import com.algorand.wallet.account.detail.domain.usecase.GetAccountsDetailsUseCase
import com.algorand.wallet.account.detail.domain.usecase.GetLocalRekeyedAccountCount
import com.algorand.wallet.account.detail.domain.usecase.GetLocalRekeyedAccountCountUseCase
import com.algorand.wallet.account.detail.domain.usecase.IsAccountRekeyedToAnotherAccount
import com.algorand.wallet.account.detail.domain.usecase.IsAccountRekeyedToAnotherAccountUseCase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand Down Expand Up @@ -51,5 +53,12 @@ internal object AccountDetailModule {
fun provideGetAccountDetail(useCase: GetAccountDetailUseCase): GetAccountDetail = useCase

@Provides
fun provideGetRekeyedAccountCount(useCase: GetLocalRekeyedAccountCountUseCase): GetLocalRekeyedAccountCount = useCase
fun provideGetRekeyedAccountCount(
useCase: GetLocalRekeyedAccountCountUseCase
): GetLocalRekeyedAccountCount = useCase

@Provides
fun provideIsAccountRekeyedToAnotherAccount(
useCase: IsAccountRekeyedToAnotherAccountUseCase
): IsAccountRekeyedToAnotherAccount = useCase
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@ fun interface GetAccountsDetails {
fun interface GetLocalRekeyedAccountCount {
suspend operator fun invoke(authAddress: String): Int
}

fun interface IsAccountRekeyedToAnotherAccount {
suspend operator fun invoke(address: String): Boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2022 Pera Wallet, LDA
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/

package com.algorand.wallet.account.detail.domain.usecase

import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAdminAddress
import javax.inject.Inject

internal class IsAccountRekeyedToAnotherAccountUseCase @Inject constructor(
private val getAccountRekeyAdminAddress: GetAccountRekeyAdminAddress
) : IsAccountRekeyedToAnotherAccount {

override suspend fun invoke(address: String): Boolean {
val adminAddress = getAccountRekeyAdminAddress(address)
return !adminAddress.isNullOrBlank() && adminAddress != address
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import com.algorand.wallet.account.info.domain.usecase.GetAccountDetailCacheStat
import com.algorand.wallet.account.info.domain.usecase.GetAccountDetailCacheStatusFlowUseCase
import com.algorand.wallet.account.info.domain.usecase.GetAccountInformation
import com.algorand.wallet.account.info.domain.usecase.GetAccountInformationFlow
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAuthAddress
import com.algorand.wallet.account.info.domain.usecase.GetAccountRekeyAdminAddress
import com.algorand.wallet.account.info.domain.usecase.GetAllAccountInformationFlow
import com.algorand.wallet.account.info.domain.usecase.GetAllAssetHoldingIds
import com.algorand.wallet.account.info.domain.usecase.GetAllFailedCachedAccountAddresses
Expand Down Expand Up @@ -284,9 +284,9 @@ internal object AccountInformationModule {
}

@Provides
fun provideGetAccountRekeyAuthAddress(
fun provideGetAccountRekeyAdminAddress(
repository: AccountInformationRepository
): GetAccountRekeyAuthAddress = GetAccountRekeyAuthAddress(repository::getRekeyAuthAddress)
): GetAccountRekeyAdminAddress = GetAccountRekeyAdminAddress(repository::getRekeyAuthAddress)

@Provides
fun provideGetAccountAssetHoldingsFlow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,6 @@ fun interface IsAccountCachedSuccessfully {
suspend operator fun invoke(address: String): Boolean
}

fun interface GetAccountRekeyAuthAddress {
fun interface GetAccountRekeyAdminAddress {
suspend operator fun invoke(address: String): String?
}
Loading

0 comments on commit f504aa2

Please sign in to comment.