Skip to content

Commit 191fc02

Browse files
authored
Revert "fix(oc): match the Coinbase stable swapper upgrade (#1439)" (#1440)
This reverts commit 7f75c0d.
1 parent 19263a3 commit 191fc02

13 files changed

Lines changed: 76 additions & 131 deletions

services/opencode/src/main/kotlin/com/getcode/opencode/internal/solana/extensions/PublicKey.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ internal fun PublicKey.Companion.deriveCoinbaseVaultTokenAccountAddress(vault: P
172172
)
173173
}
174174

175+
internal fun PublicKey.Companion.deriveCoinbaseWhitelistAddress(): ProgramDerivedAccount {
176+
return findProgramAddress(
177+
seeds = listOf("address_whitelist".toByteArray(Charsets.UTF_8)),
178+
programId = CoinbaseStableSwapperProgram.address,
179+
)
180+
}
181+
175182
/// FindProgramAddress mirrors the implementation of the Solana SDK's FindProgramAddress. Its primary
176183
/// use case (for Kin and Agora) is for deriving associated accounts.
177184
///

services/opencode/src/main/kotlin/com/getcode/opencode/internal/solana/model/CoinbaseStablecoinPoolAccount.kt

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,23 @@ import com.getcode.solana.keys.PublicKey
55
/**
66
* Represents the on-chain CoinbaseStableSwapper liquidity pool account.
77
*
8-
* Mirrors `LiquidityPool` in coinbase/stable-swapper (`programs/stable-swapper/src/state.rs`)
9-
* after the role-based authority migration (coinbase/stable-swapper#20). Layout:
10-
*
11-
* ```
12-
* [8 discriminator]
13-
* [32 pause_authority][32 unpause_authority][32 treasury_authority][32 configure_authority]
14-
* [32 fee_recipient]
15-
* [4 + 32n withdraw_recipients][4 + 32n supported_tokens]
16-
* [8 fee_rate][1 swaps_paused][1 liquidity_paused][1 bump]
17-
* ```
18-
*
19-
* Only the fixed-offset prefix is parsed; the client needs the fee recipient and nothing
20-
* after the vectors.
8+
* Layout:
9+
* [8 discriminator][32 operations_authority][32 pause_authority][32 fee_recipient]...
2110
*/
2211
internal data class CoinbaseStablecoinPoolAccount(
23-
val pauseAuthority: PublicKey,
24-
val unpauseAuthority: PublicKey,
25-
val treasuryAuthority: PublicKey,
26-
val configureAuthority: PublicKey,
2712
val feeRecipient: PublicKey,
2813
) {
2914
companion object {
30-
// LiquidityPool discriminator: [66, 38, 17, 64, 188, 80, 68, 129]
31-
private val DISCRIMINATOR = byteArrayOf(66, 38, 17, 64, 188.toByte(), 80, 68, 129.toByte())
32-
33-
private const val KEY_SIZE = 32
34-
private const val PAUSE_AUTHORITY_OFFSET = 8
35-
private const val UNPAUSE_AUTHORITY_OFFSET = PAUSE_AUTHORITY_OFFSET + KEY_SIZE
36-
private const val TREASURY_AUTHORITY_OFFSET = UNPAUSE_AUTHORITY_OFFSET + KEY_SIZE
37-
private const val CONFIGURE_AUTHORITY_OFFSET = TREASURY_AUTHORITY_OFFSET + KEY_SIZE
38-
private const val FEE_RECIPIENT_OFFSET = CONFIGURE_AUTHORITY_OFFSET + KEY_SIZE
39-
private const val FIXED_PREFIX_SIZE = FEE_RECIPIENT_OFFSET + KEY_SIZE
15+
private const val FEE_RECIPIENT_OFFSET = 8 + 32 + 32 // discriminator + ops_authority + pause_authority
4016

4117
fun fromAccountData(data: ByteArray): CoinbaseStablecoinPoolAccount {
42-
require(data.size >= FIXED_PREFIX_SIZE) {
43-
"Account data too short: expected at least $FIXED_PREFIX_SIZE bytes, got ${data.size}"
44-
}
45-
require(data.sliceArray(0 until DISCRIMINATOR.size).contentEquals(DISCRIMINATOR)) {
46-
"Account data is not a CoinbaseStableSwapper LiquidityPool (discriminator mismatch)"
18+
require(data.size >= FEE_RECIPIENT_OFFSET + 32) {
19+
"Account data too short: expected at least ${FEE_RECIPIENT_OFFSET + 32} bytes, got ${data.size}"
4720
}
21+
val feeRecipientBytes = data.sliceArray(FEE_RECIPIENT_OFFSET until FEE_RECIPIENT_OFFSET + 32)
4822
return CoinbaseStablecoinPoolAccount(
49-
pauseAuthority = data.keyAt(PAUSE_AUTHORITY_OFFSET),
50-
unpauseAuthority = data.keyAt(UNPAUSE_AUTHORITY_OFFSET),
51-
treasuryAuthority = data.keyAt(TREASURY_AUTHORITY_OFFSET),
52-
configureAuthority = data.keyAt(CONFIGURE_AUTHORITY_OFFSET),
53-
feeRecipient = data.keyAt(FEE_RECIPIENT_OFFSET),
23+
feeRecipient = PublicKey(feeRecipientBytes.toList()),
5424
)
5525
}
56-
57-
private fun ByteArray.keyAt(offset: Int): PublicKey =
58-
PublicKey(sliceArray(offset until offset + KEY_SIZE).toList())
5926
}
6027
}

services/opencode/src/main/kotlin/com/getcode/opencode/internal/solana/model/CoinbaseSwapAccounts.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.getcode.opencode.internal.solana.extensions.deriveAssociatedAccount
44
import com.getcode.opencode.internal.solana.extensions.deriveCoinbasePoolAddress
55
import com.getcode.opencode.internal.solana.extensions.deriveCoinbaseTokenVaultAddress
66
import com.getcode.opencode.internal.solana.extensions.deriveCoinbaseVaultTokenAccountAddress
7+
import com.getcode.opencode.internal.solana.extensions.deriveCoinbaseWhitelistAddress
78
import com.getcode.solana.keys.PublicKey
89

910
internal data class CoinbaseSwapAccounts(
@@ -12,6 +13,7 @@ internal data class CoinbaseSwapAccounts(
1213
val outVault: PublicKey,
1314
val inVaultTokenAccount: PublicKey,
1415
val outVaultTokenAccount: PublicKey,
16+
val whitelist: PublicKey,
1517
) {
1618
fun feeRecipientTokenAccount(feeRecipient: PublicKey, fromMint: PublicKey): PublicKey {
1719
return PublicKey.deriveAssociatedAccount(
@@ -27,13 +29,15 @@ internal data class CoinbaseSwapAccounts(
2729
val outVault = PublicKey.deriveCoinbaseTokenVaultAddress(pool, toMint).publicKey
2830
val inVaultTokenAccount = PublicKey.deriveCoinbaseVaultTokenAccountAddress(inVault).publicKey
2931
val outVaultTokenAccount = PublicKey.deriveCoinbaseVaultTokenAccountAddress(outVault).publicKey
32+
val whitelist = PublicKey.deriveCoinbaseWhitelistAddress().publicKey
3033

3134
return CoinbaseSwapAccounts(
3235
pool = pool,
3336
inVault = inVault,
3437
outVault = outVault,
3538
inVaultTokenAccount = inVaultTokenAccount,
3639
outVaultTokenAccount = outVaultTokenAccount,
40+
whitelist = whitelist,
3741
)
3842
}
3943
}

services/opencode/src/main/kotlin/com/getcode/opencode/internal/solana/programs/CoinbaseStableSwapperProgram_Swap.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ internal class CoinbaseStableSwapperProgram_Swap(
1818
private val fromMint: PublicKey,
1919
private val toMint: PublicKey,
2020
private val user: PublicKey,
21+
private val whitelist: PublicKey,
2122
private val amountIn: Long,
2223
private val minAmountOut: Long,
2324
) : InstructionType {
@@ -37,6 +38,7 @@ internal class CoinbaseStableSwapperProgram_Swap(
3738
AccountMeta.readonly(publicKey = fromMint),
3839
AccountMeta.readonly(publicKey = toMint),
3940
AccountMeta.writable(publicKey = user, signer = true),
41+
AccountMeta.readonly(publicKey = whitelist),
4042
AccountMeta.readonly(publicKey = TokenProgram.address),
4143
AccountMeta.readonly(publicKey = AssociatedTokenProgram.address),
4244
AccountMeta.readonly(publicKey = SystemProgram.address),

services/opencode/src/main/kotlin/com/getcode/opencode/solana/swap/CoinbaseStablecoinSwapperInstructions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ internal fun buildStablecoinSwapperInstructions(
130130
fromMint = fromMintMetadata.address,
131131
toMint = toMintMetadata.address,
132132
user = swapAuthority,
133+
whitelist = swapAccounts.whitelist,
133134
amountIn = amount,
134135
minAmountOut = minOutput,
135136
).instruction()

services/opencode/src/main/kotlin/com/getcode/opencode/solana/swap/UsdcDepositSweepInstructions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ internal fun buildStatelessSwapInstructions(
111111
fromMint = fromMint.address,
112112
toMint = toMint.address,
113113
user = owner,
114+
whitelist = swapAccounts.whitelist,
114115
amountIn = amount,
115116
minAmountOut = amount, // 1:1 stable pair
116117
).instruction()

services/opencode/src/main/kotlin/com/getcode/opencode/solana/swap/UsdcToUsdfSwapInstructions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ internal fun buildUsdcToUsdfSwapInstructions(
103103
fromMint = Mint.usdc,
104104
toMint = Mint.usdf,
105105
user = sender,
106+
whitelist = swapAccounts.whitelist,
106107
amountIn = amount,
107108
minAmountOut = 0,
108109
).instruction()

services/opencode/src/main/kotlin/com/getcode/opencode/solana/swap/UsdfDepositInstructions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ internal fun buildUsdfDepositInstructions(
9797
fromMint = Mint.usdc,
9898
toMint = Mint.usdf,
9999
user = sender,
100+
whitelist = swapAccounts.whitelist,
100101
amountIn = amount,
101102
minAmountOut = amount, // 1:1 stable pair
102103
).instruction()

services/opencode/src/test/kotlin/com/getcode/opencode/internal/solana/extensions/PdaDerivationTest.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,22 @@ class PdaDerivationTest {
247247
assertNotEquals(result1.publicKey, result2.publicKey)
248248
}
249249

250+
// --- deriveCoinbaseWhitelistAddress ---
251+
252+
@Test
253+
fun coinbaseWhitelistIsDeterministic() {
254+
val result1 = PublicKey.deriveCoinbaseWhitelistAddress()
255+
val result2 = PublicKey.deriveCoinbaseWhitelistAddress()
256+
assertEquals(result1.publicKey, result2.publicKey)
257+
assertEquals(result1.bump, result2.bump)
258+
}
259+
260+
@Test
261+
fun coinbaseWhitelistIsOffCurve() {
262+
val result = PublicKey.deriveCoinbaseWhitelistAddress()
263+
assertTrue(!Ed25519.onCurve(result.publicKey.bytes.toByteArray()))
264+
}
265+
250266
// --- Coinbase PDA chain: pool -> vault -> vaultTokenAccount ---
251267

252268
@Test
@@ -255,10 +271,11 @@ class PdaDerivationTest {
255271
val mint = testKey(10)
256272
val vault = PublicKey.deriveCoinbaseTokenVaultAddress(pool, mint).publicKey
257273
val vaultTA = PublicKey.deriveCoinbaseVaultTokenAccountAddress(vault).publicKey
274+
val whitelist = PublicKey.deriveCoinbaseWhitelistAddress().publicKey
258275

259-
// All three should be distinct
260-
val all = setOf(pool, vault, vaultTA)
261-
assertEquals(3, all.size, "pool, vault, and vaultTA should all be distinct")
276+
// All four should be distinct
277+
val all = setOf(pool, vault, vaultTA, whitelist)
278+
assertEquals(4, all.size, "pool, vault, vaultTA, and whitelist should all be distinct")
262279
}
263280

264281
// --- Known value: well-known associated token address ---

services/opencode/src/test/kotlin/com/getcode/opencode/internal/solana/model/CoinbaseStablecoinPoolAccountTest.kt

Lines changed: 0 additions & 73 deletions
This file was deleted.

0 commit comments

Comments
 (0)