Browse Source

Make shitcoin integration great again

master
Gaëtan Renaudeau 7 years ago
parent
commit
343c90b672
  1. 2
      package.json
  2. 4
      src/api/Ethereum.js
  3. 5
      src/api/Fees.js
  4. 15
      src/api/Ledger.js
  5. 17
      src/bridge/index.js
  6. 4
      src/components/modals/OperationDetails.js
  7. 17
      src/components/modals/Send/ConfirmationFooter.js
  8. 1
      src/components/modals/Send/SendModalBody.js
  9. 49
      src/helpers/SettingsDefaults.js
  10. 34
      src/helpers/explorers.js
  11. 35
      src/helpers/getAddressForCurrency/index.js
  12. 6
      yarn.lock

2
package.json

@ -42,7 +42,7 @@
"@ledgerhq/hw-transport": "^4.12.0", "@ledgerhq/hw-transport": "^4.12.0",
"@ledgerhq/hw-transport-node-hid": "^4.12.0", "@ledgerhq/hw-transport-node-hid": "^4.12.0",
"@ledgerhq/ledger-core": "1.4.4", "@ledgerhq/ledger-core": "1.4.4",
"@ledgerhq/live-common": "^2.25.0", "@ledgerhq/live-common": "2.26.0",
"axios": "^0.18.0", "axios": "^0.18.0",
"babel-runtime": "^6.26.0", "babel-runtime": "^6.26.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",

4
src/api/Ethereum.js

@ -42,7 +42,9 @@ export type API = {
export const apiForCurrency = (currency: CryptoCurrency): API => { export const apiForCurrency = (currency: CryptoCurrency): API => {
const baseURL = blockchainBaseURL(currency) const baseURL = blockchainBaseURL(currency)
if (!baseURL) {
throw new Error(`ledger API not available for currency ${currency.id}`)
}
return { return {
async getTransactions(address, blockHash) { async getTransactions(address, blockHash) {
const { data } = await userFriendlyError( const { data } = await userFriendlyError(

5
src/api/Fees.js

@ -1,4 +1,5 @@
// @flow // @flow
import invariant from 'invariant'
import axios from 'axios' import axios from 'axios'
import type { Currency } from '@ledgerhq/live-common/lib/types' import type { Currency } from '@ledgerhq/live-common/lib/types'
import { blockchainBaseURL, userFriendlyError } from './Ledger' import { blockchainBaseURL, userFriendlyError } from './Ledger'
@ -8,7 +9,9 @@ export type Fees = {
} }
export const getEstimatedFees = async (currency: Currency): Promise<Fees> => { export const getEstimatedFees = async (currency: Currency): Promise<Fees> => {
const { data, status } = await userFriendlyError(axios.get(`${blockchainBaseURL(currency)}/fees`)) const baseURL = blockchainBaseURL(currency)
invariant(baseURL, `Fees for ${currency.id} are not supported`)
const { data, status } = await userFriendlyError(axios.get(`${baseURL}/fees`))
if (data) { if (data) {
return data return data
} }

15
src/api/Ledger.js

@ -3,17 +3,6 @@ import type { Currency } from '@ledgerhq/live-common/lib/types'
const BASE_URL = process.env.LEDGER_REST_API_BASE || 'https://api.ledgerwallet.com/' const BASE_URL = process.env.LEDGER_REST_API_BASE || 'https://api.ledgerwallet.com/'
const mapping = {
bitcoin_cash: 'abc',
ethereum_classic: 'ethc',
ethereum_testnet: 'eth_testnet',
}
export const currencyToFeeTicker = (currency: Currency) => {
const tickerLowerCase = currency.ticker.toLowerCase()
return mapping[currency.id] || tickerLowerCase
}
export const userFriendlyError = <A>(p: Promise<A>): Promise<A> => export const userFriendlyError = <A>(p: Promise<A>): Promise<A> =>
p.catch(error => { p.catch(error => {
if (error.response) { if (error.response) {
@ -48,5 +37,5 @@ export const userFriendlyError = <A>(p: Promise<A>): Promise<A> =>
throw error throw error
}) })
export const blockchainBaseURL = (currency: Currency) => export const blockchainBaseURL = ({ ledgerExplorerId }: Currency): ?string =>
`${BASE_URL}blockchain/v2/${currencyToFeeTicker(currency)}` ledgerExplorerId ? `${BASE_URL}blockchain/v2/${ledgerExplorerId}` : null

17
src/bridge/index.js

@ -1,16 +1,19 @@
// @flow // @flow
import type { Currency } from '@ledgerhq/live-common/lib/types' import type { Currency } from '@ledgerhq/live-common/lib/types'
import invariant from 'invariant'
import { WalletBridge } from './types' import { WalletBridge } from './types'
import LibcoreBridge from './LibcoreBridge' import LibcoreBridge from './LibcoreBridge'
import EthereumJSBridge from './EthereumJSBridge' import EthereumJSBridge from './EthereumJSBridge'
import RippleJSBridge from './RippleJSBridge' import RippleJSBridge from './RippleJSBridge'
const perFamily = {
bitcoin: LibcoreBridge,
ripple: RippleJSBridge,
ethereum: EthereumJSBridge,
}
export const getBridgeForCurrency = (currency: Currency): WalletBridge<any> => { export const getBridgeForCurrency = (currency: Currency): WalletBridge<any> => {
if (currency.id.indexOf('ethereum') === 0) { const bridge = perFamily[currency.family]
return EthereumJSBridge // polyfill js invariant(bridge, `${currency.id} currency is not supported`)
} return bridge
if (currency.id === 'ripple') {
return RippleJSBridge // polyfill js
}
return LibcoreBridge // libcore for the rest
} }

4
src/components/modals/OperationDetails.js

@ -8,7 +8,7 @@ import { translate } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'
import moment from 'moment' import moment from 'moment'
import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/helpers/operation' import { getOperationAmountNumber } from '@ledgerhq/live-common/lib/helpers/operation'
import { getTxURL } from 'helpers/explorers' import { getAccountOperationExplorer } from '@ledgerhq/live-common/lib/explorers'
import type { Account, Operation } from '@ledgerhq/live-common/lib/types' import type { Account, Operation } from '@ledgerhq/live-common/lib/types'
import type { T, CurrencySettings } from 'types/common' import type { T, CurrencySettings } from 'types/common'
@ -78,7 +78,7 @@ const OperationDetails = connect(mapStateToProps)((props: Props) => {
const confirmations = operation.blockHeight ? account.blockHeight - operation.blockHeight : 0 const confirmations = operation.blockHeight ? account.blockHeight - operation.blockHeight : 0
const isConfirmed = confirmations >= currencySettings.confirmationsNb const isConfirmed = confirmations >= currencySettings.confirmationsNb
const url = getTxURL(account, operation) const url = getAccountOperationExplorer(account, operation)
const uniqSenders = uniq(senders) const uniqSenders = uniq(senders)
return ( return (

17
src/components/modals/Send/ConfirmationFooter.js

@ -1,17 +1,21 @@
// @flow // @flow
import React from 'react' import React from 'react'
import type { Operation } from '@ledgerhq/live-common/lib/types' import type { Operation, Account } from '@ledgerhq/live-common/lib/types'
import { shell } from 'electron'
import Button from 'components/base/Button' import Button from 'components/base/Button'
import { ModalFooter } from 'components/base/Modal' import { ModalFooter } from 'components/base/Modal'
import { getAccountOperationExplorer } from '@ledgerhq/live-common/lib/explorers'
import type { T } from 'types/common' import type { T } from 'types/common'
export default ({ export default ({
t, t,
account,
optimisticOperation, optimisticOperation,
onClose, onClose,
onGoToFirstStep, onGoToFirstStep,
}: { }: {
t: T, t: T,
account: ?Account,
optimisticOperation: ?Operation, optimisticOperation: ?Operation,
onClose: () => void, onClose: () => void,
onGoToFirstStep: () => void, onGoToFirstStep: () => void,
@ -20,7 +24,16 @@ export default ({
<Button onClick={onClose}>{t('common:close')}</Button> <Button onClick={onClose}>{t('common:close')}</Button>
{optimisticOperation ? ( {optimisticOperation ? (
// TODO: actually go to operations details // TODO: actually go to operations details
<Button onClick={onClose} primary> <Button
onClick={() => {
const url = account && getAccountOperationExplorer(account, optimisticOperation)
if (url) {
shell.openExternal(url)
}
onClose()
}}
primary
>
{t('send:steps.confirmation.success.cta')} {t('send:steps.confirmation.success.cta')}
</Button> </Button>
) : ( ) : (

1
src/components/modals/Send/SendModalBody.js

@ -242,6 +242,7 @@ class SendModalBody extends PureComponent<Props, State<*>> {
{stepIndex === 3 ? ( {stepIndex === 3 ? (
<ConfirmationFooter <ConfirmationFooter
t={t} t={t}
account={account}
optimisticOperation={optimisticOperation} optimisticOperation={optimisticOperation}
onClose={onClose} onClose={onClose}
onGoToFirstStep={this.onGoToFirstStep} onGoToFirstStep={this.onGoToFirstStep}

49
src/helpers/SettingsDefaults.js

@ -1,6 +1,6 @@
// @flow // @flow
import type { CryptoCurrencyConfig, CryptoCurrency } from '@ledgerhq/live-common/lib/types' import type { CryptoCurrency } from '@ledgerhq/live-common/lib/types'
type ConfirmationDefaults = { type ConfirmationDefaults = {
confirmationsNb: ?{ confirmationsNb: ?{
@ -10,44 +10,15 @@ type ConfirmationDefaults = {
}, },
} }
// This is approximated to be a 30mn confirmation in number of blocks on blockchains export const currencySettingsDefaults = ({
// to disable the confirmations feature simply set 0. blockAvgTime,
const confirmationsNbPerCoin: CryptoCurrencyConfig<number> = { }: CryptoCurrency): ConfirmationDefaults => {
bitcoin_cash: 2, let confirmationsNb
bitcoin_gold: 2, if (blockAvgTime) {
bitcoin_testnet: 2, const def = Math.ceil(30 * 60 / blockAvgTime) // 30 min approx validation
bitcoin: 2, confirmationsNb = { min: 1, def, max: 2 * def }
dash: 12, }
digibyte: 30,
dogecoin: 30,
ethereum_classic: 120,
ethereum_testnet: 120,
ethereum: 120,
hcash: 12, // FIXME can't grab the block time info anywhere...
komodo: 30,
litecoin: 6,
peercoin: 4,
pivx: 12, // FIXME can't grab the block time info anywhere...
poswallet: 28,
qtum: 15,
ripple: 0,
stealthcoin: 12, // FIXME can't grab the block time info anywhere...
stratis: 12, // FIXME can't grab the block time info anywhere...
vertcoin: 12,
viacoin: 75,
zcash: 12,
zencash: 12,
}
export const currencySettingsDefaults = (currency: CryptoCurrency): ConfirmationDefaults => {
const confirmationsNbDef = confirmationsNbPerCoin[currency.id]
return { return {
confirmationsNb: confirmationsNbDef confirmationsNb,
? {
min: 1,
def: confirmationsNbDef,
max: 2 * confirmationsNbDef,
}
: null,
} }
} }

34
src/helpers/explorers.js

@ -1,34 +0,0 @@
// @flow
import type { Account, Operation, CryptoCurrencyConfig } from '@ledgerhq/live-common/lib/types'
type Explorer = Operation => ?string
const txExplorers: CryptoCurrencyConfig<Explorer> = {
bitcoin_cash: op => `https://bitcoincash.blockexplorer.com/tx/${op.hash}`,
bitcoin_gold: op => `https://btgexplorer.com/tx/${op.hash}`,
bitcoin_testnet: op => `https://testnet.blockchain.info/tx/${op.hash}`,
bitcoin: op => `https://blockchain.info/tx/${op.hash}`,
dash: op => `https://explorer.dash.org/tx/${op.hash}`,
digibyte: op => `https://digiexplorer.info/tx/${op.hash}`,
dogecoin: op => `https://dogechain.info/tx/${op.hash}`,
ethereum_classic: op => `https://gastracker.io/tx/${op.hash}`,
ethereum_testnet: op => `https://ropsten.etherscan.io/tx/${op.hash}`,
ethereum: op => `https://etherscan.io/tx/${op.hash}`,
hcash: op => `http://explorer.h.cash/tx/${op.hash}`,
komodo: op => `https://kmd.explorer.supernet.org/tx/${op.hash}`,
litecoin: op => `http://explorer.litecoin.net/tx/${op.hash}`,
peercoin: op => `https://explorer.peercoin.net/tx/${op.hash}`,
pivx: () => null, // FIXME can't find a reliable/official explorer
poswallet: () => null, // FIXME can't find a reliable/official explorer
qtum: op => `https://explorer.qtum.org/tx/${op.hash}`,
ripple: op => `https://bithomp.com/explorer/${op.hash}`,
stealthcoin: () => null, // FIXME can't find a reliable/official explorer
stratis: () => null, // FIXME can't find a reliable/official explorer
vertcoin: op => `http://explorer.vertcoin.info/tx/${op.hash}`,
viacoin: op => `https://explorer.viacoin.org/tx/${op.hash}`,
zcash: op => `https://explorer.zcha.in/transactions/${op.hash}`,
zencash: op => `https://explorer.zensystem.io/tx/${op.hash}`,
}
export const getTxURL = (account: Account, operation: Operation): ?string =>
txExplorers[account.currency.id](operation)

35
src/helpers/getAddressForCurrency/index.js

@ -1,8 +1,8 @@
// @flow // @flow
import type { CryptoCurrencyConfig } from '@ledgerhq/live-common/lib/types' import invariant from 'invariant'
import type Transport from '@ledgerhq/hw-transport' import type Transport from '@ledgerhq/hw-transport'
import btc from './btc' import bitcoin from './btc'
import ethereum from './ethereum' import ethereum from './ethereum'
import ripple from './ripple' import ripple from './ripple'
@ -18,33 +18,16 @@ type Resolver = (
type Module = (currencyId: string) => Resolver type Module = (currencyId: string) => Resolver
const all: CryptoCurrencyConfig<Resolver> = { const perFamily = {
bitcoin_cash: btc, bitcoin,
bitcoin_gold: btc,
bitcoin_testnet: btc,
bitcoin: btc,
dash: btc,
digibyte: btc,
dogecoin: btc,
ethereum_classic: ethereum,
ethereum_testnet: ethereum,
ethereum, ethereum,
hcash: btc,
komodo: btc,
litecoin: btc,
peercoin: btc,
pivx: btc,
poswallet: btc,
qtum: btc,
ripple, ripple,
stealthcoin: btc,
stratis: btc,
vertcoin: btc,
viacoin: btc,
zcash: btc,
zencash: btc,
} }
const getAddressForCurrency: Module = (currencyId: string) => all[currencyId] const getAddressForCurrency: Module = (currencyId: string) => {
const getAddress = perFamily[currencyId]
invariant(getAddress, `getAddress not implemented for ${currencyId}`)
return getAddress
}
export default getAddressForCurrency export default getAddressForCurrency

6
yarn.lock

@ -1495,9 +1495,9 @@
npm "^5.7.1" npm "^5.7.1"
prebuild-install "^2.2.2" prebuild-install "^2.2.2"
"@ledgerhq/live-common@^2.25.0": "@ledgerhq/live-common@2.26.0":
version "2.25.0" version "2.26.0"
resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.25.0.tgz#00cca102598c9b604922fc4047d2c009d316aede" resolved "https://registry.yarnpkg.com/@ledgerhq/live-common/-/live-common-2.26.0.tgz#2dde8da97ef28b9326f1b060e8db9033ad788efe"
dependencies: dependencies:
axios "^0.18.0" axios "^0.18.0"
invariant "^2.2.2" invariant "^2.2.2"

Loading…
Cancel
Save