Merge pull request #102 from donne1226/master

reimplement operation modal
This commit is contained in:
ping 2022-04-04 23:06:07 +08:00 committed by GitHub
commit f5126c553a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 2517 additions and 164 deletions

View File

@ -21,9 +21,6 @@
</b-input-group>
</b-col>
</b-row>
<operation-transfer-component
:recipient-address.sync="selectedAddress"
/>
</div>
</template>
@ -36,7 +33,6 @@ import Ripple from 'vue-ripple-directive'
import {
addressEnCode, addressDecode,
} from '@/libs/utils'
import OperationTransferComponent from '@/views/OperationTransferComponent.vue'
export default {
name: 'AppFooter',
@ -47,7 +43,6 @@ export default {
BFormInput,
BInputGroupPrepend,
BInputGroup,
OperationTransferComponent,
},
directives: {
Ripple,

View File

@ -106,16 +106,26 @@
</b-button>
</template>
<!-- <b-dropdown-item
:to="{ name: 'portfolio' }"
class="d-none"
<b-dropdown-item
v-for="(item,k) in accounts"
:key="k"
:disabled="!item.address"
@click="updateDefaultWallet(item.wallet)"
>
<feather-icon
icon="PieChartIcon"
size="16"
/>
<span class="align-middle ml-50">Portofolio</span>
</b-dropdown-item> -->
<div class="d-flex flex-column">
<span class="font-weight-bolder">{{ item.wallet }}
<b-avatar
v-if="item.wallet===walletName"
variant="success"
size="sm"
>
<feather-icon icon="CheckIcon" />
</b-avatar>
</span>
<small>{{ item.address ? formatAddr(item.address.addr) : `Not available for ${selected_chain.chain_name}` }}</small>
</div>
</b-dropdown-item>
<b-dropdown-divider />
<b-dropdown-item :to="{ name: 'accounts' }">
<feather-icon
@ -155,7 +165,7 @@
<script>
import {
BLink, BNavbarNav, BMedia, BMediaAside, BAvatar, BMediaBody, VBTooltip, BButton,
BDropdown, BDropdownItem,
BDropdown, BDropdownItem, BDropdownDivider,
} from 'bootstrap-vue'
import Ripple from 'vue-ripple-directive'
import DarkToggler from '@core/layouts/components/app-navbar/components/DarkToggler.vue'
@ -176,6 +186,7 @@ export default {
BButton,
BDropdown,
BDropdownItem,
BDropdownDivider,
// Navbar Components
DarkToggler,
@ -224,14 +235,26 @@ export default {
}
return [conf.api]
},
accounts() {
let accounts = getLocalAccounts() || {}
accounts = Object.entries(accounts).map(v => ({ wallet: v[0], address: v[1].address.find(x => x.chain === this.selected_chain.chain_name) }))
if (!this.$store.state.chains.defaultWallet && this.accounts.length > 0) {
this.updateDefaultWallet(this.accounts[0].wallet)
}
return accounts
},
},
mounted() {
const accounts = Object.keys(getLocalAccounts() || {})
if (!this.$store.state.chains.defaultWallet && accounts.length > 0) {
this.$store.commit('setDefaultWallet', accounts[0])
}
},
methods: {
formatAddr(v) {
return v.substring(0, 10).concat('...', v.substring(v.length - 10))
},
updateDefaultWallet(v) {
this.$store.commit('setDefaultWallet', v)
},
change(v) {
this.index = v
const conf = this.$store.state.chains.selected

View File

@ -60,7 +60,7 @@ export default class ChainFetch {
if (conf.chain_name === 'injective') {
return ChainFetch.fetch('https://tm.injective.network', '/block').then(data => Block.create(commonProcess(data)))
}
return this.get('/blocks/latest', config).then(data => Block.create(data))
return this.get(`/blocks/latest?${new Date().getTime()}`, config).then(data => Block.create(data))
}
async getBlockByHeight(height, config = null) {
@ -386,6 +386,19 @@ export default class ChainFetch {
return ChainFetch.fetchCoinMarketCap(`/quote/${symbol}`)
}
// Simulate Execution of tx
async simulate(tx, config = null) {
return this.post('/cosmos/tx/v1beta1/simulate', tx, config).then(res => {
if (res.code && res.code !== 0) {
throw new Error(res.message)
}
if (res.tx_response && res.tx_response.code !== 0) {
throw new Error(res.tx_response.raw_log)
}
return res
})
}
// Tx Submit
async broadcastTx(bodyBytes, config = null) {
const txString = toBase64(TxRaw.encode(bodyBytes).finish())

View File

@ -157,19 +157,19 @@
</router-link>
<b-button
v-if="p.status===1"
v-b-modal.deposit-window
v-b-modal.operation-modal
variant="primary"
class="btn float-right mg-2"
@click="selectProposal(p.id, p.title)"
@click="selectProposal('GovDeposit',p.id, p.title)"
>
{{ $t('btn_deposit') }}
</b-button>
<b-button
v-if="p.status===2"
v-b-modal.vote-window
v-b-modal.operation-modal
variant="primary"
class="btn float-right mg-2"
@click="selectProposal(p.id, p.title)"
@click="selectProposal('Vote',p.id, p.title)"
>
{{ $t('btn_vote') }}
</b-button>
@ -177,13 +177,10 @@
</b-card>
</b-col>
</b-row>
<operation-vote-component
<operation-modal
:type="operationModalType"
:proposal-id="selectedProposalId"
:title="selectedTitle"
/>
<operation-gov-deposit-component
:proposal-id="selectedProposalId"
:title="selectedTitle"
:proposal-title="selectedTitle"
/>
</div>
</template>
@ -196,8 +193,7 @@ import Ripple from 'vue-ripple-directive'
import { Proposal } from '@/libs/data'
import { percent, tokenFormatter } from '@/libs/utils'
import dayjs from 'dayjs'
import OperationVoteComponent from './OperationVoteComponent.vue'
import OperationGovDepositComponent from './OperationGovDepositComponent.vue'
import OperationModal from '@/views/components/OperationModal/index.vue'
export default {
components: {
@ -212,8 +208,7 @@ export default {
BCardBody,
BRow,
BCol,
OperationVoteComponent,
OperationGovDepositComponent,
OperationModal,
},
directives: {
'b-modal': VBModal,
@ -225,6 +220,7 @@ export default {
selectedTitle: '',
proposals: [new Proposal()],
max: 1,
operationModalType: '',
}
},
mounted() {
@ -234,7 +230,8 @@ export default {
percent: v => percent(v),
formatDate: v => dayjs(v).format('YYYY-MM-DD'),
formatToken: v => tokenFormatter(v, {}),
selectProposal(pid, title) {
selectProposal(modal, pid, title) {
this.operationModalType = modal
this.selectedProposalId = Number(pid)
this.selectedTitle = title
},

View File

@ -109,10 +109,11 @@
</b-button>
</router-link>
<b-button
v-b-modal.vote-window
v-b-modal.operation-modal
:disabled="proposal.status!=2"
variant="primary"
class="btn float-right mg-2"
@click="openModal('Vote')"
>
{{ $t('btn_vote') }}
</b-button>
@ -232,30 +233,29 @@
</b-button>
</router-link>
<b-button
v-b-modal.deposit-window
v-b-modal.operation-modal
:disabled="proposal.status!=1"
variant="primary"
class="btn float-right mg-2"
@click="openModal('GovDeposit')"
>
{{ $t('btn_deposit') }}
</b-button>
<b-button
v-b-modal.vote-window
v-b-modal.operation-modal
:disabled="proposal.status!=2"
variant="primary"
class="btn float-right mg-2 mr-1"
@click="openModal('Vote')"
>
{{ $t('btn_vote') }}
</b-button>
</b-card-footer>
</b-card>
<operation-vote-component
<operation-modal
:type="operationModalType"
:proposal-id="Number(proposal.id)"
:title="proposal.title"
/>
<operation-gov-deposit-component
:proposal-id="Number(proposal.id)"
:title="proposal.title"
:proposal-title="proposal.title"
/>
</section>
</template>
@ -273,9 +273,9 @@ import {
} from '@/libs/utils'
import { Proposal, Proposer } from '@/libs/data'
import dayjs from 'dayjs'
import OperationModal from '@/views/components/OperationModal/index.vue'
import ObjectFieldComponent from './ObjectFieldComponent.vue'
import OperationVoteComponent from './OperationVoteComponent.vue'
import OperationGovDepositComponent from './OperationGovDepositComponent.vue'
// import { formatToken } from '@/libs/data/data'
export default {
@ -295,9 +295,8 @@ export default {
BTooltip,
BBadge,
ObjectFieldComponent,
OperationVoteComponent,
OperationGovDepositComponent,
FlipCountdown,
OperationModal,
},
data() {
return {
@ -307,6 +306,7 @@ export default {
proposer: new Proposer(),
deposits: [],
votes: [],
operationModalType: '',
votes_fields: [
{
key: 'voter',
@ -411,6 +411,9 @@ export default {
formatAddress(v) {
return getStakingValidatorByAccount(this.$http.config.chain_name, v)
},
openModal(type) {
this.operationModalType = type
},
},
}
</script>

View File

@ -78,7 +78,7 @@
</template>
<template #cell(operation)="data">
<b-button
v-b-modal.delegate-window
v-b-modal.operation-modal
:name="data.item.operator_address"
variant="primary"
size="sm"
@ -186,7 +186,7 @@
</template>
<template #cell(operation)="data">
<b-button
v-b-modal.delegate-window
v-b-modal.operation-modal
:name="data.item.operator_address"
variant="primary"
size="sm"
@ -210,7 +210,10 @@
</small>
</template>
</b-card>
<operation-delegate-component :validator-address="validator_address" />
<operation-modal
type="Delegate"
:validator-address="validator_address"
/>
</div>
</template>
@ -222,9 +225,9 @@ import {
percent, StakingParameters, formatToken,
} from '@/libs/utils'
import { keybase } from '@/libs/fetch'
import OperationModal from '@/views/components/OperationModal/index.vue'
// import { toHex } from '@cosmjs/encoding'
// import fetch from 'node-fetch'
import OperationDelegateComponent from './OperationDelegateComponent.vue'
export default {
components: {
@ -237,32 +240,15 @@ export default {
BCardTitle,
BCardBody,
BButton,
OperationDelegateComponent,
BFormRadioGroup,
BFormGroup,
OperationModal,
},
directives: {
'b-tooltip': VBTooltip,
},
data() {
return {
keys: [
'bitsongvaloper1jxv0u20scum4trha72c7ltfgfqef6nscl86wxa',
'akashvaloper1vgstlgtsx4w80gphwgre0fcvc04lcnaelukvll',
'certikvaloper1jxv0u20scum4trha72c7ltfgfqef6nsczkvcu7',
'cosmosvaloper1jxv0u20scum4trha72c7ltfgfqef6nsch7q6cu',
'iva16plp8cmfkjssp222taq6pv6mkm8c5pa9lcktta',
'junovaloper1jxv0u20scum4trha72c7ltfgfqef6nscm9pmg2',
'kavavaloper1xftqdxvq0xkv2mu8c5y0jrsc578tak4m9u0s44',
'kivaloper1jxv0u20scum4trha72c7ltfgfqef6nschqtan9',
'osmovaloper1jxv0u20scum4trha72c7ltfgfqef6nscqx0u46',
'persistencevaloper1jxv0u20scum4trha72c7ltfgfqef6nsc4zjpnj',
'starsvaloper1jxv0u20scum4trha72c7ltfgfqef6nscdghxyx',
'digvaloper1jxv0u20scum4trha72c7ltfgfqef6nsc4s577p',
'bcnavaloper1jxv0u20scum4trha72c7ltfgfqef6nsc384wxf',
'pbvaloper1jxv0u20scum4trha72c7ltfgfqef6nsc5nn6cf',
'rizonvaloper1jxv0u20scum4trha72c7ltfgfqef6nsczn2l68',
],
islive: true,
validator_address: null,
mintInflation: 0,
@ -314,7 +300,7 @@ export default {
},
computed: {
pingVals() {
return this.list.filter(x => this.keys.includes(x.operator_address))
return this.list.filter(x => x.description.identity === '6783E9F948541962')
},
list() {
return this.validators.map(x => {

View File

@ -6,7 +6,7 @@
<b-card-header>
<b-card-title>Outstanding Rewards</b-card-title>
<feather-icon
v-b-modal.withdraw-commission-window
v-b-modal.WithdrawCommission
icon="MoreVerticalIcon"
size="18"
class="cursor-pointer"
@ -76,7 +76,7 @@
</b-card-body>
<b-card-body class="pt-0">
<b-button
v-b-modal.withdraw-commission-window
v-b-modal.WithdrawCommission
block
size="sm"
variant="primary"
@ -84,9 +84,11 @@
Withdraw Commission
</b-button>
</b-card-body>
<operation-withdraw-commission-component
:validator-address="validator"
<operation-modal
type="WithdrawCommission"
modal-id="WithdrawCommission"
:address="address"
:validator-address="validator"
/>
</b-card>
</template>
@ -98,7 +100,7 @@ import {
import { sha256 } from '@cosmjs/crypto'
import { toHex } from '@cosmjs/encoding'
import { formatToken, numberWithCommas } from '@/libs/utils'
import OperationWithdrawCommissionComponent from './OperationWithdrawCommissionComponent.vue'
import OperationModal from '@/views/components/OperationModal/index.vue'
export default {
components: {
@ -111,7 +113,7 @@ export default {
BMedia,
BMediaAside,
BAvatar,
OperationWithdrawCommissionComponent,
OperationModal,
},
props: {
data: {

View File

@ -25,7 +25,7 @@
</div>
<div class="d-flex flex-wrap">
<b-button
v-b-modal.delegate-window
v-b-modal.operation-modal
variant="primary"
class="mr-25 mb-25"
>
@ -268,7 +268,10 @@
</b-col>
</b-row>
</template>
<operation-delegate-component :validator-address="validator.operator_address" />
<operation-modal
type="Delegate"
:validator-address="validator.operator_address"
/>
</div>
</template>
@ -281,10 +284,10 @@ import {
percent, formatToken, StakingParameters, Validator, operatorAddressToAccount, consensusPubkeyToHexAddress, toDay, abbrMessage, abbrAddress,
} from '@/libs/utils'
import { keybase } from '@/libs/fetch'
import OperationModal from '@/views/components/OperationModal/index.vue'
import StakingAddressComponent from './StakingAddressComponent.vue'
import StakingCommissionComponent from './StakingCommissionComponent.vue'
import StakingRewardComponent from './StakingRewardComponent.vue'
import OperationDelegateComponent from './OperationDelegateComponent.vue'
export default {
components: {
@ -300,7 +303,7 @@ export default {
StakingAddressComponent,
StakingCommissionComponent,
StakingRewardComponent,
OperationDelegateComponent,
OperationModal,
},
directives: {
'b-modal': VBModal,

View File

@ -39,26 +39,28 @@
<b-card-title>Assets</b-card-title>
<div>
<b-button
v-b-modal.transfer-window
v-b-modal.operation-modal
variant="primary"
size="sm"
class="mr-25"
><feather-icon
@click="setOperationModalType('Transfer')"
>
<feather-icon
icon="SendIcon"
class="d-md-none"
/>
<span class="d-none d-md-block">Transfer</span>
/><small class="d-none d-md-block">Transfer</small>
</b-button>
<b-button
v-b-modal.ibc-transfer-window
v-b-modal.operation-modal
variant="danger"
size="sm"
@click="setOperationModalType('IBCTransfer')"
><feather-icon
icon="SendIcon"
class="d-md-none"
/>
<span class="d-none d-md-block">IBC Transfer
</span></b-button>
<span class="d-none d-md-block">IBC Transfer</span>
</b-button>
</div>
</b-card-header>
<b-card-body class="pl-0 pr-0">
@ -160,10 +162,11 @@
<b-card-title>Delegation</b-card-title>
<div>
<b-button
v-b-modal.delegate-window
v-b-modal.operation-modal
variant="primary"
size="sm"
class="mr-25"
@click="setOperationModalType('Delegate')"
>
<feather-icon
icon="LogInIcon"
@ -172,9 +175,10 @@
</b-button>
<b-button
v-if="delegations"
v-b-modal.withdraw-window
v-b-modal.operation-modal
variant="primary"
size="sm"
@click="setOperationModalType('Withdraw')"
>
<feather-icon
icon="ShareIcon"
@ -194,29 +198,29 @@
size="sm"
>
<b-button
v-b-modal.delegate-window
v-b-modal.operation-modal
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
v-b-tooltip.hover.top="'Delegate'"
variant="outline-primary"
@click="selectValue(data.value)"
@click="selectValue(data.value,'Delegate')"
>
<feather-icon icon="LogInIcon" />
</b-button>
<b-button
v-b-modal.redelegate-window
v-b-modal.operation-modal
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
v-b-tooltip.hover.top="'Redelegate'"
variant="outline-primary"
@click="selectValue(data.value)"
@click="selectValue(data.value,'Redelegate')"
>
<feather-icon icon="ShuffleIcon" />
</b-button>
<b-button
v-b-modal.unbond-window
v-b-modal.operation-modal
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
v-b-tooltip.hover.top="'Unbond'"
variant="outline-primary"
@click="selectValue(data.value)"
@click="selectValue(data.value,'Unbond')"
>
<feather-icon icon="LogOutIcon" />
</b-button>
@ -374,20 +378,10 @@
<vue-qr :text="address" />
</b-popover>
<operation-transfer-component :address="address" />
<operation-transfer-2-component :address="address" />
<operation-withdraw-component :address="address" />
<operation-unbond-component
<operation-modal
:type="operationModalType"
:address="address"
:validator-address.sync="selectedValidator"
/>
<operation-delegate-component
:address="address"
:validator-address.sync="selectedValidator"
/>
<operation-redelegate-component
:address="address"
:validator-address.sync="selectedValidator"
:validator-address="selectedValidator"
/>
</div>
</template>
@ -410,13 +404,8 @@ import {
} from '@/libs/utils'
import { sha256 } from '@cosmjs/crypto'
import { toHex } from '@cosmjs/encoding'
import OperationModal from '@/views/components/OperationModal/index.vue'
import ObjectFieldComponent from './ObjectFieldComponent.vue'
import OperationTransferComponent from './OperationTransferComponent.vue'
import OperationWithdrawComponent from './OperationWithdrawComponent.vue'
import OperationUnbondComponent from './OperationUnbondComponent.vue'
import OperationDelegateComponent from './OperationDelegateComponent.vue'
import OperationRedelegateComponent from './OperationRedelegateComponent.vue'
import OperationTransfer2Component from './OperationTransfer2Component.vue'
import ChartComponentDoughnut from './ChartComponentDoughnut.vue'
export default {
@ -442,13 +431,8 @@ export default {
// eslint-disable-next-line vue/no-unused-components
ToastificationContent,
ObjectFieldComponent,
OperationTransferComponent,
OperationWithdrawComponent,
OperationDelegateComponent,
OperationRedelegateComponent,
OperationUnbondComponent,
OperationTransfer2Component,
ChartComponentDoughnut,
OperationModal,
},
directives: {
'b-modal': VBModal,
@ -472,6 +456,7 @@ export default {
quotes: {},
transactions: [],
stakingParameters: {},
operationModalType: '',
}
},
computed: {
@ -662,8 +647,12 @@ export default {
this.transactions = res
})
},
selectValue(v) {
selectValue(v, type) {
this.selectedValidator = v
this.setOperationModalType(type)
},
setOperationModalType(type) {
this.operationModalType = type
},
formatHash: abbrAddress,
formatDenom(v) {

View File

@ -153,15 +153,15 @@
/>
<b-dropdown-item
v-if="balances[acc.addr]"
v-b-modal.transfer-window
@click="transfer(acc.addr)"
v-b-modal.operation-modal
@click="transfer('Transfer',acc.addr)"
>
<feather-icon icon="SendIcon" /> Transfer
</b-dropdown-item>
<b-dropdown-item
v-if="balances[acc.addr]"
v-b-modal.ibc-transfer-window
@click="transfer(acc.addr)"
v-b-modal.operation-modal
@click="transfer('IBCTransfer',acc.addr)"
>
<feather-icon icon="SendIcon" /> IBC Transfer
</b-dropdown-item>
@ -265,10 +265,10 @@
Connect Wallet
</b-card>
</router-link>
<operation-transfer-component
:address.sync="selectedAddress"
<operation-modal
:type="operationModalType"
:address="selectedAddress"
/>
<operation-transfer-2-component :address="selectedAddress" />
</div>
</template>
@ -287,8 +287,7 @@ import {
import ToastificationContent from '@core/components/toastification/ToastificationContent.vue'
import AppCollapse from '@core/components/app-collapse/AppCollapse.vue'
import AppCollapseItem from '@core/components/app-collapse/AppCollapseItem.vue'
import OperationTransferComponent from './OperationTransferComponent.vue'
import OperationTransfer2Component from './OperationTransfer2Component.vue'
import OperationModal from '@/views/components/OperationModal/index.vue'
import ChartComponentDoughnut from './ChartComponentDoughnut.vue'
import EchartScatter from './components/charts/EchartScatter.vue'
@ -309,14 +308,13 @@ export default {
// eslint-disable-next-line vue/no-unused-components
VBTooltip,
FeatherIcon,
OperationTransferComponent,
// eslint-disable-next-line vue/no-unused-components
ToastificationContent,
OperationTransfer2Component,
ChartComponentDoughnut,
AppCollapse,
AppCollapseItem,
EchartScatter,
OperationModal,
},
directives: {
'b-tooltip': VBTooltip,
@ -335,6 +333,7 @@ export default {
delegations: {},
ibcDenom: {},
quotes: {},
operationModalType: '',
options: {
maintainAspectRatio: false,
legend: {
@ -533,7 +532,8 @@ export default {
this.currency2 = c
this.currency = getUserCurrencySign()
},
transfer(addr) {
transfer(type, addr) {
this.operationModalType = type
this.selectedAddress = addr
},
completeAdd() {

View File

@ -42,29 +42,29 @@
size="sm"
>
<b-button
v-b-modal.delegate-window
v-b-modal.operation-modal
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
v-b-tooltip.hover.top="'Delegate'"
variant="outline-primary"
@click="selectValue(data.item)"
@click="selectValue('Delegate',data.item)"
>
<feather-icon icon="LogInIcon" />
</b-button>
<b-button
v-b-modal.redelegate-window
v-b-modal.operation-modal
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
v-b-tooltip.hover.top="'Redelegate'"
variant="outline-primary"
@click="selectValue(data.item)"
@click="selectValue('Redelegate',data.item)"
>
<feather-icon icon="ShuffleIcon" />
</b-button>
<b-button
v-b-modal.unbond-window
v-b-modal.operation-modal
v-ripple.400="'rgba(113, 102, 240, 0.15)'"
v-b-tooltip.hover.top="'Unbond'"
variant="outline-primary"
@click="selectValue(data.item)"
@click="selectValue('Unbond',data.item)"
>
<feather-icon icon="LogOutIcon" />
</b-button>
@ -74,18 +74,10 @@
</b-card>
<!--- not completed--->
<operation-withdraw-component :address="address" />
<operation-unbond-component
<operation-modal
:type="operationModalType"
:address="address"
:validator-address.sync="selectedValidator"
/>
<operation-delegate-component
:address="address"
:validator-address.sync="selectedValidator"
/>
<operation-redelegate-component
:address="address"
:validator-address.sync="selectedValidator"
:validator-address="selectedValidator"
/>
</div>
</template>
@ -99,11 +91,7 @@ import {
formatToken, getCachedValidators, getLocalAccounts, getLocalChains, tokenFormatter,
} from '@/libs/utils'
import FeatherIcon from '@/@core/components/feather-icon/FeatherIcon.vue'
import OperationWithdrawComponent from './OperationWithdrawComponent.vue'
import OperationUnbondComponent from './OperationUnbondComponent.vue'
import OperationDelegateComponent from './OperationDelegateComponent.vue'
import OperationRedelegateComponent from './OperationRedelegateComponent.vue'
import OperationModal from '@/views/components/OperationModal/index.vue'
export default {
components: {
@ -113,11 +101,7 @@ export default {
BTable,
BCard,
FeatherIcon,
OperationWithdrawComponent,
OperationDelegateComponent,
OperationRedelegateComponent,
OperationUnbondComponent,
OperationModal,
},
directives: {
'b-tooltip': VBTooltip,
@ -153,6 +137,7 @@ export default {
accounts: [],
delegations: [],
rewards: {},
operationModalType: '',
}
},
computed: {
@ -200,7 +185,8 @@ export default {
this.init()
},
methods: {
selectValue(v) {
selectValue(type, v) {
this.operationModalType = type
this.address = v.delegator_address
this.selectedValidator = v.validator.validator
return v

View File

@ -0,0 +1,214 @@
<template>
<div>
<div
v-if="false"
class="board "
>
<div class="data">
<div class="board-row">
<div class="key">
AMOUNT
</div>
<div class="value">
1,233,000 USDT
</div>
</div>
<div class="board-row">
<div class="key">
BRIDGE FEE
</div>
<div class="value">
3,000 USDT
</div>
</div>
</div>
<div class="sum">
<div class="key">
TRANSFER AMOUNT
</div>
<div class="value">
1,213,000 USDT
</div>
</div>
</div>
<p
v-if="succeed"
class="result-text mt-1 text-success"
>
Congratulations! Transfer completed successfully.
</p>
<p
v-else-if="error"
class="result-text mt-1 text-danger"
>
{{ error }}
</p>
<p
v-else
class="result-text mt-1 text-primary"
>
Processing...
</p>
<div class="status">
<!-- <b-progress
:value="100"
:variant="progressColor"
:animated="isLoading"
/> -->
<b-progress :animated="isLoading">
<b-progress-bar
variant="success"
:value="progresBar[0]"
/>
<b-progress-bar
variant="danger"
:value="progresBar[1]"
/>
<b-progress-bar
variant="info"
:value="progresBar[2]"
/>
</b-progress>
<div class="status-text">
<span v-if="hash">SUBMITED</span>
<span v-if="succeed">COMPLETED</span>
<span v-if="error">FAILED</span>
</div>
</div>
<div class="link">
<router-link
to="/"
>
View details
</router-link>
</div>
</div>
</template>
<script>
import { BProgress, BProgressBar } from 'bootstrap-vue'
export default {
components: {
BProgress,
BProgressBar,
},
props: {
hash: {
type: String,
default: null,
},
},
data() {
return {
isLoading: true,
succeed: false,
error: '',
checkTimes: 0,
}
},
computed: {
progresBar() {
// success: [100, 0, 0]
// fail: [50, 50, 0]
// pending: [0, 0, 100]
if (!this.hash) {
return [0, 0, 100]
}
if (this.succeed) {
return [100, 0, 0]
}
return [50, 0, 50]
},
},
mounted() {
this.timer = setInterval(this.trace, 6000)
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
trace() {
if (this.hash) {
this.error = null
this.$http.getTxs(this.hash).then(res => {
if (res.code === 0) {
this.succeed = true
clearInterval(this.timer)
window.location.reload()
} else if (res.code !== 3) { // code 3 is tx unconfirmed(not founded).
this.error = res.raw_log
clearInterval(this.timer)
}
}, () => {
// error statement
this.checkTimes += 1
if (this.checkTimes > 5) {
clearInterval(this.timer)
this.error = 'Timeout'
}
}).catch(e => {
this.error = e
clearInterval(this.timer)
})
}
},
},
}
</script>
<style lang="scss" scoped>
.board {
background: #f8f8f8;
border-radius: 8px;
padding: 0 12px;
margin: 35px 0;
.data {
padding: 10px 0;
border-bottom: 1px solid #607d8b;
}
.board-row {
display: flex;
justify-content: space-between;
font-size: 16px;
line-height: 34px;
.key {
color: #666;
}
.value {
font-weight: bold;
}
}
.sum {
display: flex;
justify-content: space-between;
font-size: 18px;
font-weight: bold;
padding: 15px 0;
}
}
.result-text {
text-align: center;
font-size: 16px;
}
.status {
margin-top: 30px;
.status-text {
display: flex;
justify-content: space-between;
margin-top: 20px;
font-weight: 500;
}
}
.link {
margin-top: 20px;
text-align: center;
a {
text-decoration: underline;
color: var(--purple)
}
}
</style>

View File

@ -0,0 +1,240 @@
<template>
<div>
<b-row>
<b-col>
<b-form-group
label="Delegator"
label-for="Delegator"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Delegator"
>
<b-form-input
v-model="selectedAddress"
readonly
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<validation-provider
#default="{ errors }"
rules="required"
name="Validator"
>
<b-form-group
label="Validator"
label-for="validator"
>
<v-select
v-model="selectedValidator"
:options="valOptions"
:reduce="val => val.value"
placeholder="Select a validator"
:readonly="validatorAddress"
:selectable="(v) => v.value"
/>
</b-form-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Available Token"
label-for="Token"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Token"
>
<b-form-select
v-model="token"
text-field="label"
>
<b-form-select-option
v-for="x in balanceOptions"
:key="x.denom"
:value="x.denom"
>
{{ format(x) }}
</b-form-select-option>
</b-form-select>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Amount"
label-for="Amount"
>
<validation-provider
v-slot="{ errors }"
rules="required|regex:^([0-9\.]+)$"
name="amount"
>
<b-input-group>
<b-form-input
id="Amount"
v-model="amount"
:state="errors.length > 0 ? false:null"
placeholder="Input a number"
type="number"
/>
<b-input-group-append is-text>
{{ printDenom() }}
</b-input-group-append>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
import {
BRow, BCol, BInputGroup, BFormInput, BFormGroup, BFormSelect, BFormSelectOption,
BInputGroupAppend,
} from 'bootstrap-vue'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
formatToken, formatTokenDenom, getUnitAmount,
} from '@/libs/utils'
import vSelect from 'vue-select'
export default {
components: {
BRow,
BCol,
BInputGroup,
BFormInput,
BFormGroup,
BFormSelect,
BFormSelectOption,
vSelect,
BInputGroupAppend,
ValidationProvider,
},
props: {
validatorAddress: {
type: String,
default: null,
},
address: {
type: String,
default: null,
},
balance: {
type: Array,
default: () => [],
},
},
data() {
return {
selectedAddress: this.address,
availableAddress: [],
validators: [],
unbundValidators: [],
selectedValidator: this.validatorAddress,
token: '',
amount: null,
selectedChain: '',
IBCDenom: {},
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
valOptions() {
let options = []
const vals = this.validators.map(x => ({ value: x.operator_address, label: `${x.description.moniker} (${Number(x.commission.rate) * 100}%)` }))
if (vals.length > 0) {
options.push({ value: null, label: '=== ACTIVE VALIDATORS ===' })
options = options.concat(vals)
}
const unbunded = this.unbundValidators.map(x => ({ value: x.operator_address, label: `* ${x.description.moniker} (${Number(x.commission.rate) * 100}%)` }))
if (unbunded.length > 0) {
options.push({ value: null, label: '=== INACTIVE VALIDATORS ===', disabled: true })
options = options.concat(unbunded)
}
return options
},
balanceOptions() {
return this.setupBalance()
},
msg() {
return [{
typeUrl: '/cosmos.staking.v1beta1.MsgDelegate',
value: {
delegatorAddress: this.selectedAddress,
validatorAddress: this.selectedValidator,
amount: {
amount: getUnitAmount(this.amount, this.token),
denom: this.token,
},
},
}]
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Delegate Token',
historyName: 'delegate',
})
this.loadData()
},
methods: {
loadData() {
this.$http.getValidatorList().then(v => {
this.validators = v
})
this.$http.getValidatorUnbondedList().then(v => {
this.unbundValidators = v
})
},
setupBalance() {
if (this.balance && this.balance.length > 0) {
this.token = this.balance[0].denom
return this.balance
}
return []
},
printDenom() {
return formatTokenDenom(this.token)
},
format(v) {
return formatToken(v, this.IBCDenom, 6)
},
},
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,194 @@
<template>
<div>
<b-row>
<b-col>
<h4>{{ proposalId }}. {{ proposalTitle }}</h4>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Depositor"
label-for="Voter"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Voter"
>
<b-form-input
v-model="address"
readonly
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Available Token"
label-for="Token"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Token"
>
<b-form-select
v-model="token"
>
<b-form-select-option
v-for="item in balanceOptions"
:key="item.denom"
:value="item.denom"
>
{{ format(item) }}
</b-form-select-option>
</b-form-select>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Amount"
label-for="Amount"
>
<validation-provider
v-slot="{ errors }"
rules="required|regex:^([0-9\.]+)$"
name="amount"
>
<b-input-group class="mb-25">
<b-form-input
id="Amount"
v-model="amount"
:state="errors.length > 0 ? false:null"
placeholder="Input a number"
type="number"
/>
<b-input-group-append is-text>
{{ printDenom() }}
</b-input-group-append>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
import {
BRow, BCol, BInputGroup, BFormInput, BFormGroup, BFormSelect,
BInputGroupAppend, BFormSelectOption,
} from 'bootstrap-vue'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
formatToken, formatTokenDenom, getUnitAmount,
} from '@/libs/utils'
export default {
name: 'DepositDialogue',
components: {
BRow,
BCol,
BInputGroup,
BFormInput,
BFormGroup,
BFormSelect,
BInputGroupAppend,
BFormSelectOption,
ValidationProvider,
},
props: {
proposalId: {
type: Number,
required: true,
},
proposalTitle: {
type: String,
required: true,
},
balance: {
type: Array,
default: () => [],
},
address: {
type: String,
default: null,
},
},
data() {
return {
token: null,
amount: '',
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
msg() {
return [{
typeUrl: '/cosmos.gov.v1beta1.MsgDeposit',
value: {
depositor: this.address,
proposalId: String(this.proposalId),
amount: [
{
amount: getUnitAmount(this.amount, this.token),
denom: this.token,
},
],
},
}]
},
balanceOptions() {
return this.setupBalance()
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Deposit',
historyName: 'deposit',
})
},
methods: {
printDenom() {
return formatTokenDenom(this.token)
},
format(v) {
return formatToken(v)
},
setupBalance() {
if (this.balance && this.balance.length > 0) {
this.token = this.balance[0].denom
return this.balance
}
return []
},
},
}
</script>
<style lang="scss">
@import '@core/scss/vue/libs/vue-select.scss';
</style>

View File

@ -0,0 +1,307 @@
<template>
<div>
<b-row>
<b-col>
<b-form-group
label="Sender"
label-for="Account"
>
<b-input-group class="mb-25">
<b-form-input
:value="address"
readonly
/>
</b-input-group>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Available Token"
label-for="Token"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Token"
>
<b-form-select
v-model="token"
@change="tokenChange"
>
<template #first>
<b-form-select-option
value=""
>
-- Please select a token --
</b-form-select-option>
</template>
<b-form-select-option
v-for="item in balance"
:key="item.denom"
:value="item.denom"
>
{{ format(item) }}
</b-form-select-option>
</b-form-select>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Amount"
label-for="Amount"
>
<validation-provider
v-slot="{ errors }"
rules="required|regex:^([0-9\.]+)$"
name="amount"
>
<b-input-group class="mb-25">
<b-form-input
id="Amount"
v-model="amount"
:state="errors.length > 0 ? false:null"
placeholder="Input a number"
type="number"
/>
<b-input-group-append is-text>
{{ printDenom() }}
</b-input-group-append>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
:label="`Destination: ${targetChainId}`"
label-for="destination"
>
<validation-provider
#default="{ errors }"
rules="required"
name="destination"
>
<v-select
v-model="destination"
name="destination"
:options="destinationOptions"
placeholder="Select a channel"
@input="onChannelChange()"
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Recipient"
label-for="Recipient"
>
<validation-provider
#default="{ errors }"
rules="required"
name="recipient"
>
<b-input-group class="mb-25">
<b-form-input
id="Recipient"
v-model="recipient"
:state="errors.length > 0 ? false:null"
placeholder="Input a destination address"
/>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
import {
BRow, BCol, BInputGroup, BInputGroupAppend, BFormInput, BFormGroup, BFormSelect, BFormSelectOption,
} from 'bootstrap-vue'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
formatToken, formatTokenDenom, getUnitAmount,
} from '@/libs/utils'
import vSelect from 'vue-select'
import { coin } from '@cosmjs/amino'
import dayjs from 'dayjs'
import { toHex } from '@cosmjs/encoding'
import { sha256 } from '@cosmjs/crypto'
export default {
name: 'TransforDialogue',
components: {
BRow,
BCol,
BInputGroup,
BInputGroupAppend,
BFormInput,
BFormGroup,
BFormSelect,
BFormSelectOption,
vSelect,
ValidationProvider,
},
props: {
address: {
type: String,
default: '',
},
balance: {
type: Array,
default: () => [],
},
},
data() {
return {
targetChainId: '',
token: '',
amount: null,
recipient: null,
IBCDenom: {},
paths: {},
destination: {},
channels: [],
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
destinationOptions() {
if (!this.token && this.token === '') return []
const options = this.channels.map(x => ({ port_id: x.port_id, channel_id: x.channel_id, label: `${x.chain_id ? x.chain_id : ''} ${x.port_id}/${x.channel_id}` }))
if (this.token.startsWith('ibc/')) {
const query = this.paths[this.token]
return query ? options.filter(x => x.channel_id === query.channel_id) : options
}
return options
},
msg() {
const timeout = dayjs().add(4, 'hour')
return [
{
typeUrl: '/ibc.applications.transfer.v1.MsgTransfer',
value: {
sourcePort: this.destination.port_id,
sourceChannel: this.destination.channel_id,
token: coin(Number(getUnitAmount(this.amount, this.token)), this.token),
sender: this.address,
receiver: this.recipient,
timeoutTimestamp: String(timeout.utc().valueOf() * 1000000),
},
},
]
},
balanceOptions() {
return this.setupBalance()
},
selectedChain() {
return this.$store.state.chains.selected
},
},
mounted() {
this.$emit('update', {
modalTitle: 'IBC Transfer Tokens',
historyName: 'transfer',
})
this.setActionName()
this.loadData()
},
methods: {
loadData() {
this.destination = null
this.channels = []
this.token = ''
this.targetChainId = ''
if (this.address) {
this.$http.getAllIBCDenoms(this.selectedChain).then(x => {
x.denom_traces.forEach(trace => {
const hash = toHex(sha256(new TextEncoder().encode(`${trace.path}/${trace.base_denom}`)))
const ibcDenom = `ibc/${hash.toUpperCase()}`
// add base_denom to cache
this.$set(this.IBCDenom, ibcDenom, trace.base_denom)
// store channel/part for ibc denoms
const path = trace.path.split('/')
if (path.length >= 2) {
this.paths[ibcDenom] = {
channel_id: path[path.length - 1],
port_id: path[path.length - 2],
}
}
})
})
this.$http.getIBCChannels(this.selectedChain, null).then(ret => {
const chans = ret.channels.filter(x => x.state === 'STATE_OPEN').map(x => ({ channel_id: x.channel_id, port_id: x.port_id }))
this.$set(this, 'channels', chans)
})
}
},
setupBalance() {
if (this.balance && this.balance.length > 0) {
this.token = this.balance[0].denom
return this.balance
}
return []
},
tokenChange() {
// eslint-disable-next-line prefer-destructuring
this.destination = this.destinationOptions[0]
this.recipient = null
this.setActionName()
this.onChannelChange()
},
setActionName() {
this.$emit('update', {
actionName: this.token.startsWith('ibc') ? 'Withdraw' : 'Deposit',
})
},
format(v) {
return formatToken(v, this.IBCDenom)
},
printDenom() {
return formatTokenDenom(this.IBCDenom[this.token] || this.token)
},
onChannelChange() {
this.$http.getIBCChannelClientState(this.destination.channel_id, this.destination.port_id, this.selectedChain).then(cs => {
this.targetChainId = cs.identified_client_state.client_state.chain_id
})
},
},
}
</script>
<style lang="scss">
@import '@core/scss/vue/libs/vue-select.scss';
</style>

View File

@ -0,0 +1,239 @@
<template>
<div>
<b-row>
<b-col>
<b-form-group
label="Delegator"
label-for="Account"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Delegator"
>
<b-form-input
v-model="address"
readonly
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="From Validator"
label-for="validator"
>
<v-select
:value="validatorAddress"
:options="valOptions"
:reduce="val => val.value"
placeholder="Select a validator"
:disabled="true"
/>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Current Delegation"
label-for="Token"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Token"
>
<v-select
v-model="token"
:options="tokenOptions"
:reduce="token => token.value"
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="To Validator"
label-for="validator"
>
<v-select
v-model="toValidator"
:options="valOptions"
:reduce="val => val.value"
placeholder="Select a validator"
:selectable="(v) => v.value"
/>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Amount"
label-for="Amount"
>
<validation-provider
v-slot="{ errors }"
rules="required|regex:^([0-9\.]+)$"
name="amount"
>
<b-input-group>
<b-form-input
id="Amount"
v-model="amount"
:state="errors.length > 0 ? false:null"
placeholder="Input a number"
type="number"
/>
<b-input-group-append is-text>
{{ printDenom() }}
</b-input-group-append>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
import {
BRow, BCol, BInputGroup, BFormInput, BFormGroup,
BInputGroupAppend,
} from 'bootstrap-vue'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
formatToken, formatTokenDenom, getUnitAmount,
} from '@/libs/utils'
import vSelect from 'vue-select'
export default {
name: 'Redelegate',
components: {
BRow,
BCol,
BInputGroup,
BFormInput,
BFormGroup,
vSelect,
BInputGroupAppend,
ValidationProvider,
},
props: {
validatorAddress: {
type: String,
default: null,
},
address: {
type: String,
default: null,
},
},
data() {
return {
selectedAddress: this.address,
unbundValidators: [],
validators: [],
toValidator: null,
token: '',
amount: null,
delegations: [],
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
valOptions() {
let options = []
const vals = this.validators.map(x => ({ value: x.operator_address, label: `${x.description.moniker} (${Number(x.commission.rate) * 100}%)` }))
if (vals.length > 0) {
options.push({ value: null, label: '=== ACTIVE VALIDATORS ===' })
options = options.concat(vals)
}
const unbunded = this.unbundValidators.map(x => ({ value: x.operator_address, label: `* ${x.description.moniker} (${Number(x.commission.rate) * 100}%)` }))
if (unbunded.length > 0) {
options.push({ value: null, label: '=== INACTIVE VALIDATORS ===', disabled: true })
options = options.concat(unbunded)
}
return options
},
tokenOptions() {
if (!this.delegations) return []
return this.delegations.filter(x => x.delegation.validator_address === this.validatorAddress).map(x => ({ value: x.balance.denom, label: formatToken(x.balance) }))
},
msg() {
return [{
typeUrl: '/cosmos.staking.v1beta1.MsgBeginRedelegate',
value: {
delegatorAddress: this.address,
validatorSrcAddress: this.validatorAddress,
validatorDstAddress: this.toValidator,
amount: {
amount: getUnitAmount(this.amount, this.token),
denom: this.token,
},
},
}]
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Redelegate Token',
historyName: 'redelegate',
})
this.loadData()
},
methods: {
loadData() {
this.$http.getValidatorList().then(v => {
this.validators = v
})
this.$http.getValidatorUnbondedList().then(v => {
this.unbundValidators = v
})
this.$http.getStakingDelegations(this.address).then(res => {
this.delegations = res.delegation_responses
this.delegations.forEach(x => {
if (x.delegation.validator_address === this.validatorAddress) {
this.token = x.balance.denom
this.$emit('update', {
feeDenom: x.balance.denom,
})
}
})
})
},
format(v) {
return formatToken(v)
},
printDenom() {
return formatTokenDenom(this.token)
},
},
}
</script>
<style lang="scss">
@import '@core/scss/vue/libs/vue-select.scss';
</style>

View File

@ -0,0 +1,204 @@
<template>
<div>
<b-row>
<b-col>
<b-form-group
label="Sender"
label-for="sender"
>
<b-input-group class="mb-25">
<b-form-input
name="sender"
:value="address"
readonly
/>
</b-input-group>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Recipient"
label-for="Recipient"
>
<validation-provider
#default="{ errors }"
rules="required"
name="recipient"
>
<b-input-group class="mb-25">
<b-form-input
id="Recipient"
v-model="recipient"
:state="errors.length > 0 ? false:null"
/>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Available Token"
label-for="Token"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Token"
>
<b-form-select
v-model="token"
>
<b-form-select-option
v-for="item in balanceOptions"
:key="item.denom"
:value="item.denom"
>
{{ format(item) }}
</b-form-select-option>
</b-form-select>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Amount"
label-for="Amount"
>
<validation-provider
v-slot="{ errors }"
rules="required|regex:^([0-9\.]+)$"
name="amount"
>
<b-input-group class="mb-25">
<b-form-input
id="Amount"
v-model="amount"
:state="errors.length > 0 ? false:null"
placeholder="Input a number"
type="number"
/>
<b-input-group-append is-text>
{{ printDenom() }}
</b-input-group-append>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
import {
BRow, BCol, BInputGroup, BInputGroupAppend, BFormInput, BFormGroup, BFormSelect, BFormSelectOption,
} from 'bootstrap-vue'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
formatToken, formatTokenDenom, getUnitAmount,
} from '@/libs/utils'
export default {
name: 'TransforDialogue',
components: {
BRow,
BCol,
BInputGroup,
BInputGroupAppend,
BFormInput,
BFormGroup,
BFormSelect,
BFormSelectOption,
ValidationProvider,
},
props: {
address: {
type: String,
default: '',
},
balance: {
type: Array,
default: () => [],
},
},
data() {
return {
token: '',
amount: null,
recipient: '',
IBCDenom: {},
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
msg() {
return [
{
typeUrl: '/cosmos.bank.v1beta1.MsgSend',
value: {
fromAddress: this.address,
toAddress: this.recipient,
amount: [
{
amount: getUnitAmount(this.amount, this.token),
denom: this.token,
},
],
},
},
]
},
balanceOptions() {
return this.setupBalance()
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Transfer Tokens',
historyName: 'send',
})
},
methods: {
setupBalance() {
if (this.balance && this.balance.length > 0) {
this.token = this.balance[0].denom
return this.balance
}
return []
},
format(v) {
return formatToken(v, this.IBCDenom)
},
printDenom() {
return formatTokenDenom(this.IBCDenom[this.token] || this.token)
},
},
}
</script>

View File

@ -0,0 +1,197 @@
<template>
<div>
<b-row>
<b-col>
<b-form-group
label="Delegator"
label-for="Account"
>
<b-form-input
v-model="address"
readonly
/>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Validator"
label-for="validator"
>
<v-select
:value="validatorAddress"
:options="valOptions"
:reduce="val => val.value"
placeholder="Select a validator"
:disabled="true"
/>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Current Delegation"
label-for="Token"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Token"
>
<v-select
v-model="token"
:options="tokenOptions"
:reduce="token => token.value"
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Amount"
label-for="Amount"
>
<validation-provider
v-slot="{ errors }"
rules="required|regex:^([0-9\.]+)$"
name="amount"
>
<b-input-group>
<b-form-input
id="Amount"
v-model="amount"
:state="errors.length > 0 ? false:null"
placeholder="Input a number"
type="number"
/>
<b-input-group-append is-text>
{{ printDenom() }}
</b-input-group-append>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
import {
BRow, BCol, BInputGroup, BFormInput, BFormGroup, BInputGroupAppend,
} from 'bootstrap-vue'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
formatToken, formatTokenDenom, getUnitAmount,
} from '@/libs/utils'
import vSelect from 'vue-select'
export default {
name: 'UnbondDialogue',
components: {
BRow,
BCol,
BInputGroup,
BFormInput,
BFormGroup,
vSelect,
BInputGroupAppend,
ValidationProvider,
},
props: {
validatorAddress: {
type: String,
default: null,
},
address: {
type: String,
default: null,
},
},
data() {
return {
validators: [],
token: '',
amount: null,
delegations: [],
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
valOptions() {
return this.validators.map(x => ({ value: x.operator_address, label: `${x.description.moniker} (${Number(x.commission.rate) * 100}%)` }))
},
tokenOptions() {
if (!this.delegations) return []
return this.delegations.filter(x => x.delegation.validator_address === this.validatorAddress).map(x => ({ value: x.balance.denom, label: formatToken(x.balance) }))
},
msg() {
return [{
typeUrl: '/cosmos.staking.v1beta1.MsgUndelegate',
value: {
delegatorAddress: this.address,
validatorAddress: this.validatorAddress,
amount: {
amount: getUnitAmount(this.amount, this.token),
denom: this.token,
},
},
}]
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Unbond Token',
historyName: 'unbond',
})
this.loadData()
},
methods: {
loadData() {
if (this.address) {
this.$http.getValidatorList().then(v => {
this.validators = v
})
}
this.$http.getStakingDelegations(this.address).then(res => {
this.delegations = res.delegation_responses
this.delegations.forEach(x => {
if (x.delegation.validator_address === this.validatorAddress) {
this.token = x.balance.denom
this.$emit('update', {
feeDenom: x.balance.denom,
})
}
})
})
},
printDenom() {
return formatTokenDenom(this.token)
},
},
}
</script>
<style lang="scss">
@import '@core/scss/vue/libs/vue-select.scss';
</style>

View File

@ -0,0 +1,166 @@
<template>
<div>
<b-row>
<b-col>
<h4>{{ proposalId }}. {{ proposalTitle }}</h4>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Voter"
label-for="Account"
>
<validation-provider
#default="{ errors }"
rules="required"
name="Voter"
>
<b-form-input
v-model="address"
readonly
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row>
<b-col>
<b-form-group
label="Option"
label-for="option"
>
<validation-provider
#default="{ errors }"
rules="required"
name="option"
>
<div class="demo-inline-spacing">
<b-form-radio
v-model="option"
name="option"
value="1"
class="custom-control-success"
>
Yes
</b-form-radio>
<b-form-radio
v-model="option"
name="option"
value="3"
class="custom-control-warning"
>
No
</b-form-radio>
<b-form-radio
v-model="option"
name="option"
value="4"
class="custom-control-danger"
>
No With Veto
</b-form-radio>
<b-form-radio
v-model="option"
name="option"
value="2"
class="custom-control-secondary"
>
Abstain
</b-form-radio>
</div>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
import {
BRow, BCol, BFormGroup, BFormInput,
BFormRadio,
} from 'bootstrap-vue'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
formatToken,
} from '@/libs/utils'
export default {
name: 'VoteDialogue',
components: {
BRow,
BCol,
BFormGroup,
BFormRadio,
ValidationProvider,
BFormInput,
},
props: {
proposalId: {
type: Number,
required: true,
},
proposalTitle: {
type: String,
required: true,
},
address: {
type: String,
default: null,
},
},
data() {
return {
option: null,
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
msg() {
return [{
typeUrl: '/cosmos.gov.v1beta1.MsgVote',
value: {
voter: this.address,
proposalId: this.proposalId,
option: Number(this.option),
},
}]
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Vote',
historyName: 'vote',
})
},
methods: {
format(v) {
return formatToken(v)
},
},
}
</script>
<style lang="scss">
@import '@core/scss/vue/libs/vue-select.scss';
</style>

View File

@ -0,0 +1,80 @@
<template>
<div>
<b-row>
<b-col>
<b-form-group
label="Sender"
label-for="Account"
>
<b-input-group class="mb-25">
<b-form-input
:value="address"
readonly
/>
</b-input-group>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import {
BRow, BCol, BInputGroup, BFormInput, BFormGroup,
} from 'bootstrap-vue'
export default {
name: 'WithdrawDialogue',
components: {
BRow,
BCol,
BInputGroup,
BFormInput,
BFormGroup,
},
props: {
address: {
type: String,
default: '',
},
},
data() {
return {
account: [],
balance: [],
delegations: [],
feeDenom: '',
}
},
computed: {
msg() {
const txMsgs = []
this.delegations.forEach(i => {
txMsgs.push({
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
value: {
delegatorAddress: this.address,
validatorAddress: i.delegation.validator_address,
},
})
})
return txMsgs
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Withdraw Rewards',
historyName: 'withdraw',
})
this.loadData()
},
methods: {
loadData() {
this.$http.getStakingDelegations(this.address).then(res => {
this.delegations = res.delegation_responses
})
},
},
}
</script>

View File

@ -0,0 +1,84 @@
<template>
<div>
<b-row>
<b-col>
<b-form-group
label="Sender"
label-for="Account"
>
<b-input-group class="mb-25">
<b-form-input
:value="address"
readonly
/>
</b-input-group>
</b-form-group>
</b-col>
</b-row>
</div>
</template>
<script>
import {
BRow,
BCol,
BInputGroup,
BFormInput,
BFormGroup,
} from 'bootstrap-vue'
export default {
name: 'WithdrawCommissionDialogue',
components: {
BRow,
BCol,
BInputGroup,
BFormInput,
BFormGroup,
},
props: {
address: {
type: String,
default: '',
},
validatorAddress: {
type: String,
default: '',
},
},
data() {
return {
}
},
computed: {
msg() {
return [
{
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward',
value: {
delegatorAddress: this.address,
validatorAddress: this.validatorAddress,
},
},
{
typeUrl: '/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission',
value: {
validatorAddress: this.validatorAddress,
},
},
]
},
},
mounted() {
this.$emit('update', {
modalTitle: 'Withdraw Validator Commission',
historyName: 'withdraw',
})
},
methods: {
},
}
</script>

View File

@ -0,0 +1,431 @@
<template>
<b-modal
:id="modalId"
centered
size="md"
:title="modalTitle"
:ok-title="actionName"
scrollable
:hide-header-close="!showResult"
:hide-footer="showResult"
modal-class="custom-transaction-modal"
:ok-disabled="isOwner"
@hidden="resetModal"
@ok="handleOk"
@show="initialize"
>
<b-overlay
:show="isOwner"
rounded="sm"
>
<template #overlay>
<div class="text-center">
<b-avatar
icon="stopwatch"
font-scale="3"
animation="cylon"
/>
<p id="cancel-label">
{{ blockingMsg }}
</p>
<b-button
v-ripple.400="'rgba(255, 255, 255, 0.15)'"
variant="outline-primary"
to="/wallet/import"
>
Connect Wallet
</b-button>
</div>
</template>
<validation-observer
v-if="!showResult"
ref="simpleRules"
>
<b-form>
<component
:is="type"
ref="component"
:address="selectedAddress"
:validator-address="validatorAddress"
:balance="balance"
:proposal-id="proposalId"
:proposal-title="proposalTitle"
@update="componentUpdate"
/>
<b-row>
<b-col cols="12">
<b-form-group>
<b-form-checkbox
v-model="advance"
name="advance"
value="true"
>
<small>Advanced</small>
</b-form-checkbox>
</b-form-group>
</b-col>
</b-row>
<b-row v-if="advance">
<b-col cols="12">
<b-form-group
label="Fee"
label-for="Fee"
>
<validation-provider
v-slot="{ errors }"
rules="required|integer"
name="fee"
>
<b-input-group>
<b-form-input v-model="fee" />
<b-input-group-append>
<b-form-select
v-model="feeDenom"
:options="feeDenoms"
value-field="denom"
text-field="denom"
/>
</b-input-group-append>
</b-input-group>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
<b-col cols="12">
<b-form-group
label="Gas"
label-for="gas"
>
<validation-provider
v-slot="{ errors }"
name="gas"
>
<b-form-input
id="gas"
v-model="gas"
type="number"
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
<b-col cols="12">
<b-form-group
label="Memo"
label-for="Memo"
>
<validation-provider
v-slot="{ errors }"
name="memo"
>
<b-form-input
id="Memo"
v-model="memo"
max="2"
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
</b-col>
</b-row>
<b-row v-if="advance">
<b-col>
<wallet-input-vue v-model="wallet" />
</b-col>
</b-row>
</b-form>
{{ error }}
</validation-observer>
<TransactionResult
v-else
:hash="txHash"
/>
</b-overlay>
</b-modal>
</template>
<script>
import { ValidationProvider, ValidationObserver } from 'vee-validate'
import {
BAvatar, BModal, BRow, BCol, BInputGroup, BFormInput, BFormGroup, BFormSelect, BFormSelectOption,
BForm, BButton, BInputGroupAppend, BFormCheckbox, BOverlay,
} from 'bootstrap-vue'
import Ripple from 'vue-ripple-directive'
import {
required, email, url, between, alpha, integer, password, min, digits, alphaDash, length,
} from '@validations'
import {
extractAccountNumberAndSequence, getLocalAccounts, setLocalTxHistory, sign, timeIn,
} from '@/libs/utils'
import vSelect from 'vue-select'
import ToastificationContent from '@core/components/toastification/ToastificationContent.vue'
import WalletInputVue from '../WalletInput.vue'
import Delegate from './components/Delegate.vue'
import Redelegate from './components/Redelegate.vue'
import Withdraw from './components/Withdraw.vue'
import Unbond from './components/Unbond.vue'
import Transfer from './components/Transfer.vue'
import IBCTransfer from './components/IBCTransfer.vue'
import Vote from './components/Vote.vue'
import WithdrawCommission from './components/WithdrawCommission.vue'
import GovDeposit from './components/GovDeposit.vue'
import TransactionResult from './TransactionResult.vue'
export default {
name: 'DelegateDialogue',
components: {
BAvatar,
BModal,
BRow,
BCol,
BForm,
BInputGroup,
BFormInput,
BFormGroup,
BFormSelect,
BFormSelectOption,
BFormCheckbox,
vSelect,
BButton,
BInputGroupAppend,
BOverlay,
WalletInputVue,
ValidationProvider,
ValidationObserver,
// eslint-disable-next-line vue/no-unused-components
ToastificationContent,
Delegate,
Redelegate,
Withdraw,
Unbond,
Transfer,
IBCTransfer,
Vote,
WithdrawCommission,
GovDeposit,
TransactionResult,
},
directives: {
Ripple,
},
props: {
type: {
type: String,
default: '',
},
modalId: {
type: String,
default: 'operation-modal',
},
validatorAddress: {
type: String,
default: null,
},
address: {
type: String,
default: null,
},
proposalId: {
type: Number,
default: null,
},
proposalTitle: {
type: String,
default: null,
},
},
data() {
return {
modalTitle: '',
historyName: '',
selectedValidator: null,
selectedChain: null,
token: '',
chainId: '',
balance: [],
IBCDenom: {},
error: null,
sequence: 1,
accountNumber: 0,
advance: false,
fee: '900',
feeDenom: '',
wallet: 'ledgerUSB',
gas: '250000',
memo: '',
blockingMsg: this.address ? 'You are not the owner' : 'No available account found.',
actionName: 'Send',
showResult: false,
txHash: '',
required,
password,
email,
min,
integer,
url,
alpha,
between,
digits,
length,
alphaDash,
}
},
computed: {
feeDenoms() {
if (!this.balance) return []
return this.balance.filter(item => !item.denom.startsWith('ibc'))
},
accounts() {
const accounts = getLocalAccounts()
const selectedWallet = this.$store.state.chains.defaultWallet
return accounts[selectedWallet]
},
isOwner() {
if (this.accounts) {
this.updateWallet(this.accounts.device)
if (this.accounts.address.findIndex(x => x.addr === this.selectedAddress) > -1) {
return false
}
}
return true
},
selectedAddress() {
if (this.address) {
return this.address
}
const chain = this.$store.state.chains.selected.chain_name
const selectedAddress = this.accounts.address.find(x => x.chain === chain)
return selectedAddress?.addr
},
},
methods: {
initialize() {
this.$http.getLatestBlock().then(ret => {
this.chainId = ret.block.header.chain_id
const notSynced = timeIn(ret.block.header.time, 10, 'm')
if (notSynced) {
this.error = 'Client is not synced or blockchain is halted'
} else {
this.error = null
}
})
this.$http.getAuthAccount(this.selectedAddress).then(ret => {
const account = extractAccountNumberAndSequence(ret)
this.accountNumber = account.accountNumber
this.sequence = account.sequence
})
this.$http.getBankBalances(this.selectedAddress).then(res => {
if (res && res.length > 0) {
this.balance = res.reverse()
const token = this.balance.find(i => !i.denom.startsWith('ibc'))
this.token = token.denom
if (token) this.feeDenom = token.denom
}
})
this.fee = this.$store.state.chains.selected?.min_tx_fee || '1000'
this.feeDenom = this.$store.state.chains.selected?.assets[0]?.base || ''
},
componentUpdate(obj) {
Object.keys(obj).forEach(key => {
this[key] = obj[key]
})
},
handleOk(bvModalEvt) {
bvModalEvt.preventDefault()
this.$refs.simpleRules.validate().then(ok => {
if (ok) {
this.sendTx().then(ret => {
console.log(ret)
this.error = ret
})
}
})
},
resetModal() {
this.feeDenom = ''
this.error = null
this.showResult = false
},
async sendTx() {
const txMsgs = this.$refs.component.msg
if (txMsgs.length === 0) {
this.error = 'No delegation found'
return ''
}
if (!this.accountNumber) {
this.error = 'Account number should not be empty!'
return ''
}
const txFee = {
amount: [
{
amount: this.fee,
denom: this.feeDenom,
},
],
gas: this.gas,
}
const signerData = {
accountNumber: this.accountNumber,
sequence: this.sequence,
chainId: this.chainId,
}
sign(
this.wallet,
this.chainId,
this.selectedAddress,
txMsgs,
txFee,
this.memo,
signerData,
).then(bodyBytes => {
this.showResult = true
this.$http.broadcastTx(bodyBytes, this.selectedChain).then(res => {
this.txHash = res.tx_response.txhash
setLocalTxHistory({
chain: this.$store.state.chains.selected,
op: this.historyName,
hash: res.tx_response.txhash,
time: new Date(),
})
}).catch(e => {
console.log(e)
this.error = e
})
}).catch(e => {
console.log(e)
this.error = e
})
return ''
},
updateWallet(v) {
console.log('device', v)
if (v && v !== 'address') {
this.wallet = v
}
this.wallet = 'keplr'
},
},
}
</script>
<style lang="scss">
@import '@core/scss/vue/libs/vue-select.scss';
.custom-transaction-modal {
.modal-header {
.modal-title {
font-size: 24px;
}
.close {
margin: 0;
}
}
}
</style>