Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Fix #8604: Restore Add Custom Token Auto-complete #8605

Merged
merged 3 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
57 changes: 46 additions & 11 deletions Sources/BraveWallet/Crypto/Portfolio/AddCustomAssetView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,25 @@ struct AddCustomAssetView: View {
header: WalletListHeaderView(title: networkSelectionStore.networkSelectionInForm?.coin == .sol ? Text(Strings.Wallet.tokenMintAddress) : Text(Strings.Wallet.tokenAddress))
) {
TextField(Strings.Wallet.enterAddress, text: $addressInput)
.onChange(of: addressInput) { newValue in
guard !newValue.isEmpty,
let network = networkSelectionStore.networkSelectionInForm else { return }
userAssetStore.tokenInfo(address: newValue, chainId: network.chainId) { token in
guard let token else { return }
if nameInput.isEmpty {
nameInput = token.name
}
if symbolInput.isEmpty {
symbolInput = token.symbol
}
if !token.isErc721, !token.isNft, decimalsInput.isEmpty {
decimalsInput = "\(token.decimals)"
}
}
}
.autocapitalization(.none)
.autocorrectionDisabled()
.disabled(userAssetStore.isSearchingToken)
.listRowBackground(Color(.secondaryBraveGroupedBackground))
}
Section(
Expand All @@ -104,27 +121,45 @@ struct AddCustomAssetView: View {
Section(
header: WalletListHeaderView(title: Text(Strings.Wallet.tokenName))
) {
TextField(Strings.Wallet.enterTokenName, text: $nameInput)
.autocapitalization(.none)
.autocorrectionDisabled()
.listRowBackground(Color(.secondaryBraveGroupedBackground))
HStack {
TextField(Strings.Wallet.enterTokenName, text: $nameInput)
.autocapitalization(.none)
.autocorrectionDisabled()
.disabled(userAssetStore.isSearchingToken)
if userAssetStore.isSearchingToken && nameInput.isEmpty {
ProgressView()
}
}
.listRowBackground(Color(.secondaryBraveGroupedBackground))
}
Section(
header: WalletListHeaderView(title: Text(Strings.Wallet.tokenSymbol))
) {
TextField(Strings.Wallet.enterTokenSymbol, text: $symbolInput)
.autocapitalization(.none)
.autocorrectionDisabled()
.listRowBackground(Color(.secondaryBraveGroupedBackground))
HStack {
TextField(Strings.Wallet.enterTokenSymbol, text: $symbolInput)
.autocapitalization(.none)
.autocorrectionDisabled()
.disabled(userAssetStore.isSearchingToken)
if userAssetStore.isSearchingToken && symbolInput.isEmpty {
ProgressView()
}
}
.listRowBackground(Color(.secondaryBraveGroupedBackground))
}
switch selectedTokenType {
case .token:
Section(
header: WalletListHeaderView(title: Text(Strings.Wallet.decimalsPrecision))
) {
TextField(NumberFormatter().string(from: NSNumber(value: 0)) ?? "0", text: $decimalsInput)
.keyboardType(.numberPad)
.listRowBackground(Color(.secondaryBraveGroupedBackground))
HStack {
TextField(NumberFormatter().string(from: NSNumber(value: 0)) ?? "0", text: $decimalsInput)
.keyboardType(.numberPad)
.disabled(userAssetStore.isSearchingToken)
if userAssetStore.isSearchingToken && decimalsInput.isEmpty {
ProgressView()
}
}
.listRowBackground(Color(.secondaryBraveGroupedBackground))
}
Section {
Button(
Expand Down
32 changes: 32 additions & 0 deletions Sources/BraveWallet/Crypto/Stores/UserAssetsStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public class AssetStore: ObservableObject, Equatable, WalletObserverStore {

public class UserAssetsStore: ObservableObject, WalletObserverStore {
@Published private(set) var assetStores: [AssetStore] = []
@Published var isSearchingToken: Bool = false
@Published var networkFilters: [Selectable<BraveWallet.NetworkInfo>] = [] {
didSet {
guard !oldValue.isEmpty else { return } // initial assignment to `networkFilters`
Expand Down Expand Up @@ -198,6 +199,37 @@ public class UserAssetsStore: ObservableObject, WalletObserverStore {
completion(true)
}
}

func tokenInfo(
address: String,
chainId: String,
completion: @escaping (BraveWallet.BlockchainToken?) -> Void
) {
// First check user's visible assets
if let assetStore = assetStores.first(where: { $0.token.contractAddress.caseInsensitiveCompare(address) == .orderedSame }) {
completion(assetStore.token)
} // else check full tokens list
else if let token = allTokens.first(where: { $0.contractAddress.caseInsensitiveCompare(address) == .orderedSame }) {
completion(token)
} // else use network request to get token info
else if address.isETHAddress { // only Eth networks supported, require ethereum address
timer?.invalidate()
timer = Timer.scheduledTimer(
withTimeInterval: 0.25, repeats: false,
block: { [weak self] _ in
guard let self = self else { return }
self.isSearchingToken = true
self.rpcService.ethTokenInfo(
address,
chainId: chainId,
completion: { token, status, error in
self.isSearchingToken = false
completion(token)
}
)
})
}
}

@MainActor func networkInfo(by chainId: String, coin: BraveWallet.CoinType) async -> BraveWallet.NetworkInfo? {
let allNetworks = await rpcService.allNetworks(coin)
Expand Down
35 changes: 2 additions & 33 deletions Sources/BraveWallet/Extensions/RpcServiceExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -385,37 +385,6 @@ extension BraveWalletJsonRpcService {
})
}
}

/// Helper to fetch `symbol` and `decimals` ONLY given a token contract address & chainId.
/// This function will be replaced by a BraveCore function that will also fetch `name` & `coingeckoId`.
func getEthTokenInfo(
contractAddress: String,
chainId: String
) async -> BraveWallet.BlockchainToken? {
// Fetches token info by contract address and chain ID. The returned token
// has the following fields populated:
// - contract_address
// - chain_id
// - coin
// - name
// - symbol
// - decimals
// - coingecko_id
//
// The following fields are always set to false, and callers must NOT rely
// on them:
// - is_erc721
// - is_erc1155
// - is_erc20
// - is_nft
let (token, status, _) = await ethTokenInfo(contractAddress, chainId: chainId)
guard status == .success else { return nil }
token?.logo = ""
token?.isSpam = false
token?.visible = false
token?.tokenId = ""
return token
}

/// Fetches the BlockchainToken for the given contract addresses. The token for a given contract
/// address is not guaranteed to be found, and will not be provided in the result if not found.
Expand All @@ -425,8 +394,8 @@ extension BraveWalletJsonRpcService {
await withTaskGroup(of: [BraveWallet.BlockchainToken?].self) { @MainActor group in
for contractAddressesChainIdPair in contractAddressesChainIdPairs {
group.addTask {
let token = await self.getEthTokenInfo(
contractAddress: contractAddressesChainIdPair.contractAddress,
let (token, _, _) = await self.ethTokenInfo(
contractAddressesChainIdPair.contractAddress,
chainId: contractAddressesChainIdPair.chainId
)
if let token {
Expand Down