Skip to content

Commit de9af8a

Browse files
authored
Merge pull request #325 from soramitsu/rc/1.8.3
Rc/1.8.3
2 parents c33bf77 + e37d57b commit de9af8a

File tree

112 files changed

+889
-521
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+889
-521
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ To build Fearless Wallet Android project, you need to provide several keys eithe
2121
``` properties
2222
MOONPAY_TEST_SECRET=stub
2323
MOONPAY_PRODUCTION_SECRET=stub
24+
SUBSCAN_API_KEY=
2425
```
2526

2627
Note, that with stub keys buy via moonpay will not work correctly. However, other parts of application will not be affected.
2728

29+
If you will leave SUBSCAN_API_KEY value empty, you will have very strict limits for the subscan api requests. If you want to increase the limits, check the [Subscan website](https://docs.api.subscan.io/#introduction) and then add the api key to the 'local.properties' -> SUBSCAN_API_KEY=YOUR_API_KEY
30+
2831
## License
2932
Fearless Wallet Android is available under the Apache 2.0 license. See the LICENSE file for more info.

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
buildscript {
22
ext {
33
// App version
4-
versionName = '1.8.2'
5-
versionCode = 17
4+
versionName = '1.8.3'
5+
versionCode = 19
66

77
// SDK and tools
88
compileSdkVersion = 29

common/src/main/java/jp/co/soramitsu/common/data/network/NetworkApiCreator.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ class NetworkApiCreator(
1010
private val baseUrl: String
1111
) {
1212

13-
fun <T> create(service: Class<T>): T {
13+
fun <T> create(
14+
service: Class<T>,
15+
customBaseUrl: String = baseUrl
16+
): T {
1417
val retrofit = Retrofit.Builder()
1518
.client(okHttpClient)
16-
.baseUrl(baseUrl)
19+
.baseUrl(customBaseUrl)
1720
.addConverterFactory(ScalarsConverterFactory.create())
1821
.addConverterFactory(GsonConverterFactory.create())
1922
.build()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package jp.co.soramitsu.common.data.network.rpc
2+
3+
import jp.co.soramitsu.fearless_utils.extensions.toHexString
4+
import java.io.ByteArrayOutputStream
5+
6+
private const val CHILD_KEY_DEFAULT = ":child_storage:default:"
7+
8+
suspend fun childStateKey(
9+
builder: suspend ByteArrayOutputStream.() -> Unit
10+
): String {
11+
val buffer = ByteArrayOutputStream().apply {
12+
write(CHILD_KEY_DEFAULT.encodeToByteArray())
13+
14+
builder()
15+
}
16+
17+
return buffer.toByteArray().toHexString(withPrefix = true)
18+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package jp.co.soramitsu.common.data.network.runtime.binding
2+
3+
import java.math.BigInteger
4+
5+
typealias BalanceOf = BigInteger

common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/BindingHelpers.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ annotation class HelperBinding
1818
fun incompatible(): Nothing = throw IllegalStateException("Binding is incompatible")
1919

2020
typealias Binder<T> = (scale: String?, RuntimeSnapshot) -> T
21+
typealias BinderWithKey<T, K> = (scale: String?, RuntimeSnapshot, key: K) -> T
2122
typealias NonNullBinder<T> = (scale: String, RuntimeSnapshot) -> T
23+
typealias NonNullBinderWithType<T> = (scale: String, RuntimeSnapshot, Type<*>) -> T
2224
typealias BinderWithType<T> = (scale: String?, RuntimeSnapshot, Type<*>) -> T
2325

2426
@OptIn(ExperimentalContracts::class)

common/src/main/java/jp/co/soramitsu/common/data/network/runtime/binding/Primitive.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ import java.math.BigInteger
44

55
@HelperBinding
66
fun bindNumber(dynamicInstance: Any?): BigInteger = dynamicInstance.cast()
7+
8+
@HelperBinding
9+
fun bindString(dynamicInstance: Any?): String = dynamicInstance.cast<ByteArray>().decodeToString()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package jp.co.soramitsu.common.data.network.runtime.calls
2+
3+
import jp.co.soramitsu.fearless_utils.wsrpc.request.runtime.RuntimeRequest
4+
5+
class GetChildStateRequest(
6+
storageKey: String,
7+
childKey: String
8+
) : RuntimeRequest(
9+
method = "childstate_getStorage",
10+
params = listOf(childKey, storageKey)
11+
)

common/src/main/java/jp/co/soramitsu/common/mixin/impl/ValidatableUi.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@ package jp.co.soramitsu.common.mixin.impl
22

33
import android.content.Context
44
import jp.co.soramitsu.common.base.BaseFragment
5-
import jp.co.soramitsu.common.base.BaseViewModel
65
import jp.co.soramitsu.common.mixin.api.Validatable
76
import jp.co.soramitsu.common.validation.DefaultFailureLevel
87
import jp.co.soramitsu.common.view.dialog.errorDialog
98
import jp.co.soramitsu.common.view.dialog.warningDialog
109

11-
fun <T> BaseFragment<T>.observeValidations(
12-
viewModel: T,
10+
fun BaseFragment<*>.observeValidations(
11+
viewModel: Validatable,
1312
dialogContext: Context = requireContext()
14-
) where T : BaseViewModel, T : Validatable {
13+
) {
1514
viewModel.validationFailureEvent.observeEvent {
1615
val level = it.level
1716

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package jp.co.soramitsu.common.utils
2+
3+
import jp.co.soramitsu.fearless_utils.hash.Hasher.blake2b256
4+
import kotlinx.coroutines.sync.Mutex
5+
import kotlinx.coroutines.sync.withLock
6+
7+
object ConcurrentHasher {
8+
9+
private val mutex = Mutex()
10+
11+
suspend fun ByteArray.concurrentBlake2b256() = mutex.withLock { blake2b256() }
12+
}

common/src/main/java/jp/co/soramitsu/common/utils/FearlessLibExt.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot
1111
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.generics.GenericEvent
1212
import jp.co.soramitsu.fearless_utils.runtime.metadata.Module
1313
import jp.co.soramitsu.fearless_utils.runtime.metadata.RuntimeMetadata
14+
import jp.co.soramitsu.fearless_utils.runtime.metadata.StorageEntry
1415
import jp.co.soramitsu.fearless_utils.runtime.metadata.module
1516
import jp.co.soramitsu.fearless_utils.runtime.metadata.moduleOrNull
17+
import jp.co.soramitsu.fearless_utils.runtime.metadata.storageKey
1618
import jp.co.soramitsu.fearless_utils.scale.EncodableStruct
1719
import jp.co.soramitsu.fearless_utils.scale.Schema
1820
import jp.co.soramitsu.fearless_utils.scale.dataType.DataType
@@ -24,6 +26,8 @@ import jp.co.soramitsu.fearless_utils.wsrpc.mappers.nonNull
2426
import jp.co.soramitsu.fearless_utils.wsrpc.mappers.pojo
2527
import java.io.ByteArrayOutputStream
2628

29+
fun StorageEntry.defaultInHex() = default.toHexString(withPrefix = true)
30+
2731
fun ByteArray.toAddress(networkType: Node.NetworkType) = toAddress(networkType.runtimeConfiguration.addressByte)
2832

2933
fun <T> DataType<T>.fromHex(hex: String): T {
@@ -74,6 +78,10 @@ fun RuntimeMetadata.babe() = module(Modules.BABE)
7478

7579
fun RuntimeMetadata.slots() = module(Modules.SLOTS)
7680

81+
fun <T> StorageEntry.storageKeys(runtime: RuntimeSnapshot, singleMapKeys: Collection<T>): Map<String, T> {
82+
return singleMapKeys.associateBy { storageKey(runtime, it) }
83+
}
84+
7785
fun String.networkType() = Node.NetworkType.findByAddressByte(addressByte())!!
7886

7987
fun RuntimeMetadata.hasModule(name: String) = moduleOrNull(name) != null

common/src/main/java/jp/co/soramitsu/common/validation/ValidationExecutor.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ValidationExecutor(
2121
errorDisplayer: (Throwable) -> Unit,
2222
validationFailureTransformer: (S) -> TitleAndMessage,
2323
progressConsumer: ProgressConsumer? = null,
24-
autoFixPayload: (original: P, failureStatus: S) -> P,
24+
autoFixPayload: (original: P, failureStatus: S) -> P = { original, _ -> original },
2525
block: (P) -> Unit,
2626
) {
2727
progressConsumer?.invoke(true)

common/src/main/res/values/strings.xml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<resources>
3+
<string name="staking_set_validators_title">Select validators to start staking</string>
4+
<string name="staking_set_validators_message">Validators are not selected</string>
5+
6+
<string name="staking_max_nominators_reached_title">Cannot nominate</string>
7+
<string name="staking_max_nominators_reached_message">Maximum number of nominators has been reached</string>
8+
9+
<string name="crowdloan_my_contribution">You contributed: %s</string>
10+
311
<string name="staking_alert_bond_more_title">Bond more tokens.</string>
412
<string name="staking_alert_bond_more_message">Staking is inactive. Current minimal stake is %s</string>
513
<string name="staking_alert_redeem_title">Redeem unbonded tokens.</string>
614

715
<string name="staking_alert_title">Alerts</string>
816
<string name="staking_alert_no_alerts_now">Everything is fine now. Alerts will appear here.</string>
917
<string name="staking_alert_start_next_era_message">Please wait for the next era to start.</string>
10-
<string name="staking_alert_election">Staking actions are currently unavailable.</string>
11-
<string name="staking_alert_election_message">The network is electing validators.\nUsually it takes less than 10 minutes.</string>
1218
<string name="staking_alert_change_validators">Change your validators.</string>
1319
<string name="staking_alert_change_validators_message">Staking is inactive. None of your validators were elected by network.</string>
1420

@@ -175,7 +181,6 @@
175181

176182
<string name="staking_nominator_status_alert_active_title">Active status</string>
177183
<string name="staking_nominator_status_alert_active_message">One of your validators have been elected by network.</string>
178-
<string name="staking_nominator_status_alert_election_message">The network is electing validators. Please wait a few minutes.</string>
179184
<string name="staking_nominator_status_alert_waiting_message">Your staking will start in the next era.</string>
180185
<string name="staking_nominator_status_alert_inactive_title">Inactive status</string>
181186
<string name="staking_nominator_status_alert_no_validators">None of your validators have been elected by network.</string>
@@ -189,7 +194,6 @@
189194

190195
<string name="staking_stake">Stake</string>
191196
<string name="staking_total_rewards">Total rewards</string>
192-
<string name="staking_nominator_status_election">Election period</string>
193197
<string name="staking_nominator_status_active">Active</string>
194198
<string name="staking_nominator_status_inactive">Inactive</string>
195199
<string name="staking_nominator_status_waiting">Waiting for the next era</string>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding
2+
3+
import jp.co.soramitsu.common.data.network.runtime.binding.bindNumber
4+
import jp.co.soramitsu.common.data.network.runtime.binding.bindString
5+
import jp.co.soramitsu.common.data.network.runtime.binding.cast
6+
import jp.co.soramitsu.common.data.network.runtime.binding.incompatible
7+
import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot
8+
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.fromHex
9+
import java.math.BigInteger
10+
11+
class Contribution(
12+
val amount: BigInteger,
13+
val memo: String
14+
)
15+
16+
fun bindContribution(scale: String, runtime: RuntimeSnapshot): Contribution {
17+
val type = runtime.typeRegistry["(BalanceOf, Vec<u8>)"] ?: incompatible()
18+
19+
val dynamicInstance = type.fromHex(runtime, scale).cast<List<*>>()
20+
21+
return Contribution(
22+
amount = bindNumber(dynamicInstance[0]),
23+
memo = bindString(dynamicInstance[1])
24+
)
25+
}

feature-crowdloan-api/src/main/java/jp/co/soramitsu/feature_crowdloan_api/data/network/blockhain/binding/FundInfo.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import jp.co.soramitsu.common.utils.Modules
99
import jp.co.soramitsu.fearless_utils.runtime.AccountId
1010
import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot
1111
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.composite.Struct
12+
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.primitives.u32
13+
import jp.co.soramitsu.fearless_utils.runtime.definitions.types.toByteArray
1214
import java.math.BigInteger
1315

1416
class FundInfo(
@@ -19,10 +21,13 @@ class FundInfo(
1921
val firstSlot: BigInteger,
2022
val end: BigInteger,
2123
val cap: BigInteger,
22-
val verifier: Any?
24+
val verifier: Any?,
25+
val trieIndex: TrieIndex,
26+
val paraId: ParaId,
27+
val bidderAccountId: AccountId
2328
)
2429

25-
fun bindFundInfo(scale: String, runtime: RuntimeSnapshot): FundInfo {
30+
fun bindFundInfo(scale: String, runtime: RuntimeSnapshot, paraId: ParaId): FundInfo {
2631
val type = runtime.metadata.storageReturnType(Modules.CROWDLOAN, "Funds")
2732

2833
val dynamicInstance = type.fromHexOrIncompatible(scale, runtime)
@@ -36,6 +41,18 @@ fun bindFundInfo(scale: String, runtime: RuntimeSnapshot): FundInfo {
3641
cap = bindNumber(dynamicInstance["cap"]),
3742
firstSlot = bindNumber(dynamicInstance["firstPeriod"] ?: dynamicInstance["firstSlot"]),
3843
lastSlot = bindNumber(dynamicInstance["lastPeriod"] ?: dynamicInstance["lastSlot"]),
39-
verifier = dynamicInstance["verifier"]
44+
verifier = dynamicInstance["verifier"],
45+
trieIndex = bindTrieIndex(dynamicInstance["trieIndex"]),
46+
bidderAccountId = createBidderAccountId(runtime, paraId),
47+
paraId = paraId
4048
)
4149
}
50+
51+
private val ADDRESS_PADDING = ByteArray(32)
52+
private val ADDRESS_PREFIX = "modlpy/cfund".encodeToByteArray()
53+
54+
private fun createBidderAccountId(runtime: RuntimeSnapshot, paraId: ParaId): AccountId {
55+
val fullKey = ADDRESS_PREFIX + u32.toByteArray(runtime, paraId) + ADDRESS_PADDING
56+
57+
return fullKey.copyOfRange(0, 32)
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding
2+
3+
import jp.co.soramitsu.common.data.network.runtime.binding.BalanceOf
4+
import jp.co.soramitsu.common.data.network.runtime.binding.bindAccountId
5+
import jp.co.soramitsu.common.data.network.runtime.binding.bindNumber
6+
import jp.co.soramitsu.common.data.network.runtime.binding.cast
7+
import jp.co.soramitsu.common.data.network.runtime.binding.fromHexOrIncompatible
8+
import jp.co.soramitsu.common.data.network.runtime.binding.returnType
9+
import jp.co.soramitsu.common.utils.slots
10+
import jp.co.soramitsu.fearless_utils.runtime.AccountId
11+
import jp.co.soramitsu.fearless_utils.runtime.RuntimeSnapshot
12+
import jp.co.soramitsu.fearless_utils.runtime.metadata.storage
13+
14+
class LeaseEntry(
15+
val accountId: AccountId,
16+
val locked: BalanceOf
17+
)
18+
19+
fun bindLeases(scale: String, runtimeSnapshot: RuntimeSnapshot): List<LeaseEntry?> {
20+
val type = runtimeSnapshot.metadata.slots().storage("Leases").returnType()
21+
22+
val dynamicInstance = type.fromHexOrIncompatible(scale, runtimeSnapshot).cast<List<*>>()
23+
24+
return dynamicInstance.map {
25+
it?.let {
26+
val (accountIdRaw, balanceRaw) = it.cast<List<*>>()
27+
28+
LeaseEntry(
29+
accountId = bindAccountId(accountIdRaw),
30+
locked = bindNumber(balanceRaw)
31+
)
32+
}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding
2+
3+
import jp.co.soramitsu.common.data.network.runtime.binding.HelperBinding
4+
import jp.co.soramitsu.common.data.network.runtime.binding.bindNumber
5+
import java.math.BigInteger
6+
7+
typealias TrieIndex = BigInteger
8+
9+
@HelperBinding
10+
fun bindTrieIndex(dynamicInstance: Any?) = bindNumber(dynamicInstance)

feature-crowdloan-api/src/main/java/jp/co/soramitsu/feature_crowdloan_api/data/repository/CrowdloanRepository.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package jp.co.soramitsu.feature_crowdloan_api.data.repository
22

33
import jp.co.soramitsu.core.model.Node
4+
import jp.co.soramitsu.fearless_utils.runtime.AccountId
5+
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.Contribution
46
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.FundInfo
57
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.ParaId
8+
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.TrieIndex
69
import kotlinx.coroutines.flow.Flow
710
import java.math.BigDecimal
811
import java.math.BigInteger
@@ -13,8 +16,12 @@ interface CrowdloanRepository {
1316

1417
suspend fun allFundInfos(): Map<ParaId, FundInfo>
1518

19+
suspend fun getWinnerInfo(funds: Map<ParaId, FundInfo>): Map<ParaId, Boolean>
20+
1621
suspend fun getParachainMetadata(): Map<ParaId, ParachainMetadata>
1722

23+
suspend fun getContribution(accountId: AccountId, paraId: ParaId, trieIndex: TrieIndex): Contribution?
24+
1825
suspend fun blocksPerLeasePeriod(): BigInteger
1926

2027
fun fundInfoFlow(parachainId: ParaId, networkType: Node.NetworkType): Flow<FundInfo>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package jp.co.soramitsu.feature_crowdloan_api.data.repository
2+
3+
import jp.co.soramitsu.fearless_utils.runtime.AccountId
4+
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.Contribution
5+
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.FundInfo
6+
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.ParaId
7+
import jp.co.soramitsu.feature_crowdloan_api.data.network.blockhain.binding.TrieIndex
8+
import kotlinx.coroutines.Dispatchers
9+
import kotlinx.coroutines.async
10+
import kotlinx.coroutines.awaitAll
11+
import kotlinx.coroutines.withContext
12+
13+
suspend fun CrowdloanRepository.getContributions(
14+
accountId: AccountId,
15+
keys: Map<ParaId, TrieIndex>
16+
): Map<ParaId, Contribution?> = withContext(Dispatchers.Default) {
17+
keys.map { (paraId, trieIndex) ->
18+
async { paraId to getContribution(accountId, paraId, trieIndex) }
19+
}
20+
.awaitAll()
21+
.toMap()
22+
}
23+
24+
suspend fun CrowdloanRepository.hasWonAuction(fundInfo: FundInfo): Boolean {
25+
val paraId = fundInfo.paraId
26+
27+
return getWinnerInfo(mapOf(paraId to fundInfo)).getValue(paraId)
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package jp.co.soramitsu.feature_crowdloan_impl.data.network.api.bifrost
2+
3+
import jp.co.soramitsu.common.data.network.subquery.SubQueryResponse
4+
import retrofit2.http.Body
5+
import retrofit2.http.POST
6+
7+
interface BifrostApi {
8+
9+
companion object {
10+
const val BASE_URL = "https://salp-api.bifrost.finance"
11+
}
12+
13+
@POST("/")
14+
suspend fun getAccountByReferralCode(@Body body: BifrostReferralCheckRequest): SubQueryResponse<GetAccountByReferralCodeResponse>
15+
}
16+
17+
suspend fun BifrostApi.getAccountByReferralCode(code: String) = getAccountByReferralCode(BifrostReferralCheckRequest(code))

0 commit comments

Comments
 (0)