Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Setup staking proxy #935

Merged
merged 10 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions novawallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ protocol StakingLocalSubscriptionFactoryProtocol {
for accountId: AccountId,
chainId: ChainModel.Id
) throws -> AnyDataProvider<DecodedBagListNode>

func getProxyListProvider(
for accountId: AccountId,
chainId: ChainModel.Id
) throws -> AnyDataProvider<DecodedProxyDefinition>
}

final class StakingLocalSubscriptionFactory: SubstrateLocalSubscriptionFactory,
Expand Down Expand Up @@ -355,4 +360,25 @@ final class StakingLocalSubscriptionFactory: SubstrateLocalSubscriptionFactory,

return provider
}

func getProxyListProvider(
for accountId: AccountId,
chainId: ChainModel.Id
) throws -> AnyDataProvider<DecodedProxyDefinition> {
clearIfNeeded()

let codingPath = Proxy.proxyList
let localKey = try LocalStorageKeyFactory().createFromStoragePath(
codingPath,
accountId: accountId,
chainId: chainId
)

return try getDataProvider(
for: localKey,
chainId: chainId,
storageCodingPath: codingPath,
shouldUseFallback: false
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ typealias DecodedBondedPool = ChainStorageDecodedItem<NominationPools.BondedPool
typealias DecodedRewardPool = ChainStorageDecodedItem<NominationPools.RewardPool>
typealias DecodedSubPools = ChainStorageDecodedItem<NominationPools.SubPools>
typealias DecodedPoolId = ChainStorageDecodedItem<StringScaleMapper<NominationPools.PoolId>>
typealias DecodedProxyDefinition = ChainStorageDecodedItem<ProxyDefinition>
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ protocol StakingLocalStorageSubscriber where Self: AnyObject {
assetPrecision: Int16
) -> AnySingleValueProvider<TotalRewardItem>?

func subscribeStashItemProvider(
for address: AccountAddress,
func subscribeProxies(
for accountId: AccountId,
chainId: ChainModel.Id
) -> StreamableProvider<StashItem>?
) -> AnyDataProvider<DecodedProxyDefinition>?
}

extension StakingLocalStorageSubscriber {
Expand Down Expand Up @@ -653,6 +653,52 @@ extension StakingLocalStorageSubscriber {

return provider
}

func subscribeProxies(
for accountId: AccountId,
chainId: ChainModel.Id
) -> AnyDataProvider<DecodedProxyDefinition>? {
guard let provider = try? stakingLocalSubscriptionFactory.getProxyListProvider(
for: accountId,
chainId: chainId
) else {
return nil
}

let updateClosure = { [weak self] (changes: [DataProviderChange<DecodedProxyDefinition>]) in
let proxies = changes.reduceToLastChange()
self?.stakingLocalSubscriptionHandler.handleProxies(
result: .success(proxies?.item),
accountId: accountId,
chainId: chainId
)
return
}

let failureClosure = { [weak self] (error: Error) in
self?.stakingLocalSubscriptionHandler.handleProxies(
result: .failure(error),
accountId: accountId,
chainId: chainId
)
return
}

let options = DataProviderObserverOptions(
alwaysNotifyOnRefresh: false,
waitsInProgressSyncOnAdd: false
)

provider.addObserver(
self,
deliverOn: .main,
executing: updateClosure,
failing: failureClosure,
options: options
)

return provider
}
}

extension StakingLocalStorageSubscriber where Self: StakingLocalSubscriptionHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ protocol StakingLocalSubscriptionHandler {
func handleActiveEra(result: Result<ActiveEraInfo?, Error>, chainId: ChainModel.Id)

func handleCurrentEra(result: Result<EraIndex?, Error>, chainId: ChainModel.Id)

func handleProxies(
result: Result<ProxyDefinition?, Error>,
accountId: AccountId,
chainId: ChainModel.Id
)
}

extension StakingLocalSubscriptionHandler {
Expand Down Expand Up @@ -103,4 +109,10 @@ extension StakingLocalSubscriptionHandler {
func handleActiveEra(result _: Result<ActiveEraInfo?, Error>, chainId _: ChainModel.Id) {}

func handleCurrentEra(result _: Result<EraIndex?, Error>, chainId _: ChainModel.Id) {}

func handleProxies(
result _: Result<ProxyDefinition?, Error>,
accountId _: AccountId,
chainId _: ChainModel.Id
) {}
}
2 changes: 1 addition & 1 deletion novawallet/Common/Services/Proxy/ProxyModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ struct AccountIdKey: JSONListConvertible, Hashable {
}
}

struct ProxyDefinition: Decodable {
struct ProxyDefinition: Decodable, Equatable {
let definition: [Proxy.ProxyDefinition]

init(from decoder: Decoder) throws {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ final class StakingAccountSubscription: WebSocketSubscribing {
)
}

requests.append(.init(storagePath: Proxy.proxyList, accountId: stashId))
lynx56 marked this conversation as resolved.
Show resolved Hide resolved

return requests
}

Expand Down
2 changes: 1 addition & 1 deletion novawallet/Common/Substrate/Types/Proxy/Proxy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import BigInt
enum Proxy {
static var name: String { "Proxy" }

struct ProxyDefinition: Decodable {
struct ProxyDefinition: Decodable, Equatable {
enum CodingKeys: String, CodingKey {
case proxy = "delegate"
case proxyType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import UIKit
import SoraUI
import Kingfisher

final class SwapNetworkFeeView: GenericTitleValueView<RoundedButton, GenericPairValueView<RoundedButton, UILabel>>,
final class NetworkFeeInfoView: GenericTitleValueView<RoundedButton, GenericPairValueView<RoundedButton, UILabel>>,
SkeletonableView {
var titleButton: RoundedButton { titleView }
var valueTopButton: RoundedButton { valueView.fView }
Expand Down Expand Up @@ -60,16 +60,16 @@ final class SwapNetworkFeeView: GenericTitleValueView<RoundedButton, GenericPair
}
}

extension SwapNetworkFeeView {
func bind(viewModel: SwapFeeViewModel) {
extension NetworkFeeInfoView {
func bind(viewModel: NetworkFeeInfoViewModel) {
valueTopButton.imageWithTitleView?.iconImage = viewModel.isEditable ? iconPencil : nil
valueTopButton.isUserInteractionEnabled = viewModel.isEditable
valueTopButton.imageWithTitleView?.title = viewModel.balanceViewModel.amount
valueBottomLabel.text = viewModel.balanceViewModel.price
valueTopButton.invalidateLayout()
}

func bind(loadableViewModel: LoadableViewModelState<SwapFeeViewModel>) {
func bind(loadableViewModel: LoadableViewModelState<NetworkFeeInfoViewModel>) {
switch loadableViewModel {
case let .cached(value), let .loaded(value):
isLoading = false
Expand All @@ -82,7 +82,7 @@ extension SwapNetworkFeeView {
}
}

extension SwapNetworkFeeView {
extension NetworkFeeInfoView {
func createSkeletons(for spaceSize: CGSize) -> [Skeletonable] {
let size = CGSize(width: 68, height: 8)
let offset = CGPoint(
Expand Down
4 changes: 4 additions & 0 deletions novawallet/Common/ViewModel/NetworkFeeInfoViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
struct NetworkFeeInfoViewModel {
var isEditable: Bool
var balanceViewModel: BalanceViewModelProtocol
}
21 changes: 21 additions & 0 deletions novawallet/Modules/Staking/Model/StakingManageOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ enum StakingManageOption {
case controllerAccount
case yourValidator
case yieldBoost(enabled: Bool)
case addProxy
case editProxies(currentCount: Int)

func titleForLocale(_ locale: Locale, statics: StakingMainStaticViewModelProtocol?) -> String {
switch self {
Expand All @@ -35,6 +37,10 @@ enum StakingManageOption {
return R.string.localizable.stakingYourValidatorTitle(preferredLanguages: locale.rLanguages)
case .yieldBoost:
return R.string.localizable.commonYieldBoost(preferredLanguages: locale.rLanguages)
case .addProxy:
return R.string.localizable.stakingSetupAddYourProxy(preferredLanguages: locale.rLanguages)
case let .editProxies:
return R.string.localizable.stakingSetupYourProxies(preferredLanguages: locale.rLanguages)
}
}

Expand All @@ -56,6 +62,11 @@ enum StakingManageOption {
}
}

if case let .editProxies(count) = self {
let formatter = NumberFormatter.quantity.localizableResource().value(for: locale)
return formatter.string(from: NSNumber(value: count))
}

return nil
}

Expand All @@ -75,6 +86,16 @@ enum StakingManageOption {
return R.image.iconControllerAccount()
case .yieldBoost:
return R.image.iconYieldBoost()
case .addProxy, .editProxies:
return R.image.iconDelegate()
}
}

static func proxyAction(from proxyDefinition: ProxyDefinition?) -> StakingManageOption {
guard let proxiesCount = proxyDefinition?.definition.count, proxiesCount > 0 else {
return .addProxy
}

return .editProxies(currentCount: proxiesCount)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ extension StakingRelaychainInteractor {
clear(dataProvider: &payeeProvider)
clear(streamableProvider: &controllerAccountProvider)
clear(streamableProvider: &stashAccountProvider)
clear(dataProvider: &proxyProvider)

if
let stashItem = stashItem,
Expand All @@ -25,6 +26,7 @@ extension StakingRelaychainInteractor {
nominatorProvider = subscribeNomination(for: stashAccountId, chainId: chainId)
validatorProvider = subscribeValidator(for: stashAccountId, chainId: chainId)
payeeProvider = subscribePayee(for: stashAccountId, chainId: chainId)
proxyProvider = subscribeProxies(for: stashAccountId, chainId: chainId)

performTotalRewardSubscription()

Expand Down Expand Up @@ -102,6 +104,7 @@ extension StakingRelaychainInteractor {
clear(singleValueProvider: &totalRewardProvider)
clear(dataProvider: &payeeProvider)
clear(streamableProvider: &stashControllerProvider)
clear(dataProvider: &proxyProvider)
}

func performStashControllerSubscription() {
Expand Down Expand Up @@ -259,6 +262,10 @@ extension StakingRelaychainInteractor: StakingLocalStorageSubscriber, StakingLoc
presenter?.didReceiveBagListScoreFactor(result: .failure(error))
}
}

func handleProxies(result: Result<ProxyDefinition?, Error>, accountId _: AccountId, chainId _: ChainModel.Id) {
presenter?.didReceiveProxy(result: result)
}
}

extension StakingRelaychainInteractor: PriceLocalStorageSubscriber, PriceLocalSubscriptionHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ final class StakingRelaychainInteractor: RuntimeConstantFetching, AnyCancellable
var bagListSizeProvider: AnyDataProvider<DecodedU32>?
var totalIssuanceProvider: AnyDataProvider<DecodedBigUInt>?
var totalRewardInterval: (startTimestamp: Int64?, endTimestamp: Int64?)?
var proxyProvider: AnyDataProvider<DecodedProxyDefinition>?

init(
selectedWalletSettings: SelectedWalletSettings,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@ extension StakingRelaychainPresenter: StakingMainChildPresenterProtocol {
let stashAddress = validatorState.stashItem.stash
wireframe.showYourValidatorInfo(stashAddress, from: view)
}
case .addProxy:
wireframe.showAddProxies(from: view)
case .editProxies:
// TODO: show proxy list
fallthrough
lynx56 marked this conversation as resolved.
Show resolved Hide resolved
default:
logger?.warning("Unsupported action: \(action)")
}
Expand Down Expand Up @@ -604,4 +609,13 @@ extension StakingRelaychainPresenter: StakingRelaychainInteractorOutputProtocol
handle(error: error)
}
}

func didReceiveProxy(result: Result<ProxyDefinition?, Error>) {
switch result {
case let .success(proxy):
stateMachine.state.process(proxy: proxy)
case let .failure(error):
handle(error: error)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ protocol StakingRelaychainInteractorOutputProtocol: AnyObject {
func didReceiveMaxNominatorsPerValidator(result: Result<UInt32, Error>)

func didReceiveAccount(_ account: MetaChainAccountResponse?, for accountId: AccountId)
func didReceiveProxy(result: Result<ProxyDefinition?, Error>)
}

protocol StakingRelaychainWireframeProtocol: AlertPresentable, ErrorPresentable, StakingErrorPresentable {
Expand All @@ -63,4 +64,5 @@ protocol StakingRelaychainWireframeProtocol: AlertPresentable, ErrorPresentable,
func showRebagConfirm(from view: ControllerBackedProtocol?)

func showYourValidatorInfo(_ stashAddress: AccountAddress, from view: ControllerBackedProtocol?)
func showAddProxies(from view: ControllerBackedProtocol?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,12 @@ extension StakingRelaychainWireframe: StakingRelaychainWireframeProtocol {
let navigationController = NovaNavigationController(rootViewController: validatorInfoView.controller)
view?.controller.present(navigationController, animated: true, completion: nil)
}

func showAddProxies(from view: ControllerBackedProtocol?) {
guard let setupProxyView = StakingSetupProxyViewFactory.createView() else {
return
}
let navigationController = NovaNavigationController(rootViewController: setupProxyView.controller)
view?.controller.present(navigationController, animated: true, completion: nil)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protocol StakingStateProtocol {
func process(bagListScoreFactor: BigUInt?)
func process(eraCountdown: EraCountdown)
func process(totalRewardFilter: StakingRewardFiltersPeriod?)
func process(proxy: ProxyDefinition?)
}

protocol StakingStateMachineProtocol: AnyObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,10 @@ class BaseStakingState: StakingStateProtocol {

stateMachine?.transit(to: self)
}

func process(proxy: ProxyDefinition?) {
commonData = commonData.byReplacing(proxy: proxy)

stateMachine?.transit(to: self)
}
}
Loading
Loading