Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate Keystone #1911

Merged
merged 28 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
58adc71
[Keystone] feat: add entry for adding keystone owner key
zhiying-fan Mar 27, 2023
4f49798
[Keystone] fix: delete the duplicated logic in QR code activity, and …
zhiying-fan Mar 27, 2023
963873e
docs: add weekly sync up
zhiying-fan Apr 7, 2023
f7076e2
feat: parse xpub
zhiying-fan Apr 12, 2023
db03464
docs: update weekly sync up
zhiying-fan Apr 14, 2023
d19ffcc
feat: support live key
zhiying-fan Apr 16, 2023
5c8256e
feat: store keystone owner key
zhiying-fan Apr 24, 2023
fd74809
feat: show request signature placeholder view
zhiying-fan Apr 25, 2023
cde38c8
feat: render unsigned ur code
zhiying-fan Apr 27, 2023
4b9f41e
feat: parse signature
zhiying-fan Apr 27, 2023
4c8c705
feat: submit the signature
zhiying-fan Apr 29, 2023
cf73602
refactor: improve error handling for code scanner
zhiying-fan Apr 30, 2023
85c4fb4
feat: error handling for signing
zhiying-fan May 9, 2023
3945e41
test: add unit tests for KeystoneSignViewModel
zhiying-fan May 11, 2023
79b192f
docs: delete weekly sync-up
zhiying-fan May 11, 2023
9179ae2
fix: add parameters name
zhiying-fan May 11, 2023
d83fdc9
fix: unit test in TransactionDetailsViewModelTest
zhiying-fan May 15, 2023
e07e4db
refactor: update tracking event names
zhiying-fan May 15, 2023
6b5150f
fix: update keystone sdk to 0.3.1
zhiying-fan May 16, 2023
54651fe
feat: update illustrations on owner info screen
zhiying-fan May 20, 2023
f9adcab
fix: update QR code using timer
zhiying-fan May 24, 2023
6653421
fix: scale the QR code on request signature screen properly
zhiying-fan May 24, 2023
e13eed3
fix: show dialog on main thread when QR code is not valid
zhiying-fan May 24, 2023
2d3f14f
fix: restart timer next time on request signature screen
zhiying-fan Jun 5, 2023
882b763
fix: removeHexPrefix for sign data
zhiying-fan Jun 14, 2023
f78ff17
fix: reset qr decoder after decode finished
zhiying-fan Jun 14, 2023
a3cea01
fix: add missing logic for keystone owner type
zhiying-fan Jun 22, 2023
943e2f5
refactor: move all keystone related files to keystone package
zhiying-fan Jun 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,9 @@ dependencies {
implementation "io.intercom.android:intercom-sdk:$versions.intercom"

implementation "com.airbnb.android:lottie:$versions.lottie"

// Keystone
implementation "com.github.KeystoneHQ:keystone-sdk-android:$versions.keystone"
}

task printVersion {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package io.gnosis.app.test

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.keystone.sdk.KeystoneEthereumSDK
import io.gnosis.data.repositories.CredentialsRepository
import io.gnosis.safe.ui.base.AppDispatchers
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneSignViewModel
import io.gnosis.safe.ui.transactions.details.SigningMode
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import org.junit.runner.RunWith
import pm.gnosis.svalinn.common.utils.QrCodeGenerator

@RunWith(AndroidJUnit4::class)
class KeystoneSignViewModelTest {

private val credentialsRepository = mockk<CredentialsRepository>()
private val qrCodeGenerator = mockk<QrCodeGenerator>()
private val appDispatchers = mockk<AppDispatchers>(relaxed = true)
private val viewModel =
KeystoneSignViewModel(credentialsRepository, qrCodeGenerator, appDispatchers)

@Test
fun parse_signature_should_return_null_when_signature_size_less_than_65() {
val result = viewModel.parseSignature(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
KeystoneEthereumSDK.DataType.PersonalMessage,
5,
SigningMode.CONFIRMATION
)

assertNull(result)
}

@Test
fun parse_signature_should_return_correct_signature_when_signature_size_more_than_65() {
val result = viewModel.parseSignature(
"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e",
KeystoneEthereumSDK.DataType.PersonalMessage,
5,
SigningMode.CONFIRMATION
)
val expected =
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"

assertEquals(expected, result)
}

@Test
fun parse_signature_should_return_correct_signature_when_signature_size_equals_to_65_for_legacy_transaction() {
val result = viewModel.parseSignature(
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001c",
KeystoneEthereumSDK.DataType.Transaction,
5,
SigningMode.CONFIRMATION
)
val expected =
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"

assertEquals(expected, result)
}

@Test
fun parse_signature_should_return_correct_signature_when_signature_size_equals_to_65_and_v_more_than_35_for_non_legacy_transaction() {
val result = viewModel.parseSignature(
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e",
KeystoneEthereumSDK.DataType.PersonalMessage,
5,
SigningMode.CONFIRMATION
)
val expected =
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005"

assertEquals(expected, result)
}

@Test
fun parse_signature_should_return_correct_signature_when_signature_size_equals_to_65_and_v_less_than_35_for_non_legacy_transaction() {
val result = viewModel.parseSignature(
"3f109ebc816a7ac4d245922527246ff018c4272edc33c85d799389867fa2ae526a6a81efdf33f8efa96f9c455de5f2fded46410f0072ceb00d0ae9825d2354e01b",
KeystoneEthereumSDK.DataType.PersonalMessage,
5,
SigningMode.CONFIRMATION
)
val expected =
"3f109ebc816a7ac4d245922527246ff018c4272edc33c85d799389867fa2ae526a6a81efdf33f8efa96f9c455de5f2fded46410f0072ceb00d0ae9825d2354e01f"

assertEquals(expected, result)
}
}
4 changes: 4 additions & 0 deletions app/src/main/java/io/gnosis/safe/ErrorHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.gnosis.safe.ui.safe.add.*
import io.gnosis.safe.ui.settings.owner.InvalidPrivateKey
import io.gnosis.safe.ui.settings.owner.InvalidSeedPhrase
import io.gnosis.safe.ui.settings.owner.KeyAlreadyImported
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneSignFailed
import io.gnosis.safe.ui.transactions.details.MismatchingSafeTxHash
import pm.gnosis.utils.HttpCodes
import pm.gnosis.utils.exceptions.InvalidAddressException
Expand Down Expand Up @@ -92,6 +93,8 @@ sealed class Error(

object Error1114: Error(1114, null, R.string.error_client_address_not_matching_network_reason, R.string.error_client_address_not_matching_network_fix)

object Error2100: Error(2100, null, R.string.error_client_keystone_sign_reason, R.string.error_client_keystone_sign_fix)

object Error6357 : Error(6357, null, R.string.error_client_UD_invalid_domain_reason, R.string.error_client_UD_invalid_domain_fix)
object Error6358 : Error(6358, null, R.string.error_client_UD_name_not_registered_reason, R.string.error_client_UD_name_not_registered_fix)

Expand Down Expand Up @@ -139,6 +142,7 @@ fun Throwable.toError(): Error =
this is EnsInvalidError -> Error.Error1108
this is SafeNotFound -> Error.Error1109
this is InvalidName -> Error.Error1110
this is KeystoneSignFailed -> Error.Error2100

// Network-related errors
this is HttpException -> {
Expand Down
18 changes: 17 additions & 1 deletion app/src/main/java/io/gnosis/safe/Tracker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ class Tracker internal constructor(
firebaseAnalytics.setUserProperty(Param.NUM_KEYS_LEDGER, numKeysLedger.toString())
}

fun setNumKeysKeystone(numKeysKeystone: Int) {
firebaseAnalytics.setUserProperty(Param.NUM_KEYS_KEYSTONE, numKeysKeystone.toString())
}

fun setPushInfo(enabled: Boolean) {
firebaseAnalytics.setUserProperty(Param.PUSH_INFO, if (enabled) ParamValues.PUSH_ENABLED else ParamValues.PUSH_DISABLED)
}
Expand Down Expand Up @@ -99,6 +103,10 @@ class Tracker internal constructor(
logEvent(Event.KEY_IMPORTED_LEDGER, null)
}

fun logKeystoneKeyImported() {
logEvent(Event.KEY_IMPORTED_KEYSTONE, null)
}

fun logKeyDeleted() {
logEvent(name = Event.KEY_DELETED, attrs = null)
}
Expand Down Expand Up @@ -207,11 +215,14 @@ class Tracker internal constructor(
val KEY_GENERATED = "user_key_generated"
val KEY_IMPORTED = "user_key_imported"
val KEY_IMPORTED_LEDGER = "user_ledger_nano_x_key_imported"
val KEY_IMPORTED_KEYSTONE = "user_keystone_key_imported"
val KEY_DELETED = "user_key_deleted"
val TRANSACTION_CONFIRMED = "user_transaction_confirmed"
val TRANSACTION_CONFIRMED_LEDGER = "user_transaction_confirmed_ledger_nano_x"
zhiying-fan marked this conversation as resolved.
Show resolved Hide resolved
val TRANSACTION_CONFIRMED_KEYSTONE = "user_transaction_confirmed_keystone"
val TRANSACTION_REJECTED = "user_transaction_rejected"
val TRANSACTION_REJECTED_LEDGER = "user_transaction_rejected_ledger_nano_x"
val TRANSACTION_REJECTED_KEYSTONE = "user_transaction_rejected_keystone"
val TRANSACTION_EXEC_EDIT_FEE_FIELDS = "user_edit_exec_tx_fee_fields"
val TRANSACTION_EXEC_KEY_CHANGE = "user_select_exec_key_change"
val TRANSACTION_EXEC_FAILED = "user_exec_tx_failed"
Expand All @@ -236,6 +247,7 @@ class Tracker internal constructor(
val NUM_KEYS_GENERATED = "num_keys_generated"
val NUM_KEYS_IMPORTED = "num_keys_imported"
val NUM_KEYS_LEDGER = "num_keys_ledger_nano_x"
val NUM_KEYS_KEYSTONE = "num_keys_keystone"
val KEY_IMPORT_TYPE = "import_type"
val PASSCODE_IS_SET = "passcode_is_set"
val TX_EXEC_FIELDS = "fields"
Expand Down Expand Up @@ -289,6 +301,7 @@ enum class ScreenId(val value: String) {
OWNER_INFO("screen_owner_info"),
OWNER_GENERATE_INFO("screen_owner_generate_info"),
OWNER_LEDGER_INFO("screen_owner_ledger_nano_x_info"),
OWNER_KEYSTONE_INFO("screen_owner_keystone_info"),
OWNER_ENTER_SEED("screen_owner_enter_seed"),
OWNER_ENTER_NAME("screen_owner_enter_name"),
OWNER_EDIT_NAME("screen_owner_edit_name"),
Expand Down Expand Up @@ -329,6 +342,7 @@ enum class ScreenId(val value: String) {
SETTINGS_SAFE_EDIT_NAME("screen_settings_safe_edit_name"),
SETTINGS_SAFE_ADVANCED("screen_settings_safe_advanced"),
SCANNER("screen_camera"),
SCANNER_KEYSTONE("screen_keystone_scan"),
OWNER_SELECT_ACCOUNT("screen_owner_select_account"),
OWNER_SELECT_LEDGER_ACCOUNT("screen_owner_ledger_account"),
PASSCODE_CREATE("screen_passcode_create"),
Expand All @@ -341,5 +355,7 @@ enum class ScreenId(val value: String) {
UPDATE_DEPRECATED_SOON("screen_update_deprecated_soon"),
UPDATE_NEW_VERSION("screen_update_new_version"),
LEDGER_DEVICE_LIST("screen_ledger_nano_x_device"),
LEDGER_SIGN("screen_ledger_nano_x_sign")
LEDGER_SIGN("screen_ledger_nano_x_sign"),
KEYSTONE_OWNER_SELECTION("screen_keystone_owner_selection"),
KEYSTONE_REQUEST_SIGNATURE("screen_keystone_request_signature")
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ import io.gnosis.safe.ui.settings.owner.export.OwnerExportKeyFragment
import io.gnosis.safe.ui.settings.owner.export.OwnerExportSeedFragment
import io.gnosis.safe.ui.settings.owner.intro.OwnerInfoFragment
import io.gnosis.safe.ui.settings.owner.intro.OwnerInfoGenerateFragment
import io.gnosis.safe.ui.settings.owner.intro.OwnerInfoKeystoneFragment
import io.gnosis.safe.ui.settings.owner.intro.OwnerInfoLedgerFragment
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneRequestSignatureFragment
import io.gnosis.safe.ui.settings.owner.ledger.LedgerOwnerSelectionFragment
import io.gnosis.safe.ui.settings.owner.ledger.LedgerTabsFragment
import io.gnosis.safe.ui.settings.owner.ledger.LedgerDeviceListFragment
import io.gnosis.safe.ui.settings.owner.ledger.LedgerSignDialog
import io.gnosis.safe.ui.settings.owner.list.OwnerListFragment
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneOwnerSelectionFragment
import io.gnosis.safe.ui.settings.owner.selection.OwnerSelectionFragment
import io.gnosis.safe.ui.settings.safe.AdvancedSafeSettingsFragment
import io.gnosis.safe.ui.settings.safe.SafeSettingsEditNameFragment
Expand Down Expand Up @@ -108,10 +111,14 @@ interface ViewComponent {

fun inject(fragment: OwnerInfoLedgerFragment)

fun inject(fragment: OwnerInfoKeystoneFragment)

fun inject(fragment: OwnerSelectionFragment)

fun inject(fragment: LedgerOwnerSelectionFragment)

fun inject(fragment: KeystoneOwnerSelectionFragment)

fun inject(fragment: LedgerTabsFragment)

fun inject(fragment: OwnerSeedPhraseFragment)
Expand Down Expand Up @@ -192,6 +199,8 @@ interface ViewComponent {

fun inject(fragment: LedgerDeviceListFragment)

fun inject(fragment: KeystoneRequestSignatureFragment)

// Dialogs
fun inject(dialog: EnsInputDialog)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import io.gnosis.safe.ui.transactions.paging.TransactionPagingProvider
import io.gnosis.safe.utils.BalanceFormatter
import io.gnosis.safe.utils.MnemonicKeyAndAddressDerivator
import io.gnosis.safe.utils.ParamSerializer
import io.gnosis.safe.utils.PublicKeyAndAddressDerivator
import io.gnosis.safe.workers.HeimdallWorkerFactory
import io.intercom.android.sdk.push.IntercomPushClient
import okhttp3.CertificatePinner
Expand Down Expand Up @@ -242,6 +243,10 @@ class ApplicationModule(private val application: Application) {
@Singleton
fun providesMnemonicKeyAndAddressDerivator(bip39: Bip39): MnemonicKeyAndAddressDerivator = MnemonicKeyAndAddressDerivator(bip39)

@Provides
@Singleton
fun providesPublicKeyAndAddressDerivator(): PublicKeyAndAddressDerivator = PublicKeyAndAddressDerivator()

@Provides
@Singleton
fun providesEncryptionManager(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.gnosis.data.db.HeimdallDatabase.Companion.MIGRATION_3_4
import io.gnosis.data.db.HeimdallDatabase.Companion.MIGRATION_4_5
import io.gnosis.data.db.HeimdallDatabase.Companion.MIGRATION_5_6
import io.gnosis.data.db.HeimdallDatabase.Companion.MIGRATION_6_7
import io.gnosis.data.db.HeimdallDatabase.Companion.MIGRATION_7_8
import io.gnosis.data.db.daos.ChainDao
import io.gnosis.data.db.daos.OwnerDao
import io.gnosis.data.db.daos.SafeDao
Expand All @@ -30,7 +31,8 @@ class DatabaseModule {
MIGRATION_3_4,
MIGRATION_4_5,
MIGRATION_5_6,
MIGRATION_6_7
MIGRATION_6_7,
MIGRATION_7_8
)
.build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import io.gnosis.safe.ui.settings.owner.ledger.LedgerDeviceListViewModel
import io.gnosis.safe.ui.settings.owner.ledger.LedgerOwnerSelectionViewModel
import io.gnosis.safe.ui.settings.owner.ledger.LedgerSignViewModel
import io.gnosis.safe.ui.settings.owner.list.OwnerListViewModel
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneOwnerSelectionViewModel
import io.gnosis.safe.ui.settings.owner.selection.OwnerSelectionViewModel
import io.gnosis.safe.ui.settings.safe.AdvancedSafeSettingsViewModel
import io.gnosis.safe.ui.settings.safe.SafeSettingsEditNameViewModel
Expand All @@ -41,6 +42,7 @@ import io.gnosis.safe.ui.splash.SplashViewModel
import io.gnosis.safe.ui.transactions.TransactionListViewModel
import io.gnosis.safe.ui.transactions.TransactionsViewModel
import io.gnosis.safe.ui.transactions.details.ConfirmRejectionViewModel
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneSignViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsActionViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsViewModel
import io.gnosis.safe.ui.transactions.execution.TxEditFeeViewModel
Expand Down Expand Up @@ -125,6 +127,16 @@ abstract class ViewModelFactoryModule {
@ViewModelKey(LedgerOwnerSelectionViewModel::class)
abstract fun providesLedgerOwnerSelectionViewModel(viewModel: LedgerOwnerSelectionViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(KeystoneOwnerSelectionViewModel::class)
abstract fun providesKeystoneOwnerSelectionViewModel(viewModel: KeystoneOwnerSelectionViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(KeystoneSignViewModel::class)
abstract fun providesKeystoneSignViewModel(viewModel: KeystoneSignViewModel): ViewModel

@Binds
@IntoMap
@ViewModelKey(OwnerGenerateViewModel::class)
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/io/gnosis/safe/di/modules/ViewModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import io.gnosis.safe.ui.settings.owner.ledger.LedgerDeviceListViewModel
import io.gnosis.safe.ui.settings.owner.ledger.LedgerOwnerSelectionViewModel
import io.gnosis.safe.ui.settings.owner.ledger.LedgerSignViewModel
import io.gnosis.safe.ui.settings.owner.list.OwnerListViewModel
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneOwnerSelectionViewModel
import io.gnosis.safe.ui.settings.owner.selection.OwnerSelectionViewModel
import io.gnosis.safe.ui.settings.safe.AdvancedSafeSettingsViewModel
import io.gnosis.safe.ui.settings.safe.SafeSettingsEditNameViewModel
Expand All @@ -45,6 +46,7 @@ import io.gnosis.safe.ui.splash.SplashViewModel
import io.gnosis.safe.ui.transactions.TransactionListViewModel
import io.gnosis.safe.ui.transactions.TransactionsViewModel
import io.gnosis.safe.ui.transactions.details.ConfirmRejectionViewModel
import io.gnosis.safe.ui.settings.owner.keystone.KeystoneSignViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsActionViewModel
import io.gnosis.safe.ui.transactions.details.TransactionDetailsViewModel
import io.gnosis.safe.ui.transactions.execution.TxEditFeeViewModel
Expand Down Expand Up @@ -154,6 +156,14 @@ class ViewModule(
@ForView
fun providesLedgerOwnerSelectionViewModel(provider: ViewModelProvider) = provider[LedgerOwnerSelectionViewModel::class.java]

@Provides
@ForView
fun providesKeystoneOwnerSelectionViewModel(provider: ViewModelProvider) = provider[KeystoneOwnerSelectionViewModel::class.java]

@Provides
@ForView
fun providesKeystoneSignViewModel(provider: ViewModelProvider) = provider[KeystoneSignViewModel::class.java]

@Provides
@ForView
fun providesSettingsViewModel(provider: ViewModelProvider) = provider[SettingsViewModel::class.java]
Expand Down
18 changes: 17 additions & 1 deletion app/src/main/java/io/gnosis/safe/helpers/AddressInputHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import io.gnosis.safe.R
import io.gnosis.safe.ScreenId
import io.gnosis.safe.Tracker
import io.gnosis.safe.databinding.BottomSheetAddressInputBinding
import io.gnosis.safe.qrscanner.HasFinished
import io.gnosis.safe.qrscanner.IsValid
import io.gnosis.safe.qrscanner.QRCodeScanActivity
import io.gnosis.safe.ui.base.fragment.BaseFragment
import io.gnosis.safe.ui.dialogs.EnsInputDialog
Expand Down Expand Up @@ -69,7 +71,10 @@ class AddressInputHelper(
}

bottomSheetAddressInputQrTouch.setOnClickListener {
QRCodeScanActivity.startForResult(fragment)
QRCodeScanActivity.startForResult(
fragment = fragment,
scannedValueValidator = ::validator
)
tracker.logScreen(ScreenId.SCANNER, selectedChain.chainId)
dismiss()
}
Expand Down Expand Up @@ -167,4 +172,15 @@ class AddressInputHelper(
private fun handleError(error: Throwable, input: String) {
errorCallback.invoke(error, input)
}

private fun validator(scannedValue: String): Pair<IsValid, HasFinished> {
val address =
if (scannedValue.contains(":")) {
parseEthereumAddress(scannedValue.split(":")[1])
} else {
parseEthereumAddress(scannedValue)
}

return Pair(address != null, true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ class SendAssetReviewViewModel
it.asEthereumAddressString()
}.toTypedArray(),
signingMode = SigningMode.INITIATE_TRANSFER,
chain = activeSafe.chain,
safeTxHash = safeTxHash
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ class OwnerAddOptionsFragment : BaseViewBindingFragment<FragmentOwnerAddOptionsB
itemConnectLedger.setOnClickListener {
findNavController().navigate(OwnerAddOptionsFragmentDirections.actionOwnerAddOptionsFragmentToOwnerInfoLedgerFragment())
}
itemConnectKeystone.setOnClickListener {
findNavController().navigate(OwnerAddOptionsFragmentDirections.actionOwnerAddOptionsFragmentToOwnerInfoKeystoneFragment())
}
}
}
}
Loading