Skip to content

Commit

Permalink
Improve loading token balances using wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
manhlx3006 committed Dec 30, 2019
1 parent eda0e12 commit 04c2fdd
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct KNCustomRPC {
let authorizedAddress: String
let tokenIEOAddress: String
let reserveAddress: String
let wrapperAddress: String
let limitOrderAddress: String
let etherScanEndpoint: String
let enjinScanEndpoint: String
Expand Down Expand Up @@ -47,6 +48,7 @@ struct KNCustomRPC {
self.enjinScanEndpoint = dictionary["enjinx"] as? String ?? ""
self.tradeTopic = dictionary["trade_topic"] as? String ?? ""
self.ensAddress = dictionary["ens_address"] as? String ?? ""
self.wrapperAddress = dictionary["wrapper"] as? String ?? ""
self.customRPC = CustomRPC(
chainID: chainID,
name: name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class KNLoadingInterval {
static let defaultLoadingInterval: TimeInterval = 10.0
static let cacheRateLoadingInterval: TimeInterval = 30.0
static let getUserTradeCapInterval: TimeInterval = 30.0
static let loadingBalance: TimeInterval = 30.0
static let loadingBalance: TimeInterval = 15.0
static let loadingCoinTickerInterval: TimeInterval = 60.0
static let loadingSupportedTokenInterval: TimeInterval = 5.0 * 60.0
static let loadingListIEOInterval: TimeInterval = 60.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ class KNLoadBalanceCoordinator {
}

@objc func shouldRefreshBalance(_ sender: Any?) {
if Date().timeIntervalSince(self.lastRefreshTime) < 15.0 {
if Date().timeIntervalSince(self.lastRefreshTime) < 5.0 {
self.lastRefreshTime = Date()
self.fetchETHBalance(nil)
self.fetchOtherTokensBalance(nil)
Expand All @@ -127,25 +127,25 @@ class KNLoadBalanceCoordinator {

fetchOtherTokensBalanceTimer?.invalidate()
isFetchingOtherTokensBalance = false
fetchOtherTokensBalance(nil)
fetchOtherTokenBalancesNew(nil)

fetchOtherTokensBalanceTimer = Timer.scheduledTimer(
withTimeInterval: KNLoadingInterval.loadingBalance,
repeats: true,
block: { [weak self] timer in
self?.fetchOtherTokensBalance(timer)
self?.fetchOtherTokenBalancesNew(timer)
}
)

fetchNonSupportedBalanceTimer?.invalidate()
isFetchNonSupportedBalance = false
fetchNonSupportedTokensBalance(nil)
fetchNonSupportedTokensBalancesNew(nil)

fetchNonSupportedBalanceTimer = Timer.scheduledTimer(
withTimeInterval: KNLoadingInterval.loadingCoinTickerInterval,
repeats: true,
block: { [weak self] timer in
self?.fetchNonSupportedTokensBalance(timer)
self?.fetchNonSupportedTokensBalancesNew(timer)
}
)
}
Expand Down Expand Up @@ -255,15 +255,37 @@ class KNLoadBalanceCoordinator {
}
}

@objc func fetchOtherTokensBalance(_ sender: Timer?) {
@objc func fetchOtherTokenBalancesNew(_ sender: Timer?) {
if isFetchingOtherTokensBalance { return }
isFetchingOtherTokensBalance = true
var isBalanceChanged: Bool = false

let tokenContracts = self.session.tokenStorage.tokens.filter({ return !$0.isETH && $0.isSupported }).sorted { (token0, token1) -> Bool in
if token0.value.isEmpty || token0.value == "0" { return false }
if token1.value.isEmpty || token1.value == "0" { return true }
return true
}.map({ $0.contract })

let tokens = tokenContracts.map({ return Address(string: $0)! })

self.fetchTokenBalances(tokens: tokens) { [weak self] result in
guard let `self` = self else { return }
self.isFetchingOtherTokensBalance = false
switch result {
case .success(let isLoaded):
if !isLoaded {
self.fetchOtherTokensBalance(sender)
}
case .failure:
self.fetchOtherTokensBalance(sender)
}
}
}

@objc func fetchOtherTokensBalance(_ sender: Timer?) {
if isFetchingOtherTokensBalance { return }
isFetchingOtherTokensBalance = true
var isBalanceChanged: Bool = false
let tokenContracts = self.session.tokenStorage.tokens.filter({ return !$0.isETH && $0.isSupported }).map({ $0.contract })
let currentWallet = self.session.wallet
let group = DispatchGroup()
var counter = 0
Expand Down Expand Up @@ -303,8 +325,30 @@ class KNLoadBalanceCoordinator {
}
}

@objc func fetchNonSupportedTokensBalancesNew(_ sender: Any?) {
if self.isFetchNonSupportedBalance { return }
self.isFetchNonSupportedBalance = true
let tokenContracts = self.session.tokenStorage.tokens.filter({ return !$0.isETH && !$0.isSupported }).map({ $0.contract })

let tokens = tokenContracts.map({ return Address(string: $0)! })

self.fetchTokenBalances(tokens: tokens) { [weak self] result in
guard let `self` = self else { return }
self.isFetchingOtherTokensBalance = false
switch result {
case .success(let isLoaded):
if !isLoaded {
self.fetchNonSupportedTokensBalance(sender)
}
case .failure:
self.fetchNonSupportedTokensBalance(sender)
}
}
}

@objc func fetchNonSupportedTokensBalance(_ sender: Any?) {
if self.isFetchNonSupportedBalance { return }
self.isFetchNonSupportedBalance = true
var isBalanceChanged: Bool = false
let tokenContracts = self.session.tokenStorage.tokens.filter({ return !$0.isETH && !$0.isSupported }).sorted { (token0, token1) -> Bool in
if token0.value.isEmpty || token0.value == "0" { return false }
Expand Down Expand Up @@ -349,4 +393,40 @@ class KNLoadBalanceCoordinator {
}
}
}

fileprivate func fetchTokenBalances(tokens: [Address], completion: @escaping (Result<Bool, AnyError>) -> Void) {
if tokens.isEmpty {
completion(.success(true))
return
}
var isBalanceChanged = false
self.session.externalProvider.getMultipleERC20Balances(tokens) { [weak self] result in
guard let `self` = self else { return }
switch result {
case .success(let values):
if values.count == tokens.count {
for id in 0..<values.count {
let balance = Balance(value: values[id])
let addr = tokens.description.lowercased()
if self.otherTokensBalance[addr] == nil || self.otherTokensBalance[addr]!.value != values[id] {
isBalanceChanged = true
}
self.otherTokensBalance[addr] = balance
self.session.tokenStorage.updateBalance(for: tokens[id], balance: values[id])
if isDebug {
NSLog("---- Balance: Fetch token balance for contract \(addr) successfully: \(values[id].shortString(decimals: 0))")
}
}
if isBalanceChanged {
KNNotificationUtil.postNotification(for: kOtherBalanceDidUpdateNotificationKey)
}
completion(.success(true))
} else {
completion(.success(false))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ class KNExternalProvider {
}
}

func getMultipleERC20Balances(_ tokens: [Address], completion: @escaping (Result<[BigInt], AnyError>) -> Void) {
KNGeneralProvider.shared.getMutipleERC20Balances(for: self.account.address, tokens: tokens, completion: completion)
}

// MARK: Sign transaction
private func signTransactionData(from transaction: UnconfirmedTransaction, nonce: Int, data: Data?, completion: @escaping (Result<Data, AnyError>) -> Void) {
let defaultGasLimit: BigInt = KNGasConfiguration.calculateDefaultGasLimitTransfer(token: transaction.transferType.tokenObject())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ class KNGeneralProvider {
return Address(string: KNEnvironment.default.knCustomRPC?.limitOrderAddress ?? "")!
}()

lazy var wrapperAddress: Address = {
return Address(string: KNEnvironment.default.knCustomRPC?.wrapperAddress ?? "")!
}()

init() { DispatchQueue.main.async { self.web3Swift.start() } }

// MARK: Balance
Expand Down Expand Up @@ -92,6 +96,31 @@ class KNGeneralProvider {
}
}

func getMutipleERC20Balances(for address: Address, tokens: [Address], completion: @escaping (Result<[BigInt], AnyError>) -> Void) {
let data = "0x6a385ae9"
+ "000000000000000000000000\(address.description.lowercased().drop0x)"
+ "0000000000000000000000000000000000000000000000000000000000000040"
var tokenCount = BigInt(tokens.count).hexEncoded.drop0x
tokenCount = [Character].init(repeating: "0", count: 64 - tokenCount.count) + tokenCount
let tokenAddresses = tokens.map({ return "000000000000000000000000\($0.description.lowercased().drop0x)" }).joined(separator: "")
let request = EtherServiceRequest(
batch: BatchFactory().create(CallRequest(to: self.wrapperAddress.description, data: "\(data)\(tokenCount)\(tokenAddresses)"))
)
DispatchQueue.global().async {
Session.send(request) { [weak self] result in
guard let `self` = self else { return }
DispatchQueue.main.async {
switch result {
case .success(let data):
self.getMultipleERC20BalancesDecode(data: data, completion: completion)
case .failure(let error):
completion(.failure(AnyError(error)))
}
}
}
}
}

// MARK: Transaction count
func getTransactionCount(for address: String, state: String = "latest", completion: @escaping (Result<Int, AnyError>) -> Void) {
let request = EtherServiceRequest(batch: BatchFactory().create(GetTransactionCountRequest(
Expand Down Expand Up @@ -582,6 +611,18 @@ extension KNGeneralProvider {
}
return node.hexEncoded
}

fileprivate func getMutipleERC20BalancesEncode(from address: Address, tokens: [Address], completion: @escaping (Result<String, AnyError>) -> Void) {
let request = GetMultipleERC20BalancesEncode(address: address, tokens: tokens)
self.web3Swift.request(request: request) { result in
switch result {
case .success(let data):
completion(.success(data))
case .failure(let error):
completion(.failure(AnyError(error)))
}
}
}
}

// MARK: Web3Swift Decoding
Expand Down Expand Up @@ -660,4 +701,20 @@ extension KNGeneralProvider {
}
}
}

fileprivate func getMultipleERC20BalancesDecode(data: String, completion: @escaping (Result<[BigInt], AnyError>) -> Void) {
let request = GetMultipleERC20BalancesDecode(data: data)
self.web3Swift.request(request: request) { result in
switch result {
case .success(let data):
let res = data.map({ val -> BigInt in
if val == "0x" { return BigInt(0) }
return BigInt(val) ?? BigInt(0)
})
completion(.success(res))
case .failure(let error):
completion(.failure(error))
}
}
}
}
26 changes: 26 additions & 0 deletions KyberNetwork/Vendors/New Group/Commands/GetERC20Balance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,29 @@ struct GetERC20BalanceDecode: Web3Request {
return .script(command: run)
}
}

struct GetMultipleERC20BalancesEncode: Web3Request {
typealias Response = String

static let abi = "{\"constant\":true,\"inputs\":[{\"name\":\"reserve\",\"type\":\"address\"},{\"name\":\"tokens\",\"type\":\"address[]\"}],\"name\":\"getBalances\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}"

let address: Address
let tokens: [Address]

var type: Web3RequestType {
let tokenAddresses = tokens.map({ return $0.description }).description
let run = "web3.eth.abi.encodeFunctionCall(\(GetMultipleERC20BalancesEncode.abi), [\"\(address.description)\", \"\(tokenAddresses)\"])"
return .script(command: run)
}
}

struct GetMultipleERC20BalancesDecode: Web3Request {
typealias Response = [String]

let data: String

var type: Web3RequestType {
let run = "web3.eth.abi.decodeParameter('uint256[]', '\(data)')"
return .script(command: run)
}
}

0 comments on commit 04c2fdd

Please sign in to comment.