Skip to content

Commit 2bbd59c

Browse files
committed
refactor(ocp): introduce IntentStatelessSwap and rename swap request/response types
Add IntentStatelessSwap mirroring IntentSwap pattern — encapsulates initiate and submit-signatures protobuf request building. Update StatelessSwapExecutor to take the intent instead of raw params + lambda. Create StatelessSwapRequest model and rename stateful counterparts: SwapRequest -> StatefulSwapRequest, SwapResponseServerParameters -> StatefulSwapResponseServerParameters, SwapResult -> StatefulSwapResult. Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent 3c3f52b commit 2bbd59c

25 files changed

Lines changed: 161 additions & 168 deletions

services/opencode/src/androidTest/kotlin/com/getcode/opencode/solana/swap/SwapTests.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import com.getcode.opencode.model.financial.LaunchpadMetadata
2323
import com.getcode.opencode.model.financial.MintMetadata
2424
import com.getcode.opencode.model.financial.VmMetadata
2525
import com.getcode.opencode.model.financial.usdf
26-
import com.getcode.opencode.model.transactions.SwapResponseServerParameters
26+
import com.getcode.opencode.model.transactions.StatefulSwapResponseServerParameters
2727
import com.getcode.opencode.tests.generateRandomPublicKeyForTest
2828
import com.getcode.opencode.utils.generate
2929
import com.getcode.solana.keys.Mint
@@ -89,7 +89,7 @@ class SwapInstructionsTest {
8989
)
9090

9191
// Mock Server Response (Stateless for simplicity)
92-
private val mockServerParams = SwapResponseServerParameters.ExistingCurrency(
92+
private val mockServerParams = StatefulSwapResponseServerParameters.ExistingCurrency(
9393
payer = mockPayer,
9494
blockhash = mockRecentBlockhash,
9595
nonce = mockNonce,
@@ -337,7 +337,7 @@ class SwapInstructionsTest {
337337
private val mockNewCurrencyAuthority = generateRandomPublicKeyForTest()
338338
private val mockSeed = generateRandomPublicKeyForTest()
339339

340-
private val mockNewCurrencyServerParams = SwapResponseServerParameters.NewCurrency(
340+
private val mockNewCurrencyServerParams = StatefulSwapResponseServerParameters.NewCurrency(
341341
payer = mockPayer,
342342
blockhash = mockRecentBlockhash,
343343
nonce = mockNonce,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import com.getcode.opencode.model.financial.Token
2424
import com.getcode.opencode.model.transactions.ExchangeData
2525
import com.getcode.opencode.model.transactions.SwapFundingSource
2626
import com.getcode.opencode.model.transactions.SwapMetadata
27-
import com.getcode.opencode.model.transactions.SwapRequest
27+
import com.getcode.opencode.model.transactions.StatefulSwapRequest
2828
import com.getcode.opencode.model.transactions.SwapState
2929
import com.getcode.opencode.model.transactions.TransactionMetadata
3030
import com.getcode.opencode.model.transactions.WithdrawalAvailability
@@ -269,7 +269,7 @@ class TransactionController @Inject constructor(
269269
swapId: SwapId?,
270270
of: Token,
271271
source: SwapFundingSource,
272-
fund: (suspend (SwapRequest) -> Result<Unit>)?,
272+
fund: (suspend (StatefulSwapRequest) -> Result<Unit>)?,
273273
): Result<SwapId> {
274274
trace("Starting ${amount.localFiat.nativeAmount.formatted()} buy of ${of.symbol}")
275275
val tokenizedOwner = owner.withTimelockForToken(of)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import com.getcode.opencode.model.financial.LocalFiat
99
import com.getcode.opencode.model.financial.Token
1010
import com.getcode.opencode.model.transactions.SwapFundingSource
1111
import com.getcode.opencode.model.transactions.SwapMetadata
12-
import com.getcode.opencode.model.transactions.SwapRequest
12+
import com.getcode.opencode.model.transactions.StatefulSwapRequest
1313
import com.getcode.opencode.model.transactions.SwapState
1414
import com.getcode.opencode.model.transactions.WithdrawalAvailability
1515
import com.getcode.opencode.solana.intents.IntentType
@@ -35,7 +35,7 @@ interface TransactionOperations {
3535
swapId: SwapId? = null,
3636
of: Token,
3737
source: SwapFundingSource = SwapFundingSource.SubmitIntent(),
38-
fund: (suspend (SwapRequest) -> Result<Unit>)? = null,
38+
fund: (suspend (StatefulSwapRequest) -> Result<Unit>)? = null,
3939
): Result<SwapId>
4040

4141
suspend fun sell(

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import com.getcode.opencode.model.financial.Limits
99
import com.getcode.opencode.model.financial.LocalFiat
1010
import com.getcode.opencode.model.financial.Token
1111
import com.getcode.opencode.model.transactions.SwapFundingSource
12-
import com.getcode.opencode.model.transactions.SwapRequest
12+
import com.getcode.opencode.model.transactions.StatefulSwapRequest
1313
import com.getcode.opencode.model.transactions.TransactionMetadata
1414
import com.getcode.opencode.model.transactions.WithdrawalAvailability
1515
import com.getcode.opencode.model.core.errors.SubmitIntentError
@@ -73,7 +73,7 @@ internal class InternalTransactionRepository @Inject constructor(
7373
swapId: SwapId?,
7474
verifiedState: VerifiedState,
7575
source: SwapFundingSource,
76-
fund: (suspend (SwapRequest) -> Result<Unit>)?
76+
fund: (suspend (StatefulSwapRequest) -> Result<Unit>)?
7777
): Result<SwapId> = service.buy(
7878
scope = scope,
7979
swapId = swapId,

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/api/intents/IntentSwap.kt renamed to services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/api/intents/IntentStatefulSwap.kt

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,55 +10,55 @@ import com.getcode.opencode.internal.network.extensions.stablecoinParams
1010
import com.getcode.opencode.internal.network.extensions.verifiedMetadata
1111
import com.getcode.opencode.model.financial.Token
1212
import com.getcode.opencode.model.financial.usdf
13-
import com.getcode.opencode.model.transactions.SwapRequest
14-
import com.getcode.opencode.model.transactions.SwapResponseServerParameters
13+
import com.getcode.opencode.model.transactions.StatefulSwapRequest
14+
import com.getcode.opencode.model.transactions.StatefulSwapResponseServerParameters
1515
import com.getcode.opencode.model.transactions.SwapStartKind
1616
import com.getcode.opencode.model.transactions.VerifiedSwapMetadata
1717
import com.getcode.opencode.solana.SolanaTransaction
1818
import com.getcode.opencode.solana.TransactionBuilder
1919
import com.getcode.solana.keys.Signature
2020

21-
internal class IntentSwap(
22-
val request: SwapRequest,
21+
internal class IntentStatefulSwap(
22+
val request: StatefulSwapRequest,
2323
val metadata: VerifiedSwapMetadata,
2424
){
25-
var parameters: SwapResponseServerParameters? = null
25+
var parameters: StatefulSwapResponseServerParameters? = null
2626

27-
fun sign(parameters: SwapResponseServerParameters): List<Signature> {
27+
fun sign(parameters: StatefulSwapResponseServerParameters): List<Signature> {
2828
val transaction = transaction(parameters)
2929
return when (parameters) {
30-
is SwapResponseServerParameters.ExistingCurrency -> {
30+
is StatefulSwapResponseServerParameters.ExistingCurrency -> {
3131
transaction.signatures(request.owner.authority.keyPair, request.swapAuthority)
3232
}
33-
is SwapResponseServerParameters.NewCurrency -> {
33+
is StatefulSwapResponseServerParameters.NewCurrency -> {
3434
// For new currency, owner == swapAuthority, so only 1 unique signature needed
3535
transaction.signatures(request.owner.authority.keyPair)
3636
}
3737

38-
is SwapResponseServerParameters.Stablecoin -> {
38+
is StatefulSwapResponseServerParameters.Stablecoin -> {
3939
transaction.signatures(request.owner.authority.keyPair, request.swapAuthority)
4040
}
4141
}
4242
}
4343

44-
fun transaction(parameters: SwapResponseServerParameters): SolanaTransaction {
44+
fun transaction(parameters: StatefulSwapResponseServerParameters): SolanaTransaction {
4545
return when (parameters) {
46-
is SwapResponseServerParameters.ExistingCurrency -> TransactionBuilder.swap(
46+
is StatefulSwapResponseServerParameters.ExistingCurrency -> TransactionBuilder.swap(
4747
response = parameters,
4848
authority = request.owner.authorityPublicKey,
4949
swapAuthority = request.swapAuthority.toPublicKey(),
5050
direction = request.direction,
5151
amount = request.swapAmount.underlyingTokenAmount.quarks,
5252
)
53-
is SwapResponseServerParameters.NewCurrency -> TransactionBuilder.buyNewCurrency(
53+
is StatefulSwapResponseServerParameters.NewCurrency -> TransactionBuilder.buyNewCurrency(
5454
response = parameters,
5555
authority = request.owner.authorityPublicKey,
5656
coreMintMetadata = Token.usdf,
5757
amount = request.swapAmount.underlyingTokenAmount.quarks,
5858
feeAmount = request.feeAmount?.underlyingTokenAmount?.quarks,
5959
)
6060

61-
is SwapResponseServerParameters.Stablecoin -> TransactionBuilder.stablecoinSwap(
61+
is StatefulSwapResponseServerParameters.Stablecoin -> TransactionBuilder.stablecoinSwap(
6262
response = parameters,
6363
authority = request.owner.authorityPublicKey,
6464
swapAuthority = request.swapAuthority.toPublicKey(),
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.getcode.opencode.internal.network.api.intents
2+
3+
import com.codeinc.opencode.gen.transaction.v1.TransactionService
4+
import com.getcode.opencode.internal.network.extensions.asSignature
5+
import com.getcode.opencode.internal.network.extensions.asSolanaAccountId
6+
import com.getcode.opencode.internal.network.extensions.sign
7+
import com.getcode.opencode.model.transactions.StatelessSwapRequest
8+
import com.getcode.opencode.model.transactions.StatelessSwapServerParameters
9+
import com.getcode.opencode.solana.SolanaTransaction
10+
import com.getcode.solana.keys.Signature
11+
12+
internal class IntentStatelessSwap(
13+
val request: StatelessSwapRequest,
14+
) {
15+
var parameters: StatelessSwapServerParameters? = null
16+
17+
fun sign(parameters: StatelessSwapServerParameters): List<Signature> {
18+
val transaction = transaction(parameters)
19+
return transaction.signatures(request.owner.authority.keyPair)
20+
}
21+
22+
fun transaction(parameters: StatelessSwapServerParameters): SolanaTransaction {
23+
TODO("Stateless swap transaction building not yet implemented")
24+
}
25+
26+
fun initiate(): TransactionService.StatelessSwapRequest {
27+
val owner = request.owner.authority.keyPair
28+
val initiate = TransactionService.StatelessSwapRequest.Initiate.newBuilder()
29+
.setOwner(owner.asSolanaAccountId())
30+
.setStablecoin(
31+
TransactionService.StatelessSwapRequest.Initiate.CoinbaseStableSwapperClientParameters.newBuilder()
32+
.setFromMint(request.fromMint.asSolanaAccountId())
33+
.setToMint(request.toMint.asSolanaAccountId())
34+
.setSwapAmount(request.amount)
35+
.build()
36+
)
37+
.setWaitForFinalization(request.waitForFinalization)
38+
.apply { setSignature(sign(owner)) }
39+
.build()
40+
41+
return TransactionService.StatelessSwapRequest.newBuilder()
42+
.setInitiate(initiate)
43+
.build()
44+
}
45+
46+
fun requestToSubmitSignatures(): TransactionService.StatelessSwapRequest {
47+
val params = parameters ?: throw IllegalStateException("parameters not set")
48+
49+
return TransactionService.StatelessSwapRequest.newBuilder()
50+
.setSubmitSignatures(
51+
TransactionService.StatelessSwapRequest.SubmitSignatures.newBuilder()
52+
.addAllTransactionSignatures(sign(params).map { it.asSignature() })
53+
.build()
54+
)
55+
.build()
56+
}
57+
}

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/executors/SwapExecutor.kt renamed to services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/executors/StatefulSwapExecutor.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import com.codeinc.opencode.gen.transaction.v1.TransactionService
44
import com.getcode.opencode.internal.bidi.BidirectionalStreamReference
55
import com.getcode.opencode.internal.bidi.openBidirectionalStreamForResult
66
import com.getcode.opencode.internal.network.api.TransactionApi
7-
import com.getcode.opencode.internal.network.api.intents.IntentSwap
7+
import com.getcode.opencode.internal.network.api.intents.IntentStatefulSwap
88
import com.getcode.opencode.internal.network.extensions.toCode
99
import com.getcode.opencode.internal.network.extensions.toProps
1010
import com.getcode.opencode.model.core.errors.SubmitIntentError
1111
import com.getcode.opencode.model.core.errors.SwapError
12-
import com.getcode.opencode.model.transactions.SwapRequest
12+
import com.getcode.opencode.model.transactions.StatefulSwapRequest
1313
import com.getcode.opencode.model.transactions.SwapResult
1414
import com.getcode.opencode.model.transactions.SwapStartKind
1515
import com.getcode.opencode.model.transactions.VerifiedSwapMetadata
@@ -27,7 +27,7 @@ import kotlinx.coroutines.sync.Mutex
2727
import kotlinx.coroutines.sync.withLock
2828
import kotlin.coroutines.resume
2929

30-
typealias OcpSwapStreamReference = BidirectionalStreamReference<SwapRequest, TransactionService.StatefulSwapResponse>
30+
typealias OcpSwapStreamReference = BidirectionalStreamReference<StatefulSwapRequest, TransactionService.StatefulSwapResponse>
3131

3232

3333
/**
@@ -49,7 +49,7 @@ internal class SwapExecutor(
4949

5050
suspend fun execute(
5151
scope: CoroutineScope,
52-
request: SwapRequest,
52+
request: StatefulSwapRequest,
5353
): SwapResult = suspendCancellableCoroutine { cont ->
5454
trace(
5555
tag = "Swap",
@@ -81,7 +81,7 @@ internal class SwapExecutor(
8181
)
8282
}
8383

84-
val intent = IntentSwap(request = request, metadata = metadata)
84+
val intent = IntentStatefulSwap(request = request, metadata = metadata)
8585

8686
scope.launch {
8787
try {
@@ -120,7 +120,7 @@ internal class SwapExecutor(
120120

121121
private suspend fun openSwapStream(
122122
streamRef: OcpSwapStreamReference,
123-
intent: IntentSwap,
123+
intent: IntentStatefulSwap,
124124
): SwapResult = openBidirectionalStreamForResult(
125125
streamRef = streamRef,
126126
apiCall = api::swap,
@@ -172,7 +172,7 @@ internal class SwapExecutor(
172172
}
173173

174174
private fun handleServerParameters(
175-
intent: IntentSwap,
175+
intent: IntentStatefulSwap,
176176
serverParameters: TransactionService.StatefulSwapResponse.ServerParameters?,
177177
requestChannel: (TransactionService.StatefulSwapRequest) -> Unit,
178178
onResult: (SwapResult) -> Unit,
@@ -217,7 +217,7 @@ private fun handleServerParameters(
217217
}
218218

219219
private fun handleErrors(
220-
intent: IntentSwap,
220+
intent: IntentStatefulSwap,
221221
errorDetails: List<TransactionService.ErrorDetails>
222222
): List<String> {
223223
val errors = mutableListOf<String>()

0 commit comments

Comments
 (0)