转账
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
"morgan": "^1.9.0",
|
"morgan": "^1.9.0",
|
||||||
"serve-favicon": "^2.4.5",
|
"serve-favicon": "^2.4.5",
|
||||||
"socket.io": "^2.1.1",
|
"socket.io": "^2.1.1",
|
||||||
|
"tic.action": "git+https://git.faronear.org/tic/tic.action.git#20190109_preview",
|
||||||
"tic.crypto": "git+https://git.faronear.org/tic/tic.crypto#20190109_preview",
|
"tic.crypto": "git+https://git.faronear.org/tic/tic.crypto#20190109_preview",
|
||||||
"vue": "^2.5.17",
|
"vue": "^2.5.17",
|
||||||
"vue-axios": "^2.1.3",
|
"vue-axios": "^2.1.3",
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import ticCrypto from 'tic.crypto'
|
import ticCrypto from 'tic.crypto'
|
||||||
|
import { TIC } from '../tic'
|
||||||
|
|
||||||
export const seckey2pubkey = seckey => ticCrypto.seckey2pubkey(seckey)
|
export const seckey2pubkey = seckey => ticCrypto.seckey2pubkey(seckey)
|
||||||
export const secword2address = secword => ticCrypto.secword2address(secword)
|
export const secword2address = secword => ticCrypto.secword2address(secword)
|
||||||
export const secword2account = secword => ticCrypto.secword2account(secword)
|
export const secword2account = secword => ticCrypto.secword2account(secword)
|
||||||
// export const randomSecword = lang => ticCrypto.randomSecword(lang)
|
// export const randomSecword = lang => ticCrypto.randomSecword(lang)
|
||||||
export const isAddress = address => ticCrypto.isAddress(address)
|
export const isAddress = address => ticCrypto.isAddress(address)
|
||||||
|
|
||||||
|
export const transferTic = ({ mnemonic, to, amount, fee }) => {
|
||||||
|
const instance = TIC.fromMnemonic(mnemonic)
|
||||||
|
return instance.sendTransaction(to, amount, { gasFee: fee || 10 })
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,9 +4,10 @@ import * as worker from 'workerize-loader!./tic-bg'
|
|||||||
const instance = worker()
|
const instance = worker()
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
seckey2pubkey: async seckey => instance.seckey2pubkey(seckey),
|
seckey2pubkey: seckey => instance.seckey2pubkey(seckey),
|
||||||
secword2address: async secword => instance.secword2address(secword),
|
secword2address: secword => instance.secword2address(secword),
|
||||||
isAddress: async address => instance.isAddress(address),
|
isAddress: address => instance.isAddress(address),
|
||||||
secword2account: async secword => instance.secword2account(secword),
|
secword2account: secword => instance.secword2account(secword),
|
||||||
// randomSecword: async lang => instance.randomSecword(lang),
|
// randomSecword: async lang => instance.randomSecword(lang),
|
||||||
|
transferTic: params => instance.transferTic(params),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
export default {
|
export default {
|
||||||
dev: process.env.NODE_ENV === 'development',
|
dev: process.env.NODE_ENV === 'development',
|
||||||
|
|
||||||
|
delay: timeout => new Promise(resolve => setTimeout(resolve, timeout)),
|
||||||
}
|
}
|
||||||
|
|||||||
148
src/services/tic.js
Normal file
148
src/services/tic.js
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import * as Ticrypto from 'tic.crypto'
|
||||||
|
import * as ticAction from 'tic.action'
|
||||||
|
|
||||||
|
const TicActTransfer = ticAction.ActTransfer
|
||||||
|
|
||||||
|
const TIC_TXFEE = 10
|
||||||
|
|
||||||
|
export class TIC {
|
||||||
|
constructor(seckey, option = {}) {
|
||||||
|
if (!seckey || !Ticrypto.isSeckey(seckey)) throw Error('ERROR:Invalid Seckey')
|
||||||
|
Object.defineProperties(this, {
|
||||||
|
'seckey': {
|
||||||
|
value: seckey,
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
},
|
||||||
|
'pubkey': {
|
||||||
|
value: Ticrypto.seckey2pubkey(seckey),
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
},
|
||||||
|
'address': {
|
||||||
|
value: Ticrypto.pubkey2address(Ticrypto.seckey2pubkey(seckey)),
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
Object.assign(this, {
|
||||||
|
_defaultFee: option.fee || TIC_TXFEE, // fee cannot be zero
|
||||||
|
})
|
||||||
|
}
|
||||||
|
get txfee() {
|
||||||
|
return this._defaultFee
|
||||||
|
}
|
||||||
|
set txfee(fee) {
|
||||||
|
this._defaultFee = fee
|
||||||
|
}
|
||||||
|
|
||||||
|
static generateNewAccount() {
|
||||||
|
var secword = Ticrypto.randomSecword()
|
||||||
|
return Object.assign(new TIC(Ticrypto.secword2keypair(secword).seckey), {
|
||||||
|
secword: secword,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
static fromMnemonic(secword) {
|
||||||
|
if (!secword || !Ticrypto.isSecword(secword)) throw Error('ERROR:Invalid Secword')
|
||||||
|
return new TIC(Ticrypto.secword2keypair(secword).seckey)
|
||||||
|
}
|
||||||
|
static async getBalance(address) {
|
||||||
|
if (!address) {
|
||||||
|
throw new Error('Address is required')
|
||||||
|
}
|
||||||
|
return (await axios.post('Account/getBalance', {
|
||||||
|
'Account': {
|
||||||
|
'address': address,
|
||||||
|
},
|
||||||
|
})).data
|
||||||
|
}
|
||||||
|
static async getActions(address) {
|
||||||
|
if (!address) {
|
||||||
|
throw new Error('Address is required')
|
||||||
|
}
|
||||||
|
return (await axios.post('Action/getActionList', {
|
||||||
|
'Action': {
|
||||||
|
'actorAddress': address,
|
||||||
|
'toAddress': address,
|
||||||
|
},
|
||||||
|
'config': {
|
||||||
|
'logic': 'OR',
|
||||||
|
},
|
||||||
|
})).data
|
||||||
|
}
|
||||||
|
static encrypt(data, key) {
|
||||||
|
if (!data || !key) throw new Error('Required Params Missing')
|
||||||
|
return Ticrypto.encrypt(data, key)
|
||||||
|
}
|
||||||
|
static decrypt(data, key) {
|
||||||
|
return Ticrypto.decrypt(data, key, {
|
||||||
|
format: 'json',
|
||||||
|
}) // return null for wrong key
|
||||||
|
}
|
||||||
|
|
||||||
|
static isValidAddress(address) {
|
||||||
|
return Ticrypto.isAddress(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendTransaction(toAddress, amount, option = {
|
||||||
|
gasFee: TIC_TXFEE,
|
||||||
|
}) {
|
||||||
|
if (!toAddress || !amount) {
|
||||||
|
throw new Error('ERROR:RequiredParamsMissing')
|
||||||
|
} // amount cannot be zero
|
||||||
|
let action = new TicActTransfer({
|
||||||
|
amount: parseInt(amount),
|
||||||
|
toAddress: toAddress,
|
||||||
|
fee: option.gasFee,
|
||||||
|
})
|
||||||
|
// 对交易数据签名,packMe 内的参数是交易发起人的keypair
|
||||||
|
action.packMe({
|
||||||
|
seckey: this.seckey,
|
||||||
|
pubkey: this.pubkey,
|
||||||
|
address: this.address,
|
||||||
|
})
|
||||||
|
let data = {
|
||||||
|
Action: action,
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let res = (await axios.post('Action/prepare', data)).data
|
||||||
|
return res
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getBalance() {
|
||||||
|
return TIC.getBalance(this.address)
|
||||||
|
}
|
||||||
|
async getActions() {
|
||||||
|
return TIC.getActions(this.address)
|
||||||
|
}
|
||||||
|
getSerializedTx(option) {
|
||||||
|
if (!option.toAddress || !option.amount) {
|
||||||
|
throw new Error('ERROR:RequiredParamsMissing')
|
||||||
|
}
|
||||||
|
let action = new TicActTransfer({
|
||||||
|
amount: parseInt(option.amount),
|
||||||
|
toAddress: option.toAddress,
|
||||||
|
fee: option.fee || this._defaultFee,
|
||||||
|
})
|
||||||
|
// sign for txBody use function packMe, which needs actor's keypair as parameter
|
||||||
|
action.packMe({
|
||||||
|
seckey: this.seckey,
|
||||||
|
pubkey: this.pubkey,
|
||||||
|
address: this.address,
|
||||||
|
})
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
// default key for sign&encrypt is account's seckey,other keys are optional.
|
||||||
|
sign(message, key = this.seckey) {
|
||||||
|
return Ticrypto.sign(message, key)
|
||||||
|
}
|
||||||
|
verify(message, signature) {
|
||||||
|
return Ticrypto.sign(message, signature, this.seckey)
|
||||||
|
}
|
||||||
|
encrypt(key) {
|
||||||
|
return TIC.encrypt(this, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ export default {
|
|||||||
async login({ state, commit }) {
|
async login({ state, commit }) {
|
||||||
commit('updateSign', true)
|
commit('updateSign', true)
|
||||||
const account = (await ticApi.secword2account(state.mnemonic)) || {}
|
const account = (await ticApi.secword2account(state.mnemonic)) || {}
|
||||||
|
account.mnemonic = state.mnemonic
|
||||||
commit('updateSign', false)
|
commit('updateSign', false)
|
||||||
if (await ticApi.isAddress(account.address)) {
|
if (await ticApi.isAddress(account.address)) {
|
||||||
commit('updateAccount', _.omit(account, 'seckey'), { root: true })
|
commit('updateAccount', _.omit(account, 'seckey'), { root: true })
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
import { app } from '../../services'
|
||||||
|
import { ticApi } from '../../services/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
namespaced: true,
|
namespaced: true,
|
||||||
@@ -33,11 +35,17 @@ export default {
|
|||||||
address: '',
|
address: '',
|
||||||
|
|
||||||
transferVisible: false,
|
transferVisible: false,
|
||||||
|
submitting: false, // 提交转账
|
||||||
|
|
||||||
targetAddress: '',
|
targetAddress: '',
|
||||||
transferAmount: '',
|
transferAmount: '',
|
||||||
transferFee: '',
|
transferFee: '',
|
||||||
transferRemark: '',
|
transferRemark: '',
|
||||||
|
|
||||||
|
wrongAddress: '',
|
||||||
|
wrongAmount: '',
|
||||||
|
wrongFee: '',
|
||||||
|
transferResult: '',
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
@@ -59,6 +67,7 @@ export default {
|
|||||||
|
|
||||||
toggleTransferDlg(state, value) {
|
toggleTransferDlg(state, value) {
|
||||||
state.transferVisible = value
|
state.transferVisible = value
|
||||||
|
state.transferResult = ''
|
||||||
},
|
},
|
||||||
|
|
||||||
changeTargetAddress(state, value) {
|
changeTargetAddress(state, value) {
|
||||||
@@ -76,6 +85,26 @@ export default {
|
|||||||
changeTransferRemark(state, value) {
|
changeTransferRemark(state, value) {
|
||||||
state.transferRemark = value
|
state.transferRemark = value
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateSubmitting(state, submitting) {
|
||||||
|
state.submitting = submitting
|
||||||
|
},
|
||||||
|
|
||||||
|
updateWrongAddress(state, msg) {
|
||||||
|
state.wrongAddress = msg
|
||||||
|
},
|
||||||
|
|
||||||
|
updateWrongAmount(state, msg) {
|
||||||
|
state.wrongAmount = msg
|
||||||
|
},
|
||||||
|
|
||||||
|
updateWrongFee(state, msg) {
|
||||||
|
state.wrongFee = msg
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTransferResult(state, msg) {
|
||||||
|
state.transferResult = msg
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -94,6 +123,56 @@ export default {
|
|||||||
commit('setActions', actions && actions.data ? actions.data : [])
|
commit('setActions', actions && actions.data ? actions.data : [])
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async performTransfer({ state, commit, rootState }) {
|
||||||
|
commit('updateSubmitting', true)
|
||||||
|
const {
|
||||||
|
targetAddress,
|
||||||
|
transferAmount,
|
||||||
|
transferFee,
|
||||||
|
// transferRemark,
|
||||||
|
} = state
|
||||||
|
let wrongAddress, wrongAmount, wrongFee
|
||||||
|
if (!(await ticApi.isAddress(targetAddress))) {
|
||||||
|
wrongAddress = '错误的地址'
|
||||||
|
} else {
|
||||||
|
wrongAddress = ''
|
||||||
|
}
|
||||||
|
if (!(+transferAmount > 0)) {
|
||||||
|
wrongAmount = '请输入正确的交易金额'
|
||||||
|
} else {
|
||||||
|
wrongAmount = ''
|
||||||
|
}
|
||||||
|
if (!(+transferFee > 0)) {
|
||||||
|
wrongFee = '请输入正确的手续费'
|
||||||
|
} else {
|
||||||
|
wrongFee = ''
|
||||||
|
}
|
||||||
|
commit('updateWrongAddress', wrongAddress)
|
||||||
|
commit('updateWrongAmount', wrongAmount)
|
||||||
|
commit('updateWrongFee', wrongFee)
|
||||||
|
if (wrongAddress || wrongAmount || wrongFee) {
|
||||||
|
commit('updateSubmitting', false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await ticApi.transferTic({
|
||||||
|
mnemonic: rootState.account.mnemonic,
|
||||||
|
to: targetAddress,
|
||||||
|
amount: +transferAmount,
|
||||||
|
fee: +transferFee,
|
||||||
|
})
|
||||||
|
if (res) {
|
||||||
|
// success
|
||||||
|
commit('updateTransferResult', '转账成功')
|
||||||
|
await app.delay(1000)
|
||||||
|
commit('toggleTransferDlg', false)
|
||||||
|
} else {
|
||||||
|
// fail
|
||||||
|
commit('updateTransferResult', '转账失败')
|
||||||
|
}
|
||||||
|
commit('updateSubmitting', false)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
getters: {},
|
getters: {},
|
||||||
|
|||||||
@@ -14,26 +14,33 @@
|
|||||||
|
|
||||||
<span class="title-decoration dlg-line">接受地址</span>
|
<span class="title-decoration dlg-line">接受地址</span>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
|
autofocus
|
||||||
|
single-line
|
||||||
|
:error-messages="wrongAddress"
|
||||||
:value="targetAddress"
|
:value="targetAddress"
|
||||||
@input="changeTargetAddress"
|
@input="changeTargetAddress"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
|
|
||||||
<div class="group-line">
|
<div class="group-line">
|
||||||
<span class="title-decoration">转账金额</span>
|
<span class="title-decoration title-left">转账金额</span>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
|
single-line
|
||||||
|
:error-messages="wrongAmount"
|
||||||
:value="transferAmount"
|
:value="transferAmount"
|
||||||
@input="changeTransferAmount"
|
@input="changeTransferAmount"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<span class="title-decoration">TIC</span>
|
<span class="title-decoration title-right">TIC</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="group-line">
|
<div class="group-line">
|
||||||
<span class="title-decoration">手续费</span>
|
<span class="title-decoration title-left">手续费</span>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
|
single-line
|
||||||
|
:error-messages="wrongFee"
|
||||||
:value="transferFee"
|
:value="transferFee"
|
||||||
@input="changeTransferFee"
|
@input="changeTransferFee"
|
||||||
></v-text-field>
|
></v-text-field>
|
||||||
<span class="title-decoration">TIC</span>
|
<span class="title-decoration title-right">TIC</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span class="title-decoration dlg-line">备注</span>
|
<span class="title-decoration dlg-line">备注</span>
|
||||||
@@ -44,8 +51,14 @@
|
|||||||
</section>
|
</section>
|
||||||
<v-card-actions>
|
<v-card-actions>
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
<span class="result-hint">{{ transferResult }}</span>
|
||||||
<v-btn color="grey darken-1" flat @click="hideTransferDlg">关闭</v-btn>
|
<v-btn color="grey darken-1" flat @click="hideTransferDlg">关闭</v-btn>
|
||||||
<v-btn color="blue darken-1" flat @click="hideTransferDlg">转账</v-btn>
|
<v-btn
|
||||||
|
color="blue darken-1"
|
||||||
|
flat
|
||||||
|
@click="performTransfer"
|
||||||
|
:loading="submitting"
|
||||||
|
>转账</v-btn>
|
||||||
</v-card-actions>
|
</v-card-actions>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-dialog>
|
</v-dialog>
|
||||||
@@ -135,6 +148,11 @@ export default {
|
|||||||
'transferAmount',
|
'transferAmount',
|
||||||
'transferFee',
|
'transferFee',
|
||||||
'transferRemark',
|
'transferRemark',
|
||||||
|
'submitting',
|
||||||
|
'wrongAddress',
|
||||||
|
'wrongAmount',
|
||||||
|
'wrongFee',
|
||||||
|
'transferResult'
|
||||||
]),
|
]),
|
||||||
...mapGetters([]),
|
...mapGetters([]),
|
||||||
},
|
},
|
||||||
@@ -142,6 +160,7 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
...mapActions([
|
...mapActions([
|
||||||
'updateOwnerInfo',
|
'updateOwnerInfo',
|
||||||
|
'performTransfer',
|
||||||
]),
|
]),
|
||||||
...mapMutations([
|
...mapMutations([
|
||||||
'inputFilter',
|
'inputFilter',
|
||||||
@@ -186,10 +205,17 @@ export default {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
color: $primaryColor;
|
color: $primaryColor;
|
||||||
margin-top: $baseMargin;
|
margin-top: $baseMargin;
|
||||||
min-width: 5em;
|
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.title-left {
|
||||||
|
min-width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-right {
|
||||||
|
margin-left: $baseMargin;
|
||||||
|
}
|
||||||
|
|
||||||
.address-decoration {
|
.address-decoration {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
color: grey;
|
color: grey;
|
||||||
@@ -209,6 +235,10 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.result-hint {
|
||||||
|
margin-right: 1.2em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user