Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multichain token balances #671

Merged
merged 17 commits into from
Dec 17, 2021
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
21 changes: 21 additions & 0 deletions @types/frame/ethProvider.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
declare module 'eth-provider' {
interface ProviderOpts {
name: string
}

interface RequestPayload {
id?: number,
jsonrpc?: '2.0',
method: string,
params?: any[],
chainId?: string
}

interface EthereumProvider extends NodeJS.EventEmitter {
constructor()
request(payload: RequestPayload)
setChain(chainId: string)
}

export default function provider (targets?: string | string[], opts?: ProviderOpts): EthereumProvider
}
4 changes: 4 additions & 0 deletions @types/frame/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/// <reference path="./rpc.d.ts" />
/// <reference path="./restore.d.ts" />
/// <reference path="./state.d.ts" />
/// <reference path="./ethProvider.d.ts" />

type NullableTimeout = NodeJS.Timeout | null
type Callback<T> = (err: Error | null, result?: T) => void
21 changes: 21 additions & 0 deletions @types/frame/restore.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
interface Observer {
remove: () => void
}

interface Action {
updates: any[]
}

declare type CallableStore = (...args: any[]) => any;

interface Store extends CallableStore {
observer: (cb: () => void, id?: string) => Observer,
[actionName: string]: (...args: any) => void,
api: {
feed: (handler: (state: any, actionBatch: Action[]) => any) => void;
}
}

declare module 'react-restore' {
export function create (state: any, actions: any): Store
}
39 changes: 21 additions & 18 deletions @types/frame/rpc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type RPCRequestCallback = RPCCallback<RPCResponsePayload>
type Address = string // 20 hex bytes, 0x-prefixed
enum SubscriptionType {
ACCOUNTS = 'accountsChanged',
ASSETS = 'assetsChanged',
CHAIN = 'chainChanged',
CHAINS = 'chainsChanged',
NETWORK = 'networkChanged'
Expand All @@ -20,7 +21,7 @@ interface RPCId {

interface InternalPayload {
_origin: string,
chain?: string
chainId?: string
}

interface JSONRPCRequestPayload extends RPCId {
Expand All @@ -43,30 +44,32 @@ interface EVMError {

type RPCRequestPayload = JSONRPCRequestPayload & InternalPayload

interface Balance {
chainId: number,
name: string,
symbol: string,
balance: string,
decimals: number,
displayBalance: string
}

interface Erc20 extends Balance {
address: Address
}

declare namespace RPC {
namespace GetAssets {
interface Balance {
chainId: number,
name: string,
symbol: string,
balance: string,
decimals: number,
displayBalance: string
}

interface Erc20 extends Balance {
address: Address
}

interface Assets {
erc20?: Erc20[],
nativeCurrency: Balance[]
}

interface Request extends Omit<RPCRequestPayload, 'method'> {
method: 'wallet_getAssets'
}

interface Response extends Omit<RPCResponsePayload, 'result'> {
result?: {
erc20?: Erc20[],
nativeCurrency: Balance[]
}
result?: Assets
}
}

Expand Down
59 changes: 59 additions & 0 deletions @types/frame/state.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
interface Network {
id: number,
name: string,
symbol: string
}

interface NetworkMetadata {
id: number,
name: string,
nativeCurrency: NativeCurrency,
symbol: string,
gas: GasData
}

interface NativeCurrency {
symbol: string
}

interface GasData {
fees: GasFees,
price: {
selected: string,
levels: GasLevels
}
}

interface GasFees {
nextBaseFee: string,
maxBaseFeePerGas: string,
maxPriorityFeePerGas: string,
maxFeePerGas: string
}

interface GasLevels {
slow?: string,
standard: string,
fast?: string,
asap?: string,
custom?: string
}

interface Balance {
chainId: number,
address: Address,
name: string,
symbol: string,
balance: string,
decimals: number,
displayBalance: string
}

interface Token {
chainId: number,
name: string,
symbol: string,
address: string,
decimals: number,
logoURI?: string
}
27 changes: 16 additions & 11 deletions app/App/Panel/Main/Account/Balances/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import BigNumber from 'bignumber.js'

const NATIVE_CURRENCY = '0x0000000000000000000000000000000000000000'

function formatBalance (balance, decimals = 8) {
return balance
function formatBalance (balance, totalValue, decimals = 8) {
const isZero = balance.isZero()
if (!isZero && balance.toNumber() < 0.001 && totalValue.toNumber() < 1) return '<0.001'

return !isZero
? new Intl.NumberFormat('us-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 8
Expand All @@ -19,24 +22,26 @@ function formatBalance (balance, decimals = 8) {
}

function formatUsdRate (rate, decimals = 2) {
return new Intl.NumberFormat('us-US', {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
}).format(rate.toFixed(decimals, BigNumber.ROUND_FLOOR))
return rate.isNaN()
? '?.??'
: new Intl.NumberFormat('us-US', {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
}).format(rate.toFixed(decimals, BigNumber.ROUND_FLOOR))
}

function balance (rawBalance, quote = {}) {
const balance = BigNumber(rawBalance.balance || 0).shiftedBy(-rawBalance.decimals)
const usdRate = BigNumber(quote.price || 0)
const usdRate = BigNumber(quote.price)
const totalValue = balance.times(usdRate)
const balanceDecimals = Math.max(2, usdRate.shiftedBy(1).toFixed(0, BigNumber.ROUND_DOWN).length)

return {
...rawBalance,
displayBalance: formatBalance(balance, balanceDecimals),
displayBalance: formatBalance(balance, totalValue, balanceDecimals),
price: formatUsdRate(usdRate),
priceChange: BigNumber(quote['change24hr'] || 0).toFixed(2),
totalValue,
priceChange: !usdRate.isNaN() && BigNumber(quote['change24hr'] || 0).toFixed(2),
totalValue: totalValue.isNaN() ? BigNumber(0) : totalValue,
displayValue: formatUsdRate(totalValue, 0)
}
}
Expand Down Expand Up @@ -142,7 +147,7 @@ class Balances extends React.Component {
<div className='signerBalancePrice'>
<span className='signerBalanceCurrentPrice'>{svg.usd(10)}{balanceInfo.price}</span>
<span className={priceChangeClass}>
<span>{direction === 1 ? '+' : ''}{balanceInfo.priceChange}%</span>
<span>{direction === 1 ? '+' : ''}{balanceInfo.priceChange ? balanceInfo.priceChange + '%' : ''}</span>
</span>
</div>
<div className='signerBalanceValue' style={(balanceInfo.displayBalance || '0').length >= 12 ? { fontSize: '15px', top: '10px' } : {}}>
Expand Down
2 changes: 1 addition & 1 deletion app/App/Panel/Notify/CustomTokens/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class CustomTokens extends React.Component {
}

render () {
const tokens = this.store('main.tokens')
const tokens = this.store('main.tokens.custom')

return (
<div className='notifyBoxWrap' onMouseDown={e => e.stopPropagation()}>
Expand Down
5 changes: 3 additions & 2 deletions main/accounts/Account/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ const abi = require('../../abi')
// Provider Proxy
const proxyProvider = require('../../provider/proxy').default

const nebula = require('../../nebula')()
const nebulaApi = require('../../nebula').default
const nebula = nebulaApi()

const signers = require('../../signers')
const windows = require('../../windows')
const store = require('../../store')
const store = require('../../store').default

const { Aragon } = require('../aragon')

Expand Down
2 changes: 1 addition & 1 deletion main/accounts/aragon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const log = require('electron-log')
const utils = require('ethereumjs-util')
const Wrapper = require('@aragon/wrapper').default
const { ensResolve } = require('@aragon/wrapper')
const store = require('../../store')
const store = require('../../store').default

const appNames = require('./appNames')

Expand Down
31 changes: 17 additions & 14 deletions main/accounts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { addHexPrefix } = require('ethereumjs-util')
const { usesBaseFee, signerCompatibility, maxFee } = require('../transaction')

const crypt = require('../crypt')
const store = require('../store')
const store = require('../store').default
const dataScanner = require('../externalData')

// Provider Proxy
Expand Down Expand Up @@ -181,7 +181,8 @@ class Accounts extends EventEmitter {
const tx = {
id: 1,
jsonrpc: '2.0',
method: 'eth_sendTransaction'
method: 'eth_sendTransaction',
chainId: targetChain.id
}

if (type === 'speed') {
Expand All @@ -199,7 +200,7 @@ class Accounts extends EventEmitter {
proxyProvider.emit('send', tx, (res = {}) => {
if (res.error) return reject(new Error(res.error.message))
resolve()
}, targetChain)
})
})
}

Expand All @@ -208,9 +209,9 @@ class Accounts extends EventEmitter {
// TODO: Route to account even if it's not current
if (!account) return reject(new Error('Unable to determine target account'))
if (!targetChain || !targetChain.type || !targetChain.id) return reject(new Error('Unable to determine target chain'))
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_blockNumber', params: [] }, (res) => {
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_blockNumber', chainId: targetChain.id, params: [] }, (res) => {
if (res.error) return reject(new Error(JSON.stringify(res.error)))
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_getTransactionReceipt', params: [hash] }, receiptRes => {
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_getTransactionReceipt', chainId: targetChain.id, params: [hash] }, receiptRes => {
if (receiptRes.error) return reject(new Error(receiptRes.error))
if (receiptRes.result && account.requests[id]) {
account.requests[id].tx.receipt = receiptRes.result
Expand Down Expand Up @@ -260,8 +261,8 @@ class Accounts extends EventEmitter {
const receiptBlock = parseInt(account.requests[id].tx.receipt.blockNumber, 16)
resolve(blockHeight - receiptBlock)
}
}, targetChain)
}, targetChain)
})
})
})
}

Expand All @@ -283,7 +284,7 @@ class Accounts extends EventEmitter {
log.error('txMonitor had no target chain')
setTimeout(() => this.accounts[account.address] && this.removeRequest(account, id), 8 * 1000)
} else {
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'] }, newHeadRes => {
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_subscribe', params: ['newHeads'], chainId: targetChain.id }, newHeadRes => {
if (newHeadRes.error) {
log.warn(newHeadRes.error)
const monitor = async () => {
Expand Down Expand Up @@ -336,17 +337,17 @@ class Accounts extends EventEmitter {
// proxyProvider.removeListener('data', handler)

proxyProvider.off(`data:${targetChain.type}:${targetChain.id}`, handler)
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', params: [headSub] }, res => {
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_unsubscribe', chainId: targetChain.id, params: [headSub] }, res => {
if (res.error) {
log.error('error sending message eth_unsubscribe', res)
}
}, targetChain)
})
}
}
}
proxyProvider.on(`data:${targetChain.type}:${targetChain.id}`, handler)
}
}, targetChain)
})
}
}

Expand Down Expand Up @@ -374,7 +375,9 @@ class Accounts extends EventEmitter {

const summary = currentAccount.summary()
cb(null, summary)
windows.broadcast('main:action', 'setSigner', summary)

store.setAccount(summary)

if (currentAccount.status === 'ok') this.verifyAddress(false, (err, verified) => {
if (!err && !verified) {
currentAccount.signer = ''
Expand Down Expand Up @@ -855,15 +858,15 @@ class Accounts extends EventEmitter {

const targetChain = { type: 'ethereum', id: parseInt(chainId, 'hex') }

proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_getTransactionCount', params: [from, 'pending'] }, (res) => {
proxyProvider.emit('send', { id: 1, jsonrpc: '2.0', method: 'eth_getTransactionCount', chainId: targetChain.id, params: [from, 'pending'] }, (res) => {
if (res.result) {
const newNonce = parseInt(res.result, 'hex')
const adjustedNonce = intToHex(nonceAdjust === 1 ? newNonce : newNonce + nonceAdjust)

txRequest.data.nonce = adjustedNonce
currentAccount.update()
}
}, targetChain)
})
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion main/api/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const log = require('electron-log')

const provider = require('../provider').default
const accounts = require('../accounts')
const store = require('../store')
const store = require('../store').default

const trusted = require('./trusted')
const validPayload = require('./validPayload').default
Expand Down
2 changes: 1 addition & 1 deletion main/api/trusted.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { v5: uuidv5 } = require('uuid')

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

const log = require('electron-log')
Expand Down
Loading