diff --git a/app/src/main/java/jp/co/soramitsu/app/root/di/RootDependencies.kt b/app/src/main/java/jp/co/soramitsu/app/root/di/RootDependencies.kt index 4cd029344d..ec3f2da0cf 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/di/RootDependencies.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/di/RootDependencies.kt @@ -5,6 +5,7 @@ import jp.co.soramitsu.common.data.network.rpc.ConnectionManager import jp.co.soramitsu.common.mixin.api.NetworkStateMixin import jp.co.soramitsu.common.resources.ResourceManager import jp.co.soramitsu.feature_account_api.domain.interfaces.AccountRepository +import jp.co.soramitsu.feature_wallet_api.di.WalletUpdaters import jp.co.soramitsu.feature_wallet_api.domain.interfaces.WalletRepository import jp.co.soramitsu.feature_wallet_api.domain.model.BuyTokenRegistry @@ -22,4 +23,6 @@ interface RootDependencies { fun buyTokenRegistry(): BuyTokenRegistry fun resourceManager(): ResourceManager + + fun walletUpdaters(): WalletUpdaters } \ No newline at end of file diff --git a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt index c57c624b0e..b6b731634f 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/di/RootFeatureModule.kt @@ -2,22 +2,32 @@ package jp.co.soramitsu.app.root.di import dagger.Module import dagger.Provides +import jp.co.soramitsu.app.root.domain.CompositeUpdater import jp.co.soramitsu.app.root.domain.RootInteractor import jp.co.soramitsu.common.di.scope.FeatureScope +import jp.co.soramitsu.core_api.data.network.Updater import jp.co.soramitsu.feature_account_api.domain.interfaces.AccountRepository -import jp.co.soramitsu.feature_wallet_api.domain.interfaces.WalletRepository +import jp.co.soramitsu.feature_wallet_api.di.WalletUpdaters import jp.co.soramitsu.feature_wallet_api.domain.model.BuyTokenRegistry @Module class RootFeatureModule { + @Provides + @FeatureScope + fun provideRootUpdater( + walletUpdaters: WalletUpdaters + ): Updater { + return CompositeUpdater(walletUpdaters.updaters) + } + @Provides @FeatureScope fun provideRootInteractor( accountRepository: AccountRepository, - walletRepository: WalletRepository, + rootUpdater: Updater, buyTokenRegistry: BuyTokenRegistry ): RootInteractor { - return RootInteractor(accountRepository, buyTokenRegistry, walletRepository) + return RootInteractor(accountRepository, rootUpdater, buyTokenRegistry) } } \ No newline at end of file diff --git a/app/src/main/java/jp/co/soramitsu/app/root/domain/CompositeUpdater.kt b/app/src/main/java/jp/co/soramitsu/app/root/domain/CompositeUpdater.kt new file mode 100644 index 0000000000..92eac22bf5 --- /dev/null +++ b/app/src/main/java/jp/co/soramitsu/app/root/domain/CompositeUpdater.kt @@ -0,0 +1,22 @@ +package jp.co.soramitsu.app.root.domain + +import jp.co.soramitsu.core_api.data.network.Updater +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.withContext + +class CompositeUpdater( + private val updaters: List +) : Updater { + + constructor(vararg updaters: Updater) : this(updaters.toList()) + + override suspend fun listenForUpdates(): Unit = withContext(Dispatchers.IO) { + val coroutines = updaters.map { + async { it.listenForUpdates() } + } + + coroutines.awaitAll() + } +} \ No newline at end of file diff --git a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt index d2c0cf5111..c0b66b6abb 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/domain/RootInteractor.kt @@ -1,28 +1,21 @@ package jp.co.soramitsu.app.root.domain +import jp.co.soramitsu.core_api.data.network.Updater import jp.co.soramitsu.feature_account_api.domain.interfaces.AccountRepository -import jp.co.soramitsu.feature_account_api.domain.model.Node -import jp.co.soramitsu.feature_wallet_api.domain.interfaces.WalletRepository import jp.co.soramitsu.feature_wallet_api.domain.model.BuyTokenRegistry import jp.co.soramitsu.feature_wallet_impl.data.buyToken.ExternalProvider -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flowOn class RootInteractor( private val accountRepository: AccountRepository, - private val buyTokenRegistry: BuyTokenRegistry, - private val walletRepository: WalletRepository + private val rootUpdater: Updater, + private val buyTokenRegistry: BuyTokenRegistry ) { + fun selectedNodeFlow() = accountRepository.selectedNodeFlow() - suspend fun listenForAccountUpdates(networkType: Node.NetworkType) = accountRepository.selectedAccountFlow() - .filter { it.network.type == networkType } - .distinctUntilChanged { old, new -> old.address == new.address } - .flowOn(Dispatchers.IO) - .collectLatest { walletRepository.listenForUpdates(it) } + suspend fun listenForUpdates() { + rootUpdater.listenForUpdates() + } fun isBuyProviderRedirectLink(link: String) = buyTokenRegistry.availableProviders .filterIsInstance() diff --git a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt index 789f7a0cf4..3a79e0184f 100644 --- a/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt +++ b/app/src/main/java/jp/co/soramitsu/app/root/presentation/RootViewModel.kt @@ -61,7 +61,7 @@ class RootViewModel( } }.flowOn(Dispatchers.IO) .collectLatest { - interactor.listenForAccountUpdates(it.networkType) + interactor.listenForUpdates() } } diff --git a/build.gradle b/build.gradle index c3b6908c37..57a8f9168a 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,7 @@ buildscript { bouncyCastleVersion = '1.60' - fearlessLibVersion = '1.0.35' + fearlessLibVersion = '1.0.47' gifVersion = '1.2.19' diff --git a/common/src/main/java/jp/co/soramitsu/common/utils/FearlessLibExt.kt b/common/src/main/java/jp/co/soramitsu/common/utils/FearlessLibExt.kt index 441e858583..5583c16a68 100644 --- a/common/src/main/java/jp/co/soramitsu/common/utils/FearlessLibExt.kt +++ b/common/src/main/java/jp/co/soramitsu/common/utils/FearlessLibExt.kt @@ -1,8 +1,36 @@ package jp.co.soramitsu.common.utils +import io.emeraldpay.polkaj.scale.ScaleCodecReader +import io.emeraldpay.polkaj.scale.ScaleCodecWriter +import jp.co.soramitsu.fearless_utils.extensions.fromHex +import jp.co.soramitsu.fearless_utils.extensions.toHexString +import jp.co.soramitsu.fearless_utils.hash.Hasher.blake2b256 +import jp.co.soramitsu.fearless_utils.scale.EncodableStruct +import jp.co.soramitsu.fearless_utils.scale.Schema +import jp.co.soramitsu.fearless_utils.scale.dataType.DataType import jp.co.soramitsu.fearless_utils.ss58.SS58Encoder import jp.co.soramitsu.feature_account_api.domain.model.Node +import java.io.ByteArrayOutputStream fun SS58Encoder.encode(publicKey: ByteArray, networkType: Node.NetworkType): String { return encode(publicKey, networkType.runtimeConfiguration.addressByte) +} + +fun DataType.fromHex(hex: String): T { + val codecReader = ScaleCodecReader(hex.fromHex()) + + return read(codecReader) +} + +fun DataType.toByteArray(value: T): ByteArray { + val stream = ByteArrayOutputStream() + val writer = ScaleCodecWriter(stream) + + write(writer, value) + + return stream.toByteArray() +} + +fun > EncodableStruct.hash(): String { + return schema.toByteArray(this).blake2b256().toHexString(withPrefix = true) } \ No newline at end of file diff --git a/common/src/main/java/jp/co/soramitsu/common/utils/SuspendableProperty.kt b/common/src/main/java/jp/co/soramitsu/common/utils/SuspendableProperty.kt new file mode 100644 index 0000000000..a2b9fb6a54 --- /dev/null +++ b/common/src/main/java/jp/co/soramitsu/common/utils/SuspendableProperty.kt @@ -0,0 +1,19 @@ +package jp.co.soramitsu.common.utils + +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.first + +class SuspendableProperty { + private val value = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + + fun invalidate() { + value.resetReplayCache() + } + + fun set(new: T) { + value.tryEmit(new) // always successful, since BufferOverflow.DROP_OLDEST is used + } + + suspend fun get(): T = value.first() +} \ No newline at end of file diff --git a/core-api/.gitignore b/core-api/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/core-api/.gitignore @@ -0,0 +1 @@ +/build diff --git a/core-api/build.gradle b/core-api/build.gradle new file mode 100644 index 0000000000..ca3a8003ae --- /dev/null +++ b/core-api/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'java-library' +apply plugin: 'kotlin' + +compileKotlin { + kotlinOptions { + freeCompilerArgs = ["-Xallow-result-return-type"] + } +} + +dependencies { + implementation coroutinesDep +} \ No newline at end of file diff --git a/core-api/src/main/java/jp/co/soramitsu/core_api/data/network/Updater.kt b/core-api/src/main/java/jp/co/soramitsu/core_api/data/network/Updater.kt new file mode 100644 index 0000000000..aad8b91f16 --- /dev/null +++ b/core-api/src/main/java/jp/co/soramitsu/core_api/data/network/Updater.kt @@ -0,0 +1,8 @@ +package jp.co.soramitsu.core_api.data.network + +interface Updater { + /** + * Implementations should be aware of cancellation + */ + suspend fun listenForUpdates() +} \ No newline at end of file diff --git a/feature-account-api/build.gradle b/feature-account-api/build.gradle index ca3a8003ae..f7e37f15c6 100644 --- a/feature-account-api/build.gradle +++ b/feature-account-api/build.gradle @@ -9,4 +9,6 @@ compileKotlin { dependencies { implementation coroutinesDep + + implementation project(':core-api') } \ No newline at end of file diff --git a/feature-account-impl/src/main/java/jp/co/soramitsu/feature_account_impl/data/network/blockchain/AccountSubstrateSourceImpl.kt b/feature-account-impl/src/main/java/jp/co/soramitsu/feature_account_impl/data/network/blockchain/AccountSubstrateSourceImpl.kt index 4a242d6371..3a62a9fea0 100644 --- a/feature-account-impl/src/main/java/jp/co/soramitsu/feature_account_impl/data/network/blockchain/AccountSubstrateSourceImpl.kt +++ b/feature-account-impl/src/main/java/jp/co/soramitsu/feature_account_impl/data/network/blockchain/AccountSubstrateSourceImpl.kt @@ -2,7 +2,7 @@ package jp.co.soramitsu.feature_account_impl.data.network.blockchain import jp.co.soramitsu.common.data.network.rpc.SocketSingleRequestExecutor import jp.co.soramitsu.fearless_utils.wsrpc.mappers.nonNull -import jp.co.soramitsu.fearless_utils.wsrpc.mappers.string +import jp.co.soramitsu.fearless_utils.wsrpc.mappers.pojo import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.system.NodeNetworkTypeRequest class AccountSubstrateSourceImpl( @@ -12,6 +12,6 @@ class AccountSubstrateSourceImpl( override suspend fun getNodeNetworkType(nodeHost: String): String { val request = NodeNetworkTypeRequest() - return socketRequestExecutor.executeRequest(request, nodeHost, string().nonNull()) + return socketRequestExecutor.executeRequest(request, nodeHost, pojo().nonNull()) } } \ No newline at end of file diff --git a/feature-wallet-api/build.gradle b/feature-wallet-api/build.gradle index 2610b1eb55..a641af565e 100644 --- a/feature-wallet-api/build.gradle +++ b/feature-wallet-api/build.gradle @@ -11,4 +11,5 @@ dependencies { implementation coroutinesDep implementation project(':feature-account-api') + api project(':core-api') } \ No newline at end of file diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/di/WalletFeatureApi.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/di/WalletFeatureApi.kt index 6d508b9dbb..ccce0016d2 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/di/WalletFeatureApi.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/di/WalletFeatureApi.kt @@ -4,6 +4,9 @@ import jp.co.soramitsu.feature_wallet_api.domain.interfaces.WalletRepository import jp.co.soramitsu.feature_wallet_api.domain.model.BuyTokenRegistry interface WalletFeatureApi { + + fun provideUpdaters(): WalletUpdaters + fun provideWalletRepository(): WalletRepository fun provideTokenRegistry(): BuyTokenRegistry diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/di/WalletUpdaters.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/di/WalletUpdaters.kt new file mode 100644 index 0000000000..178ee1f6ec --- /dev/null +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/di/WalletUpdaters.kt @@ -0,0 +1,5 @@ +package jp.co.soramitsu.feature_wallet_api.di + +import jp.co.soramitsu.core_api.data.network.Updater + +class WalletUpdaters(val updaters: List) \ No newline at end of file diff --git a/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/domain/interfaces/WalletRepository.kt b/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/domain/interfaces/WalletRepository.kt index 1ffb2e9b35..59370185af 100644 --- a/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/domain/interfaces/WalletRepository.kt +++ b/feature-wallet-api/src/main/java/jp/co/soramitsu/feature_wallet_api/domain/interfaces/WalletRepository.kt @@ -36,5 +36,7 @@ interface WalletRepository { suspend fun checkTransferValidity(transfer: Transfer): TransferValidityStatus - suspend fun listenForUpdates(account: Account) + suspend fun listenForAccountInfoUpdates(account: Account) + + suspend fun listenForStakingLedgerUpdates(account: Account) } \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/SubstrateRemoteSource.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/SubstrateRemoteSource.kt index c19d9e718a..f2e324eeb9 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/SubstrateRemoteSource.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/SubstrateRemoteSource.kt @@ -7,14 +7,14 @@ import jp.co.soramitsu.feature_account_api.domain.model.Node import jp.co.soramitsu.feature_wallet_api.domain.model.Transfer import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.BalanceChange import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.FeeResponse -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.ActiveEraInfo import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.StakingLedger -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoSchema +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.TransferExtrinsic import kotlinx.coroutines.flow.Flow interface SubstrateRemoteSource { - suspend fun fetchAccountInfo(address: String, networkType: Node.NetworkType): EncodableStruct + suspend fun fetchAccountInfo(address: String, networkType: Node.NetworkType): EncodableStruct suspend fun getTransferFee( account: Account, @@ -37,8 +37,8 @@ interface SubstrateRemoteSource { suspend fun getActiveEra(): EncodableStruct - suspend fun fetchAccountTransactionInBlock( + suspend fun fetchAccountTransfersInBlock( blockHash: String, account: Account - ): List> + ): List } \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/WssSubstrateSource.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/WssSubstrateSource.kt index 0f060da753..49b22de7dd 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/WssSubstrateSource.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/WssSubstrateSource.kt @@ -4,7 +4,6 @@ package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain import jp.co.soramitsu.fearless_utils.encrypt.EncryptionType import jp.co.soramitsu.fearless_utils.encrypt.KeypairFactory -import jp.co.soramitsu.fearless_utils.encrypt.Signer import jp.co.soramitsu.fearless_utils.encrypt.model.Keypair import jp.co.soramitsu.fearless_utils.runtime.Module import jp.co.soramitsu.fearless_utils.runtime.storageKey @@ -16,10 +15,12 @@ import jp.co.soramitsu.fearless_utils.wsrpc.executeAsync import jp.co.soramitsu.fearless_utils.wsrpc.mappers.nonNull import jp.co.soramitsu.fearless_utils.wsrpc.mappers.pojo import jp.co.soramitsu.fearless_utils.wsrpc.mappers.scale -import jp.co.soramitsu.fearless_utils.wsrpc.mappers.string import jp.co.soramitsu.fearless_utils.wsrpc.request.DeliveryType import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.account.AccountInfoRequest import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.chain.RuntimeVersionRequest +import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.storage.GetStorageRequest +import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.storage.SubscribeStorageRequest +import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.storage.storageChange import jp.co.soramitsu.fearless_utils.wsrpc.subscription.response.SubscriptionChange import jp.co.soramitsu.fearless_utils.wsrpc.subscriptionFlow import jp.co.soramitsu.feature_account_api.domain.model.Account @@ -27,42 +28,23 @@ import jp.co.soramitsu.feature_account_api.domain.model.CryptoType import jp.co.soramitsu.feature_account_api.domain.model.Node import jp.co.soramitsu.feature_wallet_api.domain.model.Transfer import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.extrinsics.TransferRequest -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.extrinsics.signExtrinsic import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests.FeeCalculationRequest import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests.GetBlockRequest -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests.GetStorageRequest import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests.NextAccountIndexRequest -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests.SubscribeStorageRequest import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.BalanceChange import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.FeeResponse import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.RuntimeVersion import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.SignedBlock -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.feeFrozen -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.free -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.miscFrozen -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.reserved import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountId -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo.data -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo.nonce -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo.refCount import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.ActiveEraInfo -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Call -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Call.args -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Call.callIndex -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.ExtrinsicPayloadValue -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Signature -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignedExtrinsic -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignedExtrinsic.accountId -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignedExtrinsic.call -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignedExtrinsic.signature import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.StakingLedger -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic.byteLength -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic.signedExtrinsic -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.TransferArgs -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.TransferArgs.recipientId +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountData +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoFactory +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoSchema +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoSchemaV28 +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.EncodeExtrinsicParams +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.TransferExtrinsic +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.TransferExtrinsicFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged @@ -75,21 +57,23 @@ import java.math.BigInteger class WssSubstrateSource( private val socketService: SocketService, - private val signer: Signer, private val keypairFactory: KeypairFactory, + private val accountInfoFactory: AccountInfoFactory, + private val extrinsicFactory: TransferExtrinsicFactory, private val sS58Encoder: SS58Encoder ) : SubstrateRemoteSource { override suspend fun fetchAccountInfo( address: String, networkType: Node.NetworkType - ): EncodableStruct { + ): EncodableStruct { val publicKeyBytes = getAccountId(address) val request = AccountInfoRequest(publicKeyBytes) - val response = socketService.executeAsync(request, mapper = scale(AccountInfo)) + val response = socketService.executeAsync(request) + val accountInfo = (response.result as? String)?.let { accountInfoFactory.decode(it) } - return response.result ?: emptyAccountInfo() + return accountInfo ?: emptyAccountInfo() } override suspend fun getTransferFee(account: Account, transfer: Transfer): FeeResponse { @@ -111,7 +95,7 @@ class WssSubstrateSource( return socketService.executeAsync( transferRequest, - mapper = string().nonNull(), + mapper = pojo().nonNull(), deliveryType = DeliveryType.AT_MOST_ONCE ) } @@ -124,7 +108,7 @@ class WssSubstrateSource( .map(::buildBalanceChange) } - override suspend fun fetchAccountTransactionInBlock(blockHash: String, account: Account): List> { + override suspend fun fetchAccountTransfersInBlock(blockHash: String, account: Account): List { val request = GetBlockRequest(blockHash) val block = socketService.executeAsync(request, mapper = pojo().nonNull()) @@ -137,11 +121,9 @@ class WssSubstrateSource( val request = SubscribeStorageRequest(key) return socketService.subscriptionFlow(request) - .map { it.params.result.getSingleChange() } + .map { it.storageChange().getSingleChange() } .distinctUntilChanged() - .flatMapLatest { change -> - val controllerId = change.value - + .flatMapLatest { controllerId -> if (controllerId != null) { subscribeToLedger(stashAddress, controllerId) } else { @@ -152,7 +134,7 @@ class WssSubstrateSource( override suspend fun getActiveEra(): EncodableStruct { val key = Module.Staking.ActiveEra.storageKey() - val request = GetStorageRequest(key) + val request = GetStorageRequest(listOf(key)) return socketService.executeAsync(request, mapper = scale(ActiveEraInfo).nonNull()) } @@ -165,12 +147,12 @@ class WssSubstrateSource( val request = SubscribeStorageRequest(key) return socketService.subscriptionFlow(request) - .map { it.params.result.getSingleChange() } + .map { it.storageChange().getSingleChange() } .map { change -> - if (change.value.isNullOrBlank()) { - createEmptyLedger(stashAddress) + if (change != null) { + StakingLedger.read(change) } else { - StakingLedger.read(change.value!!) + createEmptyLedger(stashAddress) } } } @@ -185,12 +167,13 @@ class WssSubstrateSource( } } - private fun buildBalanceChange(subscriptionChange: SubscriptionChange): BalanceChange { - val block = subscriptionChange.params.result.block + private suspend fun buildBalanceChange(subscriptionChange: SubscriptionChange): BalanceChange { + val storageChange = subscriptionChange.storageChange() - val change = subscriptionChange.params.result.getSingleChange() + val block = storageChange.block - val accountInfo = if (change.value != null) AccountInfo.read(change.value!!) else emptyAccountInfo() + val change = storageChange.getSingleChange() + val accountInfo = readAccountInfo(change) return BalanceChange(block, accountInfo) } @@ -203,8 +186,8 @@ class WssSubstrateSource( account: Account, transfer: Transfer, keypair: Keypair - ): EncodableStruct = withContext(Dispatchers.Default) { - val runtimeInfo = getRuntimeVersion() + ): String = withContext(Dispatchers.Default) { + val runtimeVersion = getRuntimeVersion() val cryptoType = mapCryptoTypeToEncryption(account.cryptoType) val accountIdValue = getAccountId(account.address) @@ -212,54 +195,18 @@ class WssSubstrateSource( val genesis = account.network.type.runtimeConfiguration.genesisHash val genesisBytes = Hex.decode(genesis) - val callStruct = createTransferCall(account.network.type, transfer.recipient, transfer.amountInPlanks) - - val payload = ExtrinsicPayloadValue { payload -> - payload[ExtrinsicPayloadValue.call] = callStruct - payload[ExtrinsicPayloadValue.nonce] = currentNonce - payload[ExtrinsicPayloadValue.specVersion] = runtimeInfo.specVersion.toUInt() - payload[ExtrinsicPayloadValue.transactionVersion] = runtimeInfo.transactionVersion.toUInt() - - payload[ExtrinsicPayloadValue.genesis] = genesisBytes - payload[ExtrinsicPayloadValue.blockHash] = genesisBytes - } - - val signatureValue = Signature( + val params = EncodeExtrinsicParams( + senderId = accountIdValue, + recipientId = getAccountId(transfer.recipient), + amountInPlanks = transfer.amountInPlanks, + nonce = currentNonce, + runtimeVersion = runtimeVersion, + networkType = account.network.type, encryptionType = cryptoType, - value = signer.signExtrinsic(payload, keypair, cryptoType) + genesis = genesisBytes ) - val extrinsic = SignedExtrinsic { extrinsic -> - extrinsic[accountId] = accountIdValue - extrinsic[signature] = signatureValue - extrinsic[SignedExtrinsic.nonce] = currentNonce - extrinsic[call] = callStruct - } - - val extrinsicBytes = SignedExtrinsic.toByteArray(extrinsic) - val byteLengthValue = extrinsicBytes.size.toBigInteger() - - val submittableExtrinsic = SubmittableExtrinsic { struct -> - struct[byteLength] = byteLengthValue - struct[signedExtrinsic] = extrinsic - } - - submittableExtrinsic - } - - private fun createTransferCall( - networkType: Node.NetworkType, - recipientAddress: String, - amount: BigInteger - ): EncodableStruct { - return Call { call -> - call[Call.callIndex] = networkType.runtimeConfiguration.pallets.transfers.transfer.index - - call[Call.args] = TransferArgs { args -> - args[TransferArgs.recipientId] = sS58Encoder.decode(recipientAddress) - args[TransferArgs.amount] = amount - } - } + extrinsicFactory.createEncodedExtrinsic(params, keypair) } private suspend fun getNonce(account: Account): BigInteger { @@ -284,15 +231,20 @@ class WssSubstrateSource( return socketService.executeAsync(request, mapper = pojo().nonNull()) } - private fun emptyAccountInfo() = AccountInfo { info -> - info[nonce] = 0.toUInt() - info[refCount] = 0.toUInt() + private suspend fun readAccountInfo(hex: String?): EncodableStruct { + return hex?.let { accountInfoFactory.decode(it) } ?: emptyAccountInfo() + } + + private fun emptyAccountInfo(): EncodableStruct = AccountInfoSchemaV28 { info -> + info[AccountInfoSchemaV28.nonce] = 0.toUInt() + info[AccountInfoSchemaV28.providers] = 0.toUInt() + info[AccountInfoSchemaV28.consumers] = 0.toUInt() - info[data] = AccountData { data -> - data[free] = 0.toBigInteger() - data[reserved] = 0.toBigInteger() - data[miscFrozen] = 0.toBigInteger() - data[feeFrozen] = 0.toBigInteger() + info[AccountInfoSchemaV28.data] = AccountData { data -> + data[AccountData.free] = 0.toBigInteger() + data[AccountData.reserved] = 0.toBigInteger() + data[AccountData.miscFrozen] = 0.toBigInteger() + data[AccountData.feeFrozen] = 0.toBigInteger() } } @@ -304,25 +256,16 @@ class WssSubstrateSource( } } - private suspend fun filterAccountTransactions(account: Account, extrinsics: List): List> { + private suspend fun filterAccountTransactions(account: Account, extrinsics: List): List { return withContext(Dispatchers.Default) { val currentPublicKey = getAccountId(account.address) val transfersPalette = account.network.type.runtimeConfiguration.pallets.transfers - extrinsics.filter { hex -> - val stub = SubmittableExtrinsic.readOrNull(hex) ?: return@filter false - - val callIndex = stub[signedExtrinsic][call][callIndex] - - callIndex in transfersPalette - } - .map(SubmittableExtrinsic::read) + extrinsics.mapNotNull { hex -> extrinsicFactory.decode(hex) } .filter { transfer -> - val signed = transfer[signedExtrinsic] - val sender = signed[accountId] - val receiver = signed[call][args][recipientId] + if (transfer.index !in transfersPalette) return@filter false - sender.contentEquals(currentPublicKey) || receiver.contentEquals(currentPublicKey) + transfer.senderId.contentEquals(currentPublicKey) || transfer.recipientId.contentEquals(currentPublicKey) } } } diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/Signer.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/Signer.kt index eef41155ce..6ad1d668b2 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/Signer.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/Signer.kt @@ -1,10 +1,10 @@ package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.extrinsics -import jp.co.soramitsu.fearless_utils.scale.EncodableStruct import jp.co.soramitsu.fearless_utils.encrypt.EncryptionType import jp.co.soramitsu.fearless_utils.encrypt.Signer import jp.co.soramitsu.fearless_utils.encrypt.model.Keypair -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.ExtrinsicPayloadValue +import jp.co.soramitsu.fearless_utils.scale.EncodableStruct +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.ExtrinsicPayloadValue fun Signer.signExtrinsic( payload: EncodableStruct, diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/TransferRequest.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/TransferRequest.kt index da6aab74f3..3ee589a895 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/TransferRequest.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/extrinsics/TransferRequest.kt @@ -1,12 +1,8 @@ package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.extrinsics -import jp.co.soramitsu.fearless_utils.scale.EncodableStruct import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.RuntimeRequest -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic -class TransferRequest(extrinsic: EncodableStruct) : RuntimeRequest( +class TransferRequest(extrinsic: String) : RuntimeRequest( method = "author_submitExtrinsic", - params = listOf( - SubmittableExtrinsic.toHexString(extrinsic) - ) + params = listOf(extrinsic) ) \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/FeeCalculationRequest.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/FeeCalculationRequest.kt index b414d29528..48f404fc3c 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/FeeCalculationRequest.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/FeeCalculationRequest.kt @@ -1,12 +1,8 @@ package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests -import jp.co.soramitsu.fearless_utils.scale.EncodableStruct import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.RuntimeRequest -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic -class FeeCalculationRequest(submittableExtrinsic: EncodableStruct) : RuntimeRequest( +class FeeCalculationRequest(extrinsicInHex: String) : RuntimeRequest( method = "payment_queryInfo", - params = listOf( - SubmittableExtrinsic.toHexString(submittableExtrinsic) - ) + params = listOf(extrinsicInHex) ) \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/GetStorageRequest.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/GetStorageRequest.kt deleted file mode 100644 index dc86eeea7b..0000000000 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/GetStorageRequest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests - -import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.RuntimeRequest - -class GetStorageRequest(storageKey: String) : RuntimeRequest( - "state_getStorage", - listOf( - storageKey - ) -) \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/SubscribeStorageRequest.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/SubscribeStorageRequest.kt deleted file mode 100644 index 675527c091..0000000000 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/requests/SubscribeStorageRequest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests - -import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.RuntimeRequest - -class SubscribeStorageRequest(storageKey: String) : RuntimeRequest( - "state_subscribeStorage", - listOf( - listOf( - storageKey - ) - ) -) \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/response/BalanceChange.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/response/BalanceChange.kt index c3763376ce..e4e6f99b4b 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/response/BalanceChange.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/response/BalanceChange.kt @@ -1,9 +1,9 @@ package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response import jp.co.soramitsu.fearless_utils.scale.EncodableStruct -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoSchema class BalanceChange( val block: String, - val newAccountInfo: EncodableStruct + val newAccountInfo: EncodableStruct ) \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/Balance.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/Balance.kt index ab125c66aa..b0fb118718 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/Balance.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/Balance.kt @@ -3,27 +3,10 @@ package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct import jp.co.soramitsu.fearless_utils.scale.Schema import jp.co.soramitsu.fearless_utils.scale.compactInt import jp.co.soramitsu.fearless_utils.scale.dataType.uint32 -import jp.co.soramitsu.fearless_utils.scale.schema import jp.co.soramitsu.fearless_utils.scale.sizedByteArray -import jp.co.soramitsu.fearless_utils.scale.uint128 import jp.co.soramitsu.fearless_utils.scale.uint32 import jp.co.soramitsu.fearless_utils.scale.vector -object AccountData : Schema() { - val free by uint128() - val reserved by uint128() - val miscFrozen by uint128() - val feeFrozen by uint128() -} - -object AccountInfo : Schema() { - val nonce by uint32() - - val refCount by uint32() - - val data by schema(AccountData) -} - object StakingLedger : Schema() { val stash by sizedByteArray(32) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/account/AccountInfo.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/account/AccountInfo.kt new file mode 100644 index 0000000000..6b4375c57a --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/account/AccountInfo.kt @@ -0,0 +1,38 @@ +package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account + +import jp.co.soramitsu.fearless_utils.scale.EncodableStruct +import jp.co.soramitsu.fearless_utils.scale.Field +import jp.co.soramitsu.fearless_utils.scale.Schema +import jp.co.soramitsu.fearless_utils.scale.schema +import jp.co.soramitsu.fearless_utils.scale.uint128 +import jp.co.soramitsu.fearless_utils.scale.uint32 + +abstract class AccountInfoSchema : Schema() { + abstract val nonce: Field + + abstract val data: Field> +} + +object AccountData : Schema() { + val free by uint128() + val reserved by uint128() + val miscFrozen by uint128() + val feeFrozen by uint128() +} + +object AccountInfoSchemaV27 : AccountInfoSchema() { + override val nonce by uint32() + + val refCount by uint32() + + override val data by schema(AccountData) +} + +object AccountInfoSchemaV28 : AccountInfoSchema() { + override val nonce by uint32() + + val consumers by uint32() + val providers by uint32() + + override val data by schema(AccountData) +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/account/AccountInfoFactory.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/account/AccountInfoFactory.kt new file mode 100644 index 0000000000..421a7f04c0 --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/account/AccountInfoFactory.kt @@ -0,0 +1,18 @@ +package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account + +import jp.co.soramitsu.common.utils.SuspendableProperty +import jp.co.soramitsu.fearless_utils.scale.EncodableStruct + +class AccountInfoFactory( + val isUpgradedToDualRefCount: SuspendableProperty +) { + + suspend fun decode(scale: String): EncodableStruct { + + return if (isUpgradedToDualRefCount.get()) { + AccountInfoSchemaV28.read(scale) + } else { + AccountInfoSchemaV27.read(scale) + } + } +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/SubmittableExtrinsic.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/SubmittableExtrinsic.kt similarity index 51% rename from feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/SubmittableExtrinsic.kt rename to feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/SubmittableExtrinsic.kt index 1c2d5267b6..f62c0ae359 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/SubmittableExtrinsic.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/SubmittableExtrinsic.kt @@ -1,29 +1,46 @@ -package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct +package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic -import jp.co.soramitsu.fearless_utils.extensions.toHexString -import jp.co.soramitsu.fearless_utils.scale.EncodableStruct +import io.emeraldpay.polkaj.scale.ScaleCodecReader +import io.emeraldpay.polkaj.scale.ScaleCodecWriter import jp.co.soramitsu.fearless_utils.scale.Schema import jp.co.soramitsu.fearless_utils.scale.compactInt import jp.co.soramitsu.fearless_utils.scale.custom +import jp.co.soramitsu.fearless_utils.scale.dataType.DataType import jp.co.soramitsu.fearless_utils.scale.dataType.uint8 import jp.co.soramitsu.fearless_utils.scale.pair import jp.co.soramitsu.fearless_utils.scale.schema import jp.co.soramitsu.fearless_utils.scale.sizedByteArray import jp.co.soramitsu.fearless_utils.scale.uint32 import jp.co.soramitsu.fearless_utils.scale.uint8 -import org.bouncycastle.crypto.digests.Blake2bDigest -import org.bouncycastle.jcajce.provider.digest.BCMessageDigest +import jp.co.soramitsu.fearless_utils.scale.utils.directWrite +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Era +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.EraType +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignatureType private val VERSION = "84".toUByte(radix = 16) private val TIP = 0.toBigInteger() -object SubmittableExtrinsic : Schema() { +object OpaqueCall : DataType() { + override fun conformsType(value: Any?): Boolean { + return value is ByteArray + } + + override fun read(reader: ScaleCodecReader): ByteArray { + throw NotImplementedError("Cannot decode opaque call without runtime metadata") + } + + override fun write(writer: ScaleCodecWriter, value: ByteArray) { + writer.directWrite(value) + } +} + +object SubmittableExtrinsicV27 : Schema() { val byteLength by compactInt() - val signedExtrinsic by schema(SignedExtrinsic) + val signedExtrinsic by schema(SignedExtrinsicV27) } -object SignedExtrinsic : Schema() { +object SignedExtrinsicV27 : Schema() { val version by uint8(default = VERSION) val accountId by sizedByteArray(32) @@ -36,23 +53,23 @@ object SignedExtrinsic : Schema() { val tip by compactInt(default = TIP) - val call by schema(Call) + val call by schema(TransferCallV27) } -object Call : Schema() { +object TransferCallV27 : Schema() { val callIndex by pair(uint8, uint8) - val args by schema(TransferArgs) + val args by schema(TransferArgsV27) } -object TransferArgs : Schema() { +object TransferArgsV27 : Schema() { val recipientId by sizedByteArray(32) val amount by compactInt() } object ExtrinsicPayloadValue : Schema() { - val call by schema(Call) + val call by custom(OpaqueCall) val era by custom(EraType, default = Era.Immortal) @@ -65,12 +82,4 @@ object ExtrinsicPayloadValue : Schema() { val genesis by sizedByteArray(32) val blockHash by sizedByteArray(32) -} - -object Blake2b256 : BCMessageDigest(Blake2bDigest(256)) - -fun EncodableStruct.hash(): String { - val bytes = Blake2b256.digest(SubmittableExtrinsic.toByteArray(this)) - - return bytes.toHexString(withPrefix = true) } \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/SubmittableExtrinsicV28.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/SubmittableExtrinsicV28.kt new file mode 100644 index 0000000000..3e124ead65 --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/SubmittableExtrinsicV28.kt @@ -0,0 +1,97 @@ +package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic + +import io.emeraldpay.polkaj.scale.ScaleCodecReader +import io.emeraldpay.polkaj.scale.ScaleCodecWriter +import jp.co.soramitsu.fearless_utils.scale.Schema +import jp.co.soramitsu.fearless_utils.scale.compactInt +import jp.co.soramitsu.fearless_utils.scale.custom +import jp.co.soramitsu.fearless_utils.scale.dataType.DataType +import jp.co.soramitsu.fearless_utils.scale.dataType.byte +import jp.co.soramitsu.fearless_utils.scale.dataType.byteArray +import jp.co.soramitsu.fearless_utils.scale.dataType.byteArraySized +import jp.co.soramitsu.fearless_utils.scale.dataType.compactInt +import jp.co.soramitsu.fearless_utils.scale.dataType.uint8 +import jp.co.soramitsu.fearless_utils.scale.pair +import jp.co.soramitsu.fearless_utils.scale.schema +import jp.co.soramitsu.fearless_utils.scale.uint8 +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Era +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.EraType +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignatureType +import java.math.BigInteger + +private val VERSION = "84".toUByte(radix = 16) +private val TIP = 0.toBigInteger() + +sealed class MultiAddress(val enumIndex: Int) { + class Id(val value: ByteArray) : MultiAddress(0) + class Index(val value: BigInteger) : MultiAddress(1) + class Raw(val value: ByteArray) : MultiAddress(2) + class Address32(val value: ByteArray) : MultiAddress(3) + class Address20(val value: ByteArray) : MultiAddress(4) +} + +object MultiAddressType : DataType() { + + private val idCoder = byteArraySized(32) + private val address32Coder = idCoder + private val address20Coder = byteArraySized(20) + + override fun conformsType(value: Any?) = value is MultiAddress + + override fun read(reader: ScaleCodecReader): MultiAddress { + return when (val typeIndex = reader.readByte().toInt()) { + 0 -> MultiAddress.Id(idCoder.read(reader)) + 1 -> MultiAddress.Index(compactInt.read(reader)) + 2 -> MultiAddress.Raw(byteArray.read(reader)) + 3 -> MultiAddress.Address32(address32Coder.read(reader)) + 4 -> MultiAddress.Address32(address20Coder.read(reader)) + else -> throw IllegalArgumentException("$typeIndex is not supported in MultiAddress") + } + } + + override fun write(writer: ScaleCodecWriter, multiAddress: MultiAddress) { + byte.write(writer, multiAddress.enumIndex.toByte()) + + return when (multiAddress) { + is MultiAddress.Id -> idCoder.write(writer, multiAddress.value) + is MultiAddress.Index -> compactInt.write(writer, multiAddress.value) + is MultiAddress.Raw -> byteArray.write(writer, multiAddress.value) + is MultiAddress.Address32 -> address32Coder.write(writer, multiAddress.value) + is MultiAddress.Address20 -> address20Coder.write(writer, multiAddress.value) + } + } +} + +object SubmittableExtrinsicV28 : Schema() { + val byteLength by compactInt() + + val signedExtrinsic by schema(SignedExtrinsicV28) +} + +object SignedExtrinsicV28 : Schema() { + val version by uint8(default = VERSION) + + val accountId by custom(MultiAddressType) + + val signature by custom(SignatureType) + + val era by custom(EraType, default = Era.Immortal) + + val nonce by compactInt() + + val tip by compactInt(default = TIP) + + val call by schema(TransferCallV28) +} + +object TransferCallV28 : Schema() { + val callIndex by pair(uint8, uint8) + + val args by schema(TransferArgsV28) +} + +object TransferArgsV28 : Schema() { + val recipientId by custom(MultiAddressType) + + val amount by compactInt() +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/TransferExtrinsicFactory.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/TransferExtrinsicFactory.kt new file mode 100644 index 0000000000..edc2d08353 --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/extrinsic/TransferExtrinsicFactory.kt @@ -0,0 +1,187 @@ +package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic + +import jp.co.soramitsu.common.utils.SuspendableProperty +import jp.co.soramitsu.common.utils.hash +import jp.co.soramitsu.fearless_utils.encrypt.EncryptionType +import jp.co.soramitsu.fearless_utils.encrypt.Signer +import jp.co.soramitsu.fearless_utils.encrypt.model.Keypair +import jp.co.soramitsu.fearless_utils.scale.invoke +import jp.co.soramitsu.feature_account_api.domain.model.Node +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.extrinsics.signExtrinsic +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.RuntimeVersion +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Signature +import java.math.BigInteger + +class TransferExtrinsic( + val senderId: ByteArray, + val recipientId: ByteArray, + val amountInPlanks: BigInteger, + val index: Pair, + val hash: String +) + +class EncodeExtrinsicParams( + val senderId: ByteArray, + val recipientId: ByteArray, + val amountInPlanks: BigInteger, + val nonce: BigInteger, + val runtimeVersion: RuntimeVersion, + val networkType: Node.NetworkType, + val encryptionType: EncryptionType, + val genesis: ByteArray +) + +class TransferExtrinsicFactory( + private val isUpgradedToDualRefCount: SuspendableProperty, + private val signer: Signer +) { + + suspend fun decode(scale: String): TransferExtrinsic? { + return if (isUpgradedToDualRefCount.get()) { + decodeV28(scale) + } else { + decodeV27(scale) + } + } + + suspend fun createEncodedExtrinsic( + encodeExtrinsicParams: EncodeExtrinsicParams, + keypair: Keypair + ): String { + return if (isUpgradedToDualRefCount.get()) { + createExtrinsicV28(encodeExtrinsicParams, keypair) + } else { + createExtrinsicV27(encodeExtrinsicParams, keypair) + } + } + + private fun createExtrinsicV28( + encodeExtrinsicParams: EncodeExtrinsicParams, + keypair: Keypair + ): String = with(encodeExtrinsicParams) { + + val callStruct = TransferCallV28 { call -> + call[TransferCallV28.callIndex] = networkType.runtimeConfiguration.pallets.transfers.transfer.index + + call[TransferCallV28.args] = TransferArgsV28 { args -> + args[TransferArgsV28.recipientId] = MultiAddress.Id(recipientId) + args[TransferArgsV28.amount] = amountInPlanks + } + } + + val callBytes = TransferCallV28.toByteArray(callStruct) + val payload = createExtrinsicPayloadValue(callBytes, encodeExtrinsicParams) + + val signatureValue = Signature( + encryptionType = encryptionType, + value = signer.signExtrinsic(payload, keypair, encryptionType) + ) + + val extrinsic = SignedExtrinsicV28 { extrinsic -> + extrinsic[SignedExtrinsicV28.accountId] = MultiAddress.Id(senderId) + extrinsic[SignedExtrinsicV28.signature] = signatureValue + extrinsic[SignedExtrinsicV28.nonce] = nonce + extrinsic[SignedExtrinsicV28.call] = callStruct + } + + val extrinsicBytes = SignedExtrinsicV28.toByteArray(extrinsic) + val byteLengthValue = extrinsicBytes.size.toBigInteger() + + val submittableExtrinsic = SubmittableExtrinsicV28 { struct -> + struct[SubmittableExtrinsicV28.byteLength] = byteLengthValue + struct[SubmittableExtrinsicV28.signedExtrinsic] = extrinsic + } + + SubmittableExtrinsicV28.toHexString(submittableExtrinsic) + } + + private fun createExtrinsicV27( + encodeExtrinsicParams: EncodeExtrinsicParams, + keypair: Keypair + ): String = with(encodeExtrinsicParams) { + val callStruct = TransferCallV27 { call -> + call[TransferCallV27.callIndex] = networkType.runtimeConfiguration.pallets.transfers.transfer.index + + call[TransferCallV27.args] = TransferArgsV27 { args -> + args[TransferArgsV27.recipientId] = recipientId + args[TransferArgsV27.amount] = amountInPlanks + } + } + + val callBytes = TransferCallV27.toByteArray(callStruct) + + val payload = createExtrinsicPayloadValue(callBytes, encodeExtrinsicParams) + + val signatureValue = Signature( + encryptionType = encryptionType, + value = signer.signExtrinsic(payload, keypair, encryptionType) + ) + + val extrinsic = SignedExtrinsicV27 { extrinsic -> + extrinsic[SignedExtrinsicV27.accountId] = senderId + extrinsic[SignedExtrinsicV27.signature] = signatureValue + extrinsic[SignedExtrinsicV27.nonce] = nonce + extrinsic[SignedExtrinsicV27.call] = callStruct + } + + val extrinsicBytes = SignedExtrinsicV27.toByteArray(extrinsic) + val byteLengthValue = extrinsicBytes.size.toBigInteger() + + val submittableExtrinsic = SubmittableExtrinsicV27 { struct -> + struct[SubmittableExtrinsicV27.byteLength] = byteLengthValue + struct[SubmittableExtrinsicV27.signedExtrinsic] = extrinsic + } + + SubmittableExtrinsicV27.toHexString(submittableExtrinsic) + } + + private fun createExtrinsicPayloadValue( + callBytes: ByteArray, + encodeExtrinsicParams: EncodeExtrinsicParams + ) = with(encodeExtrinsicParams) { + ExtrinsicPayloadValue { payload -> + payload[ExtrinsicPayloadValue.call] = callBytes + payload[ExtrinsicPayloadValue.nonce] = nonce + payload[ExtrinsicPayloadValue.specVersion] = runtimeVersion.specVersion.toUInt() + payload[ExtrinsicPayloadValue.transactionVersion] = runtimeVersion.transactionVersion.toUInt() + + payload[ExtrinsicPayloadValue.genesis] = genesis + payload[ExtrinsicPayloadValue.blockHash] = genesis + } + } + + private fun decodeV27(scale: String): TransferExtrinsic? { + val struct = SubmittableExtrinsicV27.readOrNull(scale) ?: return null + + val signedExtrinsic = struct[SubmittableExtrinsicV27.signedExtrinsic] + val call = signedExtrinsic[SignedExtrinsicV27.call] + val args = call[TransferCallV27.args] + + return TransferExtrinsic( + senderId = signedExtrinsic[SignedExtrinsicV27.accountId], + recipientId = args[TransferArgsV27.recipientId], + amountInPlanks = args[TransferArgsV27.amount], + index = call[TransferCallV27.callIndex], + hash = struct.hash() + ) + } + + private fun decodeV28(scale: String): TransferExtrinsic? { + val struct = SubmittableExtrinsicV28.readOrNull(scale) ?: return null + + val signedExtrinsic = struct[SubmittableExtrinsicV28.signedExtrinsic] + val call = signedExtrinsic[SignedExtrinsicV28.call] + val args = call[TransferCallV28.args] + + val senderId = signedExtrinsic[SignedExtrinsicV28.accountId] as? MultiAddress.Id ?: return null + val recipientId = args[TransferArgsV28.recipientId] as? MultiAddress.Id ?: return null + + return TransferExtrinsic( + senderId = senderId.value, + recipientId = recipientId.value, + amountInPlanks = args[TransferArgsV28.amount], + index = call[TransferCallV28.callIndex], + hash = struct.hash() + ) + } +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/updaters/AccountBalanceUpdater.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/updaters/AccountBalanceUpdater.kt new file mode 100644 index 0000000000..9ea42993ad --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/updaters/AccountBalanceUpdater.kt @@ -0,0 +1,34 @@ +package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.updaters + +import jp.co.soramitsu.core_api.data.network.Updater +import jp.co.soramitsu.feature_account_api.domain.interfaces.AccountRepository +import jp.co.soramitsu.feature_wallet_api.domain.interfaces.WalletRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.withContext + +class AccountBalanceUpdater( + private val accountRepository: AccountRepository, + private val walletRepository: WalletRepository +) : Updater { + + override suspend fun listenForUpdates() = withContext(Dispatchers.IO) { + val networkType = accountRepository.getSelectedNode().networkType + + accountRepository.selectedAccountFlow() + .filter { it.network.type == networkType } + .distinctUntilChanged { old, new -> old.address == new.address } + .flowOn(Dispatchers.IO) + .collectLatest { + val accountInfoUpdates = async { walletRepository.listenForAccountInfoUpdates(it) } + val stakingLedgerUpdates = async { walletRepository.listenForStakingLedgerUpdates(it) } + + listOf(accountInfoUpdates, stakingLedgerUpdates).awaitAll() + } + } +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/updaters/DualRefCountUpdater.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/updaters/DualRefCountUpdater.kt new file mode 100644 index 0000000000..e9f56bfb9b --- /dev/null +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/updaters/DualRefCountUpdater.kt @@ -0,0 +1,51 @@ +package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.updaters + +import jp.co.soramitsu.common.utils.fromHex +import jp.co.soramitsu.core_api.data.network.Updater +import jp.co.soramitsu.fearless_utils.runtime.Module +import jp.co.soramitsu.fearless_utils.runtime.Service +import jp.co.soramitsu.fearless_utils.runtime.StorageUtils +import jp.co.soramitsu.fearless_utils.runtime.storageKey +import jp.co.soramitsu.fearless_utils.scale.dataType.boolean +import jp.co.soramitsu.fearless_utils.wsrpc.SocketService +import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.storage.SubscribeStorageRequest +import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.storage.storageChange +import jp.co.soramitsu.fearless_utils.wsrpc.subscription.response.SubscriptionChange +import jp.co.soramitsu.fearless_utils.wsrpc.subscriptionFlow +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoFactory +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach + +object DualRefCountService : Service(Module.System, "UpgradedToDualRefCount") { + override fun storageKey(storageArgs: Unit): String { + return StorageUtils.createStorageKey(this, null) + } +} + +private const val DEFAULT_DUAL_REF_COUNT = false + +private val upgradedToDualRefCountRequest = SubscribeStorageRequest(DualRefCountService.storageKey()) + +private fun SubscriptionChange.dualRefCountChange(): Boolean { + val storageChange = storageChange() + + val raw = storageChange.getSingleChange() + + return raw?.let(boolean::fromHex) ?: DEFAULT_DUAL_REF_COUNT +} + +class AccountInfoSchemaUpdater( + private val accountInfoFactory: AccountInfoFactory, + private val socketService: SocketService +) : Updater { + + override suspend fun listenForUpdates() { + accountInfoFactory.isUpgradedToDualRefCount.invalidate() + + socketService.subscriptionFlow(upgradedToDualRefCountRequest) + .map { it.dualRefCountChange() } + .onEach(accountInfoFactory.isUpgradedToDualRefCount::set) + .collect() + } +} \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletExtensions.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletExtensions.kt index 3a1aeca60d..fd18931567 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletExtensions.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletExtensions.kt @@ -3,10 +3,10 @@ package jp.co.soramitsu.feature_wallet_impl.data.repository import jp.co.soramitsu.common.utils.sumBy import jp.co.soramitsu.fearless_utils.scale.EncodableStruct import jp.co.soramitsu.feature_wallet_api.domain.model.calculateTotalBalance -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.StakingLedger import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.UnlockChunk +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountData +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoSchema import java.math.BigInteger fun EncodableStruct.sumStaking( @@ -18,8 +18,8 @@ fun EncodableStruct.sumStaking( .sumBy { it[UnlockChunk.value] } } -fun EncodableStruct.totalBalanceInPlanks(): BigInteger { - val accountData = this[AccountInfo.data] +fun EncodableStruct.totalBalanceInPlanks(): BigInteger { + val accountData = this[schema.data] return calculateTotalBalance( freeInPlanks = accountData[AccountData.free], diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt index f8258d4220..bb558cb05b 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/data/repository/WalletRepositoryImpl.kt @@ -30,19 +30,11 @@ import jp.co.soramitsu.feature_wallet_impl.data.mappers.mapTransactionLocalToTra import jp.co.soramitsu.feature_wallet_impl.data.mappers.mapTransactionToTransactionLocal import jp.co.soramitsu.feature_wallet_impl.data.mappers.mapTransferToTransaction import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.SubstrateRemoteSource -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.feeFrozen -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.free -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.miscFrozen -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.reserved -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo.data import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.ActiveEraInfo -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Call -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignedExtrinsic import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.StakingLedger -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.TransferArgs -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.hash +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountData +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoSchema +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.TransferExtrinsic import jp.co.soramitsu.feature_wallet_impl.data.network.model.request.AssetPriceRequest import jp.co.soramitsu.feature_wallet_impl.data.network.model.request.TransactionHistoryRequest import jp.co.soramitsu.feature_wallet_impl.data.network.model.response.AssetPriceStatistics @@ -54,8 +46,6 @@ import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flattenMerge -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.sync.Mutex @@ -166,22 +156,22 @@ class WalletRepositoryImpl( return transfer.validityStatus(asset.transferable, asset.total, feeResponse.feeAmount, totalRecipientBalance) } - override suspend fun listenForUpdates(account: Account) { - val accountUpdatesFlow = substrateSource.listenForAccountUpdates(account.address) + override suspend fun listenForAccountInfoUpdates(account: Account) { + substrateSource.listenForAccountUpdates(account.address) .onEach { change -> updateAssetBalance(account, change.newAccountInfo) - fetchTransactions(account, change.block) - } + fetchTransfers(account, change.block) + }.collect() + } - val stakingLedgerUpdates = substrateSource.listenStakingLedger(account.address) + override suspend fun listenForStakingLedgerUpdates(account: Account) { + substrateSource.listenStakingLedger(account.address) .onEach { stakingLedger -> val era = substrateSource.getActiveEra() updateAssetStaking(account, stakingLedger, era) - } - - flowOf(accountUpdatesFlow, stakingLedgerUpdates).flattenMerge().collect() + }.collect() } private suspend fun updateAssetStaking( @@ -203,8 +193,8 @@ class WalletRepositoryImpl( } } - private suspend fun fetchTransactions(account: Account, blockHash: String) { - val transactions = substrateSource.fetchAccountTransactionInBlock(blockHash, account) + private suspend fun fetchTransfers(account: Account, blockHash: String) { + val transactions = substrateSource.fetchAccountTransfersInBlock(blockHash, account) val local = transactions.map { createTransactionLocal(it, account) } transactionsDao.insert(local) @@ -287,15 +277,15 @@ class WalletRepositoryImpl( private suspend fun updateAssetBalance( account: Account, - accountInfo: EncodableStruct + accountInfo: EncodableStruct ) = updateLocalAssetCopy(account) { cachedAsset -> - val data = accountInfo[data] + val data = accountInfo[accountInfo.schema.data] cachedAsset.copy( - freeInPlanks = data[free], - reservedInPlanks = data[reserved], - miscFrozenInPlanks = data[miscFrozen], - feeFrozenInPlanks = data[feeFrozen] + freeInPlanks = data[AccountData.free], + reservedInPlanks = data[AccountData.reserved], + miscFrozenInPlanks = data[AccountData.miscFrozen], + feeFrozenInPlanks = data[AccountData.feeFrozen] ) } @@ -336,28 +326,21 @@ class WalletRepositoryImpl( } private suspend fun createTransactionLocal( - extrinsic: EncodableStruct, + extrinsic: TransferExtrinsic, account: Account ): TransactionLocal { - val hash = extrinsic.hash() - - val localCopy = transactionsDao.getTransaction(hash) + val localCopy = transactionsDao.getTransaction(extrinsic.hash) val fee = localCopy?.feeInPlanks val networkType = account.network.type val token = Token.Type.fromNetworkType(networkType) - val signed = extrinsic[SubmittableExtrinsic.signedExtrinsic] - val transferArgs = signed[SignedExtrinsic.call][Call.args] - - val senderAddress = sS58Encoder.encode(signed[SignedExtrinsic.accountId], networkType) - val recipientAddress = sS58Encoder.encode(transferArgs[TransferArgs.recipientId], networkType) - - val amountInPlanks = transferArgs[TransferArgs.amount] + val senderAddress = sS58Encoder.encode(extrinsic.senderId, networkType) + val recipientAddress = sS58Encoder.encode(extrinsic.recipientId, networkType) return TransactionLocal( - hash = hash, + hash = extrinsic.hash, accountAddress = account.address, senderAddress = senderAddress, recipientAddress = recipientAddress, @@ -365,7 +348,7 @@ class WalletRepositoryImpl( status = Transaction.Status.COMPLETED, feeInPlanks = fee, token = token, - amount = token.amountFromPlanks(amountInPlanks), + amount = token.amountFromPlanks(extrinsic.amountInPlanks), date = System.currentTimeMillis(), networkType = networkType ) diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/di/WalletFeatureModule.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/di/WalletFeatureModule.kt index ee859adee4..9577ec6872 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/di/WalletFeatureModule.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/di/WalletFeatureModule.kt @@ -6,6 +6,7 @@ import jp.co.soramitsu.common.data.network.HttpExceptionHandler import jp.co.soramitsu.common.data.network.NetworkApiCreator import jp.co.soramitsu.common.di.scope.FeatureScope import jp.co.soramitsu.common.interfaces.FileProvider +import jp.co.soramitsu.common.utils.SuspendableProperty import jp.co.soramitsu.core_db.dao.AssetDao import jp.co.soramitsu.core_db.dao.TransactionDao import jp.co.soramitsu.fearless_utils.encrypt.KeypairFactory @@ -13,12 +14,17 @@ import jp.co.soramitsu.fearless_utils.encrypt.Signer import jp.co.soramitsu.fearless_utils.ss58.SS58Encoder import jp.co.soramitsu.fearless_utils.wsrpc.SocketService import jp.co.soramitsu.feature_account_api.domain.interfaces.AccountRepository +import jp.co.soramitsu.feature_wallet_api.di.WalletUpdaters import jp.co.soramitsu.feature_wallet_api.domain.interfaces.WalletInteractor import jp.co.soramitsu.feature_wallet_api.domain.interfaces.WalletRepository import jp.co.soramitsu.feature_wallet_api.domain.model.BuyTokenRegistry import jp.co.soramitsu.feature_wallet_impl.BuildConfig import jp.co.soramitsu.feature_wallet_impl.data.buyToken.RampProvider import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.WssSubstrateSource +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoFactory +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.TransferExtrinsicFactory +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.updaters.AccountBalanceUpdater +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.updaters.AccountInfoSchemaUpdater import jp.co.soramitsu.feature_wallet_impl.data.network.subscan.SubscanNetworkApi import jp.co.soramitsu.feature_wallet_impl.data.repository.WalletRepositoryImpl import jp.co.soramitsu.feature_wallet_impl.domain.WalletInteractorImpl @@ -36,14 +42,38 @@ class WalletFeatureModule { return networkApiCreator.create(SubscanNetworkApi::class.java) } + @Provides + @FeatureScope + fun provideDualRefCountProperty() = SuspendableProperty() + + @Provides + @FeatureScope + fun provideExtrinsicFactory( + dualRefCountProperty: SuspendableProperty, + signer: Signer + ) = TransferExtrinsicFactory(dualRefCountProperty, signer) + + @Provides + @FeatureScope + fun provideAccountInfoFactory( + dualRefCountProperty: SuspendableProperty + ) = AccountInfoFactory(dualRefCountProperty) + @Provides @FeatureScope fun provideSubstrateSource( socketService: SocketService, keypairFactory: KeypairFactory, - signer: Signer, + accountInfoFactory: AccountInfoFactory, + extrinsicFactory: TransferExtrinsicFactory, sS58Encoder: SS58Encoder - ) = WssSubstrateSource(socketService, signer, keypairFactory, sS58Encoder) + ) = WssSubstrateSource( + socketService, + keypairFactory, + accountInfoFactory, + extrinsicFactory, + sS58Encoder + ) @Provides @FeatureScope @@ -91,4 +121,29 @@ class WalletFeatureModule { @Provides @FeatureScope fun provideTransferChecks(): TransferValidityChecks.Presentation = TransferValidityChecksProvider() + + @Provides + @FeatureScope + fun provideAccountSchemaUpdater( + accountInfoFactory: AccountInfoFactory, + socketService: SocketService + ): AccountInfoSchemaUpdater { + return AccountInfoSchemaUpdater(accountInfoFactory, socketService) + } + + @Provides + @FeatureScope + fun provideBalanceUpdater( + accountRepository: AccountRepository, + walletRepository: WalletRepository + ): AccountBalanceUpdater { + return AccountBalanceUpdater(accountRepository, walletRepository) + } + + @Provides + @FeatureScope + fun provideFeatureUpdaters( + schemaUpdater: AccountInfoSchemaUpdater, + balanceUpdater: AccountBalanceUpdater + ): WalletUpdaters = WalletUpdaters(listOf(schemaUpdater, balanceUpdater)) } \ No newline at end of file diff --git a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/send/amount/ChooseAmountViewModel.kt b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/send/amount/ChooseAmountViewModel.kt index 0abddea630..eeeeaae259 100644 --- a/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/send/amount/ChooseAmountViewModel.kt +++ b/feature-wallet-impl/src/main/java/jp/co/soramitsu/feature_wallet_impl/presentation/send/amount/ChooseAmountViewModel.kt @@ -2,7 +2,6 @@ package jp.co.soramitsu.feature_wallet_impl.presentation.send.amount import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.asLiveData import androidx.lifecycle.liveData import androidx.lifecycle.viewModelScope import jp.co.soramitsu.common.account.AddressIconGenerator @@ -161,6 +160,8 @@ class ChooseAmountViewModel( .catch { _feeErrorLiveData.postValue(Event(RetryReason.LOAD_FEE)) + it.printStackTrace() + emit(null) }.onEach { _feeLoadingLiveData.value = false diff --git a/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/AccountDataTest.kt b/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/AccountDataTest.kt index 992368608b..c4809eb0b7 100644 --- a/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/AccountDataTest.kt +++ b/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/AccountDataTest.kt @@ -2,9 +2,8 @@ package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct import io.emeraldpay.polkaj.scale.ScaleCodecReader import io.emeraldpay.polkaj.scale.ScaleCodecWriter -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountData.free -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo.data -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo.nonce +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountData +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.account.AccountInfoSchemaV27 import junit.framework.Assert.assertEquals import org.bouncycastle.util.encoders.Hex import org.junit.Test @@ -24,17 +23,17 @@ class AccountDataTest { val reader = ScaleCodecReader(decode) - val struct = AccountInfo.read(reader) + val struct = AccountInfoSchemaV27.read(reader) - val balanceInPlanks = struct[data][free] - val nonce = struct[nonce] + val balanceInPlanks = struct[AccountInfoSchemaV27.data][AccountData.free] + val nonce = struct[AccountInfoSchemaV27.nonce] assert(balanceInPlanks == actualBalance) assert(nonce == actualRefCount) val outputStream = ByteArrayOutputStream() val writer = ScaleCodecWriter(outputStream) - writer.write(AccountInfo, struct) + writer.write(AccountInfoSchemaV27, struct) val bytes = outputStream.toByteArray() val encodedResponse = Hex.toHexString(bytes) diff --git a/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/SubmittableExtrinsicTest.kt b/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/SubmittableExtrinsicTest.kt deleted file mode 100644 index cda72bc29f..0000000000 --- a/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/blockchain/struct/SubmittableExtrinsicTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct - -import jp.co.soramitsu.feature_account_api.domain.model.Node -import org.bouncycastle.util.encoders.Hex -import org.junit.Assert.assertEquals -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner - -@RunWith(MockitoJUnitRunner::class) -class SubmittableExtrinsicTest { - - @Test - fun `should deserialize immortal extrinsic`() { - val expected = "3502848ad2a3fba73321961cd5d1b8272aa95a21e75dd5b098fb36ed996961ac7b2931015604975bd1ce5ac5d00210216db0944278db674146a08f69257ef45cd1f9f1680800c437195b6181bd3161bdd23fb6bb856ed7427787edef125a692bd512b5880014000400dd0072af4b3b66a01be502555d4ddafb55e8e7df3fb04c836d83255547a8a2ff0700e40b5402" - - val decoded = SubmittableExtrinsic.read(expected) - - val encodedBytes = SubmittableExtrinsic.toByteArray(decoded) - val encoded = Hex.toHexString(encodedBytes) - - assertEquals(expected, encoded) - } - - @Test - fun `should deserialize mortal extrinsic`() { - val data = "310284fdc41550fb5186d71cae699c31731b3e1baa10680c7bd6b3831a6d222cf4d168003a8eb7f3be70d98d86a9ba66f29d8aae0fea70a820a66f38272044811b21f2e7d5e16c73375a3ac775b98177ff0e125a109f0c58f7d7dc1a507b37879250060ec50238000403340a806419d5e278172e45cb0e50da1b031795366c99ddfe0a680bd53b142c6302286bee" - - val decoded = SubmittableExtrinsic.read(data) - - val callIndex = decoded[SubmittableExtrinsic.signedExtrinsic][SignedExtrinsic.call][Call.callIndex] - val expectedCallIndex = Node.NetworkType.KUSAMA.runtimeConfiguration.pallets.transfers.transferKeepAlive.index - - assertEquals(expectedCallIndex, callIndex) - } -} \ No newline at end of file diff --git a/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/integration/SendIntegrationTest.kt b/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/integration/SendIntegrationTest.kt index 608a046942..4735da1b52 100644 --- a/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/integration/SendIntegrationTest.kt +++ b/feature-wallet-impl/src/test/java/jp/co/soramitsu/feature_wallet_impl/data/network/integration/SendIntegrationTest.kt @@ -9,37 +9,29 @@ import jp.co.soramitsu.fearless_utils.encrypt.Signer import jp.co.soramitsu.fearless_utils.encrypt.model.Keypair import jp.co.soramitsu.fearless_utils.scale.EncodableStruct import jp.co.soramitsu.fearless_utils.scale.invoke +import jp.co.soramitsu.fearless_utils.scale.toHexString import jp.co.soramitsu.fearless_utils.ss58.SS58Encoder import jp.co.soramitsu.fearless_utils.wsrpc.SocketService import jp.co.soramitsu.fearless_utils.wsrpc.executeAsync import jp.co.soramitsu.fearless_utils.wsrpc.mappers.nonNull import jp.co.soramitsu.fearless_utils.wsrpc.mappers.pojo -import jp.co.soramitsu.fearless_utils.wsrpc.mappers.scale -import jp.co.soramitsu.fearless_utils.wsrpc.mappers.scaleCollection import jp.co.soramitsu.fearless_utils.wsrpc.recovery.Reconnector import jp.co.soramitsu.fearless_utils.wsrpc.request.RequestExecutor -import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.account.AccountInfoRequest -import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.author.PendingExtrinsicsRequest import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.chain.RuntimeVersionRequest import jp.co.soramitsu.feature_account_api.domain.model.Node import jp.co.soramitsu.feature_wallet_api.domain.model.Token import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.extrinsics.TransferRequest import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.extrinsics.signExtrinsic import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests.FeeCalculationRequest +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.requests.NextAccountIndexRequest import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.response.RuntimeVersion -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.AccountInfo.nonce -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Call -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Call.args -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Call.callIndex -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.ExtrinsicPayloadValue import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.Signature -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SignedExtrinsic -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.SubmittableExtrinsic.signedExtrinsic -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.TransferArgs -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.TransferArgs.amount -import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.TransferArgs.recipientId +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.ExtrinsicPayloadValue +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.MultiAddress +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.SignedExtrinsicV28 +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.SubmittableExtrinsicV28 +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.TransferArgsV28 +import jp.co.soramitsu.feature_wallet_impl.data.network.blockchain.struct.extrinsic.TransferCallV28 import kotlinx.coroutines.runBlocking import org.bouncycastle.util.encoders.Hex import org.junit.After @@ -69,20 +61,20 @@ class SendIntegrationTest { @Mock private lateinit var resourceManager: ResourceManager - private lateinit var rxWebSocket: SocketService + private lateinit var socketService: SocketService @Before fun setup() { given(resourceManager.getString(anyInt())).willReturn("Mock") - rxWebSocket = SocketService(mapper, StdoutLogger(), WebSocketFactory(), Reconnector(), RequestExecutor()) + socketService = SocketService(mapper, StdoutLogger(), WebSocketFactory(), Reconnector(), RequestExecutor()) - rxWebSocket.start(URL) + socketService.start(URL) } @After fun tearDown() { - rxWebSocket.stop() + socketService.stop() } @Test @@ -91,9 +83,9 @@ class SendIntegrationTest { val submittableExtrinsic = generateExtrinsic(keyPair) - val feeRequest = TransferRequest(submittableExtrinsic) + val feeRequest = TransferRequest(submittableExtrinsic.toHexString()) - val result = rxWebSocket.executeAsync(feeRequest).result + val result = socketService.executeAsync(feeRequest).result assert(result != null) } @@ -105,53 +97,50 @@ class SendIntegrationTest { val submittableExtrinsic = generateExtrinsic(keyPair) - val feeRequest = FeeCalculationRequest(submittableExtrinsic) + val feeRequest = FeeCalculationRequest(submittableExtrinsic.toHexString()) - val result = rxWebSocket.executeAsync(feeRequest).result + val result = socketService.executeAsync(feeRequest).result assert(result != null) } - private suspend fun generateExtrinsic(keypair: Keypair): EncodableStruct { + private suspend fun generateExtrinsic(keypair: Keypair): EncodableStruct { val accountId = Hex.decode(PUBLIC_KEY) - val genesis = Node.NetworkType.WESTEND.runtimeConfiguration.genesisHash + val westendRuntime = Node.NetworkType.WESTEND.runtimeConfiguration + + val genesis = westendRuntime.genesisHash + val addressByte = westendRuntime.addressByte val genesisBytes = Hex.decode(genesis) val transferAmount = BigDecimal("0.001").scaleByPowerOfTen(Token.Type.WND.mantissa) - val runtimeInfo = rxWebSocket + val runtimeInfo = socketService .executeAsync(RuntimeVersionRequest(), mapper = pojo().nonNull()) val specVersion = runtimeInfo.specVersion val transactionVersion = runtimeInfo.transactionVersion - val pendingExtrinsics = rxWebSocket - .executeAsync(PendingExtrinsicsRequest(), mapper = scaleCollection(SubmittableExtrinsic)) - .result!! - - val pendingForCurrent = pendingExtrinsics.count { it[signedExtrinsic][SignedExtrinsic.accountId].contentEquals(accountId) } + val address = sS58Encoder.encode(accountId, addressByte) - val accountInfo = rxWebSocket - .executeAsync(AccountInfoRequest(accountId), mapper = scale(AccountInfo)) - .result!! - - val nonce = accountInfo[nonce] + pendingForCurrent.toUInt() - val nonceBigInt = nonce.toLong().toBigInteger() + val nonce = socketService.executeAsync(NextAccountIndexRequest(address), mapper = pojo().nonNull()) + val nonceBigInt = nonce.toInt().toBigInteger() val receiverPublicKey = sS58Encoder.decode(TO_ADDRESS) - val callStruct = Call { call -> - call[callIndex] = Pair(4.toUByte(), 0.toUByte()) + val callStruct = TransferCallV28 { call -> + call[TransferCallV28.callIndex] = Pair(4.toUByte(), 0.toUByte()) - call[args] = TransferArgs { args -> - args[recipientId] = receiverPublicKey - args[amount] = transferAmount.toBigIntegerExact() + call[TransferCallV28.args] = TransferArgsV28 { args -> + args[TransferArgsV28.recipientId] = MultiAddress.Id(receiverPublicKey) + args[TransferArgsV28.amount] = transferAmount.toBigIntegerExact() } } + val callBytes = TransferCallV28.toByteArray(callStruct) + val payload = ExtrinsicPayloadValue { payload -> - payload[ExtrinsicPayloadValue.call] = callStruct + payload[ExtrinsicPayloadValue.call] = callBytes payload[ExtrinsicPayloadValue.nonce] = nonceBigInt payload[ExtrinsicPayloadValue.specVersion] = specVersion.toUInt() payload[ExtrinsicPayloadValue.transactionVersion] = transactionVersion.toUInt() @@ -165,19 +154,19 @@ class SendIntegrationTest { value = signer.signExtrinsic(payload, keypair, EncryptionType.ECDSA) ) - val extrinsic = SignedExtrinsic { extrinsic -> - extrinsic[SignedExtrinsic.accountId] = accountId - extrinsic[SignedExtrinsic.signature] = signature - extrinsic[SignedExtrinsic.nonce] = nonceBigInt - extrinsic[SignedExtrinsic.call] = callStruct + val extrinsic = SignedExtrinsicV28 { extrinsic -> + extrinsic[SignedExtrinsicV28.accountId] = MultiAddress.Id(accountId) + extrinsic[SignedExtrinsicV28.signature] = signature + extrinsic[SignedExtrinsicV28.nonce] = nonceBigInt + extrinsic[SignedExtrinsicV28.call] = callStruct } - val extrinsicBytes = SignedExtrinsic.toByteArray(extrinsic) + val extrinsicBytes = SignedExtrinsicV28.toByteArray(extrinsic) val byteLength = extrinsicBytes.size.toBigInteger() - return SubmittableExtrinsic { struct -> - struct[SubmittableExtrinsic.byteLength] = byteLength - struct[signedExtrinsic] = extrinsic + return SubmittableExtrinsicV28 { struct -> + struct[SubmittableExtrinsicV28.byteLength] = byteLength + struct[SubmittableExtrinsicV28.signedExtrinsic] = extrinsic } } } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 530edadc3b..df79f9ccbe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -2,6 +2,6 @@ include ':feature-wallet-impl' include ':feature-wallet-api' include ':feature-onboarding-impl' include ':feature-onboarding-api' -include ':app', ':test-shared', ':common', ':feature-splash', ':core-db' +include ':app', ':test-shared', ':common', ':feature-splash', 'core-db', 'core-api' include ':feature-account-api' include ':feature-account-impl'