Writes high-quality Kotlin code following official conventions and reown-kotlin project patterns. Use when writing, reviewing, or refactoring Kotlin code in this codebase.
Write production-quality Kotlin code that follows official Kotlin conventions and reown-kotlin project-specific patterns.
Stack:
Architecture: Layered Clean Architecture
Foundation (pure Kotlin) β Core (Android) β Protocol β Product
// Hide internal APIs from Java consumers
@file:JvmSynthetic
package com.reown.module.internal
@JvmSynthetic
internal fun internalHelper(): String = "..."
internal class ApproveSessionUseCase(
private val repository: SessionRepository,
private val validator: SessionValidator
) : ApproveSessionUseCaseInterface {
override suspend fun approve(params: ApproveParams): Result<Session> =
supervisorScope {
runCatching {
validator.validate(params)
repository.save(params.toSession())
}
}
}
// Module function pattern
fun featureModule() = module {
includes(coreModule())
single<FeatureRepository> { FeatureRepositoryImpl(get()) }
single<FeatureUseCaseInterface> { FeatureUseCase(get(), get()) }
}
// Enum-based qualifiers
enum class FeatureDITags { HTTP_CLIENT, JSON_ADAPTER }
// Usage
single(named(FeatureDITags.HTTP_CLIENT)) { OkHttpClient() }
// Scope definition
private val job = SupervisorJob()
val scope = CoroutineScope(job + Dispatchers.IO)
// State management
private val _state = MutableStateFlow<UiState>(UiState.Loading)
val state: StateFlow<UiState> = _state.asStateFlow()
// Context switching for IO
suspend fun loadData(): Data = withContext(Dispatchers.IO) {
repository.fetch()
}
// Custom exception hierarchy
abstract class WalletConnectException(override val message: String?) : Exception(message)
class InvalidSessionException(message: String) : WalletConnectException(message)
// Result pattern
suspend fun fetchData(): Result<Data> = runCatching {
api.getData()
}
// Usage
fetchData()
.onSuccess { data -> process(data) }
.onFailure { error -> log(error) }
// Sealed class hierarchy for type-safe ADTs
sealed class ConnectionState {
data object Connected : ConnectionState()
data object Disconnected : ConnectionState()
data class Error(val reason: Reason) : ConnectionState()
sealed class Reason {
data class Network(val code: Int) : Reason()
data class Auth(val message: String) : Reason()
}
}
// Value classes for type safety
@JvmInline
value class Topic(val value: String)
@JvmInline
value class SessionId(val value: String)
// Data class with Moshi
@JsonClass(generateAdapter = true)
data class SessionRequest(
@Json(name = "id") val id: String,
@Json(name = "topic") val topic: String,
@Json(name = "params") val params: RequestParams? = null
)
@JvmSynthetic
internal fun SessionDTO.toDomain(): Session = Session(
id = SessionId(this.id),
topic = Topic(this.topic),
expiry = this.expiry
)
@JvmSynthetic
internal fun Session.toDTO(): SessionDTO = SessionDTO(
id = this.id.value,
topic = this.topic.value,
expiry = this.expiry
)
// Public API delegates to internal implementation
object SignClient : SignInterface by SignProtocol.instance
// Interface definition
interface SignInterface {
fun initialize(params: InitParams)
suspend fun connect(params: ConnectParams): Result<Uri>
fun setDelegate(delegate: SignDelegate)
}
// Delegate callbacks
interface SignDelegate {
fun onSessionProposal(proposal: SessionProposal)
fun onSessionRequest(request: SessionRequest)
val onSessionDelete: ((DeletedSession) -> Unit)? get() = null
}
| Type | Convention | Example |
|---|---|---|
| Use Case | *UseCase |
ApproveSessionUseCase |
| Interface | *Interface |
SignInterface |
| Repository | *Repository |
SessionStorageRepository |
| DTO | *DTO |
SessionDTO |
| Domain Object | *DO |
EngineDO |
| DI Tags | *DITags enum |
SignDITags |
| Mapper | to*() extension |
toSession(), toDomain() |
| State | _private β public |
_state β state |
class FeatureUseCaseTest {
private val repository: FeatureRepository = mockk()
private val testDispatcher = StandardTestDispatcher()
private lateinit var useCase: FeatureUseCase
@Before
fun setUp() {
Dispatchers.setMain(testDispatcher)
useCase = FeatureUseCase(repository)
}
@After
fun tearDown() {
Dispatchers.resetMain()
}
@Test
fun `invoke returns success when repository succeeds`() = runTest(testDispatcher) {
// Given
coEvery { repository.fetch() } returns expectedData
// When
val result = useCase.invoke()
advanceUntilIdle()
// Then
assertTrue(result.isSuccess)
coVerify(exactly = 1) { repository.fetch() }
}
}
internal visibility for non-public APIs@file:JvmSynthetic on internal utility filessupervisorScope + runCatching_private β public asStateFlow() patternfun module() = module { }@JsonClass(generateAdapter = true) for Moshito*() naming with @JvmSyntheticmockk, runTest, and StandardTestDispatcherTask: Create a use case to fetch user sessions
@file:JvmSynthetic
package com.reown.sign.engine.use_case.calls
internal class GetUserSessionsUseCase(
private val sessionRepository: SessionStorageRepository,
private val logger: Logger
) : GetUserSessionsUseCaseInterface {
override suspend fun getSessions(userId: String): Result<List<EngineDO.Session>> =
supervisorScope {
runCatching {
sessionRepository.getSessionsByUser(userId)
.map { it.toEngineDO() }
}.onFailure { error ->
logger.error("Failed to fetch sessions: ${error.message}")
}
}
}
internal interface GetUserSessionsUseCaseInterface {
suspend fun getSessions(userId: String): Result<List<EngineDO.Session>>
}
Task: Register the new use case in DI
// In CallsModule.kt
fun callsModule() = module {
includes(storageModule())
single<GetUserSessionsUseCaseInterface> {
GetUserSessionsUseCase(
sessionRepository = get(),
logger = get()
)
}
}
Task: Model wallet events
@file:JvmSynthetic
package com.reown.walletkit.client.model
sealed class WalletEvent {
data class SessionProposal(
val id: ProposalId,
val proposer: AppMetaData,
val namespaces: Map<String, Namespace>
) : WalletEvent()
data class SessionRequest(
val topic: Topic,
val chainId: String,
val request: Request
) : WalletEvent()
data class SessionDelete(
val topic: Topic,
val reason: String
) : WalletEvent()
@JvmInline
value class ProposalId(val value: Long)
}