Skip to content

Commit

Permalink
complete Byron-era tx support (#863)
Browse files Browse the repository at this point in the history
* wip

* some bug fixes

* misc fixes

* update

* more shelley -> ITN fixes

* disable tx sending for existing ledger wallets

* disable ITN wallet creation/restoration

* use HASKELL_SHELLEY as default network id

* allow sending to shelley addresses

* use production backend
  • Loading branch information
v-almonacid committed Aug 10, 2020
1 parent f0fc376 commit b0a080a
Show file tree
Hide file tree
Showing 36 changed files with 1,130 additions and 338 deletions.
14 changes: 10 additions & 4 deletions ios/Podfile.lock
Expand Up @@ -75,10 +75,12 @@ PODS:
- React
- react-native-camera/RN (3.30.0):
- React
- react-native-chain-libs (2.0.0):
- react-native-chain-libs (2.0.1):
- React
- react-native-config (0.12.0):
- React
- react-native-haskell-shelley (1.0.0):
- React
- react-native-netinfo (5.6.2):
- React
- react-native-randombytes (3.5.3):
Expand Down Expand Up @@ -111,7 +113,7 @@ PODS:
- React-RCTWebSocket (0.60.0):
- React-Core (= 0.60.0)
- React-fishhook (= 0.60.0)
- RNCardano (0.2.2):
- RNCardano (0.2.3):
- React
- RNCAsyncStorage (1.5.1):
- React
Expand Down Expand Up @@ -152,6 +154,7 @@ DEPENDENCIES:
- react-native-camera (from `../node_modules/react-native-camera`)
- react-native-chain-libs (from `../node_modules/react-native-chain-libs`)
- react-native-config (from `../node_modules/react-native-config`)
- react-native-haskell-shelley (from `../node_modules/react-native-haskell-shelley`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
Expand Down Expand Up @@ -219,6 +222,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-chain-libs"
react-native-config:
:path: "../node_modules/react-native-config"
react-native-haskell-shelley:
:path: "../node_modules/react-native-haskell-shelley"
react-native-netinfo:
:path: "../node_modules/@react-native-community/netinfo"
react-native-randombytes:
Expand Down Expand Up @@ -285,8 +290,9 @@ SPEC CHECKSUMS:
react-native-background-timer: 1f7d560647b40e6a60b01c452ba29c54bf581fc4
react-native-ble-plx: 21acd4201f9de46ab690b24898e8d3ad02f518fd
react-native-camera: eeb5b5f4ba4c1365fed3563af8a72007cbad7aab
react-native-chain-libs: 38eafe3d820fde7a0010bb88d99b20fe2094f2d8
react-native-chain-libs: 7085b2605e47437842a5e68a6f3e589abbc909d4
react-native-config: f2c2ae45625a068c35681a16b9bfb1ca58b0adc7
react-native-haskell-shelley: f46f8f75597882d5ca69bccadd3cfdea75bf9457
react-native-netinfo: 73303369946c2487c600418961bfdc87748b832f
react-native-randombytes: 3638d24759d67c68f6ccba60c52a7a8a8faa6a23
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
Expand All @@ -301,7 +307,7 @@ SPEC CHECKSUMS:
React-RCTText: 685fca2e13b024271048e7e247ef24476f28a41e
React-RCTVibration: 4ee1cf208ab17a50fafb1c16ffe28fe594a64e4f
React-RCTWebSocket: fca087d583724aa0e5fef7d911f0f2a28d0f2736
RNCardano: 7b7060d93453e30b1889ff66d41e1874eadeb9e2
RNCardano: ffdd908e7dde81508914dbff2fbd739183b2bf34
RNCAsyncStorage: b63d6e83fc629b01df6b624688f17944cea5637f
RNDeviceInfo: e2102056bde3ad5d137fd029d8d431510a00486a
RNFS: c9bbde46b0d59619f8e7b735991c60e0f73d22c1
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -102,7 +102,7 @@
"vm-browserify": "^1.1.0"
},
"devDependencies": {
"@emurgo/cardano-serialization-lib-nodejs": "^1.0.1",
"@emurgo/cardano-serialization-lib-nodejs": "^1.1.0",
"@emurgo/js-chain-libs-node": "git+https://github.com/SebastienGllmt/js-chain-libs-node-pkg.git",
"@storybook/addon-actions": "^5.3.14",
"@storybook/addon-links": "^5.3.14",
Expand Down
31 changes: 21 additions & 10 deletions src/actions.js
Expand Up @@ -51,7 +51,7 @@ import * as api from './api/byron/api'

import {type Dispatch} from 'redux'
import {type State} from './state'
import type {PreparedTransactionData} from './crypto/types'
import type {BaseSignRequest} from './crypto/types'
import type {HWDeviceInfo} from './crypto/byron/ledgerUtils'
import type {NetworkId} from './config/types'

Expand Down Expand Up @@ -526,20 +526,31 @@ export const submitSignedTx = (signedTx: string) => async (
dispatch(updateHistory())
}

export const submitTransaction = (
// note: eslint doesn't like polymorphic types
/* eslint-disable indent */
export const submitTransaction = <T>(
decryptedKey: string,
transactionData: PreparedTransactionData,
signRequest: BaseSignRequest<T>,
) => async (dispatch: Dispatch<any>) => {
const signedTx = await walletManager.signTx(transactionData, decryptedKey)
dispatch(submitSignedTx(signedTx))
const {encodedTx} = await walletManager.signTx(signRequest, decryptedKey)
Logger.info('submitTransaction::encodedTx', encodedTx)
const signedTxBase64 = Buffer.from(encodedTx).toString('base64')
dispatch(submitSignedTx(signedTxBase64))
}

export const submitShelleyTx = (encodedTx: Uint8Array) => (
dispatch: Dispatch<any>,
) => {
const signedTx64 = Buffer.from(encodedTx).toString('base64')
dispatch(submitSignedTx(signedTx64))
export const submitDelegationTx = <T>(
decryptedKey: string,
signRequest: T,
) => async (dispatch: Dispatch<any>) => {
const {encodedTx} = await walletManager.signDelegationTx(
signRequest,
decryptedKey,
)
Logger.info('submitDelegationTransaction::encodedTx', encodedTx)
const signedTxBase64 = Buffer.from(encodedTx).toString('base64')
dispatch(submitSignedTx(signedTxBase64))
}
/* eslint-enable indent */

export const checkForFlawedWallets = () => async (dispatch: Dispatch<any>) => {
let isFlawed = false
Expand Down
6 changes: 3 additions & 3 deletions src/api/byron/facade.js
Expand Up @@ -3,7 +3,7 @@ import moment from 'moment'

import assert from '../../utils/assert'
import {TRANSACTION_STATUS} from '../../types/HistoryTransaction'
import {isValidAddress} from '../../crypto/byron/util'
import {normalizeToAddress} from '../../crypto/shelley/utils'

import type {
Transaction,
Expand Down Expand Up @@ -68,7 +68,7 @@ export const checkAndFacadeTransactionAsync = async (
await Promise.all(
tx.inputs.map(async (input) => {
assert.assert(
await isValidAddress(input.address),
(await normalizeToAddress(input.address)) != null,
'Invalid input address',
input.address,
)
Expand All @@ -78,7 +78,7 @@ export const checkAndFacadeTransactionAsync = async (
await Promise.all(
tx.outputs.map(async (output) => {
assert.assert(
await isValidAddress(output.address),
(await normalizeToAddress(output.address)) != null,
'Invalid output address',
output.address,
)
Expand Down
16 changes: 6 additions & 10 deletions src/components/Delegation/DelegationConfirmation.js
Expand Up @@ -20,7 +20,7 @@ import {ignoreConcurrentAsyncHandler} from '../../utils/utils'
import {
showErrorDialog,
handleGeneralError,
submitShelleyTx,
submitDelegationTx,
} from '../../actions'
import {
SEND_ROUTES,
Expand Down Expand Up @@ -84,7 +84,7 @@ const handleOnConfirm = async (
navigation,
isEasyConfirmationEnabled,
password,
submitShelleyTx,
submitDelegationTx,
setSendingTransaction,
setProcessingTx,
intl,
Expand All @@ -94,12 +94,8 @@ const handleOnConfirm = async (

const signAndSubmitTx = async (decryptedKey) => {
try {
const signedTx = await walletManager.signDelegationTx(
delegationTxData.unsignedTx,
decryptedKey,
)
setSendingTransaction(true)
await submitShelleyTx(signedTx.encodedTx)
await submitDelegationTx(decryptedKey, delegationTxData.unsignedTx)
navigation.navigate(JORMUN_WALLET_ROUTES.DELEGATION_SUMMARY)
} catch (e) {
if (e instanceof NetworkError) {
Expand Down Expand Up @@ -265,7 +261,7 @@ export default injectIntl(
isEasyConfirmationEnabled: easyConfirmationSelector(state),
}),
{
submitShelleyTx,
submitDelegationTx,
},
),
withStateHandlers(
Expand All @@ -291,7 +287,7 @@ export default injectIntl(
navigation,
isEasyConfirmationEnabled,
password,
submitShelleyTx,
submitDelegationTx,
setSendingTransaction,
setProcessingTx,
intl,
Expand All @@ -300,7 +296,7 @@ export default injectIntl(
navigation,
isEasyConfirmationEnabled,
password,
submitShelleyTx,
submitDelegationTx,
setSendingTransaction,
setProcessingTx,
intl,
Expand Down
14 changes: 7 additions & 7 deletions src/components/Send/ConfirmScreen.js
Expand Up @@ -55,9 +55,9 @@ import LedgerTransportSwitchModal from '../Ledger/LedgerTransportSwitchModal'
import LedgerConnect from '../Ledger/LedgerConnect'
import HWInstructions from '../Ledger/HWInstructions'

import styles from './styles/ConfirmScreen.style'
import type {BaseSignRequest} from '../../crypto/types'

import type {PreparedTransactionData} from '../../crypto/types'
import styles from './styles/ConfirmScreen.style'

const messages = defineMessages({
title: {
Expand Down Expand Up @@ -86,8 +86,8 @@ const handleOnConfirm = async (
) => {
const transactionData = navigation.getParam('transactionData')

const submitTx = async (
tx: string | PreparedTransactionData,
const submitTx = async <T>(
tx: string | BaseSignRequest<T>,
decryptedKey: ?string,
) => {
await withPleaseWaitModal(async () => {
Expand All @@ -111,6 +111,7 @@ const handleOnConfirm = async (
})
}

// TODO(v-almonacid): this need to be re-written
if (isHW) {
withDisabledButton(async () => {
try {
Expand Down Expand Up @@ -233,9 +234,9 @@ const ConfirmScreen = ({
}) => {
const amount = navigation.getParam('amount')
const address = navigation.getParam('address')
const transactionData = navigation.getParam('transactionData')
const balanceAfterTx = navigation.getParam('balanceAfterTx')
const availableAmount = navigation.getParam('availableAmount')
const fee = navigation.getParam('fee')

const isConfirmationDisabled =
!isEasyConfirmationEnabled && !password && !isHW
Expand All @@ -254,8 +255,7 @@ const ConfirmScreen = ({

<ScrollView style={styles.container}>
<Text small>
{intl.formatMessage(txLabels.fees)}:{' '}
{formatAdaWithSymbol(transactionData.fee)}
{intl.formatMessage(txLabels.fees)}: {formatAdaWithSymbol(fee)}
</Text>
<Text small>
{intl.formatMessage(txLabels.balanceAfterTx)}:{' '}
Expand Down
23 changes: 20 additions & 3 deletions src/components/Send/SendScreen.js
Expand Up @@ -66,6 +66,12 @@ const amountInputErrorMessages = defineMessages({
defaultMessage: '!!!Amount too large',
description: 'some desc',
},
// TODO: should not be ADA specific
TOO_LOW: {
id: 'components.send.sendscreen.amountInput.error.TOO_LOW',
defaultMessage: '!!!Cannot send less than 1 ADA',
description: 'some desc',
},
NEGATIVE: {
id: 'components.send.sendscreen.amountInput.error.NEGATIVE',
defaultMessage: '!!!Amount must be positive',
Expand Down Expand Up @@ -145,9 +151,13 @@ const messages = defineMessages({
},
})

const getTransactionData = (utxos, address, amount) => {
const getTransactionData = async (utxos, address, amount) => {
const adaAmount = parseAdaDecimal(amount)
return walletManager.prepareTransaction(utxos, address, adaAmount)
return await walletManager.createUnsignedTx(
utxos,
address,
adaAmount.toString(),
)
}

const recomputeAll = async ({amount, address, utxos}) => {
Expand All @@ -160,7 +170,10 @@ const recomputeAll = async ({amount, address, utxos}) => {
if (_.isEmpty(addressErrors) && _.isEmpty(amountErrors) && utxos) {
try {
const parsedAmount = parseAdaDecimal(amount)
const {fee: _fee} = await getTransactionData(utxos, address, amount)
const {unsignedTx} = await getTransactionData(utxos, address, amount)
const _fee = new BigNumber(
await (await unsignedTx.get_fee_or_calc()).to_str(),
)
balanceAfter = getUtxoBalance(utxos)
.minus(parsedAmount)
.minus(_fee)
Expand Down Expand Up @@ -299,6 +312,9 @@ class SendScreen extends Component<Props, State> {
if (isValid === true) {
/* :: if (!utxos) throw 'assert' */
const transactionData = await getTransactionData(utxos, address, amount)
const fee = new BigNumber(
await (await transactionData.unsignedTx.get_fee_or_calc()).to_str(),
)

navigation.navigate(SEND_ROUTES.CONFIRM, {
availableAmount,
Expand All @@ -307,6 +323,7 @@ class SendScreen extends Component<Props, State> {
transactionData,
balanceAfterTx: balanceAfter,
utxos,
fee,
})
}
}
Expand Down
14 changes: 11 additions & 3 deletions src/components/TxHistory/TxNavigationButtons.js
Expand Up @@ -7,9 +7,11 @@ import {View} from 'react-native'
import {withHandlers} from 'recompose'
import {injectIntl, defineMessages} from 'react-intl'

import {hasAnyTransaction} from '../../selectors'
import {hasAnyTransaction, isHWSelector} from '../../selectors'
import {WALLET_ROUTES} from '../../RoutesList'
import {Button} from '../UiKit'
import {showErrorDialog} from '../../actions'
import {errorMessages} from '../../i18n/global-messages'

import styles from './styles/TxNavigationButtons.style'

Expand Down Expand Up @@ -66,12 +68,18 @@ export default injectIntl(
compose(
connect((state) => ({
sendDisabled: !hasAnyTransaction(state),
isHW: isHWSelector(state),
})),
withHandlers({
navigateToReceive: ({navigation}) => (event) =>
navigation.navigate(WALLET_ROUTES.RECEIVE),
navigateToSend: ({navigation}) => (event) =>
navigation.navigate(WALLET_ROUTES.SEND),
navigateToSend: ({navigation, isHW, intl}) => (event) => {
if (isHW) {
showErrorDialog(errorMessages.notSupportedError, intl)
return
}
navigation.navigate(WALLET_ROUTES.SEND)
},
}),
)(TxNavigationButtons),
)
16 changes: 7 additions & 9 deletions src/components/WalletInit/CreateWallet/MnemonicShowScreen.js
Expand Up @@ -109,19 +109,17 @@ export default injectIntl(
const name = navigation.getParam('name')
const password = navigation.getParam('password')
const mnemonic = navigation.getParam('mnemonic')
const isShelleyWallet = navigation.getParam('isShelleyWallet')
assert.assert(!!mnemonic, 'handleWalletConfirmation:: mnemonic')
assert.assert(!!password, 'handleWalletConfirmation:: password')
assert.assert(!!name, 'handleWalletConfirmation:: name')
assert.assert(
isShelleyWallet != null,
'handleWalletConfirmation:: isShelleyWallet',
)
const networkId = navigation.getParam('networkId')
assert.assert(!!mnemonic, 'navigateToMnemonicCheck:: mnemonic')
assert.assert(!!password, 'navigateToMnemonicCheck:: password')
assert.assert(!!name, 'navigateToMnemonicCheck:: name')
assert.assert(networkId != null, 'navigateToMnemonicCheck:: networkId')

navigation.navigate(WALLET_INIT_ROUTES.MNEMONIC_CHECK, {
mnemonic,
password,
name,
isShelleyWallet,
networkId,
})
hideModal()
},
Expand Down
Expand Up @@ -4,10 +4,11 @@ import React from 'react'
import {storiesOf} from '@storybook/react-native'

import RestoreWalletScreen from './RestoreWalletScreen'
import {NETWORK_REGISTRY} from '../../../config/types'

storiesOf('RestoreWalletScreen', module).add('Default', ({navigation}) => {
navigation.getParam = (param) => {
if (param === 'isShelleyWallet') return true
if (param === 'networkId') return NETWORK_REGISTRY.JORMUNGANDR
return ''
}
return <RestoreWalletScreen navigation={navigation} />
Expand Down

0 comments on commit b0a080a

Please sign in to comment.