Skip to content

Commit

Permalink
Merge pull request #479 from nova-wallet/fix/gov1-and-xcm
Browse files Browse the repository at this point in the history
Governance improvements
  • Loading branch information
ERussel authored Nov 23, 2022
2 parents 47f0b8d + 0e32c13 commit fe6ce29
Show file tree
Hide file tree
Showing 43 changed files with 1,074 additions and 413 deletions.
52 changes: 52 additions & 0 deletions novawallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ typealias ReferendumMetadataMapping = [ReferendumIdLocal: ReferendumMetadataLoca

protocol GovMetadataLocalSubscriptionFactoryProtocol: AnyObject {
func getMetadataProvider(
for chain: ChainModel
for option: GovernanceSelectedOption
) -> StreamableProvider<ReferendumMetadataLocal>?

func getMetadataProvider(
for chain: ChainModel,
for option: GovernanceSelectedOption,
referendumId: ReferendumIdLocal
) -> StreamableProvider<ReferendumMetadataLocal>?
}
Expand Down Expand Up @@ -44,14 +44,17 @@ final class GovMetadataLocalSubscriptionFactory {

extension GovMetadataLocalSubscriptionFactory: GovMetadataLocalSubscriptionFactoryProtocol {
func getMetadataProvider(
for chain: ChainModel
for option: GovernanceSelectedOption
) -> StreamableProvider<ReferendumMetadataLocal>? {
guard
let governanceApi = chain.externalApi?.governance,
case .governanceV1 = option.type,
let governanceApi = option.chain.externalApi?.governance,
let apiType = GovernanceOffchainApi(rawValue: governanceApi.type) else {
return nil
}

let chain = option.chain

let chainId = chain.chainId
let url = governanceApi.url

Expand Down Expand Up @@ -103,16 +106,17 @@ extension GovMetadataLocalSubscriptionFactory: GovMetadataLocalSubscriptionFacto
}

func getMetadataProvider(
for chain: ChainModel,
for option: GovernanceSelectedOption,
referendumId: ReferendumIdLocal
) -> StreamableProvider<ReferendumMetadataLocal>? {
guard
let governanceApi = chain.externalApi?.governance,
case .governanceV1 = option.type,
let governanceApi = option.chain.externalApi?.governance,
let apiType = GovernanceOffchainApi(rawValue: governanceApi.type) else {
return nil
}

let chainId = chain.chainId
let chainId = option.chain.chainId
let url = governanceApi.url

let identifier = "gov-metadata-details" + chainId + String(referendumId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ import RobinHood
protocol GovMetadataLocalStorageHandler: AnyObject {
func handleGovernanceMetadataPreview(
result: Result<[DataProviderChange<ReferendumMetadataLocal>], Error>,
chain: ChainModel
option: GovernanceSelectedOption
)

func handleGovernanceMetadataDetails(
result: Result<ReferendumMetadataLocal?, Error>,
chain: ChainModel,
option: GovernanceSelectedOption,
referendumId: ReferendumIdLocal
)
}

extension GovMetadataLocalStorageHandler {
func handleGovernanceMetadataPreview(
result _: Result<[DataProviderChange<ReferendumMetadataLocal>], Error>,
chain _: ChainModel
option _: GovernanceSelectedOption
) {}

func handleGovernanceMetadataDetails(
result _: Result<ReferendumMetadataLocal?, Error>,
chain _: ChainModel,
option _: GovernanceSelectedOption,
referendumId _: ReferendumIdLocal
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,36 @@ protocol GovMetadataLocalStorageSubscriber: AnyObject {
var govMetadataLocalSubscriptionHandler: GovMetadataLocalStorageHandler { get }

func subscribeGovernanceMetadata(
for chain: ChainModel
for option: GovernanceSelectedOption
) -> StreamableProvider<ReferendumMetadataLocal>?

func subscribeGovernanceMetadata(
for chain: ChainModel,
for option: GovernanceSelectedOption,
referendumId: ReferendumIdLocal
) -> StreamableProvider<ReferendumMetadataLocal>?
}

extension GovMetadataLocalStorageSubscriber {
func subscribeGovernanceMetadata(
for chain: ChainModel
for option: GovernanceSelectedOption
) -> StreamableProvider<ReferendumMetadataLocal>? {
guard let provider = govMetadataLocalSubscriptionFactory.getMetadataProvider(for: chain) else {
guard let provider = govMetadataLocalSubscriptionFactory.getMetadataProvider(for: option) else {
return nil
}

let updateClosure: ([DataProviderChange<ReferendumMetadataLocal>]) -> Void
updateClosure = { [weak self] changes in
self?.govMetadataLocalSubscriptionHandler.handleGovernanceMetadataPreview(
result: .success(changes),
chain: chain
option: option
)
return
}

let failureClosure: (Error) -> Void = { [weak self] error in
self?.govMetadataLocalSubscriptionHandler.handleGovernanceMetadataPreview(
result: .failure(error),
chain: chain
option: option
)
return
}
Expand All @@ -60,12 +60,12 @@ extension GovMetadataLocalStorageSubscriber {
}

func subscribeGovernanceMetadata(
for chain: ChainModel,
for option: GovernanceSelectedOption,
referendumId: ReferendumIdLocal
) -> StreamableProvider<ReferendumMetadataLocal>? {
guard
let provider = govMetadataLocalSubscriptionFactory.getMetadataProvider(
for: chain,
for: option,
referendumId: referendumId
) else {
return nil
Expand All @@ -77,7 +77,7 @@ extension GovMetadataLocalStorageSubscriber {

self?.govMetadataLocalSubscriptionHandler.handleGovernanceMetadataDetails(
result: .success(item),
chain: chain,
option: option,
referendumId: referendumId
)
return
Expand All @@ -86,7 +86,7 @@ extension GovMetadataLocalStorageSubscriber {
let failureClosure: (Error) -> Void = { [weak self] error in
self?.govMetadataLocalSubscriptionHandler.handleGovernanceMetadataDetails(
result: .failure(error),
chain: chain,
option: option,
referendumId: referendumId
)
return
Expand Down
13 changes: 13 additions & 0 deletions novawallet/Common/Extension/Foundation/Data+Fill.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import Foundation

extension Data {
func fillRightWithZeros(ifLess size: Int) -> Data {
guard count < size else {
return self
}

let neededZeros = size - count

return self + Data(repeating: 0, count: neededZeros)
}
}
22 changes: 22 additions & 0 deletions novawallet/Common/Extension/SettingsExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum SettingsKey: String {
case hidesZeroBalances
case selectedCurrency
case governanceChainId
case governanceType
}

extension SettingsManagerProtocol {
Expand Down Expand Up @@ -56,6 +57,27 @@ extension SettingsManagerProtocol {
}
}

var governanceType: GovernanceType? {
get {
if let rawValue = string(for: SettingsKey.governanceType.rawValue) {
return GovernanceType(rawValue: rawValue)
} else {
return nil
}
}

set {
if let existingValue = newValue {
set(
value: existingValue.rawValue,
for: SettingsKey.governanceType.rawValue
)
} else {
removeValue(for: SettingsKey.governanceType.rawValue)
}
}
}

var stakingAsset: ChainAssetId? {
get {
value(of: ChainAssetId.self, for: SettingsKey.stakingAsset.rawValue)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation
import SubstrateSdk

extension CallMetadata {
func isArgumentTypeOf(_ name: String, closure: (String) -> Bool) -> Bool {
guard let argument = arguments.first(where: { $0.name == name }) else {
return false
}

return closure(argument.type)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Foundation
import SubstrateSdk

extension RuntimeCoderFactoryProtocol {
func isBytesArrayType(_ type: String) -> Bool {
guard let vectorNode = getTypeNode(for: type) as? VectorNode else {
return false
}

return getTypeNode(for: vectorNode.underlying.typeName) is U8Node
}

func isUInt64Type(_ type: String) -> Bool {
getTypeNode(for: type) is U64Node
}

func isStructHasFieldsCount(_ type: String, count: Int) -> Bool {
guard let structNode = getTypeNode(for: type) as? StructNode else {
return false
}

return structNode.typeMapping.count == count
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation
import SubstrateSdk

extension RuntimeMetadataProtocol {
func isMapStorageKeyOfType(_ storagePath: StorageCodingPath, closure: (String) -> Bool) -> Bool {
guard let storage = getStorageMetadata(
in: storagePath.moduleName,
storageName: storagePath.itemName
) else {
return false
}

return storage.isMapKeyOfType(closure)
}
}

extension StorageEntryMetadata {
func isMapKeyOfType(_ closure: (String) -> Bool) -> Bool {
switch type {
case let .map(entry):
return closure(entry.key)
default:
return false
}
}
}
10 changes: 8 additions & 2 deletions novawallet/Common/Model/BlockWeights.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import Foundation
import SubstrateSdk
import BigInt

enum BlockchainWeight {
typealias WeightV1 = StringScaleMapper<UInt64>

struct WeightV2: Decodable {
struct WeightV1P5: Codable {
@StringCodable var refTime: UInt64
}

struct WeightV2: Codable {
@StringCodable var refTime: BigUInt
@StringCodable var proofSize: UInt64
}
}

struct BlockWeights: Decodable {
Expand All @@ -19,7 +25,7 @@ struct BlockWeights: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

if let weight = try? container.decode(BlockchainWeight.WeightV2.self, forKey: .maxBlock) {
if let weight = try? container.decode(BlockchainWeight.WeightV1P5.self, forKey: .maxBlock) {
maxBlock = weight.refTime
} else {
maxBlock = try container.decode(BlockchainWeight.WeightV1.self, forKey: .maxBlock).value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ struct RemoteRuntimeDispatchInfo: Decodable {

fee = try container.decode(StringScaleMapper<BigUInt>.self, forKey: .fee).value

if let remoteWeight = try? container.decode(BlockchainWeight.WeightV2.self, forKey: .weight) {
if let remoteWeight = try? container.decode(BlockchainWeight.WeightV1P5.self, forKey: .weight) {
weight = remoteWeight.refTime
} else {
weight = try container.decode(BlockchainWeight.WeightV1.self, forKey: .weight).value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ protocol RuntimeCoderFactoryProtocol {
func createEncoder() -> DynamicScaleEncoding
func createDecoder(from data: Data) throws -> DynamicScaleDecoding
func createRuntimeJsonContext() -> RuntimeJsonContext

func hasType(for name: String) -> Bool
func getTypeNode(for name: String) -> Node?
func getCall(for codingPath: CallCodingPath) -> CallMetadata?
}

final class RuntimeCoderFactory: RuntimeCoderFactoryProtocol {
Expand Down Expand Up @@ -57,4 +60,20 @@ final class RuntimeCoderFactory: RuntimeCoderFactoryProtocol {
func hasType(for name: String) -> Bool {
catalog.node(for: name, version: UInt64(specVersion)) != nil
}

func getTypeNode(for name: String) -> Node? {
let node = catalog.node(for: name, version: UInt64(specVersion))

if let aliasNode = node as? AliasNode {
return getTypeNode(for: aliasNode.underlyingTypeName)
} else if let proxyNode = node as? ProxyNode {
return getTypeNode(for: proxyNode.typeName)
} else {
return node
}
}

func getCall(for codingPath: CallCodingPath) -> CallMetadata? {
metadata.getCall(from: codingPath.moduleName, with: codingPath.callName)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ final class XcmTransferService {

let moduleResolutionOperation = ClosureOperation<String> {
let metadata = try coderFactoryOperation.extractNoCancellableResultData().metadata
guard let moduleName = Xcm.ExecuteCall.possibleModuleNames.first(
guard let moduleName = Xcm.possibleModuleNames.first(
where: { metadata.getModuleIndex($0) != nil }
) else {
throw XcmTransferServiceError.noXcmPalletFound
Expand Down Expand Up @@ -113,13 +113,22 @@ final class XcmTransferService {
let optChainAccount = wallet.fetch(for: chain.accountRequest())
let operationFactory = try createOperationFactory(for: chain, chainAccount: optChainAccount)

let coderFactoryOperation = runtimeProvider.fetchCoderFactoryOperation()

let wrapper = operationFactory.estimateFeeOperation { builder in
let moduleName = try moduleWrapper.targetOperation.extractNoCancellableResultData()
let call = Xcm.ExecuteCall(message: message, maxWeight: maxWeight)
return try builder.adding(call: call.runtimeCall(for: moduleName))
let codingFactory = try coderFactoryOperation.extractNoCancellableResultData()
return try Xcm.appendExecuteCall(
for: message,
maxWeight: maxWeight,
module: moduleName,
codingFactory: codingFactory,
builder: builder
)
}

wrapper.addDependency(wrapper: moduleWrapper)
wrapper.addDependency(operations: [coderFactoryOperation])

let mapperOperation = ClosureOperation<FeeWithWeight> {
let response = try wrapper.targetOperation.extractNoCancellableResultData()
Expand All @@ -133,7 +142,8 @@ final class XcmTransferService {

mapperOperation.addDependency(wrapper.targetOperation)

let dependencies = moduleWrapper.allOperations + wrapper.allOperations
let dependencies = [coderFactoryOperation] + moduleWrapper.allOperations +
wrapper.allOperations

return CompoundOperationWrapper(targetOperation: mapperOperation, dependencies: dependencies)
} catch {
Expand Down
Loading

0 comments on commit fe6ce29

Please sign in to comment.