From 980f3a1fdf7438296bc5ebd4f92d33747cc29b25 Mon Sep 17 00:00:00 2001 From: 0xh3rman <119309671+0xh3rman@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:12:40 +0900 Subject: [PATCH 1/2] move algorand and stellar signing to rust --- .../com/gemwallet/android/di/DataModule.kt | 8 +- .../clients/algorand/TestAlgorandSigner.kt | 5 +- .../clients/stellar/TestStellarSigner.kt | 6 +- .../clients/algorand/AlgorandSignClient.kt | 52 ------------ .../clients/stellar/StellarSignClient.kt | 60 -------------- .../blockchain/services/SignService.kt | 4 +- core | 2 +- .../Sources/Chains/AlgorandSigner.swift | 62 -------------- .../Signer/Sources/Chains/StellarSigner.swift | 82 ------------------- ios/Packages/Signer/Sources/Signer.swift | 4 +- 10 files changed, 14 insertions(+), 271 deletions(-) delete mode 100644 android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/algorand/AlgorandSignClient.kt delete mode 100644 android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/stellar/StellarSignClient.kt delete mode 100644 ios/Packages/Signer/Sources/Chains/AlgorandSigner.swift delete mode 100644 ios/Packages/Signer/Sources/Chains/StellarSigner.swift diff --git a/android/app/src/main/kotlin/com/gemwallet/android/di/DataModule.kt b/android/app/src/main/kotlin/com/gemwallet/android/di/DataModule.kt index 157df8e1bd..1f8f34e2e4 100644 --- a/android/app/src/main/kotlin/com/gemwallet/android/di/DataModule.kt +++ b/android/app/src/main/kotlin/com/gemwallet/android/di/DataModule.kt @@ -1,13 +1,11 @@ package com.gemwallet.android.di import com.gemwallet.android.application.fiat.coordinators.SyncFiatAssets -import com.gemwallet.android.blockchain.clients.algorand.AlgorandSignClient import com.gemwallet.android.blockchain.clients.bitcoin.BitcoinSignClient import com.gemwallet.android.blockchain.clients.cardano.CardanoSignClient import com.gemwallet.android.blockchain.clients.cosmos.CosmosSignClient import com.gemwallet.android.blockchain.clients.polkadot.PolkadotSignClient import com.gemwallet.android.blockchain.clients.solana.SolanaSignClient -import com.gemwallet.android.blockchain.clients.stellar.StellarSignClient import com.gemwallet.android.blockchain.clients.sui.SuiSignClient import com.gemwallet.android.blockchain.clients.ton.TonSignClient import com.gemwallet.android.blockchain.clients.tron.TronSignClient @@ -67,15 +65,15 @@ object DataModule { ChainType.Tron -> TronSignClient(it) ChainType.Xrp -> XrpSignClient(it) - ChainType.Algorand -> AlgorandSignClient(it) - ChainType.Stellar -> StellarSignClient(it) ChainType.Polkadot -> PolkadotSignClient(it) ChainType.Cardano -> CardanoSignClient(it) ChainType.Ethereum, ChainType.Aptos, ChainType.Sui, ChainType.HyperCore, - ChainType.Near -> return@mapNotNull null + ChainType.Near, + ChainType.Algorand, + ChainType.Stellar -> return@mapNotNull null } } + listOf(SignService()), ) diff --git a/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/algorand/TestAlgorandSigner.kt b/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/algorand/TestAlgorandSigner.kt index 649389d093..f4760b3e7e 100644 --- a/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/algorand/TestAlgorandSigner.kt +++ b/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/algorand/TestAlgorandSigner.kt @@ -1,6 +1,7 @@ package com.gemwallet.android.blockchain.clients.algorand import com.gemwallet.android.blockchain.includeLibs +import com.gemwallet.android.blockchain.services.SignService import com.gemwallet.android.ext.asset import com.gemwallet.android.model.ConfirmParams import com.gemwallet.android.model.DestinationAddress @@ -28,7 +29,7 @@ class TestAlgorandSigner { @Test fun testAlgorandNativeSign() { val privateKey = HDWallet(TEST_PHRASE, "").getKeyForCoin(CoinType.ALGORAND) - val signer = AlgorandSignClient(Chain.Algorand) + val signer = SignService() val sign = runBlocking { signer.signNativeTransfer( @@ -62,4 +63,4 @@ class TestAlgorandSigner { "863d47b8a3736e64c42033b2e013d23fae630e2182f241c513530bd72ea9680558c034218ab0863" + "d47b8a474797065a3706179", String(sign.first())) } -} +} \ No newline at end of file diff --git a/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/stellar/TestStellarSigner.kt b/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/stellar/TestStellarSigner.kt index 1cc572add9..9e8d4a0fb0 100644 --- a/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/stellar/TestStellarSigner.kt +++ b/android/blockchain/src/androidTest/kotlin/com/gemwallet/android/blockchain/clients/stellar/TestStellarSigner.kt @@ -1,8 +1,8 @@ package com.gemwallet.android.blockchain.clients.Stellar import com.gemwallet.android.blockchain.clients.stellar.StellarChainData -import com.gemwallet.android.blockchain.clients.stellar.StellarSignClient import com.gemwallet.android.blockchain.includeLibs +import com.gemwallet.android.blockchain.services.SignService import com.gemwallet.android.ext.asset import com.gemwallet.android.math.toHexString import com.gemwallet.android.model.ConfirmParams @@ -32,7 +32,7 @@ class TestStellarSigner { val hdWallet = HDWallet(TEST_PHRASE, "") val privateKey = hdWallet.getKeyForCoin(CoinType.STELLAR) val from = hdWallet.getAddressForCoin(CoinType.STELLAR) - val signer = StellarSignClient(Chain.Stellar) + val signer = SignService() val sign = runBlocking { signer.signNativeTransfer( @@ -65,4 +65,4 @@ class TestStellarSigner { "252514f4f56334863745975742f4b7972316e4c62424d34527952674370327576664f7954354c326" + "94d45507554797861726533515a446b544d62734756417142413d3d", sign.first().toHexString()) } -} +} \ No newline at end of file diff --git a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/algorand/AlgorandSignClient.kt b/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/algorand/AlgorandSignClient.kt deleted file mode 100644 index 788a77ac67..0000000000 --- a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/algorand/AlgorandSignClient.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.gemwallet.android.blockchain.clients.algorand - -import com.gemwallet.android.blockchain.clients.SignClient -import com.gemwallet.android.blockchain.operators.walletcore.WCChainTypeProxy -import com.gemwallet.android.math.toHexString -import com.gemwallet.android.model.ChainSignData -import com.gemwallet.android.model.ConfirmParams -import com.gemwallet.android.model.Fee -import com.google.protobuf.ByteString -import com.wallet.core.primitives.Chain -import wallet.core.java.AnySigner -import wallet.core.jni.Base64 -import wallet.core.jni.proto.Algorand -import java.math.BigInteger - -class AlgorandSignClient( - private val chain: Chain -) : SignClient { - - override suspend fun signNativeTransfer( - params: ConfirmParams.TransferParams.Native, - chainData: ChainSignData, - finalAmount: BigInteger, - fee: Fee, - privateKey: ByteArray - ): List { - val chainData = (chainData as? AlgorandChainData) - ?: throw Exception("bad params") - val input = Algorand.SigningInput.newBuilder().apply { - this.genesisId = chainData.chainId - this.genesisHash = ByteString.copyFrom(Base64.decode(chainData.block)) - if (params.memo() != null) { - this.note = params.memo()?.let { ByteString.copyFrom(it.toByteArray()) } - } - this.firstRound = chainData.sequence.toLong() - this.lastRound = chainData.sequence.toLong() + 1000 - this.fee = (fee as Fee.Plain).amount.toLong() - this.privateKey = ByteString.copyFrom(privateKey) - this.transfer = Algorand.Transfer.newBuilder().apply { - this.toAddress = params.destination().address - this.amount = finalAmount.toLong() - }.build() - }.build() - val output = AnySigner.sign(input, WCChainTypeProxy().invoke(chain), Algorand.SigningOutput.parser()) - if (!output.errorMessage.isNullOrEmpty()) { - throw Exception(output.errorMessage) - } - return listOf(output.encoded.toByteArray().toHexString("").toByteArray()) - } - - override fun supported(chain: Chain): Boolean = this.chain == chain -} \ No newline at end of file diff --git a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/stellar/StellarSignClient.kt b/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/stellar/StellarSignClient.kt deleted file mode 100644 index 8747c915da..0000000000 --- a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/clients/stellar/StellarSignClient.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.gemwallet.android.blockchain.clients.stellar - -import com.gemwallet.android.blockchain.clients.SignClient -import com.gemwallet.android.blockchain.operators.walletcore.WCChainTypeProxy -import com.gemwallet.android.model.ChainSignData -import com.gemwallet.android.model.ConfirmParams -import com.gemwallet.android.model.Fee -import com.google.protobuf.ByteString -import com.wallet.core.primitives.Chain -import uniffi.gemstone.FeeOption -import wallet.core.java.AnySigner -import wallet.core.jni.StellarPassphrase -import wallet.core.jni.proto.Stellar -import java.math.BigInteger - -class StellarSignClient( - private val chain: Chain -) : SignClient { - - override suspend fun signNativeTransfer( - params: ConfirmParams.TransferParams.Native, - chainData: ChainSignData, - finalAmount: BigInteger, - fee: Fee, - privateKey: ByteArray - ): List { - val chainData = (chainData as? StellarChainData) - ?: throw Exception("bad params") - val input = Stellar.SigningInput.newBuilder().apply { - this.passphrase = StellarPassphrase.STELLAR.toString() - this.fee = (fee as Fee.Plain).amount.toInt() - this.sequence = chainData.sequence.toLong() - this.account = params.from.address - if (!params.memo().isNullOrEmpty()) { - this.memoText = Stellar.MemoText.newBuilder().apply { - this.text = params.memo()!! - }.build() - } - if (fee.options.contains(FeeOption.TOKEN_ACCOUNT_CREATION.name)) { - this.opCreateAccount = Stellar.OperationCreateAccount.newBuilder().apply { - this.destination = params.destination().address - this.amount = finalAmount.toLong() - }.build() - } else { - this.opPayment = Stellar.OperationPayment.newBuilder().apply { - this.destination = params.destination().address - this.amount = finalAmount.toLong() - }.build() - } - this.privateKey = ByteString.copyFrom(privateKey) - }.build() - val output = AnySigner.sign(input, WCChainTypeProxy().invoke(chain), Stellar.SigningOutput.parser()) - if (!output.errorMessage.isNullOrEmpty()) { - throw Exception(output.errorMessage) - } - return listOf(output.signature.toByteArray()) - } - - override fun supported(chain: Chain): Boolean = this.chain == chain -} \ No newline at end of file diff --git a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/SignService.kt b/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/SignService.kt index 75d8ea936e..931faad2b8 100644 --- a/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/SignService.kt +++ b/android/blockchain/src/main/kotlin/com/gemwallet/android/blockchain/services/SignService.kt @@ -313,7 +313,9 @@ class SignService : SignClient { ChainType.Aptos, ChainType.Sui, ChainType.HyperCore, - ChainType.Near -> true + ChainType.Near, + ChainType.Algorand, + ChainType.Stellar -> true else -> false } } diff --git a/core b/core index 77aa557782..e67f66f806 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 77aa5577823c4b208befd2ce71f7c5687a496362 +Subproject commit e67f66f80622a0955c4c9a2a5603b430daf39513 diff --git a/ios/Packages/Signer/Sources/Chains/AlgorandSigner.swift b/ios/Packages/Signer/Sources/Chains/AlgorandSigner.swift deleted file mode 100644 index 2c3d509b36..0000000000 --- a/ios/Packages/Signer/Sources/Chains/AlgorandSigner.swift +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c). Gem Wallet. All rights reserved. - -import Foundation -import Primitives -import WalletCore - -struct AlgorandSigner: Signable { - func sign(input: SignerInput, message: AlgorandSigningInput.OneOf_MessageOneof, privateKey: Data) throws -> String { - let input = try AlgorandSigningInput.with { - $0.genesisID = try input.metadata.getChainId() - $0.genesisHash = try input.metadata.getBlockHash().base64Encoded() - if let memo = input.memo, !memo.isEmpty { - $0.note = try memo.encodedData() - } - $0.firstRound = try input.metadata.getSequence() - $0.lastRound = try input.metadata.getSequence() + 1000 - $0.fee = input.fee.gasPrice.asUInt - $0.messageOneof = message - $0.privateKey = privateKey - } - let output: AlgorandSigningOutput = AnySigner.sign(input: input, coin: .algorand) - - if !output.errorMessage.isEmpty { - throw AnyError(output.errorMessage) - } - - return output.encoded.hexString - } - - func signTransfer(input: SignerInput, privateKey: Data) throws -> String { - try sign( - input: input, - message: .transfer(.with { - $0.toAddress = input.destinationAddress - $0.amount = input.value.asUInt - }), - privateKey: privateKey, - ) - } - - func signTokenTransfer(input: SignerInput, privateKey: Data) throws -> String { - try sign( - input: input, - message: .assetTransfer(.with { - $0.toAddress = input.destinationAddress - $0.amount = input.value.asUInt - $0.assetID = try input.asset.getTokenIdAsInt().asUInt64 - }), - privateKey: privateKey, - ) - } - - func signAccountAction(input: SignerInput, privateKey: Data) throws -> String { - try sign( - input: input, - message: .assetOptIn(.with { - $0.assetID = try input.asset.getTokenIdAsInt().asUInt64 - }), - privateKey: privateKey, - ) - } -} diff --git a/ios/Packages/Signer/Sources/Chains/StellarSigner.swift b/ios/Packages/Signer/Sources/Chains/StellarSigner.swift deleted file mode 100644 index 05911245a5..0000000000 --- a/ios/Packages/Signer/Sources/Chains/StellarSigner.swift +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c). Gem Wallet. All rights reserved. - -import Foundation -import Primitives -import WalletCore - -struct StellarSigner: Signable { - func sign(input: SignerInput, operation: StellarSigningInput.OneOf_OperationOneof, privateKey: Data) throws -> String { - let input = try StellarSigningInput.with { - $0.passphrase = StellarPassphrase.stellar.description - $0.fee = Int32(input.fee.totalFee) - $0.sequence = try Int64(input.metadata.getSequence()) - $0.account = input.senderAddress - if let memo = input.memo { - $0.memoText = .with { - $0.text = memo - } - } - $0.operationOneof = operation - $0.privateKey = privateKey - } - let output: StellarSigningOutput = AnySigner.sign(input: input, coin: .stellar) - - if !output.errorMessage.isEmpty { - throw AnyError(output.errorMessage) - } - - return output.signature - } - - func signTransfer(input: SignerInput, privateKey: Data) throws -> String { - if input.fee.options.contains(where: { $0.key == .tokenAccountCreation }) { - try sign( - input: input, - operation: .opCreateAccount(.with { - $0.destination = input.destinationAddress - $0.amount = input.value.asInt64 - }), - privateKey: privateKey, - ) - } else { - try sign( - input: input, - operation: .opPayment(.with { - $0.destination = input.destinationAddress - $0.amount = input.value.asInt64 - }), - privateKey: privateKey, - ) - } - } - - func signTokenTransfer(input: SignerInput, privateKey: Data) throws -> String { - let (issuer, symbol) = try input.asset.id.twoSubTokenIds() - return try sign( - input: input, - operation: .opPayment(.with { - $0.asset = .with { - $0.alphanum4 = symbol - $0.issuer = issuer - } - $0.destination = input.destinationAddress - $0.amount = input.value.asInt64 - }), - privateKey: privateKey, - ) - } - - func signAccountAction(input: SignerInput, privateKey: Data) throws -> String { - let (issuer, symbol) = try input.asset.id.twoSubTokenIds() - return try sign( - input: input, - operation: .opChangeTrust(.with { - $0.asset = .with { - $0.issuer = issuer - $0.alphanum4 = symbol - } - }), - privateKey: privateKey, - ) - } -} diff --git a/ios/Packages/Signer/Sources/Signer.swift b/ios/Packages/Signer/Sources/Signer.swift index a28e6f9915..53d41d6b1e 100644 --- a/ios/Packages/Signer/Sources/Signer.swift +++ b/ios/Packages/Signer/Sources/Signer.swift @@ -75,14 +75,12 @@ public struct Signer: Sendable { func signer(for chain: Chain) -> Signable { switch chain.type { case .solana: SolanaSigner() - case .ethereum, .sui, .hyperCore, .aptos, .near: ChainSigner(chain: chain) + case .ethereum, .sui, .hyperCore, .aptos, .near, .stellar, .algorand: ChainSigner(chain: chain) case .cosmos: CosmosSigner() case .ton: TonSigner() case .tron: TronSigner() case .bitcoin: BitcoinSigner() case .xrp: XrpSigner() - case .stellar: StellarSigner() - case .algorand: AlgorandSigner() case .polkadot: PolkadotSigner() case .cardano: CardanoSigner() } From ee2b8eee5aa177754268be8f540175643a6afc9d Mon Sep 17 00:00:00 2001 From: 0xh3rman <119309671+0xh3rman@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:08:37 +0900 Subject: [PATCH 2/2] update core --- ios/justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/justfile b/ios/justfile index 9f42484a28..9b59872f7b 100644 --- a/ios/justfile +++ b/ios/justfile @@ -1,6 +1,6 @@ XCBEAUTIFY_ARGS := "--quieter --is-ci" BUILD_THREADS := `sysctl -n hw.ncpu` -SIMULATOR_NAME := env_var_or_default("SIMULATOR_NAME", "iPhone 17") +SIMULATOR_NAME := env("SIMULATOR_NAME", "iPhone 17") SIMULATOR_DEST := "platform=iOS Simulator,name=" + SIMULATOR_NAME DERIVED_DATA := "build/DerivedData" SPM_CACHE := "build/SourcePackages"