This commit is contained in:
saplf
2019-02-26 00:26:16 +08:00
parent 54a0bd7f88
commit 79d3577bd6
9 changed files with 10810 additions and 11 deletions

View File

@@ -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",

View File

@@ -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 })
}

View File

@@ -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),
}

View File

@@ -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
View 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)
}
}

View File

@@ -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 })

View File

@@ -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: {},

View File

@@ -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;
}
}
}

10531
yarn.lock Normal file

File diff suppressed because it is too large Load Diff