Skip to content

Commit

Permalink
Update balances on network change (#450)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Holtzman <matt@frame.sh>
  • Loading branch information
mholtzman and mholtzman authored Jun 7, 2021
1 parent 292f209 commit f94e147
Show file tree
Hide file tree
Showing 10 changed files with 139 additions and 113 deletions.
5 changes: 3 additions & 2 deletions app/App/Panel/Main/Account/Balances/Index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ function balance (rawBalance, quote = {}) {

function getBalances (chainId, defaultSymbol, rawBalances, rates) {
const mainBalance = rawBalances[defaultSymbol]
const tokenBalances = Object.values(rawBalances).filter(b => Number(b.chainId) === Number(chainId))
const tokenBalances = Object.values(rawBalances)
.filter(b => Number(b.chainId) === Number(chainId) && b.symbol !== defaultSymbol)

const balances = [mainBalance].concat(tokenBalances)
.filter(Boolean)
Expand Down Expand Up @@ -130,7 +131,7 @@ class Balances extends React.Component {
const address = this.store('main.accounts', this.props.id, 'address')
const { type, id: chainId } = this.store('main.currentNetwork')
const currentSymbol = this.store('main.networks', type, chainId, 'symbol') || 'ETH'
const storedBalances = this.store('main.balances', address) || {}
const storedBalances = this.store('main.balances', chainId, address) || {}

const rates = this.store('main.rates')

Expand Down
6 changes: 3 additions & 3 deletions main/chains/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ class ChainConnection extends EventEmitter {
}
}

getNetwork (provider, cb) {
getNetwork (provider, cb) {
provider.sendAsync({ jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 1 }, (err, response) => {
try {
response.result = !err && response && !response.error ? parseInt(response.result, 'hex').toString() : ''
cb(err, response)
} catch (e) {
cb(e)
}
})
})
}

getNodeType (provider, cb) { provider.sendAsync({ jsonrpc: '2.0', method: 'web3_clientVersion', params: [], id: 1 }, cb) }
Expand Down Expand Up @@ -295,6 +295,7 @@ class Chains extends EventEmitter {
})
})
}

send (payload, res) {
const { type, id } = store('main.currentNetwork')
if (this.connections[type] && this.connections[type][id]) {
Expand All @@ -307,5 +308,4 @@ class Chains extends EventEmitter {
}
}


module.exports = new Chains()
56 changes: 42 additions & 14 deletions main/data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,48 @@ const WebSocket = require('ws')

const store = require('../store')
const accounts = require('../accounts')
const chains = require('../chains')

let socket, reconnectTimer

store.observer(() => {
const network = store('main.currentNetwork')

if (network.id.toString() !== '1') {
// mainnet gas prices use a websocket callback (see onData() below)
chains.send({ id: 1, jsonrpc: '2.0', method: 'eth_gasPrice' }, response => {
if (response.result) {
const price = parseInt(response.result, 16) / 1000000000

setGasPrices(network.type, network.id, {
slow: price,
standard: price,
fast: price * 2,
asap: price * 4,
custom: store('main.networksMeta', network.type, network.id, 'gas.price.levels.custom') || response.result,
})
}
})
}
})

function setGasPrices (network, chainId, gas) {
store.setGasPrices(network, chainId, {
slow: ('0x' + gweiToWei(Math.round(gas.slow)).toString(16)),
slowTime: gas.slowTime,
standard: ('0x' + gweiToWei(Math.round(gas.standard)).toString(16)),
standardTime: gas.standardTime,
fast: ('0x' + gweiToWei(Math.round(gas.fast)).toString(16)),
fastTime: gas.fastTime,
asap: ('0x' + gweiToWei(Math.round(gas.asap)).toString(16)),
asapTime: gas.asapTime,
custom: store('main.networksMeta', network, chainId, 'gas.price.levels.custom') || ('0x' + gweiToWei(Math.round(gas.standard)).toString(16)),
lastUpdate: gas.lastUpdate,
quality: gas.quality,
source: gas.source
})
}

const reconnect = now => {
log.info('Trying to reconnect to realtime')
clearTimeout(reconnectTimer)
Expand All @@ -27,20 +66,9 @@ const onData = data => {
clearTimeout(staleTimer)
// If we havent recieved gas data in 90s, make sure we're connected
staleTimer = setTimeout(() => setUpSocket('staleTimer'), 90 * 1000)
store.setGasPrices('ethereum', '1', {
slow: ('0x' + gweiToWei(Math.round(gas.slow)).toString(16)),
slowTime: gas.slowTime,
standard: ('0x' + gweiToWei(Math.round(gas.standard)).toString(16)),
standardTime: gas.standardTime,
fast: ('0x' + gweiToWei(Math.round(gas.fast)).toString(16)),
fastTime: gas.fastTime,
asap: ('0x' + gweiToWei(Math.round(gas.asap)).toString(16)),
asapTime: gas.asapTime,
custom: store('main.networksMeta.ethereum.1.gas.price.levels.custom') || ('0x' + gweiToWei(Math.round(gas.standard)).toString(16)),
lastUpdate: gas.lastUpdate,
quality: gas.quality,
source: gas.source
})

setGasPrices('ethereum', '1', gas)

accounts.checkBetterGasPrice()
}
} catch (e) {
Expand Down
26 changes: 14 additions & 12 deletions main/externalData/coins.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,28 @@ const chainCoins = {
}
}

function lookupChainCoin (chainId) {
// default to eth for any chain that doesn't have its own coin
return chainCoins[chainId] || chainCoins[1]
}

module.exports = function (eth) {
return {
getCoinBalances: async function (chainId, address) {
const nativeCoin = chainCoins[chainId]
const nativeCoin = lookupChainCoin(chainId)

if (nativeCoin) {
const symbol = nativeCoin.symbol.toLowerCase()
const rawBalance = await eth.request({ method: 'eth_getBalance', params: [address, 'latest'] })
const symbol = nativeCoin.symbol.toLowerCase()
const rawBalance = await eth.request({ method: 'eth_getBalance', params: [address, 'latest'] })

const balance = BigNumber(rawBalance).shiftedBy(-nativeCoin.decimals)
const balance = BigNumber(rawBalance).shiftedBy(-nativeCoin.decimals)

return {
[symbol]: {
...nativeCoin,
balance
}
return {
[symbol]: {
...nativeCoin,
chainId,
balance
}
}

return {}
}
}
}
50 changes: 33 additions & 17 deletions main/externalData/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const store = require('../store')
let activeAddress
let trackedAddresses = []

let scanWorker, heartbeat, allAddressScan, trackedAddressScan, coinScan, rateScan, inventoryScan
let observer, scanWorker, heartbeat, allAddressScan, trackedAddressScan, coinScan, rateScan, inventoryScan

function createWorker () {
if (scanWorker) {
Expand All @@ -24,7 +24,7 @@ function createWorker () {
if (message.type === 'ready') updateRates(['eth', 'xdai', 'matic'])

if (message.type === 'tokens') {
store.setBalances(message.address, message.found, message.fullScan)
store.setBalances(message.netId, message.address, message.found, message.fullScan)
updateRates(Object.keys(message.found))
}

Expand Down Expand Up @@ -72,14 +72,30 @@ function startScan (fn, interval) {
return setInterval(fn, interval)
}

function scanActiveData () {
if (trackedAddressScan) {
clearInterval(trackedAddressScan)
}

if (inventoryScan) {
clearInterval(inventoryScan)
}

// update tokens for the active account every 15 seconds
trackedAddressScan = startScan(updateTrackedTokens, 1000 * 15)

// update inventory for the active account every 60 seconds
inventoryScan = startScan(() => updateInventory(), 1000 * 60)
}

const sendHeartbeat = () => sendCommandToWorker('heartbeat')
const updateCoinUniverse = () => sendCommandToWorker('updateCoins')
const updateRates = symbols => sendCommandToWorker('updateRates', [symbols])
const updateTrackedTokens = () => { if (activeAddress) { sendCommandToWorker('updateTokenBalances', [[activeAddress]]) } }
const updateAllTokens = () => sendCommandToWorker('updateTokenBalances', [trackedAddresses])

const updateInventory = () => {
if (activeAddress) { sendCommandToWorker('updateInventory', [[activeAddress]]) }
const updateInventory = () => {
if (activeAddress) { sendCommandToWorker('updateInventory', [[activeAddress]]) }
}

function addAddresses (addresses) {
Expand All @@ -95,11 +111,19 @@ function addAddresses (addresses) {
function setActiveAddress (address) {
addAddresses([address])
activeAddress = address
updateTrackedTokens()
updateInventory()

scanActiveData()
}

function start (addresses = [], omitList = [], knownList) {
observer = store.observer(() => {
const { id } = store('main.currentNetwork')

log.debug(`changed scanning network to chainId: ${id}`)

scanActiveData()
})

// addAddresses(addresses) // Scan becomes too heavy with many accounts added

if (scanWorker) {
Expand All @@ -125,26 +149,18 @@ function start (addresses = [], omitList = [], knownList) {
allAddressScan = startScan(updateAllTokens, 1000 * 60 * 5)
}

if (!trackedAddressScan) {
// update tokens for the active account every 15 seconds
trackedAddressScan = startScan(updateTrackedTokens, 1000 * 15)
}

if (!rateScan) {
// update base rates
const ethereum = store('main.networks.ethereum')
const baseRates = Object.keys(ethereum).map(n => ethereum[n].symbol && ethereum[n].symbol.toLowerCase()).filter(s => s)
rateScan = startScan(() => updateRates([...new Set(baseRates)]), 1000 * 15)
}

if (!inventoryScan) {
// update inventory
inventoryScan = startScan(() => updateInventory(), 1000 * 60)
}
}

function stop () {
log.info('stopping external data worker');
log.info('stopping external data worker')

observer.remove();

[heartbeat, allAddressScan, trackedAddressScan, coinScan]
.forEach(scanner => { if (scanner) clearInterval(scanner) })
Expand Down
3 changes: 1 addition & 2 deletions main/externalData/scan.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const log = require('electron-log')
const provider = require('eth-provider')()

const getTokenList = require('./inventory/tokens')
Expand All @@ -17,7 +16,7 @@ async function scan (address, omit = [], knownList) {

const coinBalances = (await coins(provider).getCoinBalances(chain, address))
// Emit progress asap, needs better pattern
process.send({ type: 'tokens', address, found: coinBalances })
process.send({ type: 'tokens', netId: chain, address, found: coinBalances })
const foundTokens = await getTokenBalances(chain, address, tokens)

const tokenBalances = Object.entries(foundTokens).reduce((found, [addr, balance]) => {
Expand Down
17 changes: 15 additions & 2 deletions main/externalData/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,23 @@ log.transports.console.level = process.env.LOG_WORKER ? 'debug' : false

let heartbeat

function groupByChainId (tokens) {
return Object.entries(tokens).reduce((grouped, [symbol, token]) => {
grouped[token.chainId] = { ...(grouped[token.chainId] || {}), [symbol]: token }
return grouped
}, {})
}

function tokenScan (addresses) {
addresses.forEach(address => {
scanTokens(address)
.then(found => process.send({ type: 'tokens', address, found, fullScan: true }))
.then(foundTokens => {
const grouped = groupByChainId(foundTokens)

Object.entries(grouped).forEach(([netId, found]) => {
process.send({ type: 'tokens', netId, address, found, fullScan: true })
})
})
.catch(err => log.error('token scan error', err))
})
}
Expand All @@ -25,7 +38,7 @@ function ratesScan (symbols) {
function inventoryScan (addresses) {
addresses.forEach(address => {
inventory(address)
.then(inventory => process.send({ type: 'inventory', address, inventory }))
.then(inventory => process.send({ type: 'inventory', address, inventory }))
.catch(err => log.error('inventory scan error', err))
})
}
Expand Down
Loading

0 comments on commit f94e147

Please sign in to comment.