OSDN Git Service

Merge branch 'master' into dev
[bytom/Byone.git] / src / views / prompts / bappPrompt.vue
1 <style lang="scss" scoped>
2   .warp {
3     position: absolute;
4     top: 0;
5     left: 0;
6     right: 0;
7     height: 600px;
8   }
9
10   .header {
11     display: flex;
12     justify-content: center;
13     font-weight: 600;
14     font-size: 28px;
15     color: rgba(0, 0, 0, 0.88);
16     margin-top: 60px;
17     margin-bottom: 25px;
18   }
19
20   .content {
21     padding: 20px;
22     overflow: scroll;
23     height: 160px;
24     background: #FAFAFA;
25     border: 1px solid #EBEBEB;
26     border-radius: 8px;
27     margin-bottom: 12px;
28     font-size: 14px;
29     word-break: break-all;
30   }
31
32   .content-black{
33     color: rgba(0, 0, 0, 0.64);
34   }
35
36   .tab-group {
37     font-size: 14px;
38     line-height: 140%;
39     letter-spacing: 0.2px;
40     display: flex;
41     margin-bottom: 12px;
42     a {
43       margin-right: 24px;
44     }
45   }
46
47   .divider {
48     margin: 12px 0;
49   }
50
51   .uint {
52     margin-left: 3px;
53   }
54
55   .btn-inline .btn {
56     margin: 10px 15px;
57   }
58
59   .row {
60     display: flex;
61     align-items: center;
62     .col {
63       display: flex;
64     }
65     .col:first-child {
66       width: 15%;
67     }
68
69     .col:last-child {
70       width: 85%;
71     }
72     .value {
73       display: flex;
74       font-size: 13px;
75       justify-content: space-between;
76     }
77
78     .wallet {
79       font-size: 14px;
80     }
81
82     .logo {
83       width: 20px;
84       height: 20px;
85       -webkit-border-radius: 16px;
86       -moz-border-radius: 16px;
87       border-radius: 16px;
88       background: white;
89       display: flex;
90       align-items: center;
91       justify-content: center;
92       font-size: 16px;
93       text-transform: uppercase;
94       color: black;
95       padding: 6px;
96     }
97   }
98
99   .form-item {
100     padding: 0;
101     margin: 0;
102     margin-bottom: 10px;
103   }
104
105   .hide {
106     width: 175px;
107     overflow: hidden;
108     text-overflow: ellipsis;
109     white-space: nowrap;
110   }
111
112   .view-link {
113     font-size: 14px;
114     color: #035BD4;
115     width: 275px;
116     display: block;
117   }
118
119   .iconfont.icon_arrow_2 {
120     transform: rotate(90deg);
121     color: #D6D6D6;
122     font-size: 24px;
123     margin-left: 4px;
124   }
125
126   .amount-list {
127     display: flex;
128     justify-content: space-between;
129     font-size: 14px;
130   }
131
132   .info {
133     margin-bottom: 20px;
134   }
135
136   pre{
137     margin: 0;
138     width: 100%;
139     word-break: break-all;
140     white-space: pre-wrap;
141     white-space: -moz-pre-wrap;
142     white-space: -pre-wrap;
143     white-space: -o-pre-wrap;
144     word-wrap: break-word;
145   }
146
147
148   .btn-round{
149     padding: 15px 2px;
150   }
151 </style>
152
153 <template>
154   <div>
155     <div class="warp">
156       <section class="header">
157         <h1 v-if="prompt.data && prompt.data.type ==='signTransaction'" >{{ $t('transfer.signComfirm') }}</h1>
158         <h1 v-else-if="prompt.data && prompt.data.type ==='message'" >{{ $t('transfer.signMessage') }}</h1>
159         <h1 v-else>{{ $t('transfer.confirmTransaction') }}</h1>
160       </section>
161
162       <section v-if="dataReady">
163         <div class="tab-group">
164           <a :class="{'color-black font-bold': tab === 'details'}" v-on:click="tab = 'details'">{{ $t('transfer.detail')
165             }}</a>
166           <a :class="{'color-black font-bold': tab === 'inout'}" v-on:click="tab = 'inout'">{{ $t('transfer.requestDetail')
167             }}</a>
168         </div>
169
170         <div v-if="tab ==='details'" class="content">
171           <!--Info-->
172           <div class="info">
173             <div class="row">
174               <div class="col">
175                 <img class="logo" src="@/assets/logo.png" alt="">
176               </div>
177               <div class="col value">
178                 <div v-if="currentWallet && currentWallet.alias" class="wallet color-black">{{currentWallet.alias}}</div>
179                 <div>{{shortAddress}}</div>
180               </div>
181             </div>
182             <div class="row">
183               <div class="col">
184                 <i class="iconfont icon_arrow_2"></i>
185               </div>
186               <div class="col divider"></div>
187             </div>
188
189             <div class="row">
190               <div class="col">
191                 <img v-if="prompt.domain && domainsMeta[prompt.domain] && domainsMeta[prompt.domain].icon" class="logo"
192                      :src="domainsMeta[prompt.domain].icon" alt="">
193                 <div v-else-if="initial" class="logo">
194                   {{initial}}
195                 </div>
196               </div>
197
198               <div class="col value">
199                 <span class="color-black"
200                       v-if="prompt.domain && domainsMeta[prompt.domain] && domainsMeta[prompt.domain].title ">{{domainsMeta[prompt.domain].title }}</span>
201                 <span class="color-black" v-else-if="prompt.domain ">{{prompt.domain }}</span>
202
203                 <div v-if="transaction.to">{{short(transaction.to)}}</div>
204               </div>
205             </div>
206           </div>
207
208           <!--transaction types-->
209           <div v-if="prompt.data && prompt.data.type ==='signTransaction'" class="amount-list">
210             <div >{{ $t('transfer.types') }}</div>
211             <div class="color-black font-bold">{{transaction.types}}</div>
212           </div>
213
214           <!--sign message-->
215           <div v-if="prompt.data && prompt.data.type ==='message'" class="amount-list">
216             <div style="min-width: 78px;" >{{ $t('signMessage.message') }}</div>
217             <div class="color-black font-bold">{{transaction.message}}</div>
218           </div>
219
220           <!--amounts-->
221           <div v-if="prompt.data && prompt.data.type ==='transfer'" class="amount-list">
222             <div>{{ $t('transfer.amount') }}</div>
223             <div class="color-black font-bold">{{transaction.amount}}<span class="uint uppercase">{{unit || short(transaction.asset) }}</span>
224             </div>
225           </div>
226           <div v-else v-for="(amountInput, index) in transaction.amounts" :key="index" class="amount-list">
227             <div>{{ index ==0 ? $t('transfer.amount'):'' }}</div>
228             <div class="color-black font-bold">{{amountInput.amount}}<span class="uint uppercase">{{amountInput.alias || short(amountInput.asset) }}</span>
229             </div>
230           </div>
231
232           <!--fee-->
233           <div v-if="transaction.fee" class="amount-list">
234             <div>{{ $t('transfer.fee') }}</div>
235             <div class="color-black font-bold">{{transaction.fee}}<span class="uint">BTM</span></div>
236           </div>
237
238         </div>
239         <div v-if="tab ==='inout'" class="content content-black">
240           <pre>{{detail}}</pre>
241         </div>
242
243         <div class="form">
244           <div class="form-item">
245             <div class="form-item-content">
246               <input type="password" :placeholder="$t('transfer.password')" v-model="password" ref="password" autofocus>
247             </div>
248           </div>
249         </div>
250         <div>
251           <div class="btn btn-primary btn-round float-right" @click="transfer"><i class="iconfont icon-right-arrow"></i></div>
252         </div>
253
254       </section>
255       <section v-else>
256         loading...
257
258       </section>
259
260     </div>
261     <Footer/>
262   </div>
263 </template>
264
265 <script>
266   import transaction from "@/models/transaction";
267   import getLang from "@/assets/language/sdk";
268   import {apis} from '@/utils/BrowserApis';
269   import NotificationService from '../../services/NotificationService'
270   import {mapActions, mapGetters, mapState} from 'vuex'
271   import _ from 'lodash';
272   import account from "@/models/account";
273   import add from "@/utils/address";
274   import { camelize, removeFromArray } from "@/utils/utils";
275   import bytomjslib from 'bytomjs-lib'
276   import BigNumber from "bignumber.js"
277
278
279   export default {
280     add,
281     data() {
282       return {
283         dataReady: false,
284         transaction: {
285           confirmations: 1,
286         },
287         unit:null,
288         password: '',
289         prompt: '',
290         tab: 'details'
291       };
292     },
293     computed: {
294       initial() {
295         if (this.prompt && this.prompt.data && this.prompt.data.title) {
296           return this.prompt.data.title.substring(0, 1)
297         } else if (this.prompt && this.prompt.domain) {
298           return this.prompt.domain.substring(0, 1)
299         }
300       },
301       address: function () {
302         if (this.netType === 'vapor') {
303           return this.currentAccount.vpAddress
304         } else {
305           return this.currentAccount.address
306         }
307       },
308       shortAddress: function () {
309         if(this.prompt.data && this.prompt.data.type ==='transfer') {
310           return add.short(this.transaction.from);
311         }else if(this.prompt.data && this.prompt.data.type ==='message'){
312           return add.short(this.transaction.address);
313         }else{
314           if (this.netType === 'vapor') {
315             return add.short(this.currentAccount.vpAddress);
316           } else {
317             return add.short(this.currentAccount.address);
318           }
319         }
320       },
321       detail(){
322         if(this.prompt.data && this.prompt.data.type ==='signTransaction'){
323           const param = this.prompt.data[0] || this.prompt.data
324           const _tx = camelize(param)
325           const { rawTransaction, signingInstructions } = _tx
326           const obj = {
327             tx: this.transaction,
328             rawTransaction,
329             signingInstructions
330           }
331           return JSON.stringify(obj, null, 2);
332         }else if(this.prompt.data && this.prompt.data.type ==='message'){
333           const {confirmations, ...Attr} = this.transaction
334           return JSON.stringify(Attr, null, 2);
335         }else{
336           return JSON.stringify(this.transaction, null, 2);
337         }
338       },
339       currentWallet(){
340         if(this.prompt.data && this.prompt.data.type ==='transfer'){
341           return this.bytom.keychain.findByAddress(this.transaction.from);
342         }else if(this.prompt.data && this.prompt.data.type ==='message'){
343           return this.bytom.keychain.findByAddress(this.transaction.address);
344         }else{
345           return this.currentAccount
346         }
347       },
348       ...mapState([
349         'bytom'
350       ]),
351       ...mapGetters([
352         'currentAccount',
353         'net',
354         'netType',
355         'domainsMeta'
356       ])
357     },
358     watch: {},
359     methods: {
360       short(address){
361         return add.short(address)
362       },
363       transfer: function () {
364         if(!this.password){
365           this.$toast.error(
366             this.$t('error.BTM0008')
367           );
368           this.$refs.password.focus();
369           return;
370         }
371         let loader = this.$loading.show({
372           // Optional parameters
373           container: null,
374           canCancel: true,
375           onCancel: this.onCancel
376         });
377
378         if(this.prompt.data.type ==='advTransfer'){
379           transaction.buildTransaction(this.address, this.transaction.input, this.transaction.output, this.transaction.fee, this.transaction.confirmations).then(async (result) => {
380
381             let arrayData
382             if (this.transaction.args) {
383               arrayData = await transaction.convertArgument(this.transaction.args)
384             }
385
386             return transaction.advancedTransfer(this.address, result[0], this.password, arrayData, this)
387               .then((resp) => {
388                 loader.hide();
389                 this.prompt.responder(resp);
390                 this.$toast.success(
391                   this.$t("transfer.success")
392                 );
393                 NotificationService.close();
394               })
395               .catch(error => {
396                 throw error
397               });
398           }).catch(error => {
399             loader.hide();
400
401             let e = error
402             if (error.code){
403               e = this.$t(`error.${error.code}`)
404             }else if(error.message){
405               e = getLang(error.message)
406             }
407             this.$toast.error(e);
408           });
409         } else if(this.prompt.data.type ==='transfer'){
410           transaction.transfer(this.transaction, this.password, this.address, this).then(result => {
411             loader.hide();
412             this.prompt.responder(result);
413             this.$toast.success(
414               this.$t("transfer.success")
415             );
416             NotificationService.close();
417
418           }).catch(error => {
419             loader.hide();
420             let e = error
421             if (error.code){
422               e = this.$t(`error.${error.code}`)
423             }else if(error.message){
424               e = getLang(error.message)
425             }
426             this.$toast.error(e);
427           });
428         } else if(this.prompt.data.type ==='signTransaction'){
429           const data = this.prompt.data.value
430
431           if(Array.isArray(data)){
432             Promise.all(data.map( (rawdata) => transaction.signTransaction(this.address, rawdata, this.password, this)))
433               .then( (result) => {
434                 loader.hide();
435                 this.prompt.responder(result);
436                 this.$toast.success(this.$t("transfer.success"));
437                 NotificationService.close();
438               }).catch(error => {
439               loader.hide();
440
441               this.$toast.error(
442                 getLang(error.message) || error.message || error
443               );
444             });
445           }else{
446             transaction.signTransaction(this.address,  data,  this.password, this).then( (result) => {
447               loader.hide();
448               this.prompt.responder(result);
449               this.$toast.success(this.$t("transfer.success"));
450
451               NotificationService.close();
452             }).catch(error => {
453               loader.hide();
454
455               let e = error
456               if (error.code){
457                 e = this.$t(`error.${error.code}`)
458               }else if(error.message){
459                 e = getLang(error.message)
460               }
461               this.$toast.error(e);
462             });
463           }
464
465         } else if(this.prompt.data.type ==='message'){
466           transaction.signMessage(this.transaction.message, this.password, this.address, this).then((resp) => {
467             loader.hide();
468             this.prompt.responder(resp);
469             this.$toast.success(
470               this.$t("transfer.success")
471             );
472             NotificationService.close();
473           }).catch(error => {
474             loader.hide();
475
476             let e = error
477             if (error.code){
478               e = this.$t(`error.${error.code}`)
479             }else if(error.message){
480               e = getLang(error.message)
481             }
482             this.$toast.error(e);
483           });
484         }
485         else{
486           loader.hide();
487           this.$toast.error(
488             'Unknown popup type'
489           );
490         }
491
492       },
493       queryAsset: function (assetID) {
494         return transaction.asset(assetID)
495       }
496     }, mounted() {
497       this.prompt = window.data || apis.extension.getBackgroundPage().notification || null;
498       console.log(this.prompt)
499
500       const params = this.prompt.data
501       if (params !== undefined) {
502         const data = params.value
503         switch(params.type){
504           case "advTransfer":{
505             const inout = data
506             if (inout.input !== undefined) {
507               this.transaction.input = inout.input
508             }
509             if (inout.output !== undefined) {
510               this.transaction.output = inout.output
511             }
512             if (inout.args !== undefined) {
513               this.transaction.args = inout.args
514             }
515             if (inout.gas !== undefined) {
516               this.transaction.fee = inout.gas
517             }
518             if (inout.confirmations !== undefined) {
519               this.transaction.confirmations = inout.confirmations
520             }
521
522             const array = inout.input.filter(action => action.type === 'spend_wallet')
523
524             if (array.length > 0) {
525               account.setupNet(`${this.net}${this.netType}`)
526               const promise =
527                 _(array)
528                   .groupBy('asset')
529                   .map((objs, key) => {
530                     return this.queryAsset(key).then(resp => {
531                       return {
532                         'asset': key,
533                         'alias': resp.symbol,
534                         'amount': _.sumBy(objs, 'amount')
535                       }
536                     })
537                   })
538
539               let that = this;
540               Promise.all(promise).then(function (output) {
541                 that.transaction.amounts = output
542                 this.dataReady = true
543               }).catch(()=>{
544                 this.dataReady = true
545               })
546
547             }
548             break;
549           }
550           case "transfer":{
551             if (data.from != undefined) {
552               this.transaction.from = data.from
553             }
554             if (data.asset != undefined) {
555               this.transaction.asset=  data.asset
556             }
557             if (data.to != undefined) {
558               this.transaction.to = data.to
559             }
560             if (data.amount != undefined) {
561               this.transaction.amount = data.amount
562             }
563
564             if(data.confirmations != undefined) {
565               this.transaction.confirmations = data.confirmations
566             }
567
568             const asset_amounts ={}
569             asset_amounts[data.asset] = data.amount
570
571             transaction.estimateFee( data.from, asset_amounts).then( (resp) =>{
572               this.transaction.fee = resp.fee
573               this.queryAsset(data.asset).then(resp => {
574                 this.unit =  resp.symbol
575                 this.dataReady = true
576               }).catch((e)=>{
577                 throw e
578               })
579
580             }).catch(()=>{
581               this.dataReady = true
582             })
583             break;
584           }
585           case "signTransaction":{
586             const param = data[0] || data
587             const _tx = camelize(param)
588             const rawTransaction = _tx.rawTransaction
589
590             const tx = this.netType === 'vapor'?
591               bytomjslib.vapor.Transaction.decodeRawTransaction(rawTransaction):
592               bytomjslib.bytom.Transaction.decodeRawTransaction(rawTransaction)
593
594             this.transaction.fee = tx.fee/100000000
595             this.transaction.input = tx.inputs
596             this.transaction.output = tx.outputs
597
598             const inputs = tx.inputs.filter(i => i.address === this.address)
599             const outputs = tx.outputs.filter(i => i.address === this.address)
600             const inputAsset = inputs.map(i => i.assetID);
601             const outputAsset = outputs.map(i => i.assetID);
602
603             const asset = _.union(inputAsset, outputAsset)
604             let that = this;
605
606             let types = ["transfer"]
607             const promise =
608               asset
609                 .map((assetId) => {
610                   return this.queryAsset(assetId).then(resp =>{
611                     const assetInput = inputs.filter(i => i.assetID ===assetId)
612                     const assetOutput = outputs.filter(o => o.assetID ===assetId)
613                     const inputAmount = new BigNumber(_.sumBy(assetInput, 'amount'))
614                     const outputAmount = new BigNumber(_.sumBy(assetOutput, 'amount'))
615
616                     const decimals = resp.decimals
617                     const amount = inputAmount.minus(outputAmount)
618
619                     const balanced_outputs = tx.outputs.find( o => o.amount === amount.toNumber())
620                     if(balanced_outputs.type == 'cross_chain_out'){
621                       that.transaction.types = (that.$t('common.cross_chain'));
622                     }
623
624                     return {
625                       'asset': assetId,
626                       'alias': resp.symbol,
627                       'amount': amount.shiftedBy(-decimals).toString()
628                     }
629                   })
630                 })
631
632             const inputType = inputs.map(i => i.type);
633             const outputType = outputs.map(o => o.type);
634             types = _.union(inputType, outputType, types);
635
636             const remove = ['spend','control'];
637             if(inputs[0].amount ===outputs[0].amount){
638               const isVeto = inputs.find(o => o.type ==='veto')
639               const isVote = outputs.find(o => o.type ==='vote')
640               if(isVeto || isVote){
641                 remove.push('transfer')
642               }
643             }
644             types = removeFromArray(types, remove);
645             types = types.map(ty => this.$t(`common.${ty}`)).join(', ');
646
647             this.transaction.types = types
648
649             Promise.all(promise).then(function(output) {
650               that.transaction.amounts = output
651               that.dataReady = true
652             }).catch(()=>{
653               that.dataReady = true
654             })
655
656             break
657           }
658           case "message":{
659             if(data.address !== undefined){
660               this.transaction.address = data.address;
661             }
662             if(data.message !== undefined){
663               this.transaction.message = data.message
664             }
665
666             this.dataReady = true
667
668             break
669           }
670         }
671       }
672     }
673   };
674 </script>