Skip to content

Commit

Permalink
feat(staking): AND-6774 Deposit from PKW (#4122)
Browse files Browse the repository at this point in the history
  • Loading branch information
dserrano-bc committed Nov 21, 2022
1 parent b518e93 commit 501fb70
Show file tree
Hide file tree
Showing 15 changed files with 360 additions and 39 deletions.
Expand Up @@ -217,7 +217,10 @@ class TransactionInteractor(
AssetAction.InterestDeposit -> {
require(targetAccount is InterestAccount)
require(targetAccount is CryptoAccount)
coincore.walletsWithActions(actions = setOf(action), sorter = defaultAccountsSorting.sorter()).map {
coincore.walletsWithActions(
actions = setOf(action),
sorter = defaultAccountsSorting.sorter()
).map {
it.filter { acc ->
acc is CryptoAccount &&
acc.currency == targetAccount.currency &&
Expand All @@ -229,7 +232,10 @@ class TransactionInteractor(
AssetAction.StakingDeposit -> {
require(targetAccount is StakingAccount)
require(targetAccount is CryptoAccount)
coincore.walletsWithActions(actions = setOf(action), sorter = defaultAccountsSorting.sorter()).map {
coincore.walletsWithActions(
actions = setOf(action),
sorter = defaultAccountsSorting.sorter()
).map {
it.filter { acc ->
acc is CryptoAccount &&
acc.currency == targetAccount.currency &&
Expand Down
@@ -1,5 +1,6 @@
package com.blockchain.api.staking

import com.blockchain.api.staking.data.StakingAddressDto
import com.blockchain.api.staking.data.StakingBalanceDto
import com.blockchain.api.staking.data.StakingEligibilityDto
import com.blockchain.api.staking.data.StakingLimitsMapDto
Expand Down Expand Up @@ -29,4 +30,9 @@ internal interface StakingApi {
@Query("currency") fiatTicker: String,
@Query("product") product: String = "STAKING"
): Outcome<Exception, StakingLimitsMapDto>

@GET("payments/accounts/staking")
suspend fun getAddress(
@Query("ccy") cryptoCurrencyTicker: String
): Outcome<Exception, StakingAddressDto>
}
@@ -1,5 +1,6 @@
package com.blockchain.api.staking

import com.blockchain.api.staking.data.StakingAddressDto
import com.blockchain.api.staking.data.StakingBalanceDto
import com.blockchain.api.staking.data.StakingEligibilityDto
import com.blockchain.api.staking.data.StakingLimitsMapDto
Expand All @@ -25,4 +26,7 @@ class StakingApiService internal constructor(

suspend fun getStakingLimits(fiatTicker: String): Outcome<Exception, StakingLimitsMapDto> =
stakingApi.getTickerLimits(null, fiatTicker)

suspend fun getAccountAddress(cryptoTicker: String): Outcome<Exception, StakingAddressDto> =
stakingApi.getAddress(cryptoTicker)
}
@@ -0,0 +1,10 @@
package com.blockchain.api.staking.data

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class StakingAddressDto(
@SerialName("accountRef")
val address: String
)
4 changes: 4 additions & 0 deletions coincore/src/main/java/com/blockchain/coincore/Coincore.kt
Expand Up @@ -6,6 +6,7 @@ import com.blockchain.coincore.impl.AllCustodialWalletsAccount
import com.blockchain.coincore.impl.AllNonCustodialWalletsAccount
import com.blockchain.coincore.impl.AllWalletsAccount
import com.blockchain.coincore.impl.CustodialInterestAccount
import com.blockchain.coincore.impl.CustodialStakingAccount
import com.blockchain.coincore.impl.CustodialTradingAccount
import com.blockchain.coincore.impl.TxProcessorFactory
import com.blockchain.coincore.loader.AssetCatalogueImpl
Expand Down Expand Up @@ -193,6 +194,9 @@ class Coincore internal constructor(
AssetAction.InterestDeposit -> sameCurrencyTransactionTargets.map {
it.filterIsInstance<CustodialInterestAccount>()
}
AssetAction.StakingDeposit -> sameCurrencyTransactionTargets.map {
it.filterIsInstance<CustodialStakingAccount>()
}
AssetAction.InterestWithdraw -> sameCurrencyTransactionTargets.map {
it.filterIsInstance<CustodialTradingAccount>()
}
Expand Down
Expand Up @@ -11,6 +11,7 @@ import com.blockchain.coincore.InterestAccount
import com.blockchain.coincore.NonCustodialAccount
import com.blockchain.coincore.SingleAccount
import com.blockchain.coincore.SingleAccountList
import com.blockchain.coincore.StakingAccount
import com.blockchain.coincore.TradingAccount
import com.blockchain.core.custodial.domain.TradingService
import com.blockchain.core.interest.domain.InterestService
Expand Down Expand Up @@ -189,7 +190,8 @@ internal abstract class CryptoAssetBase : CryptoAsset, AccountRefreshTrigger, Ko
exchangeRates = exchangeRates,
internalAccountLabel = labels.getDefaultTradingWalletLabel(),
identity = identity,
kycService = kycService
kycService = kycService,
custodialWalletManager = custodialManager
)
)
} else {
Expand Down Expand Up @@ -295,7 +297,7 @@ internal abstract class CryptoAssetBase : CryptoAsset, AccountRefreshTrigger, Ko
}
}

private fun getCustodialTargets(): Maybe<SingleAccountList> =
private fun getTradingTargets(): Maybe<SingleAccountList> =
accountGroup(AssetFilter.Trading)
.map { it.accounts }
.onErrorComplete()
Expand Down Expand Up @@ -333,17 +335,22 @@ internal abstract class CryptoAssetBase : CryptoAsset, AccountRefreshTrigger, Ko
listOf(
getPitLinkingTargets(),
getInterestTargets(),
getCustodialTargets(),
getNonCustodialTargets(exclude = account)
getTradingTargets(),
getNonCustodialTargets(exclude = account),
getStakingTargets()
)
).toList()
.map { ll -> ll.flatten() }
.onErrorReturnItem(emptyList())
is InterestAccount -> {
getCustodialTargets()
getTradingTargets()
.onErrorReturnItem(emptyList())
.defaultIfEmpty(emptyList())
}
is StakingAccount ->
getTradingTargets()
.onErrorReturnItem(emptyList())
.defaultIfEmpty(emptyList())
else -> Single.just(emptyList())
}
}
Expand Down
Expand Up @@ -132,7 +132,7 @@ abstract class CryptoAccountBase : CryptoAccount {
)

internal val defaultCustodialActions = defaultNonCustodialActions + setOf(
AssetAction.Sell, AssetAction.Buy, AssetAction.InterestWithdraw
AssetAction.Buy, AssetAction.InterestWithdraw
)
internal val defaultActions = (defaultNonCustodialActions + defaultCustodialActions).toSet()
}
Expand Down Expand Up @@ -346,13 +346,12 @@ abstract class CryptoNonCustodialAccount(
AssetAction.Swap -> swapActionEligibility(isActiveAndFunded)
AssetAction.Sell -> sellActionEligibility(isActiveAndFunded)
AssetAction.InterestDeposit -> interestDepositActionEligibility(isActiveAndFunded)
AssetAction.StakingDeposit -> stakingDepositEligibility(isActiveAndFunded)
AssetAction.ViewStatement,
AssetAction.Buy,
AssetAction.FiatWithdraw,
AssetAction.InterestWithdraw,
AssetAction.FiatDeposit,
// TODO(dserrano) - STAKING - re-enable this
AssetAction.StakingDeposit,
AssetAction.Sign -> Single.just(StateAwareAction(ActionState.Unavailable, this))
}
}
Expand Down Expand Up @@ -396,6 +395,26 @@ abstract class CryptoNonCustodialAccount(
}
}

private fun stakingDepositEligibility(activeAndFunded: Boolean): Single<StateAwareAction> {
val depositCryptoEligibility = identity.userAccessForFeature(Feature.DepositCrypto)
val stakingDepositEligibility = identity.userAccessForFeature(Feature.DepositStaking)

return Single.zip(
depositCryptoEligibility,
stakingDepositEligibility
) { depositCryptoEligible, stakingDepositEligible ->
StateAwareAction(
when {
depositCryptoEligible is FeatureAccess.Blocked -> depositCryptoEligible.toActionState()
stakingDepositEligible is FeatureAccess.Blocked -> stakingDepositEligible.toActionState()
!activeAndFunded -> ActionState.LockedForBalance
else -> ActionState.Available
},
AssetAction.StakingDeposit
)
}
}

private fun swapActionEligibility(activeAndFunded: Boolean): Single<StateAwareAction> {
val swapEligibility = identity.userAccessForFeature(Feature.Swap)
val assetAvailableForSwap = custodialWalletManager.isAssetSupportedForSwapLegacy(currency)
Expand Down
Expand Up @@ -20,20 +20,26 @@ import com.blockchain.core.price.ExchangeRatesDataManager
import com.blockchain.core.staking.domain.StakingActivity
import com.blockchain.core.staking.domain.StakingService
import com.blockchain.core.staking.domain.StakingState
import com.blockchain.data.DataResource
import com.blockchain.data.FreshnessStrategy
import com.blockchain.extensions.exhaustive
import com.blockchain.nabu.Feature
import com.blockchain.nabu.FeatureAccess
import com.blockchain.nabu.UserIdentity
import com.blockchain.nabu.datamanagers.CustodialWalletManager
import com.blockchain.nabu.datamanagers.Product
import com.blockchain.nabu.datamanagers.TransferDirection
import com.blockchain.store.asObservable
import com.blockchain.store.asSingle
import com.blockchain.utils.mapList
import info.blockchain.balance.AssetInfo
import info.blockchain.balance.CryptoValue
import io.reactivex.rxjava3.core.Completable
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.core.Single
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.rx3.rxSingle

class CustodialStakingAccount(
override val currency: AssetInfo,
Expand All @@ -42,28 +48,32 @@ class CustodialStakingAccount(
private val stakingService: StakingService,
override val exchangeRates: ExchangeRatesDataManager,
private val identity: UserIdentity,
private val kycService: KycService
private val kycService: KycService,
private val custodialWalletManager: CustodialWalletManager
) : CryptoAccountBase(), StakingAccount {

override val baseActions: Set<AssetAction> = emptySet() // Not used by this class

private val hasFunds = AtomicBoolean(false)

override val receiveAddress: Single<ReceiveAddress>
get() = Single.error(NotImplementedError())

// TODO(dserrano) - STAKING - add deposit & withdraw
/*stakingService.getAddress(currency).map { address ->
makeExternalAssetAddress(
asset = currency,
address = address,
label = label,
postTransactions = onTxCompleted
)
}*/
get() = rxSingle { stakingService.getAccountAddress(currency) }.map {
when (it) {
is DataResource.Data -> {
makeExternalAssetAddress(
asset = currency,
address = it.data,
label = label,
postTransactions = onTxCompleted
)
}
is DataResource.Error,
DataResource.Loading -> throw IllegalStateException()
}
}

override val onTxCompleted: (TxResult) -> Completable
get() = { Completable.error(NotImplementedError()) } /*{ txResult ->
get() = { txResult ->
require(txResult.amount is CryptoValue)
require(txResult is TxResult.HashedTxResult)
receiveAddress.flatMapCompletable { receiveAddress ->
Expand All @@ -72,10 +82,10 @@ class CustodialStakingAccount(
address = receiveAddress.address,
hash = txResult.txId,
amount = txResult.amount,
product = Product.SAVINGS
product = Product.STAKING
)
}
}*/
}

override val directions: Set<TransferDirection>
get() = emptySet()
Expand Down
Expand Up @@ -251,8 +251,8 @@ class CustodialTradingAccount(
}
}

private fun stakingDepositEligibility(balance: AccountBalance): Single<StateAwareAction> {
return identity.userAccessForFeature(Feature.DepositStaking).map { access ->
private fun stakingDepositEligibility(balance: AccountBalance): Single<StateAwareAction> =
identity.userAccessForFeature(Feature.DepositStaking).map { access ->
StateAwareAction(
when {
access is FeatureAccess.Blocked -> access.toActionState()
Expand All @@ -262,7 +262,6 @@ class CustodialTradingAccount(
AssetAction.StakingDeposit
)
}
}

private fun sendEligibility(balance: AccountBalance): Single<StateAwareAction> {
return Single.just(
Expand Down
Expand Up @@ -32,6 +32,7 @@ import com.blockchain.coincore.impl.txEngine.interest.InterestWithdrawOnChainTxE
import com.blockchain.coincore.impl.txEngine.interest.InterestWithdrawTradingTxEngine
import com.blockchain.coincore.impl.txEngine.sell.OnChainSellTxEngine
import com.blockchain.coincore.impl.txEngine.sell.TradingSellTxEngine
import com.blockchain.coincore.impl.txEngine.staking.StakingDepositOnChainTxEngine
import com.blockchain.coincore.impl.txEngine.staking.StakingDepositTradingEngine
import com.blockchain.coincore.impl.txEngine.swap.OnChainSwapTxEngine
import com.blockchain.coincore.impl.txEngine.swap.TradingToTradingSwapTxEngine
Expand Down Expand Up @@ -228,7 +229,6 @@ class TxProcessorFactory(
)
)
)

is CustodialInterestAccount ->
target.receiveAddress
.map {
Expand All @@ -244,6 +244,21 @@ class TxProcessorFactory(
)
)
}
is CustodialStakingAccount ->
target.receiveAddress
.map {
TransactionProcessor(
exchangeRates = exchangeRates,
sourceAccount = source,
txTarget = it,
engine = StakingDepositOnChainTxEngine(
stakingBalanceStore = stakingBalanceStore,
stakingService = stakingService,
walletManager = walletManager,
onChainEngine = engine
)
)
}
is CryptoAddress -> Single.just(
TransactionProcessor(
exchangeRates = exchangeRates,
Expand Down

0 comments on commit 501fb70

Please sign in to comment.