12 } from 'features/shared/components'
13 import { Connection } from 'sdk'
14 import {chainClient} from 'utility/environment'
15 import { addZeroToDecimalPosition } from 'utility/buildInOutDisplay'
16 import TxContainer from './NewTransactionsContainer/TxContainer'
17 import {reduxForm} from 'redux-form'
18 import React from 'react'
19 import styles from './New.scss'
20 import actions from 'actions'
21 import { btmID } from 'utility/environment'
22 import {getAssetDecimal, issueAssetTxActionBuilder} from '../../transactions'
23 import {withNamespaces} from 'react-i18next'
26 class IssueAssets extends React.Component {
29 this.connection = chainClient().connection
35 this.submitWithValidation = this.submitWithValidation.bind(this)
36 this.addReceiverItem = this.addReceiverItem.bind(this)
37 this.removeReceiverItem = this.removeReceiverItem.bind(this)
40 submitWithValidation(data) {
41 return new Promise((resolve, reject) => {
42 this.props.submitForm(Object.assign({}, data, {state: this.state, form: 'issueAssetTx'}))
50 response['_error'] = err
51 return reject(response)
57 const counter = this.state.counter
58 this.props.fields.receivers.addField({
66 removeReceiverItem(index) {
67 this.props.fields.receivers.removeField(index)
72 const rawTransaction = Connection.camelize(JSON.parse(e.target.value)).rawTransaction
73 this.props.decode(rawTransaction)
78 componentWillReceiveProps(nextProps) {
79 if(nextProps.decodedTx.length !== 0 && nextProps.decodedTx !== this.props.decodedTx && nextProps.fields.submitAction.value === 'sign'){
80 const transaction = nextProps.decodedTx
82 const inputs = transaction.inputs
83 const outputs = transaction.outputs
85 const issueAction = inputs.filter(input => input.type === 'issue')[0]
86 const issueAssetId = issueAction.assetId
88 const issueReceivers = outputs.filter(output => output.assetId == issueAssetId)
90 const diffLength = issueReceivers.length - this.props.fields.receivers.length
93 const counter = this.state.counter
94 for (let i = 0; i < diffLength; i++) {
95 this.props.fields.receivers.addField({
100 counter: counter+diffLength,
102 }else if(diffLength < 0){
103 for (let i = 0; i < -diffLength; i++) {
104 this.removeReceiverItem(i)
109 else if( nextProps.fields.submitAction.value === 'submit' && this.props.fields.submitAction.value === 'sign'){
110 const length = nextProps.fields.receivers.length
112 for (let i = 0; i < (length-1) ; i++) {
113 nextProps.fields.receivers.removeField(i)
120 estimateNormalTransactionGas() {
121 const transaction = this.props.fields
122 const accountAlias = transaction.accountAlias.value
123 const accountId = transaction.accountId.value
124 const assetAlias = transaction.assetAlias.value
125 const assetId = transaction.assetId.value
126 const receivers = transaction.receivers
127 const addresses = receivers.map(x => x.address.value)
128 const amounts = receivers.map(x => Number(x.amount.value))
130 const {t, i18n} = this.props
132 const noAccount = !accountAlias && !accountId
133 const noAsset = !assetAlias && !assetId
135 if ( addresses.includes('') || amounts.includes(0)|| noAccount || noAsset) {
136 this.setState({estimateGas: null})
140 const actions = issueAssetTxActionBuilder(transaction, Math.pow(10, 7), 'amount.value' )
142 const body = {actions, ttl: 1}
143 this.connection.request('/build-transaction', body).then(resp => {
144 if (resp.status === 'fail') {
145 this.setState({estimateGas: null})
146 const errorMsg = resp.code && i18n.exists(`btmError.${resp.code}`) && t(`btmError.${resp.code}`) || resp.msg
147 this.props.showError(new Error(errorMsg))
151 return this.connection.request('/estimate-transaction-gas', {
152 transactionTemplate: resp.data
154 if (resp.status === 'fail') {
155 this.setState({estimateGas: null})
156 const errorMsg = resp.code && i18n.exists(`btmError.${resp.code}`) && t(`btmError.${resp.code}`) || resp.msg
157 this.props.showError(new Error(errorMsg))
160 this.setState({estimateGas: Math.ceil(resp.data.totalNeu/100000)*100000})
167 fields: {assetAlias, assetId, receivers, password, submitAction, signTransaction, accountId, accountAlias, gasLevel},
172 const t = this.props.t;
173 [accountAlias, accountId, assetAlias, assetId].forEach(key => {
174 key.onBlur = this.estimateNormalTransactionGas.bind(this)
176 (receivers.map(receiver => receiver.amount)).forEach(amount =>{
177 amount.onBlur = this.estimateNormalTransactionGas.bind(this)
180 let submitLabel = t('transaction.new.submit')
181 if (submitAction.value == 'sign') {
182 submitLabel = t('transaction.issue.signTx')
187 {label: t('transaction.advance.submitToBlockchain') , value: 'submit'},
188 {label: t('transaction.issue.signRaw'), value: 'sign'}
191 const showBtmAmountUnit = (assetAlias.value === 'BTM' || assetId.value === btmID)
192 const assetDecimal = getAssetDecimal(this.props.fields, this.props.asset) || 0
194 const asset = this.props.asset.filter(a => (a.id === assetId.value || a.alias === assetAlias.value))[0]
198 if (submitAction.value === 'sign' && this.props.decodedTx.length !== 0 && signTransaction.value && signTransaction.valid) {
199 const transaction = this.props.decodedTx
201 const inputs = transaction.inputs
202 const outputs = transaction.outputs
204 const issueAction = inputs.filter(input => input.type === 'issue')[0]
205 const issueAssetId = issueAction.assetId
206 assetId.value = issueAssetId
208 gas = transaction.fee / Math.pow(10, 8) + ' BTM'
210 accountAlias.value = inputs.filter(input => input.type === 'spend')[0].address
212 const assetDefinition = issueAction.assetDefinition
214 assetItem = <KeyValueTable
215 title={t('form.assetDefinition')}
219 {label: 'ID', value: issueAssetId},
220 {label: t('form.alias'), value: assetDefinition.name},
221 {label: t('form.symbol'), value: assetDefinition.symbol},
222 {label: t('form.decimals'), value: assetDefinition.decimals},
223 {label: t('form.reissueTitle'), value: assetDefinition.reissue || 'true'},
224 {label: t('form.quorum'), value: assetDefinition.quorum},
225 {label: t('asset.additionInfo'), value: assetDefinition.description},
229 const issueReceivers = outputs.filter(output => output.assetId == issueAssetId)
231 receivers.map((receiver, index) =>{
232 if(issueReceivers[index]){
233 receiver.address.value = issueReceivers[index].address
234 receiver.amount.value = addZeroToDecimalPosition((issueReceivers[index].amount/Math.pow(10, assetDefinition.decimals)), Number(assetDefinition.decimals))
239 assetItem = <KeyValueTable
240 title={t('form.assetDefinition')}
244 {label: 'ID', value: asset.id},
245 {label: t('form.alias'), value: asset.alias},
246 {label: t('form.symbol'), value: asset.definition.symbol},
247 {label: t('form.decimals'), value: asset.definition.decimals},
248 {label: t('form.reissueTitle'), value: (asset.alias === 'BTM' || asset.limitHeight > 0) ? 'false' : 'true'},
249 {label: t('form.xpubs'), value: (asset.xpubs || []).length},
250 {label: t('form.quorum'), value: asset.quorum},
251 {label: t('asset.additionInfo'), value: asset.definition.description},
259 onSubmit={handleSubmit(this.submitWithValidation)}
260 submitting={submitting}
261 submitLabel= {submitLabel}
262 className={styles.container}
265 <FormSection title={t('transaction.issue.issueAsset')}>
267 <label className={styles.title}>{t('form.input')}</label>
268 <div className={styles.mainBox}>
270 submitAction.value === 'sign'?
271 <TextField title={t('transaction.issue.accountAddress')}
273 fieldProps={accountAlias}/>
277 key='asset-selector-field'
278 keyIndex='normaltx-asset'
279 title={ t('transaction.issue.issueAsset')}
280 aliasField={Autocomplete.AssetAlias}
287 key='account-selector-field'
288 keyIndex='normaltx-account'
289 title={t('transaction.issue.gasAccount')}
290 aliasField={Autocomplete.AccountAlias}
299 <label className={styles.title}>{t('form.output')}</label>
300 <div className={styles.mainBox}>
301 {receivers.map((receiver, index) =>
303 className={this.props.tutorialVisible? styles.tutorialItem: styles.subjectField}
304 key={`issueAsset-${receiver.id.value}`}>
305 <TextField title={t('form.address')}
306 disabled = {submitAction.value === 'sign'}
310 receiver.address.onBlur(e)
315 submitAction.value === 'sign'?
317 title={t('form.amount')}
319 fieldProps={receiver.amount}
322 isBTM={showBtmAmountUnit}
323 title={t('form.amount')}
324 fieldProps={receiver.amount}
325 decimal={assetDecimal}
330 className={`btn btn-danger btn-xs ${styles.deleteButton}`}
333 disabled = {submitAction.value === 'sign'}
334 onClick={() => this.removeReceiverItem(index)}
336 {t('commonWords.remove')}
342 className='btn btn-default'
343 disabled = {submitAction.value === 'sign'}
344 onClick={this.addReceiverItem}
346 {t('commonWords.addField')}
350 <FormSection title= { 'Gas'}>
353 submitAction.value === 'sign'?
356 <div className={styles.txFeeBox}>
358 gas={this.state.estimateGas}
359 fieldProps={gasLevel}
360 btmAmountUnit={this.props.btmAmountUnit}
362 <span className={styles.feeDescription}> {t('transaction.normal.feeDescription')}</span>
368 <FormSection title= { t('transaction.issue.transactionType')}>
369 <RadioField title={t('transaction.advance.buildType')} options={options} fieldProps={submitAction} />
371 submitAction.value === 'sign' &&
373 title={t('transaction.advance.toSignTransaction')}
377 signTransaction.onBlur(e)
385 <FormSection title={ t('key.password') }>
387 placeholder={t('key.passwordPlaceholder')}
388 fieldProps={password}
397 const validate = (values, props) => {
402 let baseTx = (values.signTransaction || '').trim()
407 errors.signTransaction = t('errorMessage.jsonError')
414 const asyncValidate = (values, dispatch, props) => {
418 values.receivers.forEach((receiver, idx) => {
419 const address = values.receivers[idx].address
420 if ( !address || address.length === 0)
421 promises.push(Promise.resolve())
424 chainClient().accounts.validateAddresses(address)
427 if (!resp.data.valid) {
428 errors[idx] = {address: props.t('errorMessage.addressError')}
436 return Promise.all(promises).then(() => {
437 if (errors.length > 0) throw {
444 const mapDispatchToProps = (dispatch) => ({
445 ...BaseNew.mapDispatchToProps('transaction')(dispatch),
446 decode: (transaction) => dispatch( actions.transaction.decode(transaction)),
447 showError: err => dispatch({type: 'ERROR', payload: err}),
450 const mapStateToProps = (state, ownProps) => ({
451 ...BaseNew.mapStateToProps('transaction')(state, ownProps),
452 decodedTx: state.transaction.decodedTx,
454 assetAlias: ownProps.location.query.alias,
456 submitAction: 'submit',
466 export default withNamespaces('translations') (BaseNew.connect(
470 form: 'IssueAssetTxForm',
475 'receivers[].amount',
476 'receivers[].address',
485 asyncBlurFields: ['receivers[].address'],