Skip to content

Commit 22674a0

Browse files
committed
Updates
1 parent a12c811 commit 22674a0

File tree

11 files changed

+222
-178
lines changed

11 files changed

+222
-178
lines changed

README.md

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The SDK is written using Kotlin Multiplatform Mobile (KMM), making it flexible a
88
- iOS projects
99
- Android projects
1010
- KMM / CMM projects
11+
- Built-in error handling for common API errors
1112

1213
## Getting Started
1314

@@ -32,28 +33,82 @@ implementation("com.zerion:mobile-sdk:<latest_version>")
3233

3334
## Usage
3435

35-
### Example of Usage in Kotlin:
36+
### Initialization
37+
38+
The SDK can be initialized with either just an authorization key (using the default API URL) or with a custom API URL:
3639

3740
```kotlin
38-
// Initialize the SDK with your authorization key
41+
// Initialize with default API URL
3942
ZerionSDK.initialize("YOUR_AUTHORIZATION_KEY")
4043

41-
// Get the Zerion API Interactor
42-
val zerionApiInteractor = ZerionSDK.getZerionApiInteractor()
44+
// Or initialize with custom API URL
45+
ZerionSDK.initialize(
46+
authorization = "YOUR_AUTHORIZATION_KEY",
47+
apiUrl = "https://your-custom-api.com/v1/",
48+
isWithLogging = true // Optional: Enable logging
49+
)
50+
```
51+
52+
For iOS:
53+
```swift
54+
// Initialize with default API URL
55+
ZerionSDK.shared.initialize(authorization: "YOUR_AUTHORIZATION_KEY")
56+
57+
// Or initialize with custom API URL
58+
ZerionSDK.shared.initialize(
59+
authorization: "YOUR_AUTHORIZATION_KEY",
60+
apiUrl: "https://your-custom-api.com/v1/"
61+
)
62+
```
63+
64+
### Error Handling
65+
66+
The SDK provides built-in error handling for common API errors. These are thrown as specific exceptions that you can catch and handle:
67+
68+
```kotlin
69+
try {
70+
val positions = ZerionSDK.getWalletPositions(address = "0x...")
71+
} catch (e: ZerionApiException) {
72+
when (e) {
73+
is ZerionApiException.MalformedParametersException -> {
74+
// Handle 400 Bad Request - malformed parameters
75+
}
76+
is ZerionApiException.UnauthorizedException -> {
77+
// Handle 401 Unauthorized - invalid API key
78+
}
79+
is ZerionApiException.TooManyRequestsException -> {
80+
// Handle 429 Too Many Requests - rate limit exceeded
81+
}
82+
is ZerionApiException.ZerionInitializationException -> {
83+
// Handle SDK initialization errors
84+
}
85+
is ZerionApiException.ZerionAuthorizationException -> {
86+
// Handle authorization errors
87+
}
88+
is ZerionApiException.GeneralException -> {
89+
// Handle other API errors
90+
}
91+
}
92+
}
93+
```
94+
95+
For iOS, these errors are mapped to NSError with appropriate error codes and descriptions.
96+
97+
### Example of Usage in Kotlin:
4398

99+
```kotlin
44100
// Example: Fetch wallet positions with filters
45101
suspend fun getWalletPositions(address: String) {
46102
try {
47-
val positions = zerionApiInteractor.getWalletPositions(
103+
val positions = ZerionSDK.getWalletPositions(
48104
address = address,
49-
chainIds = listOf("ethereum", "polygon"),
50-
positionTypes = listOf("wallet", "deposited")
105+
chainIds = listOf("ethereum", "polygon")
51106
)
52107
positions.data.forEach { position ->
53108
println("Position: ${position.attributes.name}")
54109
}
55-
} catch (e: Exception) {
56-
println("Failed to fetch wallet positions: ${e.message}")
110+
} catch (e: ZerionApiException) {
111+
println("API Error: ${e.message}")
57112
}
58113
}
59114
```
@@ -63,25 +118,18 @@ suspend fun getWalletPositions(address: String) {
63118
```swift
64119
import ZerionSDK
65120

66-
// Initialize the SDK with your authorization key
67-
ZerionSDK.shared.initialize(authorization: "YOUR_AUTHORIZATION_KEY")
68-
69-
// Get the Zerion API Interactor
70-
let zerionApiInteractor = ZerionSDK.shared.getZerionApiInteractor()
71-
72121
// Example: Fetch wallet positions with filters
73122
Task {
74123
do {
75-
let positions = try await zerionApiInteractor.getWalletPositions(
124+
let positions = try await ZerionSDK.shared.getWalletPositions(
76125
address: "0x42b9df65b219b3dd36ff330a4dd8f327a6ada990",
77-
chainIds: ["ethereum", "polygon"],
78-
positionTypes: ["wallet"]
126+
chainIds: ["ethereum", "polygon"]
79127
)
80128
positions.data.forEach { position in
81129
print("Position: \(position.attributes.name)")
82130
}
83131
} catch {
84-
print("Failed to fetch wallet positions: \(error)")
132+
print("API Error: \(error.localizedDescription)")
85133
}
86134
}
87135
```

shared/build.gradle.kts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework
33
plugins {
44
alias(libs.plugins.kotlinMultiplatform)
55
alias(libs.plugins.androidLibrary)
6-
kotlin("plugin.serialization") version "2.0.20"
6+
kotlin("plugin.serialization") version libs.versions.kotlin
77
}
88

99
kotlin {
@@ -50,11 +50,6 @@ kotlin {
5050
iosMain.dependencies {
5151
implementation(libs.ktor.client.darwin)
5252
}
53-
// named { it.lowercase().startsWith("ios") }.configureEach {
54-
// languageSettings {
55-
// optIn("kotlinx.cinterop.ExperimentalForeignApi")
56-
// }
57-
// }
5853
}
5954
}
6055

Lines changed: 92 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,119 @@
11
package io.zerion.kmm.api
22

3-
import io.github.aakira.napier.Napier
43
import io.zerion.kmm.api.api.ZerionAPI
54
import io.zerion.kmm.api.api.ZerionAPIImpl
5+
import io.zerion.kmm.api.api.ZerionApiConstants.DEFAULT_API_URL
6+
import io.zerion.kmm.api.api.ZerionApiConstants.DefaultValues
7+
import io.zerion.kmm.api.exceptions.ZerionApiException
68
import io.zerion.kmm.api.httpclient.buildHttpClient
7-
import io.zerion.kmm.api.repository.ZerionApiInteractor
8-
import io.zerion.kmm.api.repository.ZerionApiInteractorImpl
9+
import io.zerion.kmm.api.models.ChainId
10+
import io.zerion.kmm.api.models.PortfolioResponse
11+
import io.zerion.kmm.api.models.PositionFilter
12+
import io.zerion.kmm.api.models.PositionsResponse
13+
import io.zerion.kmm.api.models.SortType
14+
import io.zerion.kmm.api.models.TransactionsResponse
15+
import io.zerion.kmm.api.models.TrashFilter
916
import io.zerion.kmm.api.utils.Logging
1017

1118
object ZerionSDK {
1219

1320
private lateinit var api: ZerionAPI
14-
private lateinit var zerionApiInteractor: ZerionApiInteractor
1521

16-
fun initialize(authorization: String): Boolean {
22+
fun initialize(
23+
authorization: String,
24+
apiUrl: String = DEFAULT_API_URL,
25+
isWithLogging: Boolean = false
26+
): Boolean {
1727
return try {
18-
Logging.log("ZerionLibrary initialization started")
28+
Logging.isEnabled = isWithLogging
29+
Logging.info("ZerionLibrary initialization started")
1930

2031
if (authorization.isEmpty()) {
21-
throw IllegalArgumentException("Authorization cannot be empty")
32+
throw ZerionApiException.ZerionAuthorizationException("Authorization cannot be empty")
2233
}
2334

24-
val client = buildHttpClient(authorization)
35+
val client = buildHttpClient(authorization, apiUrl)
2536

2637
api = ZerionAPIImpl(client)
27-
zerionApiInteractor = ZerionApiInteractorImpl(api)
2838

39+
Logging.info("ZerionLibrary initialization completed successfully")
2940
true
30-
} catch (e: Exception) {
31-
Napier.e("ZerionLibrary initialization failed")
41+
} catch (e: ZerionApiException) {
42+
Logging.error("ZerionLibrary initialization failed", e)
3243
false
44+
} catch (e: Exception) {
45+
Logging.error("ZerionLibrary initialization failed", e)
46+
throw ZerionApiException.ZerionInitializationException("Unexpected error during initialization", e)
3347
}
3448
}
3549

36-
fun getZerionApiInteractor(): ZerionApiInteractor {
37-
if (!this::zerionApiInteractor.isInitialized) {
38-
throw IllegalStateException("ZerionLibrary is not initialized. Call initialize() first.")
39-
}
50+
suspend fun getWalletPortfolio(address: String): PortfolioResponse {
51+
requireInitialize()
52+
53+
return api.getWalletPortfolio(address)
54+
}
55+
56+
suspend fun getWalletPositions(
57+
address: String,
58+
positionsFilter: String = PositionFilter.ONLY_SIMPLE.apiValue,
59+
currency: String = DefaultValues.CURRENCY_USD,
60+
positionTypes: List<String>? = null,
61+
chainIds: List<ChainId>? = null,
62+
fungibleIds: List<String>? = null,
63+
dappIds: List<String>? = null,
64+
trash: String = TrashFilter.ONLY_NON_TRASH.apiValue,
65+
sort: String = SortType.VALUE_ASCENDING.apiValue
66+
): PositionsResponse {
67+
requireInitialize()
4068

41-
return zerionApiInteractor
69+
return api.getWalletPositions(
70+
address = address,
71+
positionsFilter = positionsFilter,
72+
currency = currency,
73+
positionTypes = positionTypes,
74+
chainIds = chainIds,
75+
fungibleIds = fungibleIds,
76+
dappIds = dappIds,
77+
trash = trash,
78+
sort = sort
79+
)
80+
}
81+
82+
suspend fun getWalletTransactions(
83+
address: String,
84+
currency: String = DefaultValues.CURRENCY_USD,
85+
pageSize: Int = DefaultValues.DEFAULT_PAGE_SIZE,
86+
pageAfter: String? = null,
87+
searchQuery: String? = null,
88+
operationTypes: List<String>? = null,
89+
assetTypes: List<String>? = null,
90+
chainIds: List<ChainId>? = null,
91+
minMinedAt: Long? = null,
92+
maxMinedAt: Long? = null,
93+
trash: String = TrashFilter.ONLY_NON_TRASH.apiValue,
94+
fungibleImplementations: List<String>? = null
95+
): TransactionsResponse {
96+
requireInitialize()
97+
98+
return api.getWalletTransactions(
99+
address = address,
100+
currency = currency,
101+
pageSize = pageSize,
102+
pageAfter = pageAfter,
103+
searchQuery = searchQuery,
104+
operationTypes = operationTypes,
105+
assetTypes = assetTypes,
106+
chainIds = chainIds,
107+
minMinedAt = minMinedAt,
108+
maxMinedAt = maxMinedAt,
109+
trash = trash,
110+
fungibleImplementations = fungibleImplementations
111+
)
112+
}
113+
114+
private fun requireInitialize() {
115+
if (!this::api.isInitialized) {
116+
throw ZerionApiException.ZerionInitializationException("ZerionLibrary is not initialized. Call initialize() first.")
117+
}
42118
}
43119
}

shared/src/commonMain/kotlin/io/zerion/kmm/api/api/ZerionAPI.kt

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,28 @@ import io.ktor.client.request.get
66
import io.ktor.client.request.parameter
77
import io.zerion.kmm.api.api.ZerionApiConstants.DefaultValues
88
import io.zerion.kmm.api.api.ZerionApiConstants.Params
9+
import io.zerion.kmm.api.models.ChainId
910
import io.zerion.kmm.api.models.PortfolioResponse
11+
import io.zerion.kmm.api.models.PositionFilter
1012
import io.zerion.kmm.api.models.PositionsResponse
13+
import io.zerion.kmm.api.models.SortType
1114
import io.zerion.kmm.api.models.TransactionsResponse
15+
import io.zerion.kmm.api.models.TrashFilter
1216

1317

1418
internal interface ZerionAPI {
1519
suspend fun getWalletPortfolio(address: String): PortfolioResponse
1620

1721
suspend fun getWalletPositions(
1822
address: String,
19-
positionsFilter: String = DefaultValues.POSITIONS_FILTER_SIMPLE,
23+
positionsFilter: String = PositionFilter.ONLY_SIMPLE.apiValue,
2024
currency: String = DefaultValues.CURRENCY_USD,
2125
positionTypes: List<String>? = null,
22-
chainIds: List<String>? = null,
26+
chainIds: List<ChainId>? = null,
2327
fungibleIds: List<String>? = null,
2428
dappIds: List<String>? = null,
25-
trash: String = DefaultValues.TRASH_NON_TRASH,
26-
sort: String = DefaultValues.SORT_VALUE
29+
trash: String = TrashFilter.ONLY_NON_TRASH.apiValue,
30+
sort: String = SortType.VALUE_ASCENDING.apiValue
2731
): PositionsResponse
2832

2933
suspend fun getWalletTransactions(
@@ -34,10 +38,10 @@ internal interface ZerionAPI {
3438
searchQuery: String? = null,
3539
operationTypes: List<String>? = null,
3640
assetTypes: List<String>? = null,
37-
chainIds: List<String>? = null,
41+
chainIds: List<ChainId>? = null,
3842
minMinedAt: Long? = null,
3943
maxMinedAt: Long? = null,
40-
trash: String = DefaultValues.TRASH_NO_FILTER,
44+
trash: String = TrashFilter.NO_FILTER.apiValue,
4145
fungibleImplementations: List<String>? = null
4246
): TransactionsResponse
4347
}
@@ -53,7 +57,7 @@ internal class ZerionAPIImpl(private val client: HttpClient) : ZerionAPI {
5357
positionsFilter: String,
5458
currency: String,
5559
positionTypes: List<String>?,
56-
chainIds: List<String>?,
60+
chainIds: List<ChainId>?,
5761
fungibleIds: List<String>?,
5862
dappIds: List<String>?,
5963
trash: String,
@@ -79,7 +83,7 @@ internal class ZerionAPIImpl(private val client: HttpClient) : ZerionAPI {
7983
searchQuery: String?,
8084
operationTypes: List<String>?,
8185
assetTypes: List<String>?,
82-
chainIds: List<String>?,
86+
chainIds: List<ChainId>?,
8387
minMinedAt: Long?,
8488
maxMinedAt: Long?,
8589
trash: String,

shared/src/commonMain/kotlin/io/zerion/kmm/api/api/ZerionApiConstants.kt

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.zerion.kmm.api.api
22

33
internal object ZerionApiConstants {
4+
const val DEFAULT_API_URL = "https://api.zerion.io/v1/"
5+
46
object Params {
57
const val POSITIONS_FILTER = "filter[positions]"
68
const val CURRENCY = "currency"
@@ -23,14 +25,6 @@ internal object ZerionApiConstants {
2325

2426
object DefaultValues {
2527
const val DEFAULT_PAGE_SIZE = 20
26-
27-
const val POSITIONS_FILTER_SIMPLE = "only_simple"
28-
const val POSITIONS_FILTER_COMPLEX = "only_complex"
29-
const val POSITIONS_FILTER_NONE = "no_filter"
3028
const val CURRENCY_USD = "usd"
31-
const val TRASH_NON_TRASH = "only_non_trash"
32-
const val TRASH_TRASH = "only_trash"
33-
const val SORT_VALUE = "value"
34-
const val TRASH_NO_FILTER = "no_filter"
3529
}
3630
}

0 commit comments

Comments
 (0)