OSDN Git Service

add the ConfirmModal.
[bytom/bytom-electron.git] / src / features / transactions / actions.js
1 import uuid from 'uuid'
2 import {chainClient} from 'utility/environment'
3 import {parseNonblankJSON} from 'utility/string'
4 import {push} from 'react-router-redux'
5 import {baseCreateActions, baseListActions} from 'features/shared/actions'
6
7 const type = 'transaction'
8
9 const list = baseListActions(type, {
10   defaultKey: 'id'
11 })
12 const form = baseCreateActions(type)
13
14 function preprocessTransaction(formParams) {
15   const copy = JSON.parse(JSON.stringify(formParams))
16   const builder = {
17     baseTransaction: copy.baseTransaction,
18     actions: copy.actions || [],
19   }
20
21   if (formParams.form === 'normalTx') {
22     const gasPrice = formParams.state.estimateGas * Number(formParams.gasLevel)
23
24     builder.actions.push({
25       accountAlias: formParams.accountAlias,
26       accountId: formParams.accountId,
27       assetAlias: 'BTM',
28       amount: Number(gasPrice),
29       type: 'spend_account'
30     })
31     builder.actions.push({
32       accountAlias: formParams.accountAlias,
33       accountId: formParams.accountId,
34       assetAlias: formParams.assetAlias,
35       assetId: formParams.assetId,
36       amount: formParams.amount,
37       type: 'spend_account'
38     })
39     builder.actions.push({
40       address: formParams.address,
41       assetAlias: formParams.assetAlias,
42       assetId: formParams.assetId,
43       amount: formParams.amount,
44       type: 'control_address'
45     })
46   }
47
48   if (builder.baseTransaction == '') {
49     delete builder.baseTransaction
50   }
51
52   if (formParams.submitAction == 'generate') {
53     builder.ttl = '1h' // 1 hour
54   }
55
56   for (let i in builder.actions) {
57     let a = builder.actions[i]
58
59     const intFields = ['amount', 'position']
60     intFields.forEach(key => {
61       const value = a[key]
62       if (value) {
63         if ((parseInt(value) + '') == value) {
64           a[key] = parseInt(value)
65         } else {
66           throw new Error(`Action ${parseInt(i) + 1} ${key} must be an integer.`)
67         }
68       }
69     })
70
71     try {
72       a.referenceData = parseNonblankJSON(a.referenceData)
73     } catch (err) {
74       throw new Error(`Action ${parseInt(i) + 1} reference data should be valid JSON, or blank.`)
75     }
76
77     try {
78       a.receiver = parseNonblankJSON(a.receiver)
79     } catch (err) {
80       throw new Error(`Action ${parseInt(i) + 1} receiver should be valid JSON.`)
81     }
82   }
83
84   return builder
85 }
86
87 form.submitForm = (formParams) => function (dispatch) {
88   const client = chainClient()
89
90   const builderFunction = ( builder ) => {
91     const processed = preprocessTransaction(formParams)
92
93     builder.actions = processed.actions
94     if (processed.baseTransaction) {
95       builder.baseTransaction = processed.baseTransaction
96     }
97   }
98
99   const submitSucceeded = () => {
100     dispatch(form.created())
101     dispatch(push({
102       pathname: '/transactions',
103       state: {
104         preserveFlash: true
105       }
106     }))
107   }
108
109   // normal transactions
110   if(formParams.form === 'normalTx'){
111
112     const accountId = formParams.accountId
113     const accountAlias = formParams.accountAlias
114     const accountInfo = Object.assign({},  accountAlias!== ''? {alias: accountAlias}: {id: accountId})
115
116     return client.accounts.query(accountInfo)
117       .then( resp => {
118         if(resp.data[0].xpubs.length > 1){
119           throw new Error('Your account has multiple keys, please use advanced transactions.')
120         }
121         const body = Object.assign({}, {xpub: resp.data[0].xpubs[0], password: formParams.password})
122         return client.mockHsm.keys.checkPassword(body)
123       })
124       .then( result => {
125         if(!result.data.checkResult){
126           throw new Error('Your password is wrong, please check your password.')
127         }
128         return client.transactions.build(builderFunction)
129       })
130       .then( tpl => {
131         const body = Object.assign({}, {password: formParams.password, transaction: tpl.data})
132         return client.transactions.sign(body)
133       })
134       .then(signed => {
135         if(!signed.data.signComplete){
136           throw new Error('Signature failed.')
137         }
138         return client.transactions.submit(signed.data.transaction.rawTransaction)
139       })
140       .then(submitSucceeded)
141   }
142
143   //advanced transactions
144   else{
145     const buildPromise = (formParams.state.showAdvanced && formParams.signTransaction) ? null :
146       client.transactions.build(builderFunction)
147
148     if (formParams.submitAction == 'submit') {
149       const signAndSubmitTransaction = (transaction) => {
150         const body = Object.assign({}, {password: formParams.password, transaction: transaction})
151         return client.transactions.sign(body)
152           .then( signed => client.transactions.submit(signed.data.transaction.rawTransaction) )
153           .then(submitSucceeded)
154       }
155
156       if( formParams.state.showAdvanced
157         && formParams.signTransaction ){
158         const transaction = JSON.parse(formParams.signTransaction)
159         return signAndSubmitTransaction(transaction)
160       }
161
162       return buildPromise
163         .then(tpl => signAndSubmitTransaction(tpl.data))
164     }
165
166     // submitAction == 'generate'
167     const signAndSubmitGeneratedTransaction = (transaction) => {
168       const body = Object.assign({}, {password: formParams.password, transaction: transaction})
169       return client.transactions.sign(body)
170         .then(resp => {
171           const id = uuid.v4()
172           dispatch({
173             type: 'GENERATED_TX_HEX',
174             generated: {
175               id: id,
176               hex: JSON.stringify(resp.data.transaction),
177             },
178           })
179           dispatch(push(`/transactions/generated/${id}`))
180         })
181     }
182
183     if (formParams.state.showAdvanced
184       && formParams.signTransaction) {
185       const transaction = JSON.parse(formParams.signTransaction)
186       return signAndSubmitGeneratedTransaction(transaction)
187     }
188
189     return buildPromise
190       .then(resp => signAndSubmitGeneratedTransaction(resp.data))
191   }
192 }
193
194 const decode = (data) => {
195   return (dispatch) => {
196     return  chainClient().transactions.decodeTransaction(data)
197       .then((resp) => {
198         if (resp.status === 'fail') {
199           dispatch({type: 'ERROR', payload: {'message': resp.msg}})
200         } else {
201           dispatch({type: 'DECODE_TRANSACTION', data:resp.data})
202         }
203       })
204       .catch(err => {
205         throw {_error: err}
206       })
207   }
208 }
209
210 export default {
211   ...list,
212   ...form,
213   decode,
214 }