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

Commit

Permalink
Fix #8604: Restore Add Custom Token Auto-complete (#8605)
Browse files Browse the repository at this point in the history
* Restore Add Custom Token auto-complete, now supporting all EVMs instead of just Mainnet

* Update code comment

* Remove unneeded network assignment; network required to fetch auto-complete info
  • Loading branch information
StephenHeaps authored Jan 4, 2024
1 parent 033a9fa commit d65102a
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 44 deletions.
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

0 comments on commit d65102a

Please sign in to comment.