Skip to content

Commit

Permalink
Finance: add data fallback for known tokens (#616)
Browse files Browse the repository at this point in the history
Some known tokens don’t strictly follow ERC-20 and it would be difficult to adapt to every situation. The data listed in this map is used as a fallback if either the name or the symbol can’t be determined from the contract only.
  • Loading branch information
bpierre authored and sohkai committed Dec 21, 2018
1 parent e521196 commit d99b6e9
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 24 deletions.
39 changes: 28 additions & 11 deletions apps/finance/app/src/components/NewTransfer/Deposit.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ import tokenDecimalsAbi from '../../abi/token-decimals.json'
import tokenSymbolAbi from '../../abi/token-symbol.json'
import { fromDecimals, toDecimals } from '../../lib/math-utils'
import provideNetwork from '../../lib/provideNetwork'
import { ETHER_TOKEN_FAKE_ADDRESS } from '../../lib/token-utils'
import {
ETHER_TOKEN_FAKE_ADDRESS,
tokenDataFallback,
} from '../../lib/token-utils'
import { addressesEqual, isAddress } from '../../lib/web3-utils'
import { combineLatest } from '../../rxjs'
import ToggleContent from '../ToggleContent'
Expand Down Expand Up @@ -121,7 +124,7 @@ class Deposit extends React.Component {
return selectedToken.value && !selectedToken.data.loading
}
loadTokenData(address) {
const { app, userAccount } = this.props
const { app, network, userAccount } = this.props

// ETH
if (addressesEqual(address, ETHER_TOKEN_FAKE_ADDRESS)) {
Expand All @@ -145,24 +148,38 @@ class Deposit extends React.Component {
// Tokens
const token = app.external(address, tokenAbi)

return new Promise((resolve, reject) =>
combineLatest(
token.symbol(),
token.decimals(),
token.balanceOf(userAccount)
)
return new Promise(async (resolve, reject) => {
const userBalance = await token
.balanceOf(userAccount)
.first()
.toPromise()

const decimalsFallback =
tokenDataFallback(address, 'decimals', network.type) || '0'
const symbolFallback =
tokenDataFallback(address, 'symbol', network.type) || ''

combineLatest(token.decimals(), token.symbol())
.first()
.subscribe(
([symbol, decimals, userBalance]) =>
([decimals = decimalsFallback, symbol = symbolFallback]) =>
resolve({
symbol,
userBalance,
decimals: parseInt(decimals, 10),
loading: false,
}),
reject
() => {
// Decimals and symbols are optional
resolve({
userBalance,
decimals: parseInt(decimalsFallback, 10),
loading: false,
symbol: symbolFallback,
})
}
)
)
})
}
validateInputs({ amount, selectedToken } = {}) {
amount = amount || this.state.amount
Expand Down
30 changes: 30 additions & 0 deletions apps/finance/app/src/lib/token-utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
import { ETHER_TOKEN_VERIFIED_ADDRESSES } from './verified-tokens'

// Some known tokens don’t strictly follow ERC-20 and it would be difficult to
// adapt to every situation. The data listed in this map is used as a fallback
// if either some part of their interface doesn't conform to a standard we
// support.
const KNOWN_TOKENS_FALLBACK = new Map([
[
'main',
new Map([
[
'0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359',
{ symbol: 'DAI', name: 'Dai Stablecoin v1.0', decimals: '18' },
],
]),
],
])

export const ETHER_TOKEN_FAKE_ADDRESS =
'0x0000000000000000000000000000000000000000'

Expand All @@ -8,3 +24,17 @@ export const isTokenVerified = (tokenAddress, networkType) =>
networkType === 'main'
? ETHER_TOKEN_VERIFIED_ADDRESSES.has(tokenAddress.toLowerCase())
: true

export const tokenDataFallback = (tokenAddress, fieldName, networkType) => {
// The fallback list is without checksums
const addressWithoutChecksum = tokenAddress.toLowerCase()

const fallbacksForNetwork = KNOWN_TOKENS_FALLBACK.get(networkType)
if (
fallbacksForNetwork == null ||
!fallbacksForNetwork.has(addressWithoutChecksum)
) {
return null
}
return fallbacksForNetwork.get(addressWithoutChecksum)[fieldName] || null
}
36 changes: 23 additions & 13 deletions apps/finance/app/src/script.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import Aragon from '@aragon/client'
import { of } from './rxjs'
import { getTestTokenAddresses } from './testnet'
import { ETHER_TOKEN_FAKE_ADDRESS, isTokenVerified } from './lib/token-utils'
import {
ETHER_TOKEN_FAKE_ADDRESS,
isTokenVerified,
tokenDataFallback,
} from './lib/token-utils'
import { addressesEqual } from './lib/web3-utils'
import tokenDecimalsAbi from './abi/token-decimals.json'
import tokenNameAbi from './abi/token-name.json'
Expand Down Expand Up @@ -252,9 +256,9 @@ function updateTransactions({ transactions = [] }, transactionDetails) {
async function newBalanceEntry(tokenContract, tokenAddress, settings) {
const [balance, decimals, name, symbol] = await Promise.all([
loadTokenBalance(tokenAddress, settings),
loadTokenDecimals(tokenContract),
loadTokenName(tokenContract),
loadTokenSymbol(tokenContract),
loadTokenDecimals(tokenContract, tokenAddress, settings),
loadTokenName(tokenContract, tokenAddress, settings),
loadTokenSymbol(tokenContract, tokenAddress, settings),
])

return {
Expand Down Expand Up @@ -285,66 +289,72 @@ function loadTokenBalance(tokenAddress, { vault }) {
})
}

function loadTokenDecimals(tokenContract) {
function loadTokenDecimals(tokenContract, tokenAddress, { network }) {
return new Promise((resolve, reject) => {
if (tokenDecimals.has(tokenContract)) {
resolve(tokenDecimals.get(tokenContract))
} else {
const fallback =
tokenDataFallback(tokenAddress, 'decimals', network.type) || '0'
tokenContract
.decimals()
.first()
.subscribe(
decimals => {
(decimals = fallback) => {
tokenDecimals.set(tokenContract, decimals)
resolve(decimals)
},
() => {
// Decimals is optional
resolve('0')
resolve(fallback)
}
)
}
})
}

function loadTokenName(tokenContract) {
function loadTokenName(tokenContract, tokenAddress, { network }) {
return new Promise((resolve, reject) => {
if (tokenName.has(tokenContract)) {
resolve(tokenName.get(tokenContract))
} else {
const fallback =
tokenDataFallback(tokenAddress, 'name', network.type) || ''
tokenContract
.name()
.first()
.subscribe(
name => {
(name = fallback) => {
tokenName.set(tokenContract, name)
resolve(name)
},
() => {
// Name is optional
resolve('')
resolve(fallback)
}
)
}
})
}

function loadTokenSymbol(tokenContract) {
function loadTokenSymbol(tokenContract, tokenAddress, { network }) {
return new Promise((resolve, reject) => {
if (tokenSymbols.has(tokenContract)) {
resolve(tokenSymbols.get(tokenContract))
} else {
const fallback =
tokenDataFallback(tokenAddress, 'symbol', network.type) || ''
tokenContract
.symbol()
.first()
.subscribe(
symbol => {
(symbol = fallback) => {
tokenSymbols.set(tokenContract, symbol)
resolve(symbol)
},
() => {
// Symbol is optional
resolve('')
resolve(fallback)
}
)
}
Expand Down

0 comments on commit d99b6e9

Please sign in to comment.