Updated on 2026-08-14
This commit is contained in:
parent
81a7b760ae
commit
7a1b1ff5a3
15 changed files with 62 additions and 52 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.tap.network.auth
|
package com.tangem.tap.network.auth
|
||||||
|
|
||||||
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
|
||||||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||||
|
|
||||||
internal class DefaultP2PEthPoolAuthProvider(
|
internal class DefaultP2PEthPoolAuthProvider(
|
||||||
|
|
@ -11,6 +12,6 @@ internal class DefaultP2PEthPoolAuthProvider(
|
||||||
val keys = environmentConfigStorage.getConfigSync().p2pApiKey
|
val keys = environmentConfigStorage.getConfigSync().p2pApiKey
|
||||||
?: error("No P2P api keys provided")
|
?: error("No P2P api keys provided")
|
||||||
|
|
||||||
return keys.mainnet
|
return if (P2PStakingConfig.USE_TESTNET) keys.hoodi else keys.mainnet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package com.tangem.datasource.api.common.config
|
package com.tangem.datasource.api.common.config
|
||||||
|
|
||||||
import com.tangem.datasource.BuildConfig
|
import com.tangem.datasource.BuildConfig
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
|
||||||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||||
import com.tangem.utils.ProviderSuspend
|
import com.tangem.utils.ProviderSuspend
|
||||||
|
|
||||||
|
|
@ -22,12 +23,7 @@ internal class P2PEthPool(
|
||||||
private fun getInitialEnvironment(): ApiEnvironment {
|
private fun getInitialEnvironment(): ApiEnvironment {
|
||||||
return when (BuildConfig.BUILD_TYPE) {
|
return when (BuildConfig.BUILD_TYPE) {
|
||||||
MOCKED_BUILD_TYPE -> ApiEnvironment.MOCK
|
MOCKED_BUILD_TYPE -> ApiEnvironment.MOCK
|
||||||
DEBUG_BUILD_TYPE,
|
else -> if (P2PStakingConfig.USE_TESTNET) ApiEnvironment.DEV else ApiEnvironment.PROD
|
||||||
INTERNAL_BUILD_TYPE,
|
|
||||||
EXTERNAL_BUILD_TYPE,
|
|
||||||
RELEASE_BUILD_TYPE,
|
|
||||||
-> ApiEnvironment.PROD
|
|
||||||
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,7 @@ interface P2PEthPoolApi {
|
||||||
* @param network Ethereum pool network: "mainnet" or "hoodi" (testnet)
|
* @param network Ethereum pool network: "mainnet" or "hoodi" (testnet)
|
||||||
*/
|
*/
|
||||||
@GET("api/v1/staking/pool/{network}/vaults")
|
@GET("api/v1/staking/pool/{network}/vaults")
|
||||||
suspend fun getVaults(
|
suspend fun getVaults(@Path("network") network: String): ApiResponse<P2PEthPoolResponse<P2PEthPoolVaultsResponse>>
|
||||||
@Path("network") network: String = "mainnet",
|
|
||||||
): ApiResponse<P2PEthPoolResponse<P2PEthPoolVaultsResponse>>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare deposit transaction
|
* Prepare deposit transaction
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.tangem.datasource.api.common.config.ApiConfig.Companion.MOCKED_BUILD_
|
||||||
import com.tangem.datasource.api.common.config.ApiConfig.Companion.RELEASE_BUILD_TYPE
|
import com.tangem.datasource.api.common.config.ApiConfig.Companion.RELEASE_BUILD_TYPE
|
||||||
import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.BLOCK_AID_API_KEY
|
import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.BLOCK_AID_API_KEY
|
||||||
import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.TANGEM_API_KEY
|
import com.tangem.datasource.api.common.config.managers.MockEnvironmentConfigStorage.Companion.TANGEM_API_KEY
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
|
||||||
import com.tangem.lib.auth.ExpressAuthProvider
|
import com.tangem.lib.auth.ExpressAuthProvider
|
||||||
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
import com.tangem.lib.auth.P2PEthPoolAuthProvider
|
||||||
import com.tangem.lib.auth.StakeKitAuthProvider
|
import com.tangem.lib.auth.StakeKitAuthProvider
|
||||||
|
|
@ -299,11 +300,17 @@ internal class ProdApiConfigsManagerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createP2PModel(): TestModel {
|
private fun createP2PModel(): TestModel {
|
||||||
|
val (environment, baseUrl) = if (P2PStakingConfig.USE_TESTNET) {
|
||||||
|
ApiEnvironment.DEV to "https://api-test.p2p.org/"
|
||||||
|
} else {
|
||||||
|
ApiEnvironment.PROD to "https://api.p2p.org/"
|
||||||
|
}
|
||||||
|
|
||||||
return TestModel(
|
return TestModel(
|
||||||
id = ApiConfig.ID.P2PEthPool,
|
id = ApiConfig.ID.P2PEthPool,
|
||||||
expected = ApiEnvironmentConfig(
|
expected = ApiEnvironmentConfig(
|
||||||
environment = ApiEnvironment.PROD,
|
environment = environment,
|
||||||
baseUrl = "https://api.p2p.org/",
|
baseUrl = baseUrl,
|
||||||
headers = mapOf(
|
headers = mapOf(
|
||||||
"Authorization" to ProviderSuspend { "Bearer $P2P_API_KEY" },
|
"Authorization" to ProviderSuspend { "Bearer $P2P_API_KEY" },
|
||||||
"accept" to ProviderSuspend { "application/json" },
|
"accept" to ProviderSuspend { "application/json" },
|
||||||
|
|
|
||||||
|
|
@ -223,30 +223,18 @@ internal class DefaultP2PEthPoolRepository(
|
||||||
.map { vaults ->
|
.map { vaults ->
|
||||||
if (vaults.isEmpty()) {
|
if (vaults.isEmpty()) {
|
||||||
return@map StakingAvailability.TemporaryUnavailable
|
return@map StakingAvailability.TemporaryUnavailable
|
||||||
}
|
|
||||||
|
|
||||||
val vault = findPublicVault(vaults = vaults)
|
|
||||||
|
|
||||||
if (vault != null) {
|
|
||||||
StakingAvailability.Available(StakingOption.P2P(vault))
|
|
||||||
} else {
|
} else {
|
||||||
StakingAvailability.TemporaryUnavailable
|
StakingAvailability.Available(StakingOption.P2P(vaults))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getStakingAvailabilitySync(): StakingAvailability {
|
override suspend fun getStakingAvailabilitySync(): StakingAvailability {
|
||||||
val vaults = getVaultsSync()
|
val vaults = getVaultsSync()
|
||||||
if (vaults.isEmpty()) {
|
return if (vaults.isEmpty()) {
|
||||||
return StakingAvailability.TemporaryUnavailable
|
|
||||||
}
|
|
||||||
|
|
||||||
val vault = findPublicVault(vaults = vaults)
|
|
||||||
|
|
||||||
return if (vault != null) {
|
|
||||||
StakingAvailability.Available(StakingOption.P2P(vault))
|
|
||||||
} else {
|
|
||||||
StakingAvailability.TemporaryUnavailable
|
StakingAvailability.TemporaryUnavailable
|
||||||
|
} else {
|
||||||
|
StakingAvailability.Available(StakingOption.P2P(vaults))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,8 +245,4 @@ internal class DefaultP2PEthPoolRepository(
|
||||||
private fun getVaultsFlow(): Flow<List<P2PEthPoolVault>> {
|
private fun getVaultsFlow(): Flow<List<P2PEthPoolVault>> {
|
||||||
return p2pEthPoolVaultsStore.get()
|
return p2pEthPoolVaultsStore.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun findPublicVault(vaults: List<P2PEthPoolVault>): P2PEthPoolVault? {
|
|
||||||
return vaults.firstOrNull { vault -> !vault.isPrivate }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
||||||
import com.tangem.domain.models.wallet.UserWalletId
|
import com.tangem.domain.models.wallet.UserWalletId
|
||||||
import com.tangem.domain.models.wallet.isMultiCurrency
|
import com.tangem.domain.models.wallet.isMultiCurrency
|
||||||
import com.tangem.domain.staking.model.StakingIntegrationID
|
import com.tangem.domain.staking.model.StakingIntegrationID
|
||||||
import com.tangem.domain.staking.model.ethpool.P2PEthPoolNetwork
|
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
|
||||||
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
import com.tangem.domain.staking.multi.MultiStakingBalanceFetcher
|
||||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||||
import com.tangem.utils.coroutines.runSuspendCatching
|
import com.tangem.utils.coroutines.runSuspendCatching
|
||||||
|
|
@ -155,7 +155,7 @@ internal class DefaultMultiStakingBalanceFetcher @Inject constructor(
|
||||||
for (address in addresses) {
|
for (address in addresses) {
|
||||||
runSuspendCatching {
|
runSuspendCatching {
|
||||||
val response = p2pApi.getAccountInfo(
|
val response = p2pApi.getAccountInfo(
|
||||||
network = P2PEthPoolNetwork.MAINNET.value,
|
network = P2PStakingConfig.activeNetwork.value,
|
||||||
delegatorAddress = address,
|
delegatorAddress = address,
|
||||||
vaultAddress = vault.vaultAddress,
|
vaultAddress = vault.vaultAddress,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,6 @@ package com.tangem.domain.models.staking
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
enum class StakingProvider {
|
|
||||||
STAKEKIT,
|
|
||||||
P2P,
|
|
||||||
}
|
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class StakingID(
|
data class StakingID(
|
||||||
val integrationId: String,
|
val integrationId: String,
|
||||||
|
|
|
||||||
|
|
@ -39,11 +39,11 @@ sealed interface StakingOption {
|
||||||
* P2P pooled staking option
|
* P2P pooled staking option
|
||||||
* Wraps P2P ETH Pool vault information
|
* Wraps P2P ETH Pool vault information
|
||||||
*/
|
*/
|
||||||
data class P2P(val vault: P2PEthPoolVault) : StakingOption {
|
data class P2P(val vaults: List<P2PEthPoolVault>) : StakingOption {
|
||||||
override val integrationId: String = "p2p-ethereum-pooled"
|
override val integrationId: String = "p2p-ethereum-pooled"
|
||||||
override val apy: SerializedBigDecimal = vault.apy
|
override val apy: SerializedBigDecimal = vaults.maxOf { it.apy }
|
||||||
override val token: YieldToken = createEthToken()
|
override val token: YieldToken = createEthToken()
|
||||||
override val isAvailable: Boolean = !vault.isPrivate
|
override val isAvailable: Boolean = vaults.isNotEmpty()
|
||||||
|
|
||||||
private fun createEthToken(): YieldToken { // TODO
|
private fun createEthToken(): YieldToken { // TODO
|
||||||
return YieldToken(
|
return YieldToken(
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ enum class P2PEthPoolNetwork(
|
||||||
val value: String,
|
val value: String,
|
||||||
val displayName: String,
|
val displayName: String,
|
||||||
val chainId: Int,
|
val chainId: Int,
|
||||||
|
val stakingNetworkId: String,
|
||||||
|
val isTestnet: Boolean,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Ethereum mainnet
|
* Ethereum mainnet
|
||||||
|
|
@ -20,16 +22,20 @@ enum class P2PEthPoolNetwork(
|
||||||
value = "mainnet",
|
value = "mainnet",
|
||||||
displayName = "Ethereum",
|
displayName = "Ethereum",
|
||||||
chainId = 1,
|
chainId = 1,
|
||||||
|
stakingNetworkId = "ethereum",
|
||||||
|
isTestnet = false,
|
||||||
),
|
),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ethereum testnet (Holesky)
|
* Ethereum testnet (Hoodi)
|
||||||
* Chain ID: 17000
|
* Chain ID: 17000
|
||||||
*/
|
*/
|
||||||
TESTNET(
|
TESTNET(
|
||||||
value = "hoodi",
|
value = "hoodi",
|
||||||
displayName = "Holesky Testnet",
|
displayName = "Hoodi Testnet",
|
||||||
chainId = 17000,
|
chainId = 17000,
|
||||||
|
stakingNetworkId = "ethereum/test",
|
||||||
|
isTestnet = true,
|
||||||
),
|
),
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package com.tangem.domain.staking.model.ethpool
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for P2P Ethereum staking network.
|
||||||
|
*
|
||||||
|
* Change [USE_TESTNET] to switch between testnet and mainnet.
|
||||||
|
*/
|
||||||
|
object P2PStakingConfig {
|
||||||
|
|
||||||
|
const val USE_TESTNET: Boolean = true
|
||||||
|
|
||||||
|
val activeNetwork: P2PEthPoolNetwork
|
||||||
|
get() = if (USE_TESTNET) P2PEthPoolNetwork.TESTNET else P2PEthPoolNetwork.MAINNET
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ import com.tangem.blockchainsdk.utils.toBlockchain
|
||||||
import com.tangem.blockchainsdk.utils.toMigratedCoinId
|
import com.tangem.blockchainsdk.utils.toMigratedCoinId
|
||||||
import com.tangem.domain.models.currency.CryptoCurrency
|
import com.tangem.domain.models.currency.CryptoCurrency
|
||||||
import com.tangem.domain.models.network.Network
|
import com.tangem.domain.models.network.Network
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a staking integration identifier.
|
* Represents a staking integration identifier.
|
||||||
|
|
@ -101,8 +102,10 @@ sealed interface StakingIntegrationID {
|
||||||
enum class P2P : StakingIntegrationID {
|
enum class P2P : StakingIntegrationID {
|
||||||
EthereumPooled {
|
EthereumPooled {
|
||||||
override val value: String = "p2p-ethereum-pooled"
|
override val value: String = "p2p-ethereum-pooled"
|
||||||
override val blockchain: Blockchain = Blockchain.Ethereum
|
override val blockchain: Blockchain
|
||||||
override val networkId: String = "ethereum"
|
get() = if (P2PStakingConfig.USE_TESTNET) Blockchain.EthereumTestnet else Blockchain.Ethereum
|
||||||
|
override val networkId: String
|
||||||
|
get() = P2PStakingConfig.activeNetwork.stakingNetworkId
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,13 @@ package com.tangem.domain.staking.repositories
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import com.tangem.domain.staking.model.StakingAvailability
|
import com.tangem.domain.staking.model.StakingAvailability
|
||||||
import com.tangem.domain.staking.model.ethpool.*
|
import com.tangem.domain.staking.model.ethpool.P2PEthPoolAccount
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PEthPoolBroadcastResult
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PEthPoolNetwork
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PEthPoolReward
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PEthPoolUnsignedTx
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PEthPoolVault
|
||||||
|
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
|
||||||
import com.tangem.domain.staking.model.stakekit.StakingError
|
import com.tangem.domain.staking.model.stakekit.StakingError
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
|
@ -13,7 +19,7 @@ interface P2PEthPoolRepository {
|
||||||
*
|
*
|
||||||
* @param network P2P network (MAINNET or TESTNET)
|
* @param network P2P network (MAINNET or TESTNET)
|
||||||
*/
|
*/
|
||||||
suspend fun fetchVaults(network: P2PEthPoolNetwork = P2PEthPoolNetwork.MAINNET)
|
suspend fun fetchVaults(network: P2PEthPoolNetwork = P2PStakingConfig.activeNetwork)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get list of available staking vaults
|
* Get list of available staking vaults
|
||||||
|
|
@ -22,7 +28,7 @@ interface P2PEthPoolRepository {
|
||||||
* @return Either error or list of vaults with APY, capacity, fees
|
* @return Either error or list of vaults with APY, capacity, fees
|
||||||
*/
|
*/
|
||||||
suspend fun getVaults(
|
suspend fun getVaults(
|
||||||
network: P2PEthPoolNetwork = P2PEthPoolNetwork.MAINNET,
|
network: P2PEthPoolNetwork = P2PStakingConfig.activeNetwork,
|
||||||
): Either<StakingError, List<P2PEthPoolVault>>
|
): Either<StakingError, List<P2PEthPoolVault>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ internal class StakingIdFactoryTest {
|
||||||
expected = createStakingId(integrationId = StakingIntegrationID.StakeKit.Coin.Cardano),
|
expected = createStakingId(integrationId = StakingIntegrationID.StakeKit.Coin.Cardano),
|
||||||
),
|
),
|
||||||
CreateModel(
|
CreateModel(
|
||||||
currencyId = createCurrencyId(blockchain = Blockchain.Ethereum),
|
currencyId = createCurrencyId(blockchain = StakingIntegrationID.P2P.EthereumPooled.blockchain),
|
||||||
expected = createStakingId(integrationId = StakingIntegrationID.P2P.EthereumPooled),
|
expected = createStakingId(integrationId = StakingIntegrationID.P2P.EthereumPooled),
|
||||||
),
|
),
|
||||||
CreateModel(
|
CreateModel(
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,7 @@ class StakingIntegrationIDTest {
|
||||||
expected = StakingIntegrationID.StakeKit.Coin.Cardano,
|
expected = StakingIntegrationID.StakeKit.Coin.Cardano,
|
||||||
),
|
),
|
||||||
CreateModel(
|
CreateModel(
|
||||||
currencyId = createCurrencyId(blockchain = Blockchain.Ethereum),
|
currencyId = createCurrencyId(blockchain = StakingIntegrationID.P2P.EthereumPooled.blockchain),
|
||||||
expected = StakingIntegrationID.P2P.EthereumPooled,
|
expected = StakingIntegrationID.P2P.EthereumPooled,
|
||||||
),
|
),
|
||||||
CreateModel(
|
CreateModel(
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# https://github.com/tangem/tangem-sdk-android/
|
# https://github.com/tangem/tangem-sdk-android/
|
||||||
# https://github.com/tangem/vico
|
# https://github.com/tangem/vico
|
||||||
|
|
||||||
tangemBlockchainSdk = "develop-1309"
|
tangemBlockchainSdk = "develop-1317"
|
||||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||||
tangemCardSdk = "develop-568"
|
tangemCardSdk = "develop-568"
|
||||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue