8 } from 'features/shared/components'
9 import {chainClient} from 'utility/environment'
10 import {reduxForm} from 'redux-form'
11 import React from 'react'
12 import styles from './New.scss'
13 import TxContainer from './NewTransactionsContainer/TxContainer'
14 import { btmID } from 'utility/environment'
15 import actions from 'actions'
16 import ConfirmModal from './ConfirmModal/ConfirmModal'
17 import { balance , getAssetDecimal, normalTxActionBuilder} from '../../transactions'
18 import {withNamespaces} from 'react-i18next'
20 class NormalTxForm extends React.Component {
23 this.connection = chainClient().connection
30 this.submitWithValidation = this.submitWithValidation.bind(this)
31 this.disableSubmit = this.disableSubmit.bind(this)
32 this.addReceiverItem = this.addReceiverItem.bind(this)
36 return !(this.state.estimateGas)
39 submitWithValidation(data) {
40 return new Promise((resolve, reject) => {
41 this.props.submitForm(Object.assign({}, data, {state: this.state, form: 'normalTx'}))
43 this.props.closeModal()
44 this.props.destroyForm()
47 if(err.message !== 'PasswordWrong'){
48 this.props.closeModal()
55 confirmedTransaction(e, assetDecimal){
59 cancel={this.props.closeModal}
60 onSubmit={this.submitWithValidation}
61 gas={this.state.estimateGas}
62 chainGas={this.state.chainGas}
63 btmAmountUnit={this.props.btmAmountUnit}
64 assetDecimal={assetDecimal}
65 asset={this.props.asset}
71 const counter = this.state.counter
72 this.props.fields.receivers.addField({
81 removeReceiverItem(index) {
82 const receiver = this.props.fields.receivers
83 const promise = new Promise(function(resolve, reject) {
85 receiver.removeField(index)
92 promise.then(() => this.estimateNormalTransactionGas())
95 estimateNormalTransactionGas() {
96 const transaction = this.props.values
97 const accountAlias = transaction.accountAlias
98 const accountId = transaction.accountId
99 const assetAlias = transaction.assetAlias
100 const assetId = transaction.assetId
101 const receivers = transaction.receivers
102 const addresses = receivers.map(x => x.address)
103 const amounts = receivers.map(x => Number(x.amount))
105 const {t, i18n} = this.props
107 const noAccount = !accountAlias && !accountId
108 const noAsset = !assetAlias && !assetId
110 if ( addresses.includes('') || amounts.includes(NaN)|| amounts.includes(0)|| noAccount || noAsset) {
111 this.setState({estimateGas: null})
115 const isBTM = (assetAlias==='BTM') || (assetId === btmID)
117 const actions = normalTxActionBuilder(transaction, Math.pow(10, 7), 'amount' )
119 const body = {actions, ttl: 1}
121 this.connection.request('/build-chain-transactions', body).then(resp => {
122 return this.connection.request('/estimate-chain-transaction-gas', {
123 transactionTemplates: resp.data
126 estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000,
127 chainGas: Math.ceil(resp.data.chainTxNeu/100000)*100000
133 this.setState({estimateGas: null, address: null})
134 const errorMsg = err.code && i18n.exists(`btmError.${err.code}`) && t(`btmError.${err.code}`) || err.msg
135 this.props.showError(new Error(errorMsg))
138 this.connection.request('/build-transaction', body).then(resp => {
139 return this.connection.request('/estimate-transaction-gas', {
140 transactionTemplate: resp.data
142 this.setState({estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000})
147 this.setState({estimateGas: null, address: null})
148 const errorMsg = err.code && i18n.exists(`btmError.${err.code}`) && t(`btmError.${err.code}`) || err.msg
149 this.props.showError(new Error(errorMsg))
156 fields: {accountId, accountAlias, assetId, assetAlias, receivers, gasLevel},
160 const t = this.props.t;
161 [accountAlias, accountId, assetAlias, assetId].forEach(key => {
162 key.onBlur = this.estimateNormalTransactionGas.bind(this)
164 (receivers.map(receiver => receiver.amount)).forEach(amount =>{
165 amount.onBlur = this.estimateNormalTransactionGas.bind(this)
168 let submitLabel = t('transaction.new.submit')
170 const assetDecimal = getAssetDecimal(this.props.fields, this.props.asset) || 0
172 const showAvailableBalance = (accountAlias.value || accountId.value) &&
173 (assetAlias.value || assetId.value)
175 const availableBalance = balance(this.props.values, assetDecimal, this.props.balances, this.props.btmAmountUnit)
177 const showBtmAmountUnit = (assetAlias.value === 'BTM' || assetId.value === btmID)
182 onSubmit={e => this.confirmedTransaction(e, assetDecimal)}
183 submitting={submitting}
184 submitLabel= {submitLabel}
185 disabled={this.disableSubmit()}
186 className={styles.container}
188 <div className={styles.borderBottom}>
189 <label className={styles.title}>{t('transaction.normal.from')}</label>
190 <div className={`${styles.mainBox} `}>
192 key='account-selector-field'
193 keyIndex='normaltx-account'
194 title={t('form.account')}
195 aliasField={Autocomplete.AccountAlias}
203 key='asset-selector-field'
204 keyIndex='normaltx-asset'
205 title={ t('form.asset')}
206 aliasField={Autocomplete.AssetAlias}
212 {showAvailableBalance && availableBalance &&
213 <small className={styles.balanceHint}>{t('transaction.normal.availableBalance')} {availableBalance}</small>}
217 <label className={styles.title}>{t('transaction.normal.to')}</label>
219 <div className={styles.mainBox}>
220 {receivers.map((receiver, index) =>
222 className={this.props.tutorialVisible? styles.tutorialItem: styles.subjectField}
223 key={receiver.id.value}>
224 <TextField title={t('form.address')} fieldProps={{
227 receiver.address.onBlur(e)
228 this.estimateNormalTransactionGas()
233 isBTM={showBtmAmountUnit}
234 title={t('form.amount')}
235 fieldProps={receiver.amount}
236 decimal={assetDecimal}
240 className={`btn btn-danger btn-xs ${styles.deleteButton}`}
243 onClick={() => this.removeReceiverItem(index)}
245 {t('commonWords.remove')}
251 className='btn btn-default'
252 onClick={this.addReceiverItem}
254 {t('commonWords.addField')}
258 <label className={styles.title}>{t('transaction.normal.selectFee')}</label>
259 <div className={styles.txFeeBox}>
261 gas={this.state.estimateGas}
262 chainGas={this.state.chainGas}
263 fieldProps={gasLevel}
264 btmAmountUnit={this.props.btmAmountUnit}
266 <span className={styles.feeDescription}> {t('transaction.normal.feeDescription')}</span>
274 const validate = (values, props) => {
275 const errors = {gas: {}}
279 if (values.amount && !/^\d+(\.\d+)?$/i.test(values.amount)) {
280 errors.amount = ( t('errorMessage.amountError') )
285 const asyncValidate = (values, dispatch, props) => {
289 values.receivers.forEach((receiver, idx) => {
290 const address = values.receivers[idx].address
291 if ( !address || address.length === 0)
292 promises.push(Promise.resolve())
295 chainClient().accounts.validateAddresses(address)
298 if (!resp.data.valid) {
299 errors[idx] = {address: props.t('errorMessage.addressError')}
307 return Promise.all(promises).then(() => {
308 if (errors.length > 0) throw {
315 const mapDispatchToProps = (dispatch) => ({
316 showError: err => dispatch({type: 'ERROR', payload: err}),
317 closeModal: () => dispatch(actions.app.hideModal),
318 showModal: (body) => dispatch(actions.app.showModal(
320 actions.app.hideModal,
327 ...BaseNew.mapDispatchToProps('transaction')(dispatch)
330 export default withNamespaces('translations') (BaseNew.connect(
331 BaseNew.mapStateToProps('transaction'),
334 form: 'NormalTransactionForm',
341 'receivers[].amount',
342 'receivers[].address',
346 asyncBlurFields: ['receivers[].address'],