Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-08 10:46:19 +02:00
parent 81a7b760ae
commit 7a1b1ff5a3
15 changed files with 62 additions and 52 deletions

View file

@ -1,6 +1,7 @@
package com.tangem.tap.network.auth
import com.tangem.datasource.local.config.environment.EnvironmentConfigStorage
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
import com.tangem.lib.auth.P2PEthPoolAuthProvider
internal class DefaultP2PEthPoolAuthProvider(
@ -11,6 +12,6 @@ internal class DefaultP2PEthPoolAuthProvider(
val keys = environmentConfigStorage.getConfigSync().p2pApiKey
?: error("No P2P api keys provided")
return keys.mainnet
return if (P2PStakingConfig.USE_TESTNET) keys.hoodi else keys.mainnet
}
}

View file

@ -1,6 +1,7 @@
package com.tangem.datasource.api.common.config
import com.tangem.datasource.BuildConfig
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
import com.tangem.lib.auth.P2PEthPoolAuthProvider
import com.tangem.utils.ProviderSuspend
@ -22,12 +23,7 @@ internal class P2PEthPool(
private fun getInitialEnvironment(): ApiEnvironment {
return when (BuildConfig.BUILD_TYPE) {
MOCKED_BUILD_TYPE -> ApiEnvironment.MOCK
DEBUG_BUILD_TYPE,
INTERNAL_BUILD_TYPE,
EXTERNAL_BUILD_TYPE,
RELEASE_BUILD_TYPE,
-> ApiEnvironment.PROD
else -> error("Unknown build type [${BuildConfig.BUILD_TYPE}]")
else -> if (P2PStakingConfig.USE_TESTNET) ApiEnvironment.DEV else ApiEnvironment.PROD
}
}

View file

@ -23,9 +23,7 @@ interface P2PEthPoolApi {
* @param network Ethereum pool network: "mainnet" or "hoodi" (testnet)
*/
@GET("api/v1/staking/pool/{network}/vaults")
suspend fun getVaults(
@Path("network") network: String = "mainnet",
): ApiResponse<P2PEthPoolResponse<P2PEthPoolVaultsResponse>>
suspend fun getVaults(@Path("network") network: String): ApiResponse<P2PEthPoolResponse<P2PEthPoolVaultsResponse>>
/**
* Prepare deposit transaction

View file

@ -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.managers.MockEnvironmentConfigStorage.Companion.BLOCK_AID_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.P2PEthPoolAuthProvider
import com.tangem.lib.auth.StakeKitAuthProvider
@ -299,11 +300,17 @@ internal class ProdApiConfigsManagerTest {
}
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(
id = ApiConfig.ID.P2PEthPool,
expected = ApiEnvironmentConfig(
environment = ApiEnvironment.PROD,
baseUrl = "https://api.p2p.org/",
environment = environment,
baseUrl = baseUrl,
headers = mapOf(
"Authorization" to ProviderSuspend { "Bearer $P2P_API_KEY" },
"accept" to ProviderSuspend { "application/json" },

View file

@ -223,30 +223,18 @@ internal class DefaultP2PEthPoolRepository(
.map { vaults ->
if (vaults.isEmpty()) {
return@map StakingAvailability.TemporaryUnavailable
}
val vault = findPublicVault(vaults = vaults)
if (vault != null) {
StakingAvailability.Available(StakingOption.P2P(vault))
} else {
StakingAvailability.TemporaryUnavailable
StakingAvailability.Available(StakingOption.P2P(vaults))
}
}
}
override suspend fun getStakingAvailabilitySync(): StakingAvailability {
val vaults = getVaultsSync()
if (vaults.isEmpty()) {
return StakingAvailability.TemporaryUnavailable
}
val vault = findPublicVault(vaults = vaults)
return if (vault != null) {
StakingAvailability.Available(StakingOption.P2P(vault))
} else {
return if (vaults.isEmpty()) {
StakingAvailability.TemporaryUnavailable
} else {
StakingAvailability.Available(StakingOption.P2P(vaults))
}
}
@ -257,8 +245,4 @@ internal class DefaultP2PEthPoolRepository(
private fun getVaultsFlow(): Flow<List<P2PEthPoolVault>> {
return p2pEthPoolVaultsStore.get()
}
private fun findPublicVault(vaults: List<P2PEthPoolVault>): P2PEthPoolVault? {
return vaults.firstOrNull { vault -> !vault.isPrivate }
}
}

View file

@ -24,7 +24,7 @@ import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isMultiCurrency
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.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.coroutines.runSuspendCatching
@ -155,7 +155,7 @@ internal class DefaultMultiStakingBalanceFetcher @Inject constructor(
for (address in addresses) {
runSuspendCatching {
val response = p2pApi.getAccountInfo(
network = P2PEthPoolNetwork.MAINNET.value,
network = P2PStakingConfig.activeNetwork.value,
delegatorAddress = address,
vaultAddress = vault.vaultAddress,
)

View file

@ -2,11 +2,6 @@ package com.tangem.domain.models.staking
import kotlinx.serialization.Serializable
enum class StakingProvider {
STAKEKIT,
P2P,
}
@Serializable
data class StakingID(
val integrationId: String,

View file

@ -39,11 +39,11 @@ sealed interface StakingOption {
* P2P pooled staking option
* 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 apy: SerializedBigDecimal = vault.apy
override val apy: SerializedBigDecimal = vaults.maxOf { it.apy }
override val token: YieldToken = createEthToken()
override val isAvailable: Boolean = !vault.isPrivate
override val isAvailable: Boolean = vaults.isNotEmpty()
private fun createEthToken(): YieldToken { // TODO
return YieldToken(

View file

@ -11,6 +11,8 @@ enum class P2PEthPoolNetwork(
val value: String,
val displayName: String,
val chainId: Int,
val stakingNetworkId: String,
val isTestnet: Boolean,
) {
/**
* Ethereum mainnet
@ -20,16 +22,20 @@ enum class P2PEthPoolNetwork(
value = "mainnet",
displayName = "Ethereum",
chainId = 1,
stakingNetworkId = "ethereum",
isTestnet = false,
),
/**
* Ethereum testnet (Holesky)
* Ethereum testnet (Hoodi)
* Chain ID: 17000
*/
TESTNET(
value = "hoodi",
displayName = "Holesky Testnet",
displayName = "Hoodi Testnet",
chainId = 17000,
stakingNetworkId = "ethereum/test",
isTestnet = true,
),
;

View file

@ -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
}

View file

@ -5,6 +5,7 @@ import com.tangem.blockchainsdk.utils.toBlockchain
import com.tangem.blockchainsdk.utils.toMigratedCoinId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.domain.staking.model.ethpool.P2PStakingConfig
/**
* Represents a staking integration identifier.
@ -101,8 +102,10 @@ sealed interface StakingIntegrationID {
enum class P2P : StakingIntegrationID {
EthereumPooled {
override val value: String = "p2p-ethereum-pooled"
override val blockchain: Blockchain = Blockchain.Ethereum
override val networkId: String = "ethereum"
override val blockchain: Blockchain
get() = if (P2PStakingConfig.USE_TESTNET) Blockchain.EthereumTestnet else Blockchain.Ethereum
override val networkId: String
get() = P2PStakingConfig.activeNetwork.stakingNetworkId
},
}

View file

@ -2,7 +2,13 @@ package com.tangem.domain.staking.repositories
import arrow.core.Either
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 kotlinx.coroutines.flow.Flow
@ -13,7 +19,7 @@ interface P2PEthPoolRepository {
*
* @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
@ -22,7 +28,7 @@ interface P2PEthPoolRepository {
* @return Either error or list of vaults with APY, capacity, fees
*/
suspend fun getVaults(
network: P2PEthPoolNetwork = P2PEthPoolNetwork.MAINNET,
network: P2PEthPoolNetwork = P2PStakingConfig.activeNetwork,
): Either<StakingError, List<P2PEthPoolVault>>
/**

View file

@ -149,7 +149,7 @@ internal class StakingIdFactoryTest {
expected = createStakingId(integrationId = StakingIntegrationID.StakeKit.Coin.Cardano),
),
CreateModel(
currencyId = createCurrencyId(blockchain = Blockchain.Ethereum),
currencyId = createCurrencyId(blockchain = StakingIntegrationID.P2P.EthereumPooled.blockchain),
expected = createStakingId(integrationId = StakingIntegrationID.P2P.EthereumPooled),
),
CreateModel(

View file

@ -151,7 +151,7 @@ class StakingIntegrationIDTest {
expected = StakingIntegrationID.StakeKit.Coin.Cardano,
),
CreateModel(
currencyId = createCurrencyId(blockchain = Blockchain.Ethereum),
currencyId = createCurrencyId(blockchain = StakingIntegrationID.P2P.EthereumPooled.blockchain),
expected = StakingIntegrationID.P2P.EthereumPooled,
),
CreateModel(

View file

@ -5,7 +5,7 @@
# https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico
tangemBlockchainSdk = "develop-1309"
tangemBlockchainSdk = "develop-1317"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-568"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^