-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Resources and Accounts Fiat worth (#1034)
Co-authored-by: kugel3 <gustaf.kugelberg@rdx.works>
- Loading branch information
1 parent
fe5490d
commit 939f698
Showing
60 changed files
with
1,461 additions
and
420 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 107 additions & 25 deletions
132
RadixWallet/Clients/AccountPortfoliosClient/AccountPortfoliosClient+Live.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,61 +1,143 @@ | ||
|
||
// MARK: - AccountPortfoliosClient + DependencyKey | ||
extension AccountPortfoliosClient: DependencyKey { | ||
/// Internal state that holds all loaded portfolios. | ||
actor State { | ||
let portfoliosSubject: AsyncCurrentValueSubject<[AccountAddress: OnLedgerEntity.Account]> = .init([:]) | ||
public static let liveValue: AccountPortfoliosClient = { | ||
let state = State() | ||
|
||
func setOrUpdateAccountPortfolio(_ portfolio: OnLedgerEntity.Account) { | ||
portfoliosSubject.value.updateValue(portfolio, forKey: portfolio.address) | ||
} | ||
@Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient | ||
@Dependency(\.cacheClient) var cacheClient | ||
@Dependency(\.tokenPricesClient) var tokenPricesClient | ||
@Dependency(\.appPreferencesClient) var appPreferencesClient | ||
@Dependency(\.gatewaysClient) var gatewaysClient | ||
|
||
func setOrUpdateAccountPortfolios(_ portfolios: [OnLedgerEntity.Account]) { | ||
var newValue = portfoliosSubject.value | ||
for portfolio in portfolios { | ||
newValue[portfolio.address] = portfolio | ||
/// Update currency amount visibility based on the profile state | ||
Task { | ||
for try await isCurrencyAmountVisible in await appPreferencesClient.appPreferenceUpdates().map(\.display.isCurrencyAmountVisible) { | ||
guard !Task.isCancelled else { return } | ||
await state.setIsCurrencyAmountVisble(isCurrencyAmountVisible) | ||
} | ||
portfoliosSubject.value = newValue | ||
} | ||
|
||
func portfolioForAccount(_ address: AccountAddress) -> AnyAsyncSequence<OnLedgerEntity.Account> { | ||
portfoliosSubject.compactMap { $0[address] }.eraseToAnyAsyncSequence() | ||
/// Update used currency based on the profile state | ||
Task { | ||
for try await fiatCurrency in await appPreferencesClient.appPreferenceUpdates().map(\.display.fiatCurrencyPriceTarget) { | ||
guard !Task.isCancelled else { return } | ||
await state.setSelectedCurrency(fiatCurrency) | ||
} | ||
} | ||
} | ||
|
||
public static let liveValue: AccountPortfoliosClient = { | ||
let state = State() | ||
/// Fetches the pool and stake units details for a given account; Will update the portfolio accordingly | ||
@Sendable | ||
func fetchPoolAndStakeUnitsDetails(_ account: OnLedgerEntity.Account) async { | ||
async let poolDetailsFetch = Task { | ||
do { | ||
let poolUnitDetails = try await onLedgerEntitiesClient.getOwnedPoolUnitsDetails(account) | ||
await state.set(poolDetails: .success(poolUnitDetails), forAccount: account.address) | ||
} catch { | ||
await state.set(poolDetails: .failure(error), forAccount: account.address) | ||
} | ||
}.result | ||
async let stakeUnitDetails = Task { | ||
do { | ||
let stakeUnitDetails = try await onLedgerEntitiesClient.getOwnedStakesDetails(account: account) | ||
await state.set(stakeUnitDetails: .success(stakeUnitDetails.asIdentifiable()), forAccount: account.address) | ||
} catch { | ||
await state.set(stakeUnitDetails: .failure(error), forAccount: account.address) | ||
} | ||
}.result | ||
|
||
@Dependency(\.onLedgerEntitiesClient) var onLedgerEntitiesClient | ||
@Dependency(\.cacheClient) var cacheClient | ||
_ = await (poolDetailsFetch, stakeUnitDetails) | ||
} | ||
|
||
return AccountPortfoliosClient( | ||
fetchAccountPortfolios: { accountAddresses, forceRefresh in | ||
let gateway = await gatewaysClient.getCurrentGateway() | ||
await state.setRadixGateway(gateway) | ||
if forceRefresh { | ||
for accountAddress in accountAddresses { | ||
cacheClient.removeFolder(.onLedgerEntity(.account(accountAddress.asGeneral))) | ||
} | ||
} | ||
|
||
let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses) | ||
/// Explicetely load and set the currency target and visibility to make sure | ||
/// it is available for usage before resources are loaded | ||
let preferences = await appPreferencesClient.getPreferences().display | ||
await state.setSelectedCurrency(preferences.fiatCurrencyPriceTarget) | ||
await state.setIsCurrencyAmountVisble(preferences.isCurrencyAmountVisible) | ||
|
||
let accounts = try await onLedgerEntitiesClient.getAccounts(accountAddresses).map(\.nonEmptyVaults) | ||
|
||
await state.setOrUpdateAccountPortfolios(accounts) | ||
/// Put together all resources from already fetched and new accounts | ||
let currentAccounts = state.portfoliosSubject.value.wrappedValue.map { $0.values.map(\.account) } ?? [] | ||
let allResources: [ResourceAddress] = { | ||
if gateway == .mainnet { | ||
/// Only Mainnet resources have prices | ||
return (currentAccounts + accounts) | ||
.flatMap { | ||
$0.allFungibleResourceAddresses + | ||
$0.poolUnitResources.poolUnits.flatMap(\.poolResources) + | ||
[.mainnetXRDAddress] | ||
} | ||
} else { | ||
#if DEBUG | ||
/// Helpful for testing on stokenet | ||
return [ | ||
.mainnetXRDAddress, | ||
try! .init(validatingAddress: | ||
"resource_rdx1t4tjx4g3qzd98nayqxm7qdpj0a0u8ns6a0jrchq49dyfevgh6u0gj3" | ||
), | ||
try! .init(validatingAddress: | ||
"resource_rdx1t45js47zxtau85v0tlyayerzrgfpmguftlfwfr5fxzu42qtu72tnt0" | ||
), | ||
try! .init(validatingAddress: | ||
"resource_rdx1tk7g72c0uv2g83g3dqtkg6jyjwkre6qnusgjhrtz0cj9u54djgnk3c" | ||
), | ||
try! .init(validatingAddress: | ||
"resource_rdx1tkk83magp3gjyxrpskfsqwkg4g949rmcjee4tu2xmw93ltw2cz94sq" | ||
), | ||
] | ||
#else | ||
/// No price for resources on testnets | ||
return [] | ||
#endif | ||
} | ||
}() | ||
|
||
if !allResources.isEmpty { | ||
let prices = try? await tokenPricesClient.getTokenPrices(.init( | ||
tokens: Array(allResources), | ||
currency: preferences.fiatCurrencyPriceTarget | ||
)) | ||
|
||
return accounts | ||
if let prices { | ||
await state.setTokenPrices(prices) | ||
} | ||
} | ||
|
||
let portfolios = accounts.map { AccountPortfolio(account: $0) } | ||
await state.handlePortfoliosUpdate(portfolios) | ||
|
||
// Load additional details | ||
_ = await accounts.parallelMap(fetchPoolAndStakeUnitsDetails) | ||
|
||
return Array(state.portfoliosSubject.value.wrappedValue!.values) | ||
}, | ||
fetchAccountPortfolio: { accountAddress, forceRefresh in | ||
if forceRefresh { | ||
cacheClient.removeFolder(.onLedgerEntity(.account(accountAddress.asGeneral))) | ||
} | ||
|
||
let portfolio = try await onLedgerEntitiesClient.getAccount(accountAddress) | ||
await state.setOrUpdateAccountPortfolio(portfolio) | ||
let account = try await onLedgerEntitiesClient.getAccount(accountAddress) | ||
let portfolio = AccountPortfolio(account: account) | ||
|
||
await state.handlePortfolioUpdate(portfolio) | ||
await fetchPoolAndStakeUnitsDetails(account) | ||
|
||
return portfolio | ||
}, | ||
portfolioForAccount: { address in | ||
await state.portfolioForAccount(address) | ||
}, | ||
portfolios: { state.portfoliosSubject.value.map(\.value) } | ||
portfolios: { state.portfoliosSubject.value.wrappedValue.map { Array($0.values) } ?? [] } | ||
) | ||
}() | ||
} |
Oops, something went wrong.