Skip to content

Commit c112413

Browse files
committed
Refactor loop, add test, and clean CI workflow
Change ConfirmTransactionImpl to iterate signs with index (using withIndex()) and use index < signs.lastIndex to delay between broadcasts, ensuring lastHash captures the final broadcast result reliably. Add ConfirmTransactionImplTest to assert only the final transaction hash is stored and that BroadcastService.send is invoked for each signature. Also remove Android emulator-related env vars and a commented-out instrumented_test job from the Android CI workflow to simplify the workflow configuration.
1 parent d86db75 commit c112413

6 files changed

Lines changed: 166 additions & 55 deletions

File tree

.github/workflows/android-ci.yml

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ concurrency:
1111
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
1212

1313
env:
14-
ANDROID_COMPILE_SDK: "36"
15-
ANDROID_API: "35"
16-
ANDROID_AVD_NAME: "ci-emulator"
17-
ANDROID_EMULATOR_DEVICE: "pixel_6"
1814
SCCACHE_GHA_ENABLED: "true"
1915
RUSTC_WRAPPER: "sccache"
2016

@@ -54,51 +50,3 @@ jobs:
5450
env:
5551
GPR_USERNAME: ${{ github.actor }}
5652
GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57-
58-
# instrumented_test:
59-
# name: Instrumented Tests
60-
# needs: unit_test
61-
# runs-on: macos-26
62-
# defaults:
63-
# run:
64-
# working-directory: android
65-
66-
# steps:
67-
# - name: Checkout
68-
# uses: actions/checkout@v6
69-
# with:
70-
# submodules: recursive
71-
72-
# - name: Bootstrap
73-
# run: just bootstrap
74-
75-
# - name: Clean Android build outputs
76-
# run: just clean
77-
78-
# - name: Provision Android SDK and emulator
79-
# run: just provision-emulator
80-
81-
# - name: Build
82-
# run: just build
83-
# env:
84-
# GPR_USERNAME: ${{ github.actor }}
85-
# GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
86-
87-
# - name: Start emulator
88-
# run: just start-emulator
89-
90-
# - name: Build instrumented tests
91-
# run: just build-test
92-
# env:
93-
# GPR_USERNAME: ${{ github.actor }}
94-
# GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
95-
96-
# - name: Run instrumented tests
97-
# run: just test-integration
98-
# env:
99-
# GPR_USERNAME: ${{ github.actor }}
100-
# GPR_TOKEN: ${{ secrets.GITHUB_TOKEN }}
101-
102-
# - name: Stop emulator
103-
# if: always()
104-
# run: just stop-emulator

android/data/coordinators/src/main/kotlin/com/gemwallet/android/data/coordinators/confirm/ConfirmTransactionImpl.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ class ConfirmTransactionImpl(
5353
}
5454

5555
var lastHash = ""
56-
for (sign in signs) {
56+
for ((index, sign) in signs.withIndex()) {
5757
val transactionHash = broadcastService.send(account, sign, signerParams.input.getTransactionType())
58-
if (!sign.contentEquals(signs.last())) {
58+
if (index < signs.lastIndex) {
5959
delay(500)
6060
} else {
6161
lastHash = transactionHash
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package com.gemwallet.android.data.coordinators.confirm
2+
3+
import com.gemwallet.android.application.PasswordStore
4+
import com.gemwallet.android.blockchain.services.BroadcastService
5+
import com.gemwallet.android.blockchain.services.GemSignTransactionOperator
6+
import com.gemwallet.android.cases.transactions.CreateTransaction
7+
import com.gemwallet.android.data.repositories.assets.AssetsRepository
8+
import com.gemwallet.android.model.ConfirmParams
9+
import com.gemwallet.android.model.Fee
10+
import com.gemwallet.android.model.SignerParams
11+
import com.gemwallet.android.testkit.mockAccount
12+
import com.gemwallet.android.testkit.mockAssetHyperCoreHype
13+
import com.gemwallet.android.testkit.mockAssetHyperCoreUSDC
14+
import com.gemwallet.android.testkit.mockAssetInfo
15+
import com.gemwallet.android.testkit.mockSession
16+
import com.gemwallet.android.testkit.mockWallet
17+
import com.wallet.core.primitives.FeePriority
18+
import com.wallet.core.primitives.Transaction
19+
import com.wallet.core.primitives.TransactionType
20+
import io.mockk.coEvery
21+
import io.mockk.coVerify
22+
import io.mockk.every
23+
import io.mockk.mockk
24+
import kotlinx.coroutines.test.runTest
25+
import org.junit.Assert.assertEquals
26+
import org.junit.Test
27+
import uniffi.gemstone.GemSwapQuoteDataType
28+
import uniffi.gemstone.GemTransactionLoadMetadata
29+
import uniffi.gemstone.SwapperProvider
30+
import java.math.BigInteger
31+
32+
class ConfirmTransactionImplTest {
33+
34+
@Test
35+
fun hyperCoreSwapStoresOnlyFinalTransaction() = runTest {
36+
val hype = mockAssetHyperCoreHype()
37+
val usdc = mockAssetHyperCoreUSDC()
38+
val account = mockAccount(hype.id.chain)
39+
val wallet = mockWallet(accounts = listOf(account))
40+
val createTransaction = mockk<CreateTransaction>()
41+
val createdHashes = mutableListOf<String>()
42+
val signs = List(3) { byteArrayOf(1) }
43+
val passwordStore = mockk<PasswordStore> {
44+
every { getPassword(wallet.id.id) } returns "password"
45+
}
46+
val signer = mockk<GemSignTransactionOperator> {
47+
coEvery { this@mockk.invoke(wallet, any(), "password") } returns signs
48+
}
49+
val broadcastService = mockk<BroadcastService> {
50+
coEvery { send(account, any(), TransactionType.Swap) } returnsMany listOf("action:1", "action:2", "order:3")
51+
}
52+
coEvery {
53+
createTransaction.createTransaction(
54+
hash = capture(createdHashes),
55+
walletId = any(),
56+
assetId = any(),
57+
owner = any(),
58+
to = any(),
59+
state = any(),
60+
fee = any(),
61+
amount = any(),
62+
memo = any(),
63+
type = any(),
64+
metadata = any(),
65+
direction = any(),
66+
blockNumber = any(),
67+
)
68+
} returns mockk<Transaction>()
69+
70+
val result = ConfirmTransactionImpl(
71+
passwordStore = passwordStore,
72+
signTransactionOperator = signer,
73+
broadcastService = broadcastService,
74+
createTransactionsCase = createTransaction,
75+
assetsRepository = mockk<AssetsRepository>(relaxed = true),
76+
).invoke(
77+
signerParams = SignerParams(
78+
input = ConfirmParams.SwapParams(
79+
from = account,
80+
fromAsset = hype,
81+
fromAmount = BigInteger.TEN,
82+
toAsset = usdc,
83+
toAmount = BigInteger.ONE,
84+
swapData = "",
85+
memo = null,
86+
providerId = SwapperProvider.HYPERLIQUID,
87+
providerName = "Hyperliquid",
88+
protocol = "Hyperliquid",
89+
protocolId = "hyperliquid",
90+
toAddress = account.address,
91+
value = "0",
92+
slippageBps = 50u,
93+
etaInSeconds = null,
94+
dataType = GemSwapQuoteDataType.TRANSFER,
95+
),
96+
selectedData = SignerParams.Data(
97+
fee = Fee.Plain(hype.id, FeePriority.Normal, BigInteger.ZERO, emptyMap()),
98+
metadata = GemTransactionLoadMetadata.None,
99+
),
100+
feeRates = emptyList(),
101+
finalAmount = BigInteger.TEN,
102+
),
103+
session = mockSession(wallet = wallet),
104+
assetInfo = mockAssetInfo(asset = hype, owner = account, walletId = wallet.id),
105+
scope = backgroundScope,
106+
)
107+
108+
assertEquals("order:3", result)
109+
assertEquals(listOf("order:3"), createdHashes)
110+
coVerify(exactly = 3) { broadcastService.send(account, any(), TransactionType.Swap) }
111+
}
112+
}

core/crates/search_index/src/models/asset_list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use primitives::AssetList;
22
use serde::{Deserialize, Serialize};
33

44
pub const ASSET_LISTS_INDEX_NAME: &str = "asset_lists";
5-
pub const ASSET_LISTS_FILTERS: &[&str] = &[];
5+
pub const ASSET_LISTS_FILTERS: &[&str] = &["id"];
66
pub const ASSET_LISTS_SEARCH_ATTRIBUTES: &[&str] = &["name", "id"];
77
pub const ASSET_LISTS_RANKING_RULES: &[&str] = &["words", "typo", "proximity", "attribute", "exactness"];
88
pub const ASSET_LISTS_SORTS: &[&str] = &[];

ios/Features/Transfer/Sources/Services/TransferExecutor.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ extension TransferExecutor {
129129
return []
130130
case .perpetual where !transaction.id.hash.hasPrefix(Self.hyperCoreOrderIdPrefix):
131131
return []
132+
case let .swap(_, toAsset, data)
133+
where toAsset.chain == .hyperCore
134+
&& data.quote.providerData.provider == .hyperliquid
135+
&& transactionIndex < totalTransactions - 1:
136+
return []
132137
case .stake, .perpetual, .transfer, .deposit, .withdrawal, .transferNft, .swap, .tokenApprove, .generic, .account, .earn:
133138
break
134139
}

ios/Features/Transfer/Tests/Services/TransferExecutorTests.swift

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,52 @@ struct TransferExecutorTests {
7171
#expect(transactions.map(\.id.hash).sorted() == ["hash0", "hash1"])
7272
}
7373

74+
@Test
75+
func hyperCoreSpotSwapStoresOnlyFinalOrder() async throws {
76+
let hype = Asset.mockHypercore()
77+
let usdc = Asset.hypercoreSpotUSDC()
78+
let db = DB.mockAssets(assets: [.mock(asset: hype), .mock(asset: usdc)])
79+
let transactionStore = TransactionStore(db: db)
80+
let executor = TransferExecutor(
81+
signer: TransactionSignerMock(signedData: [
82+
"approve_referral",
83+
"approve_agent",
84+
"place_order",
85+
]),
86+
chainService: ChainServiceMock.mock(broadcastResponses: [
87+
"action:1",
88+
"action:2",
89+
"order:413978262893",
90+
]),
91+
assetsEnabler: .mock(),
92+
balanceService: .mock(),
93+
transactionStateScheduler: .mock(transactionStore: transactionStore),
94+
)
95+
let swapData = SwapData.mock(
96+
quote: .mock(
97+
providerData: SwapProviderData(
98+
provider: .hyperliquid,
99+
name: "Hyperliquid",
100+
protocolName: "Hyperliquid",
101+
),
102+
),
103+
)
104+
105+
let input = TransferConfirmationInput(
106+
data: .mock(type: .swap(hype, usdc, swapData)),
107+
wallet: .mock(accounts: [Account.mock(chain: .hyperCore)]),
108+
transactionData: .mock(),
109+
amount: .mock(),
110+
delegate: nil,
111+
)
112+
try await executor.execute(input: input)
113+
114+
let transactions = try transactionStore.getTransactions(state: .pending)
115+
#expect(transactions.count == 1)
116+
#expect(transactions.first?.id.hash == "order:413978262893")
117+
#expect(transactions.first?.type == .swap)
118+
}
119+
74120
@Test
75121
func hyperCoreUnstakeStoresFinalAction() async throws {
76122
let db = DB.mockAssets(assets: [

0 commit comments

Comments
 (0)