转账
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
"morgan": "^1.9.0",
|
||||
"serve-favicon": "^2.4.5",
|
||||
"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",
|
||||
"vue": "^2.5.17",
|
||||
"vue-axios": "^2.1.3",
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import ticCrypto from 'tic.crypto'
|
||||
import { TIC } from '../tic'
|
||||
|
||||
export const seckey2pubkey = seckey => ticCrypto.seckey2pubkey(seckey)
|
||||
export const secword2address = secword => ticCrypto.secword2address(secword)
|
||||
export const secword2account = secword => ticCrypto.secword2account(secword)
|
||||
// export const randomSecword = lang => ticCrypto.randomSecword(lang)
|
||||
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()
|
||||
|
||||
export default {
|
||||
seckey2pubkey: async seckey => instance.seckey2pubkey(seckey),
|
||||
secword2address: async secword => instance.secword2address(secword),
|
||||
isAddress: async address => instance.isAddress(address),
|
||||
secword2account: async secword => instance.secword2account(secword),
|
||||
seckey2pubkey: seckey => instance.seckey2pubkey(seckey),
|
||||
secword2address: secword => instance.secword2address(secword),
|
||||
isAddress: address => instance.isAddress(address),
|
||||
secword2account: secword => instance.secword2account(secword),
|
||||
// randomSecword: async lang => instance.randomSecword(lang),
|
||||
transferTic: params => instance.transferTic(params),
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export default {
|
||||
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 }) {
|
||||
commit('updateSign', true)
|
||||
const account = (await ticApi.secword2account(state.mnemonic)) || {}
|
||||
account.mnemonic = state.mnemonic
|
||||
commit('updateSign', false)
|
||||
if (await ticApi.isAddress(account.address)) {
|
||||
commit('updateAccount', _.omit(account, 'seckey'), { root: true })
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import axios from 'axios'
|
||||
import { app } from '../../services'
|
||||
import { ticApi } from '../../services/api'
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
@@ -33,11 +35,17 @@ export default {
|
||||
address: '',
|
||||
|
||||
transferVisible: false,
|
||||
submitting: false, // 提交转账
|
||||
|
||||
targetAddress: '',
|
||||
transferAmount: '',
|
||||
transferFee: '',
|
||||
transferRemark: '',
|
||||
|
||||
wrongAddress: '',
|
||||
wrongAmount: '',
|
||||
wrongFee: '',
|
||||
transferResult: '',
|
||||
},
|
||||
|
||||
mutations: {
|
||||
@@ -59,6 +67,7 @@ export default {
|
||||
|
||||
toggleTransferDlg(state, value) {
|
||||
state.transferVisible = value
|
||||
state.transferResult = ''
|
||||
},
|
||||
|
||||
changeTargetAddress(state, value) {
|
||||
@@ -76,6 +85,26 @@ export default {
|
||||
changeTransferRemark(state, 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: {
|
||||
@@ -94,6 +123,56 @@ export default {
|
||||
commit('setActions', actions && actions.data ? actions.data : [])
|
||||
} 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: {},
|
||||
|
||||
@@ -14,26 +14,33 @@
|
||||
|
||||
<span class="title-decoration dlg-line">接受地址</span>
|
||||
<v-text-field
|
||||
autofocus
|
||||
single-line
|
||||
:error-messages="wrongAddress"
|
||||
:value="targetAddress"
|
||||
@input="changeTargetAddress"
|
||||
></v-text-field>
|
||||
|
||||
<div class="group-line">
|
||||
<span class="title-decoration">转账金额</span>
|
||||
<span class="title-decoration title-left">转账金额</span>
|
||||
<v-text-field
|
||||
single-line
|
||||
:error-messages="wrongAmount"
|
||||
:value="transferAmount"
|
||||
@input="changeTransferAmount"
|
||||
></v-text-field>
|
||||
<span class="title-decoration">TIC</span>
|
||||
<span class="title-decoration title-right">TIC</span>
|
||||
</div>
|
||||
|
||||
<div class="group-line">
|
||||
<span class="title-decoration">手续费</span>
|
||||
<span class="title-decoration title-left">手续费</span>
|
||||
<v-text-field
|
||||
single-line
|
||||
:error-messages="wrongFee"
|
||||
:value="transferFee"
|
||||
@input="changeTransferFee"
|
||||
></v-text-field>
|
||||
<span class="title-decoration">TIC</span>
|
||||
<span class="title-decoration title-right">TIC</span>
|
||||
</div>
|
||||
|
||||
<span class="title-decoration dlg-line">备注</span>
|
||||
@@ -44,8 +51,14 @@
|
||||
</section>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<span class="result-hint">{{ transferResult }}</span>
|
||||
<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>
|
||||
</v-dialog>
|
||||
@@ -135,6 +148,11 @@ export default {
|
||||
'transferAmount',
|
||||
'transferFee',
|
||||
'transferRemark',
|
||||
'submitting',
|
||||
'wrongAddress',
|
||||
'wrongAmount',
|
||||
'wrongFee',
|
||||
'transferResult'
|
||||
]),
|
||||
...mapGetters([]),
|
||||
},
|
||||
@@ -142,6 +160,7 @@ export default {
|
||||
methods: {
|
||||
...mapActions([
|
||||
'updateOwnerInfo',
|
||||
'performTransfer',
|
||||
]),
|
||||
...mapMutations([
|
||||
'inputFilter',
|
||||
@@ -186,10 +205,17 @@ export default {
|
||||
text-align: left;
|
||||
color: $primaryColor;
|
||||
margin-top: $baseMargin;
|
||||
min-width: 5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.title-left {
|
||||
min-width: 5em;
|
||||
}
|
||||
|
||||
.title-right {
|
||||
margin-left: $baseMargin;
|
||||
}
|
||||
|
||||
.address-decoration {
|
||||
text-align: left;
|
||||
color: grey;
|
||||
@@ -209,6 +235,10 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.result-hint {
|
||||
margin-right: 1.2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user