OSDN Git Service

f2254eb8112458dfe4487d4ee86c47de4cd813e3
[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   const normalT = formParams.normalTransaction
22   if (builder.actions.length == 0) {
23     builder.actions.push({
24       accountAlias: normalT.accountAlias,
25       accountId: normalT.accountId,
26       assetAlias: 'BTM',
27       amount: Number(normalT.gas.price),
28       type: 'spend_account'
29     })
30     builder.actions.push({
31       accountAlias: normalT.accountAlias,
32       accountId: normalT.accountId,
33       assetAlias: normalT.assetAlias,
34       assetId: normalT.assetId,
35       amount: normalT.amount,
36       type: 'spend_account'
37     })
38     builder.actions.push({
39       address: normalT.address,
40       assetAlias: normalT.assetAlias,
41       assetId: normalT.assetId,
42       amount: normalT.amount,
43       type: 'control_address'
44     })
45   }
46
47   if (builder.baseTransaction == '') {
48     delete builder.baseTransaction
49   }
50
51   if (formParams.submitAction == 'generate') {
52     builder.ttl = '1h' // 1 hour
53   }
54
55   for (let i in builder.actions) {
56     let a = builder.actions[i]
57
58     const intFields = ['amount', 'position']
59     intFields.forEach(key => {
60       const value = a[key]
61       if (value) {
62         if ((parseInt(value) + '') == value) {
63           a[key] = parseInt(value)
64         } else {
65           throw new Error(`Action ${parseInt(i) + 1} ${key} must be an integer.`)
66         }
67       }
68     })
69
70     try {
71       a.referenceData = parseNonblankJSON(a.referenceData)
72     } catch (err) {
73       throw new Error(`Action ${parseInt(i) + 1} reference data should be valid JSON, or blank.`)
74     }
75
76     try {
77       a.receiver = parseNonblankJSON(a.receiver)
78     } catch (err) {
79       throw new Error(`Action ${parseInt(i) + 1} receiver should be valid JSON.`)
80     }
81   }
82
83   return builder
84 }
85
86 form.submitForm = (formParams) => function (dispatch) {
87   const buildPromise = chainClient().transactions.build(builder => {
88     const processed = preprocessTransaction(formParams)
89
90     builder.actions = processed.actions.map(action => {
91       let result = {
92         address: action.address,
93         amount: action.amount,
94         account_id: action.accountId,
95         account_alias: action.accountAlias,
96         asset_id: action.assetId,
97         asset_alias: action.assetAlias,
98         type: action.type,
99       }
100       if (action.receiver) {
101         result.receiver = {
102           control_program: action.receiver.controlProgram,
103           expires_at: action.receiver.expiresAt
104         }
105       }
106       return result
107     })
108     if (processed.baseTransaction) {
109       builder.baseTransaction = processed.baseTransaction
110     }
111   })
112
113   const signAndSubmitTransaction = (transaction, password) => {
114     const connection = chainClient().connection
115     return connection.request('/sign-transaction', {
116       password,
117       transaction
118     }, true).then(resp => {
119       if (resp.status === 'fail') {
120         throw new Error(resp.msg)
121       }
122
123       const raw_transaction = resp.data.transaction.raw_transaction
124       return connection.request('/submit-transaction', {raw_transaction})
125     }).then(dealSignSubmitResp)
126   }
127
128   const dealSignSubmitResp = resp => {
129     if (resp.status === 'fail') {
130       throw new Error(resp.msg)
131     }
132
133     dispatch(form.created())
134     dispatch(push({
135       pathname: '/transactions',
136       state: {
137         preserveFlash: true
138       }
139     }))
140   }
141
142   if (formParams.state.showAdvanceTx && formParams.state.showAdvanced && formParams.baseTransaction) {
143     const transaction = JSON.parse(formParams.baseTransaction)
144     return signAndSubmitTransaction(transaction, formParams.password)
145   }
146
147   if (formParams.submitAction == 'submit') {
148     return buildPromise
149       .then((resp) => {
150         if (resp.status === 'fail') {
151           throw new Error(resp.msg)
152         }
153
154         return signAndSubmitTransaction(resp.data, formParams.password)
155       })
156   }
157
158   // submitAction == 'generate'
159   return buildPromise.then(resp => {
160     if (resp.status === 'fail') {
161       throw new Error(resp.msg)
162     }
163
164     const body = Object.assign({}, {password: formParams.password, 'transaction': resp.data})
165     return chainClient().connection.request('/sign-transaction', body, true)
166   }).then(resp => {
167     if (resp.status === 'fail') {
168       throw new Error(resp.msg)
169     }
170     const id = uuid.v4()
171     dispatch({
172       type: 'GENERATED_TX_HEX',
173       generated: {
174         id: id,
175         hex: JSON.stringify(resp.data.transaction),
176       },
177     })
178     dispatch(push(`/transactions/generated/${id}`))
179   })
180 }
181
182 export default {
183   ...list,
184   ...form,
185 }