diff --git a/src/app/components/AmountFormatter/index.tsx b/src/app/components/AmountFormatter/index.tsx
index ee6810ef30..242807ed36 100644
--- a/src/app/components/AmountFormatter/index.tsx
+++ b/src/app/components/AmountFormatter/index.tsx
@@ -14,7 +14,11 @@ interface Props {
export const AmountFormatter = memo((props: Props) => {
const amount = Number(props.amount) / 10 ** 9
- const amountString = new Intl.NumberFormat('en-US', { minimumFractionDigits: 1 }).format(amount)
+ const amountString = new Intl.NumberFormat('en-US', {
+ minimumFractionDigits: 1,
+ maximumFractionDigits: 15,
+ }).format(amount)
+
const ticker = useSelector(selectTicker)
return (
diff --git a/src/app/components/TransactionModal/index.tsx b/src/app/components/TransactionModal/index.tsx
index 87c9775fd0..eb52bbd1fb 100644
--- a/src/app/components/TransactionModal/index.tsx
+++ b/src/app/components/TransactionModal/index.tsx
@@ -1,4 +1,4 @@
-import { selectChainContext, selectSelectedNetwork } from 'app/state/network/selectors'
+import { selectChainContext } from 'app/state/network/selectors'
import { transactionActions } from 'app/state/transaction'
import { selectTransaction } from 'app/state/transaction/selectors'
import { TransactionStep } from 'app/state/transaction/types'
@@ -9,6 +9,7 @@ import * as React from 'react'
import { useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'
+
import { AmountFormatter } from '../AmountFormatter'
import { PrettyAddress } from '../PrettyAddress'
@@ -28,7 +29,7 @@ interface Props {}
*/
export function TransactionModal(props: Props) {
const { t } = useTranslation()
- const { transaction, step } = useSelector(selectTransaction)
+ const { preview, step } = useSelector(selectTransaction)
const walletAddress = useSelector(selectAddress)
const balance = useSelector(selectBalance)
const chainContext = useSelector(selectChainContext)
@@ -74,87 +75,101 @@ export function TransactionModal(props: Props) {
)}
-
-
- {t('transaction.preview.type', 'Type')} :
-
- Transfer
-
- {t('transaction.preview.from', 'From')} :
-
-
-
-
-
-
-
- {t('transaction.preview.to', 'To')} :
-
-
-
-
-
-
-
- {t('transaction.preview.amount', 'Amount')} :
-
-
-
-
-
- {t('transaction.preview.balance', 'Balance')} :
-
-
-
-
-
- {t('transaction.preview.fee', 'Fee')} :
-
- Coming soon
-
- {t('transaction.preview.gas', 'Gas')} :
-
- Coming soon
-
- {t('transaction.preview.genesisHash', 'Genesis Hash')} :
-
-
- {chainContext}
+
+ {t('transaction.preview.type', 'Type')} :
+
+ Transfer
+
+ {t('transaction.preview.from', 'From')} :
+
+
+
+
+
+
+
+ {t('transaction.preview.to', 'To')} :
+
+
+
+
+
+
+
+ {t('transaction.preview.amount', 'Amount')} :
+
+
+
+
+
+ {t('transaction.preview.balance', 'Balance')} :
+
+
+
+
+
+ {t('transaction.preview.fee', 'Fee')} :
+
+
+
+
+
+ {t('transaction.preview.gas', 'Gas')} :
+
+
+
+
+
+ {t('transaction.preview.genesisHash', 'Genesis Hash')} :
+
+
+ {chainContext}
+
+
+ )}
+ {step === TransactionStep.Preview && (
+
+ }
+ onClick={abortTransaction}
+ />
+ }
+ style={{ borderRadius: '4px' }}
+ alignSelf="end"
+ />
-
-
- }
- onClick={abortTransaction}
- />
- }
- style={{ borderRadius: '4px' }}
- alignSelf="end"
- />
-
+ )}
+ {step === TransactionStep.Building && (
+
+
+ {t('transaction.step.building', 'Building transaction')}
+
+ )}
{step === TransactionStep.Signing && (
diff --git a/src/app/lib/transaction.spec.ts b/src/app/lib/transaction.spec.ts
index fb296c40c4..9f5da206ed 100644
--- a/src/app/lib/transaction.spec.ts
+++ b/src/app/lib/transaction.spec.ts
@@ -72,10 +72,10 @@ describe('OasisTransaction', () => {
expect(tw.signedTransaction).toBeUndefined()
- await OasisTransaction.sign(nic, testSigner, tw)
+ await OasisTransaction.sign('', testSigner, tw)
const hexSignature = uint2hex(tw.signedTransaction.signature.signature)
expect(hexSignature).toEqual(
- '4a4245ccacd47236cd38c548d140ec6235701f34ec2e7cc1da0a44389bd187b1a46331b2a3b0af126af0b704e99a939670deeb2bad68a571206e4f2c65eb8405',
+ '275f18f4830f7c10dd9c6243791c24b7508de10b0575483cf875055607bcb2d7d6fb184ddc0606e4222f7f23438cae38e6aff0849c5cd38e5a6c7f798da85d07',
)
})
diff --git a/src/app/lib/transaction.ts b/src/app/lib/transaction.ts
index 0721292add..b201af9c33 100644
--- a/src/app/lib/transaction.ts
+++ b/src/app/lib/transaction.ts
@@ -18,7 +18,6 @@ export const signerFromHDSecret = (secret: Uint8Array) => {
type TW = oasis.consensus.TransactionWrapper
export class OasisTransaction {
- protected static chainContext?: string
protected static genesis?: oasis.types.GenesisDocument
public static async buildReclaimEscrow(
@@ -84,17 +83,18 @@ export class OasisTransaction {
return tw
}
- public static async signUsingLedger(nic: OasisClient, signer: ContextSigner, tw: TW): Promise {
- const chainContext = await OasisTransaction.getChaincontext(nic)
- console.log(chainContext)
+ public static async signUsingLedger(
+ chainContext: string,
+ signer: ContextSigner,
+ tw: TW,
+ ): Promise {
await tw.sign(signer, chainContext)
// @todo Upstream bug in oasis-app, the signature is larger than 64 bytes
tw.signedTransaction.signature.signature = tw.signedTransaction.signature.signature.slice(0, 64)
}
- public static async sign(nic: OasisClient, signer: Signer, tw: TW): Promise {
- const chainContext = await OasisTransaction.getChaincontext(nic)
+ public static async sign(chainContext: string, signer: Signer, tw: TW): Promise {
return tw.sign(new oasis.signature.BlindContextSigner(signer), chainContext)
}
@@ -127,13 +127,4 @@ export class OasisTransaction {
return BigInt(nonce || 0)
}
-
- protected static async getChaincontext(nic: OasisClient): Promise {
- if (!OasisTransaction.chainContext) {
- OasisTransaction.genesis = await nic.consensusGetGenesisDocument()
- OasisTransaction.chainContext = await oasis.genesis.chainContext(OasisTransaction.genesis)
- }
-
- return OasisTransaction.chainContext
- }
}
diff --git a/src/app/state/ledger/saga.ts b/src/app/state/ledger/saga.ts
index df6be3e36c..92b0a46984 100644
--- a/src/app/state/ledger/saga.ts
+++ b/src/app/state/ledger/saga.ts
@@ -1,16 +1,16 @@
// import { take, call, put, select, takeLatest } from 'redux-saga/effects';
-
-import * as oasis from '@oasisprotocol/client'
import TransportWebUSB from '@ledgerhq/hw-transport-webusb'
+import * as oasis from '@oasisprotocol/client'
import { publicKeyToAddress, uint2hex } from 'app/lib/helpers'
import { Ledger, LedgerSigner } from 'app/lib/ledger'
import { OasisTransaction } from 'app/lib/transaction'
-import { all, call, put, takeEvery } from 'typed-redux-saga'
+import { all, call, put, select, takeEvery } from 'typed-redux-saga'
import { ErrorPayload, WalletError, WalletErrors } from 'types/errors'
+
import { ledgerActions } from '.'
+import { selectChainContext } from '../network/selectors'
import { getBalance } from '../wallet/saga'
import { LedgerAccount, LedgerStep } from './types'
-import { getOasisNic } from '../network/saga'
function* setStep(step: LedgerStep) {
yield* put(ledgerActions.setStep(step))
@@ -73,11 +73,11 @@ function* enumerateAccounts() {
export function* sign(signer: LedgerSigner, tw: oasis.consensus.TransactionWrapper) {
const transport: any = yield* getUSBTransport()
- const nic = yield* call(getOasisNic)
+ const chainContext = yield* select(selectChainContext)
signer.setTransport(transport)
try {
- yield* call([OasisTransaction, OasisTransaction.signUsingLedger], nic, signer, tw)
+ yield* call([OasisTransaction, OasisTransaction.signUsingLedger], chainContext, signer, tw)
} catch (e) {
yield* call([transport, transport.close])
throw e
diff --git a/src/app/state/transaction/index.ts b/src/app/state/transaction/index.ts
index fbe3690bec..fe164ace8a 100644
--- a/src/app/state/transaction/index.ts
+++ b/src/app/state/transaction/index.ts
@@ -3,7 +3,13 @@ import { ErrorPayload } from 'types/errors'
import { createSlice } from 'utils/@reduxjs/toolkit'
import { useInjectReducer, useInjectSaga } from 'utils/redux-injectors'
import { transactionSaga } from './saga'
-import { SendTransactionPayload, TransactionSent, TransactionState, TransactionStep } from './types'
+import {
+ SendTransactionPayload,
+ TransactionPreview,
+ TransactionSent,
+ TransactionState,
+ TransactionStep,
+} from './types'
export const initialState: TransactionState = {
success: false,
@@ -15,7 +21,7 @@ const slice = createSlice({
initialState,
reducers: {
clearTransaction(state, action: PayloadAction) {
- state.transaction = undefined
+ state.preview = undefined
state.error = undefined
state.success = false
state.active = false
@@ -29,13 +35,17 @@ const slice = createSlice({
state.error = undefined
state.success = false
state.active = true
- state.transaction = Object.assign({}, action.payload)
+ },
+ updateTransactionPreview(state, action: PayloadAction) {
+ state.preview = Object.assign({}, action.payload)
},
transactionSent(state, action: PayloadAction) {
+ state.preview = undefined
state.success = true
state.active = false
},
transactionFailed(state, action: PayloadAction) {
+ state.preview = undefined
state.error = action.payload
state.active = false
},
diff --git a/src/app/state/transaction/saga.test.ts b/src/app/state/transaction/saga.test.ts
index 76907db46d..25fcdcf535 100644
--- a/src/app/state/transaction/saga.test.ts
+++ b/src/app/state/transaction/saga.test.ts
@@ -29,7 +29,10 @@ describe('Transaction Sagas', () => {
const sendProviders: (EffectProviders | StaticProvider)[] = [
[matchers.call.fn(signerFromPrivateKey), {}],
[matchers.call.fn(signerFromHDSecret), {}],
- [matchers.call.fn(OasisTransaction.buildTransfer), {}],
+ [
+ matchers.call.fn(OasisTransaction.buildTransfer),
+ { transaction: { fee: { amount: new Uint8Array(0), gas: BigInt(0) } } },
+ ],
[matchers.call.fn(OasisTransaction.sign), {}],
[matchers.call.fn(OasisTransaction.submit), {}],
]
diff --git a/src/app/state/transaction/saga.ts b/src/app/state/transaction/saga.ts
index 946d2040ae..4d6e585079 100644
--- a/src/app/state/transaction/saga.ts
+++ b/src/app/state/transaction/saga.ts
@@ -1,6 +1,6 @@
import { Signer } from '@oasisprotocol/client/dist/signature'
import { PayloadAction } from '@reduxjs/toolkit'
-import { hex2uint, isValidAddress } from 'app/lib/helpers'
+import { hex2uint, isValidAddress, uint2bigintString } from 'app/lib/helpers'
import { LedgerSigner } from 'app/lib/ledger'
import { OasisTransaction, signerFromHDSecret, signerFromPrivateKey } from 'app/lib/transaction'
import { call, put, race, select, take } from 'typed-redux-saga'
@@ -9,6 +9,7 @@ import { ErrorPayload, WalletError, WalletErrors } from 'types/errors'
import { transactionActions } from '.'
import { sign } from '../ledger/saga'
import { getOasisNic } from '../network/saga'
+import { selectChainContext } from '../network/selectors'
import { selectActiveWallet } from '../wallet/selectors'
import { WalletType } from '../wallet/types'
import { SendTransactionPayload, TransactionStep } from './types'
@@ -45,6 +46,7 @@ export function* expectConfirmation() {
export function* sendTransaction(action: PayloadAction) {
const wallet = yield* select(selectActiveWallet)
const nic = yield* call(getOasisNic)
+ const chainContext = yield* select(selectChainContext)
try {
if (!wallet) {
@@ -83,6 +85,16 @@ export function* sendTransaction(action: PayloadAction)
throw new WalletError(WalletErrors.InvalidPrivateKey, 'Invalid private key')
}
+ const tw = yield* call(OasisTransaction.buildTransfer, nic, signer as Signer, recipient, amount)
+ yield* put(
+ transactionActions.updateTransactionPreview({
+ transaction: { amount: action.payload.amount, to: recipient },
+ type: 'transfer',
+ fee: uint2bigintString(tw.transaction.fee?.amount!),
+ gas: BigInt(tw.transaction.fee?.gas).toString(),
+ }),
+ )
+
yield* setStep(TransactionStep.Preview)
const confirmed = yield* expectConfirmation()
if (!confirmed) {
@@ -91,12 +103,11 @@ export function* sendTransaction(action: PayloadAction)
}
yield* setStep(TransactionStep.Signing)
- const tw = yield* call(OasisTransaction.buildTransfer, nic, signer as Signer, recipient, amount)
if (wallet.type === WalletType.Ledger) {
yield* call(sign, signer as LedgerSigner, tw)
} else {
- yield* call(OasisTransaction.sign, nic, signer as Signer, tw)
+ yield* call(OasisTransaction.sign, chainContext, signer as Signer, tw)
}
yield* setStep(TransactionStep.Submitting)
diff --git a/src/app/state/transaction/selectors.ts b/src/app/state/transaction/selectors.ts
index 01eb1962f0..6bb9bba5b0 100644
--- a/src/app/state/transaction/selectors.ts
+++ b/src/app/state/transaction/selectors.ts
@@ -7,3 +7,7 @@ const selectSlice = (state: RootState) => state.transaction || initialState
export const selectTransaction = createSelector([selectSlice], state => state)
export const selectTransactionStep = createSelector([selectTransaction], transaction => transaction.step)
+export const selectTransactionPreview = createSelector(
+ [selectTransaction],
+ transaction => transaction.preview,
+)
diff --git a/src/app/state/transaction/types.ts b/src/app/state/transaction/types.ts
index 29231b5a92..a4c5274747 100644
--- a/src/app/state/transaction/types.ts
+++ b/src/app/state/transaction/types.ts
@@ -11,7 +11,14 @@ export interface TransactionState {
* Used to preview the transaction
* Later, need to accomodate other transaction types.
*/
- transaction?: SendTransactionPayload
+ preview?: TransactionPreview
+}
+
+export interface TransactionPreview {
+ type: 'transfer' | 'add_escrow' | 'reclaim_escrow'
+ transaction: SendTransactionPayload
+ fee?: string
+ gas?: string
}
/**