Skip to content

Commit 4c93c61

Browse files
committed
chore(ocp): add currency creator gRPC methods
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 141c4c6 commit 4c93c61

8 files changed

Lines changed: 151 additions & 18 deletions

File tree

apps/flipcash/features/discovery/src/main/kotlin/com/flipcash/app/discovery/internal/TokenDiscoveryViewModel.kt

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,14 @@ internal class TokenDiscoveryViewModel @Inject constructor(
8585
dispatchEvent(Event.OnTokensUpdated(Loadable.Loaded(it)))
8686
},
8787
onError = { error ->
88-
if (error is DiscoverTokensError.NotFound) {
89-
dispatchEvent(Event.OnTokensUpdated(Loadable.Loaded(emptyList())))
90-
} else {
91-
dispatchEvent(
92-
Event.OnTokensUpdated(
93-
Loadable.Error(
94-
message = resources.getString(R.string.error_discoverFailedToLoad),
95-
error = error
96-
)
88+
dispatchEvent(
89+
Event.OnTokensUpdated(
90+
Loadable.Error(
91+
message = resources.getString(R.string.error_discoverFailedToLoad),
92+
error = error
9793
)
9894
)
99-
}
95+
)
10096
}
10197
).launchIn(viewModelScope)
10298

definitions/opencode/protos/src/main/proto/currency/v1/currency_service.proto

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ service Currency {
2121
rpc UpdateMetadata(UpdateMetadataRequest) returns (UpdateMetadataResponse);
2222
// Discover returns a set of currencies to discover
2323
rpc Discover(DiscoverRequest) returns (stream DiscoverResponse);
24+
// CheckAvailability checks whether a currency name is available for launch
25+
rpc CheckAvailability(CheckAvailabilityRequest) returns (CheckAvailabilityResponse);
2426
}
2527
message GetMintsRequest {
2628
repeated common.v1.SolanaAccountId addresses = 1 ;
@@ -231,6 +233,12 @@ message LaunchRequest {
231233
// The ticker symbol for the currency. If not provided, a default will be
232234
// generated using the currency name.
233235
string symbol = 4 ;
236+
// Optional description
237+
string description = 5 ;
238+
// Optional bill customization. If not provided, a default will be set.
239+
BillCustomization bill_customization = 6;
240+
// The raw image data for the icon. If not provided, a default will be set.
241+
bytes icon = 7 ;
234242
}
235243
message LaunchResponse {
236244
Result result = 1;
@@ -239,7 +247,9 @@ message LaunchResponse {
239247
// The launch was denied
240248
DENIED = 1;
241249
// A similar currency already exists
242-
EXISTS = 2;
250+
NAME_EXISTS = 2;
251+
// Provided icon is invalid
252+
INVALID_ICON = 3;
243253
}
244254
// The mint address of the launched currency on success
245255
common.v1.SolanaAccountId mint = 2;
@@ -314,6 +324,18 @@ message DiscoverResponse {
314324
}
315325
repeated Mint mints = 2 ;
316326
}
327+
message CheckAvailabilityRequest {
328+
// The currency name to check availability for
329+
string name = 1 ;
330+
}
331+
message CheckAvailabilityResponse {
332+
Result result = 1;
333+
enum Result {
334+
OK = 0;
335+
}
336+
// Whether the name is available for use
337+
bool is_available = 2;
338+
}
317339
enum PredefinedRange {
318340
ALL_TIME = 0;
319341
LAST_DAY = 1;

services/opencode/src/main/kotlin/com/getcode/opencode/controllers/CurrencyController.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.getcode.opencode.controllers
22

3+
import com.getcode.ed25519.Ed25519
4+
import com.getcode.opencode.model.core.errors.CheckTokenAvailabilityError
35
import com.getcode.opencode.model.financial.LiveMintDataResponse
46
import com.getcode.opencode.model.ui.WindowedRange
57
import com.getcode.opencode.model.financial.CurrencyCode
68
import com.getcode.opencode.model.financial.HistoricalMintData
79
import com.getcode.opencode.model.financial.MintMetadata
810
import com.getcode.opencode.model.financial.Token
911
import com.getcode.opencode.model.ui.DiscoverCategory
12+
import com.getcode.opencode.model.ui.TokenBillCustomizations
1013
import com.getcode.opencode.repositories.CurrencyRepository
1114
import com.getcode.solana.keys.Mint
1215
import kotlinx.coroutines.CoroutineScope
@@ -118,4 +121,24 @@ class CurrencyController @Inject constructor(
118121
): Result<List<Token>> {
119122
return repository.discoverTokens(category)
120123
}
124+
125+
suspend fun checkTokenAvailability(name: String): Result<Unit> {
126+
return repository.checkTokenAvailability(name)
127+
.mapCatching { available ->
128+
if (!available) {
129+
throw CheckTokenAvailabilityError.Unavailable()
130+
}
131+
}
132+
}
133+
134+
suspend fun launchToken(
135+
name: String,
136+
symbol: String,
137+
description: String,
138+
bill: TokenBillCustomizations?,
139+
icon: ByteArray?,
140+
owner: Ed25519.KeyPair,
141+
): Result<Mint> {
142+
return repository.launchToken(name, symbol, description, bill, icon, owner)
143+
}
121144
}

services/opencode/src/main/kotlin/com/getcode/opencode/internal/domain/repositories/InternalCurrencyRepository.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.getcode.opencode.internal.domain.repositories
22

3+
import com.getcode.ed25519.Ed25519
34
import com.getcode.opencode.model.ui.DiscoverCategory
45
import com.getcode.opencode.model.financial.LiveMintDataResponse
56
import com.getcode.opencode.model.ui.WindowedRange
@@ -9,6 +10,7 @@ import com.getcode.opencode.model.financial.CurrencyCode
910
import com.getcode.opencode.model.financial.HistoricalMintData
1011
import com.getcode.opencode.model.financial.MintMetadata
1112
import com.getcode.opencode.model.financial.Token
13+
import com.getcode.opencode.model.ui.TokenBillCustomizations
1214
import com.getcode.opencode.repositories.CurrencyRepository
1315
import com.getcode.solana.keys.Mint
1416
import com.getcode.utils.ErrorUtils
@@ -45,4 +47,18 @@ internal class InternalCurrencyRepository @Inject constructor(
4547
ErrorUtils.handleError(error)
4648
}
4749
}
50+
51+
override suspend fun checkTokenAvailability(name: String): Result<Boolean> =
52+
service.checkTokenAvailability(name)
53+
54+
override suspend fun launchToken(
55+
name: String,
56+
symbol: String,
57+
description: String,
58+
bill: TokenBillCustomizations?,
59+
icon: ByteArray?,
60+
owner: Ed25519.KeyPair
61+
): Result<Mint> {
62+
return service.launchNewToken(name, symbol, bill, icon, owner)
63+
}
4864
}

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/api/CurrencyApi.kt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.getcode.opencode.internal.network.extensions.asSolanaAccountId
1313
import com.getcode.opencode.internal.network.extensions.sign
1414
import com.getcode.opencode.model.financial.CurrencyCode
1515
import com.getcode.opencode.model.financial.TokenUpdateRequest
16+
import com.getcode.opencode.model.ui.TokenBillCustomizations
1617
import com.getcode.solana.keys.Mint
1718
import com.getcode.solana.keys.PublicKey
1819
import com.getcode.utils.toByteString
@@ -89,14 +90,35 @@ internal class CurrencyApi @Inject constructor(
8990
return streamingApi.streamLiveMintData(requests)
9091
}
9192

92-
suspend fun launch(
93+
suspend fun checkTokenAvailability(name: String): CurrencyService.CheckAvailabilityResponse {
94+
val request = CurrencyService.CheckAvailabilityRequest.newBuilder()
95+
.setName(name)
96+
.build()
97+
98+
return withContext(Dispatchers.IO) {
99+
api.checkAvailability(request)
100+
}
101+
}
102+
103+
suspend fun launchToken(
93104
name: String,
94105
symbol: String,
106+
bill: TokenBillCustomizations?,
107+
icon: ByteArray?,
95108
owner: Ed25519.KeyPair
96109
): CurrencyService.LaunchResponse {
97110
val request = CurrencyService.LaunchRequest.newBuilder()
98111
.setName(name)
99112
.setSymbol(symbol)
113+
.apply apply@{
114+
if (bill != null) {
115+
setBillCustomization(bill.asProto())
116+
}
117+
118+
if (icon != null) {
119+
setIcon(icon.toByteString())
120+
}
121+
}
100122
.setOwner(owner.asSolanaAccountId())
101123
.apply { setSignature(sign(owner)) }
102124
.build()

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/services/CurrencyService.kt

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.getcode.opencode.internal.network.extensions.foldWithSuppression
1414
import com.getcode.opencode.internal.network.extensions.toMint
1515
import com.getcode.opencode.internal.network.streamers.LiveMintDataStreamer
1616
import com.getcode.opencode.internal.network.streamers.ManagedMintStream
17+
import com.getcode.opencode.model.core.errors.CheckTokenAvailabilityError
1718
import com.getcode.opencode.model.core.errors.DiscoverTokensError
1819
import com.getcode.opencode.model.core.errors.GetHistoricalMintDataError
1920
import com.getcode.opencode.model.core.errors.GetMintsError
@@ -24,6 +25,7 @@ import com.getcode.opencode.model.financial.CurrencyCode
2425
import com.getcode.opencode.model.financial.HistoricalMintData
2526
import com.getcode.opencode.model.financial.MintMetadata
2627
import com.getcode.opencode.model.financial.TokenUpdateRequest
28+
import com.getcode.opencode.model.ui.TokenBillCustomizations
2729
import com.getcode.solana.keys.Mint
2830
import com.getcode.solana.keys.PublicKey
2931
import kotlinx.coroutines.CoroutineScope
@@ -99,7 +101,7 @@ internal class CurrencyService @Inject constructor(
99101
onUpdate: (LiveMintDataResponse) -> Unit
100102
): ManagedMintStream {
101103
val streamer = LiveMintDataStreamer(api)
102-
return streamer.stream(scope = scope, mints = mints, tag = tag,) { update ->
104+
return streamer.stream(scope = scope, mints = mints, tag = tag) { update ->
103105
// save protos for later use
104106
when (update.typeCase) {
105107
CurrencyService.StreamLiveMintDataResponse.LiveData.TypeCase.CORE_MINT_FIAT_EXCHANGE_RATES -> verifiedStateManager.saveRates(update.coreMintFiatExchangeRates.exchangeRatesList)
@@ -115,15 +117,38 @@ internal class CurrencyService @Inject constructor(
115117
}
116118
}
117119

118-
suspend fun launchNewToken(name: String, symbol: String, owner: Ed25519.KeyPair): Result<Mint> {
120+
suspend fun checkTokenAvailability(name: String): Result<Boolean> {
119121
return runCatching {
120-
api.launch(name, symbol, owner)
122+
api.checkTokenAvailability(name)
123+
}.foldWithSuppression(
124+
onSuccess = { response ->
125+
when (response.result) {
126+
CurrencyService.CheckAvailabilityResponse.Result.OK -> Result.success(response.isAvailable)
127+
CurrencyService.CheckAvailabilityResponse.Result.UNRECOGNIZED -> Result.failure(CheckTokenAvailabilityError.Unrecognized())
128+
}
129+
},
130+
onFailure = { cause ->
131+
Result.failure(CheckTokenAvailabilityError.Other(cause))
132+
}
133+
)
134+
}
135+
136+
suspend fun launchNewToken(
137+
name: String,
138+
symbol: String,
139+
bill: TokenBillCustomizations?,
140+
icon: ByteArray?,
141+
owner: Ed25519.KeyPair
142+
): Result<Mint> {
143+
return runCatching {
144+
api.launchToken(name, symbol, bill, icon, owner)
121145
}.foldWithSuppression(
122146
onSuccess = { response ->
123147
when (response.result) {
124148
CurrencyService.LaunchResponse.Result.OK -> Result.success(response.mint.toMint())
125149
CurrencyService.LaunchResponse.Result.DENIED -> Result.failure(LaunchTokenError.Denied())
126-
CurrencyService.LaunchResponse.Result.EXISTS -> Result.failure(LaunchTokenError.Exists())
150+
CurrencyService.LaunchResponse.Result.NAME_EXISTS -> Result.failure(LaunchTokenError.Exists())
151+
CurrencyService.LaunchResponse.Result.INVALID_ICON -> Result.failure(LaunchTokenError.InvalidIcon())
127152
CurrencyService.LaunchResponse.Result.UNRECOGNIZED -> Result.failure(LaunchTokenError.Unrecognized())
128153
}
129154
},
@@ -133,7 +158,10 @@ internal class CurrencyService @Inject constructor(
133158
)
134159
}
135160

136-
suspend fun updateIcon(updateRequest: TokenUpdateRequest.Icon, owner: Ed25519.KeyPair): Result<Unit> {
161+
suspend fun updateIcon(
162+
updateRequest: TokenUpdateRequest.Icon,
163+
owner: Ed25519.KeyPair
164+
): Result<Unit> {
137165
return runCatching {
138166
api.updateIcon(updateRequest, owner)
139167
}.foldWithSuppression(
@@ -151,7 +179,11 @@ internal class CurrencyService @Inject constructor(
151179
}
152180
)
153181
}
154-
suspend fun updateMetadata(updateRequest: TokenUpdateRequest.Metadata, owner: Ed25519.KeyPair): Result<Unit> {
182+
183+
suspend fun updateMetadata(
184+
updateRequest: TokenUpdateRequest.Metadata,
185+
owner: Ed25519.KeyPair
186+
): Result<Unit> {
155187
return runCatching {
156188
api.updateMetadata(updateRequest, owner)
157189
}.foldWithSuppression(

services/opencode/src/main/kotlin/com/getcode/opencode/model/core/errors/Errors.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,22 @@ sealed class SwapError(
250250
}
251251
}
252252

253+
sealed class CheckTokenAvailabilityError(
254+
override val message: String? = null,
255+
override val cause: Throwable? = null
256+
) : CodeServerError(message, cause) {
257+
class Unavailable: CheckTokenAvailabilityError("Unavailable")
258+
class Unrecognized: CheckTokenAvailabilityError("Unrecognized")
259+
data class Other(override val cause: Throwable? = null) : CheckTokenAvailabilityError(message = cause?.message, cause = cause)
260+
}
261+
253262
sealed class LaunchTokenError(
254263
override val message: String? = null,
255264
override val cause: Throwable? = null
256265
) : CodeServerError(message, cause) {
257266
class Denied: LaunchTokenError("Denied")
258267
class Exists: LaunchTokenError("Token Already Exists")
268+
class InvalidIcon: LaunchTokenError("Invalid icon")
259269
class Unrecognized: LaunchTokenError("Unrecognized")
260270
data class Other(override val cause: Throwable? = null) : LaunchTokenError(message = cause?.message, cause = cause)
261271
}

services/opencode/src/main/kotlin/com/getcode/opencode/repositories/CurrencyRepository.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.getcode.opencode.repositories
22

3+
import com.getcode.ed25519.Ed25519
34
import com.getcode.opencode.model.ui.DiscoverCategory
45
import com.getcode.opencode.model.financial.LiveMintDataResponse
56
import com.getcode.opencode.model.ui.WindowedRange
@@ -8,6 +9,7 @@ import com.getcode.opencode.model.financial.CurrencyCode
89
import com.getcode.opencode.model.financial.HistoricalMintData
910
import com.getcode.opencode.model.financial.MintMetadata
1011
import com.getcode.opencode.model.financial.Token
12+
import com.getcode.opencode.model.ui.TokenBillCustomizations
1113
import com.getcode.solana.keys.Mint
1214
import kotlinx.coroutines.CoroutineScope
1315

@@ -30,4 +32,14 @@ interface CurrencyRepository {
3032
suspend fun discoverTokens(
3133
category: DiscoverCategory
3234
): Result<List<Token>>
35+
36+
suspend fun checkTokenAvailability(name: String): Result<Boolean>
37+
suspend fun launchToken(
38+
name: String,
39+
symbol: String,
40+
description: String,
41+
bill: TokenBillCustomizations?,
42+
icon: ByteArray?,
43+
owner: Ed25519.KeyPair
44+
): Result<Mint>
3345
}

0 commit comments

Comments
 (0)