|
|
@@ -1,121 +1,43 @@
|
|
|
<template>
|
|
|
<ElConfigProvider size="small">
|
|
|
- <ElRow :gutter="20">
|
|
|
- <ElCol :span="12">
|
|
|
- <ElCard shadow="never">
|
|
|
- <div class="text-sm flex flex-col items-center">
|
|
|
- <div class="flex items-center">
|
|
|
- Mainnet
|
|
|
- <ElIcon class="ml-2 mr-2">
|
|
|
- <ArrowNarrowRight />
|
|
|
- </ElIcon>
|
|
|
- zkSync Era
|
|
|
- <ElInput class="ml-2 !w-20" v-model="depositAmount" />
|
|
|
- <ElButton type="primary" class="ml-2" @click="deposit(0)" :loading="depositing">
|
|
|
- 官方
|
|
|
- </ElButton>
|
|
|
- <ElButton type="primary" class="ml-2" @click="deposit(1)" :loading="depositing">
|
|
|
- Orbiter
|
|
|
- </ElButton>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="flex items-center mt-4">
|
|
|
- zkSync Era
|
|
|
- <ElIcon class="ml-2 mr-2">
|
|
|
- <ArrowNarrowRight />
|
|
|
- </ElIcon>
|
|
|
- Mainnet
|
|
|
- <ElInput class="ml-2 !w-20" v-model="widthdrawAmount" />
|
|
|
- <ElButton type="primary" class="ml-2" @click="widthdraw(0)" :loading="widthdrawing"
|
|
|
- >官方</ElButton
|
|
|
- >
|
|
|
- <ElButton type="primary" class="ml-2" @click="widthdraw(1)" :loading="widthdrawing">
|
|
|
- Orbiter
|
|
|
- </ElButton>
|
|
|
- </div>
|
|
|
- <div class="flex items-center mt-4">
|
|
|
- ETH
|
|
|
- <ElIcon class="ml-2 mr-2">
|
|
|
- <ArrowsRightLeft />
|
|
|
- </ElIcon>
|
|
|
- USDC
|
|
|
- <ElInput class="ml-2 !w-20" v-model="liquidityAmount" />
|
|
|
- <ElButton class="ml-2" type="primary" @click="addLiquidity" :loading="addingLiquidity">
|
|
|
- 增加流动性
|
|
|
- </ElButton>
|
|
|
- <ElButton class="ml-2" type="primary" @click="removeLiquidity" :loading="addingLiquidity">
|
|
|
- 移除流动性
|
|
|
- </ElButton>
|
|
|
- </div>
|
|
|
- <div class="flex items-center mt-4">
|
|
|
- ETH
|
|
|
- <ElIcon class="ml-2 mr-2">
|
|
|
- <ArrowNarrowRight />
|
|
|
- </ElIcon>
|
|
|
- <ElInput class="!w-20 mr-2" v-model="exactOutAmount" />
|
|
|
- USDC
|
|
|
- <ElButton class="ml-2" type="primary" @click="swapExactOut" :loading="swaping">
|
|
|
- SWAP
|
|
|
- </ElButton>
|
|
|
- </div>
|
|
|
- <div class="flex items-center mt-4">
|
|
|
- <ElInput class="!w-20 mr-2" v-model="exactInAmount" />
|
|
|
- USDC
|
|
|
- <ElIcon class="ml-2 mr-2">
|
|
|
- <ArrowNarrowRight />
|
|
|
- </ElIcon>
|
|
|
- ETH
|
|
|
- <ElButton class="ml-2" type="primary" @click="swapExactIn" :loading="swaping">
|
|
|
- SWAP
|
|
|
- </ElButton>
|
|
|
- </div>
|
|
|
- <div class="flex items-center mt-4">
|
|
|
- <ElButton type="primary" @click="mint" :loading="minting">mint</ElButton>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </ElCard>
|
|
|
-
|
|
|
- <div class="mb-3 mt-3">
|
|
|
- <ElButton @click="onAddAccount">添加账号</ElButton>
|
|
|
- <ElButton @click="importAccounts">批量导入</ElButton>
|
|
|
- </div>
|
|
|
- <ElCard shadow="never">
|
|
|
- <ElTable :data="accounts" size="small" ref="table" @row-click="rowClick">
|
|
|
- <el-table-column type="selection" width="55" />
|
|
|
- <ElTableColumn prop="name" label="名称"></ElTableColumn>
|
|
|
- <ElTableColumn prop="address" label="地址" min-width="150">
|
|
|
- <template #default="{ row }">
|
|
|
- <WalletAddress :address="row.address" />
|
|
|
- </template>
|
|
|
- </ElTableColumn>
|
|
|
- <ElTableColumn prop="ethBalance" label="ETH余额" min-width="160" />
|
|
|
- <ElTableColumn prop="zkBalance" label="ETH余额(ZK)" min-width="160" />
|
|
|
- <ElTableColumn prop="zkUsdcBalance" label="USDC余额(ZK)" min-width="160" />
|
|
|
- <ElTableColumn width="80" fixed="right">
|
|
|
- <template #default="{ row }">
|
|
|
- <ElButton type="danger" size="small" @click="deleteAccount(row)">删除</ElButton>
|
|
|
- </template>
|
|
|
- </ElTableColumn>
|
|
|
- </ElTable>
|
|
|
- </ElCard>
|
|
|
+ <ElRow :gutter="20" class="h-full">
|
|
|
+ <ElCol :span="12" class="h-full">
|
|
|
+ <AccountsView ref="accountsView" class="h-full" />
|
|
|
</ElCol>
|
|
|
- <ElCol :span="12">
|
|
|
- <TaskView />
|
|
|
+ <ElCol :span="12" class="!flex flex-col h-full">
|
|
|
+ <TaskView class=" flex-1" @createTask="onCreateTask" />
|
|
|
+ <LogsView class="flex-1 mt-4" />
|
|
|
</ElCol>
|
|
|
</ElRow>
|
|
|
</ElConfigProvider>
|
|
|
- <ElDialog v-model="showAddDialog" title="添加账号" class="!w-10/12 max-w-md">
|
|
|
- <ElForm :model="addForm" :rules="addRules" ref="addFormRef">
|
|
|
- <ElFormItem label="名称" prop="name">
|
|
|
- <ElInput v-model="addForm.name"></ElInput>
|
|
|
+ <ElDialog v-model="showCreateTaskDialog" title="新建任务" width="500px">
|
|
|
+ <ElForm
|
|
|
+ :model="createTaskForm"
|
|
|
+ :rules="createTaskRule"
|
|
|
+ ref="createTaskFormRef"
|
|
|
+ label-position="right"
|
|
|
+ label-width="100px"
|
|
|
+ >
|
|
|
+ <ElFormItem prop="type" label="任务类型">
|
|
|
+ <ElSelect v-model="createTaskForm.type">
|
|
|
+ <ElOption v-for="key in TaskType" :key="key" :label="TaskType[key]" :value="key"></ElOption>
|
|
|
+ </ElSelect>
|
|
|
</ElFormItem>
|
|
|
- <ElFormItem label="私钥" prop="privateKey">
|
|
|
- <ElInput v-model="addForm.privateKey" type="password"></ElInput>
|
|
|
+ <ElFormItem prop="amount" label="金额">
|
|
|
+ <ElInput v-model="createTaskForm.minAmount" class="!w-36">
|
|
|
+ <template #prepend>MIN</template>
|
|
|
+ </ElInput>
|
|
|
+ <ElInput v-model="createTaskForm.maxAmount" class="ml-6 !w-36">
|
|
|
+ <template #prepend>MAX</template>
|
|
|
+ </ElInput>
|
|
|
+ </ElFormItem>
|
|
|
+ <ElFormItem prop="startTime" label="执行时间">
|
|
|
+ <ElDatePicker type="datetime" v-model="createTaskForm.startTime"></ElDatePicker>
|
|
|
</ElFormItem>
|
|
|
</ElForm>
|
|
|
<template #footer>
|
|
|
- <ElButton @click="showAddDialog = false">取消</ElButton>
|
|
|
- <ElButton type="primary" @click="saveAccount" :loading="savingAccount">确定</ElButton>
|
|
|
+ <ElButton @click="showCreateTaskDialog = false">取消</ElButton>
|
|
|
+ <ElButton type="primary" @click="createTask" :loading="creatingTask">确定</ElButton>
|
|
|
</template>
|
|
|
</ElDialog>
|
|
|
</template>
|
|
|
@@ -124,300 +46,70 @@
|
|
|
import { ref, onMounted, inject, watch } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import { http } from '@/plugins/http'
|
|
|
-import { ethers, Wallet } from 'ethers'
|
|
|
import { ArrowNarrowRight, ArrowsRightLeft } from '@vicons/tabler'
|
|
|
import { useFileDialog, useIntervalFn } from '@vueuse/core'
|
|
|
import WalletAddress from '@/components/WalletAddress.vue'
|
|
|
import { TaskType } from '@/enums'
|
|
|
+import AccountsView from './AccountsView.vue'
|
|
|
import TaskView from '@/views/TaskView.vue'
|
|
|
-const network = inject('network')
|
|
|
-watch(network, () => {
|
|
|
- getAccounts()
|
|
|
-})
|
|
|
-const accounts = ref([])
|
|
|
-const table = ref(null)
|
|
|
-const addForm = ref({})
|
|
|
-const addFormRef = ref(null)
|
|
|
-const addRules = {
|
|
|
- name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
|
|
|
- privateKey: [{ required: true, message: '请输入私钥', trigger: 'blur' }]
|
|
|
-}
|
|
|
-const showAddDialog = ref(false)
|
|
|
-const savingAccount = ref(false)
|
|
|
-function rowClick(row, column, event) {
|
|
|
- table.value.clearSelection()
|
|
|
- table.value.toggleRowSelection(row, true)
|
|
|
-}
|
|
|
-function onAddAccount() {
|
|
|
- if (addFormRef.value) {
|
|
|
- addFormRef.value.clearValidate()
|
|
|
- }
|
|
|
- showAddDialog.value = true
|
|
|
-}
|
|
|
-
|
|
|
-async function importAccounts() {
|
|
|
- await ElMessageBox.confirm(
|
|
|
- '请上传TXT文件,每行一个账号,账号名称和私钥用逗号隔开,如:<br/><p class="italic font-bold text-sm">账号1,0x123456...<br/>账号2,0x123456...<br/>账号3,0x123456...<br/>账号4,0x123456...</p>',
|
|
|
- '导入账号',
|
|
|
- {
|
|
|
- confirmButtonText: '确定',
|
|
|
- showCancelButton: false,
|
|
|
- type: 'warning',
|
|
|
- dangerouslyUseHTMLString: true
|
|
|
- }
|
|
|
- )
|
|
|
- const { files, open, reset, onChange } = useFileDialog({
|
|
|
- accept: '.txt'
|
|
|
- })
|
|
|
- onChange((files) => {
|
|
|
- /** do something with files */
|
|
|
- const reader = new FileReader()
|
|
|
- reader.addEventListener('load', (event) => {
|
|
|
- http.put(
|
|
|
- '/accounts',
|
|
|
- event.target.result
|
|
|
- .split(/\r?\n/)
|
|
|
- .filter((i) => {
|
|
|
- return !!i && i.split(/[,,]/).length === 2
|
|
|
- })
|
|
|
- .map((e) => {
|
|
|
- let [name, privateKey] = e.split(/[,,]/)
|
|
|
- const provider = new ethers.providers.JsonRpcProvider('https://zksync2-testnet.zksync.dev/')
|
|
|
- const wallet = new Wallet(privateKey, provider)
|
|
|
- return {
|
|
|
- name,
|
|
|
- address: wallet.address,
|
|
|
- privateKey,
|
|
|
- network: network.value
|
|
|
- }
|
|
|
- })
|
|
|
- )
|
|
|
- .then(() => {
|
|
|
- ElMessage.success('导入成功')
|
|
|
- getAccounts()
|
|
|
- })
|
|
|
- .catch((e) => {
|
|
|
- ElMessage.error(e.message)
|
|
|
- })
|
|
|
- })
|
|
|
- reader.readAsText(files[0])
|
|
|
- })
|
|
|
- open()
|
|
|
-}
|
|
|
+import LogsView from './LogsView.vue'
|
|
|
|
|
|
-async function saveAccount() {
|
|
|
- await addFormRef.value.validate()
|
|
|
- savingAccount.value = true
|
|
|
- try {
|
|
|
- const provider = new ethers.providers.JsonRpcProvider('https://zksync2-testnet.zksync.dev/')
|
|
|
- const wallet = new Wallet(addForm.value.privateKey, provider)
|
|
|
- addForm.value.address = wallet.address
|
|
|
- await http.put('/accounts', {
|
|
|
- ...addForm.value,
|
|
|
- network: network.value
|
|
|
- })
|
|
|
- savingAccount.value = false
|
|
|
- showAddDialog.value = false
|
|
|
- ElMessage.success('添加成功')
|
|
|
- getAccounts()
|
|
|
- } catch (e) {
|
|
|
- console.log(e)
|
|
|
- savingAccount.value = false
|
|
|
- ElMessage.error(e.message)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function getAccounts() {
|
|
|
- const res = await http.get('/accounts/my', { network: network.value })
|
|
|
- accounts.value = res
|
|
|
+const accountsView = ref(null)
|
|
|
+function selected() {
|
|
|
+ return accountsView.value && accountsView.value.selected()
|
|
|
}
|
|
|
-useIntervalFn(getAccounts, 10000)
|
|
|
-getAccounts()
|
|
|
|
|
|
-async function deleteAccount(account) {
|
|
|
- try {
|
|
|
- await ElMessageBox.confirm(`确认删除?`, '确认', {
|
|
|
- type: 'warning'
|
|
|
- })
|
|
|
- await http.delete(`/accounts/${account.id}`)
|
|
|
- ElMessage.success('删除成功')
|
|
|
- getAccounts()
|
|
|
- } catch (error) {
|
|
|
- if ('cancel' !== error) ElMessage.error(error.message)
|
|
|
- }
|
|
|
-}
|
|
|
-const depositAmount = ref('')
|
|
|
-const widthdrawAmount = ref('')
|
|
|
-const depositing = ref(false)
|
|
|
-const widthdrawing = ref(false)
|
|
|
-async function deposit(type) {
|
|
|
- if (table.value.getSelectionRows().length === 0) {
|
|
|
- ElMessage.error('请选择账号')
|
|
|
- return
|
|
|
- }
|
|
|
- if (!depositAmount.value) {
|
|
|
- ElMessage.error('请输入金额')
|
|
|
- return
|
|
|
- }
|
|
|
- depositing.value = true
|
|
|
- try {
|
|
|
- console.log(table.value.getSelectionRows())
|
|
|
- await http.put('/tasks', {
|
|
|
- accounts: table.value.getSelectionRows().map((i) => i.id),
|
|
|
- params: {
|
|
|
- amount: depositAmount.value
|
|
|
+const showCreateTaskDialog = ref(false)
|
|
|
+const createTaskFormRef = ref(null)
|
|
|
+const createTaskForm = ref({})
|
|
|
+const createTaskRule = {
|
|
|
+ type: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
|
|
+ amount: [
|
|
|
+ {
|
|
|
+ validator: (rule, value, callback) => {
|
|
|
+ if (createTaskForm.value.type !== 'min') {
|
|
|
+ if (!(createTaskForm.value.minAmount && createTaskForm.value.maxAmount)) {
|
|
|
+ callback(new Error('请输入金额'))
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ callback()
|
|
|
+ }
|
|
|
},
|
|
|
- type: type === 0 ? TaskType.bridge2zk : TaskType.orbiter2zk
|
|
|
- })
|
|
|
- ElMessage.success('任务添加成功')
|
|
|
- depositing.value = false
|
|
|
- } catch (error) {
|
|
|
- depositing.value = false
|
|
|
- ElMessage.error(error.message)
|
|
|
- }
|
|
|
-}
|
|
|
-async function widthdraw(type) {
|
|
|
- if (table.value.getSelectionRows().length === 0) {
|
|
|
- ElMessage.error('请选择账号')
|
|
|
- return
|
|
|
- }
|
|
|
- if (!widthdrawAmount.value) {
|
|
|
- ElMessage.error('请输入金额')
|
|
|
- return
|
|
|
- }
|
|
|
- widthdrawing.value = true
|
|
|
- try {
|
|
|
- console.log(table.value.getSelectionRows())
|
|
|
- await http.post(type === 0 ? '/web3/zk-withdraw' : '/web3/orbiter-withdraw', {
|
|
|
- amount: widthdrawAmount.value,
|
|
|
- accountId: table.value.getSelectionRows()[0].id,
|
|
|
- network: network.value
|
|
|
- })
|
|
|
- ElMessage.success('成功')
|
|
|
- widthdrawing.value = false
|
|
|
- } catch (error) {
|
|
|
- widthdrawing.value = false
|
|
|
- ElMessage.error(error.message)
|
|
|
- }
|
|
|
-}
|
|
|
-const addingLiquidity = ref(false)
|
|
|
-const liquidityAmount = ref('')
|
|
|
-async function addLiquidity() {
|
|
|
- if (table.value.getSelectionRows().length === 0) {
|
|
|
- ElMessage.error('请选择账号')
|
|
|
- return
|
|
|
- }
|
|
|
- if (!liquidityAmount.value) {
|
|
|
- ElMessage.error('请输入金额')
|
|
|
- return
|
|
|
- }
|
|
|
- addingLiquidity.value = true
|
|
|
- try {
|
|
|
- console.log(table.value.getSelectionRows())
|
|
|
- await http.post('/web3/add-liquidity', {
|
|
|
- accountId: table.value.getSelectionRows()[0].id,
|
|
|
- amount: liquidityAmount.value,
|
|
|
- network: network.value
|
|
|
- })
|
|
|
- ElMessage.success('成功')
|
|
|
- addingLiquidity.value = false
|
|
|
- } catch (error) {
|
|
|
- addingLiquidity.value = false
|
|
|
- ElMessage.error(error.message)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-async function removeLiquidity() {
|
|
|
- if (table.value.getSelectionRows().length === 0) {
|
|
|
- ElMessage.error('请选择账号')
|
|
|
- return
|
|
|
- }
|
|
|
- addingLiquidity.value = true
|
|
|
- try {
|
|
|
- console.log(table.value.getSelectionRows())
|
|
|
- await http.post('/web3/remove-liquidity', {
|
|
|
- accountId: table.value.getSelectionRows()[0].id,
|
|
|
- network: network.value
|
|
|
- })
|
|
|
- ElMessage.success('成功')
|
|
|
- addingLiquidity.value = false
|
|
|
- } catch (error) {
|
|
|
- addingLiquidity.value = false
|
|
|
- ElMessage.error(error.message)
|
|
|
- }
|
|
|
+ trigger: 'change'
|
|
|
+ }
|
|
|
+ ]
|
|
|
}
|
|
|
-
|
|
|
-const exactOutAmount = ref('')
|
|
|
-const swaping = ref(false)
|
|
|
-function swapExactOut() {
|
|
|
- if (table.value.getSelectionRows().length === 0) {
|
|
|
- ElMessage.error('请选择账号')
|
|
|
+const creatingTask = ref(false)
|
|
|
+function onCreateTask() {
|
|
|
+ if (selected().length === 0) {
|
|
|
+ ElMessage.warning('请先选择账号')
|
|
|
return
|
|
|
}
|
|
|
- if (!exactOutAmount.value) {
|
|
|
- ElMessage.error('请输入金额')
|
|
|
- return
|
|
|
- }
|
|
|
- swaping.value = true
|
|
|
- http.post('/web3/swap-exact-out', {
|
|
|
- accountId: table.value.getSelectionRows()[0].id,
|
|
|
- amount: exactOutAmount.value,
|
|
|
- network: network.value
|
|
|
- })
|
|
|
- .then(() => {
|
|
|
- ElMessage.success('成功')
|
|
|
- swaping.value = false
|
|
|
- })
|
|
|
- .catch((e) => {
|
|
|
- swaping.value = false
|
|
|
- ElMessage.error(e.message)
|
|
|
- })
|
|
|
+ showCreateTaskDialog.value = true
|
|
|
}
|
|
|
-
|
|
|
-const exactInAmount = ref('')
|
|
|
-function swapExactIn() {
|
|
|
- if (table.value.getSelectionRows().length === 0) {
|
|
|
- ElMessage.error('请选择账号')
|
|
|
- return
|
|
|
- }
|
|
|
- if (!exactInAmount.value) {
|
|
|
- ElMessage.error('请输入金额')
|
|
|
- return
|
|
|
- }
|
|
|
- swaping.value = true
|
|
|
- http.post('/web3/swap-exact-in', {
|
|
|
- accountId: table.value.getSelectionRows()[0].id,
|
|
|
- amount: exactInAmount.value,
|
|
|
- network: network.value
|
|
|
+function createTask() {
|
|
|
+ createTaskFormRef.value.validate(async (valid) => {
|
|
|
+ if (valid) {
|
|
|
+ creatingTask.value = true
|
|
|
+ try {
|
|
|
+ await http.put('/tasks', {
|
|
|
+ accounts: selected().map((i) => i.id),
|
|
|
+ params: {
|
|
|
+ mintAmount: createTaskForm.value.mintAmount,
|
|
|
+ maxAmount: createTaskForm.value.maxAmount
|
|
|
+ },
|
|
|
+ type: createTaskForm.value.type
|
|
|
+ })
|
|
|
+ ElMessage.success('任务添加成功')
|
|
|
+ creatingTask.value = false
|
|
|
+ showCreateTaskDialog.value = false
|
|
|
+ } catch (error) {
|
|
|
+ creatingTask.value = false
|
|
|
+ ElMessage.error(error.message)
|
|
|
+ }
|
|
|
+ }
|
|
|
})
|
|
|
- .then(() => {
|
|
|
- ElMessage.success('成功')
|
|
|
- swaping.value = false
|
|
|
- })
|
|
|
- .catch((e) => {
|
|
|
- swaping.value = false
|
|
|
- ElMessage.error(e.message)
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-const minting = ref(false)
|
|
|
-async function mint() {
|
|
|
- if (table.value.getSelectionRows().length === 0) {
|
|
|
- ElMessage.error('请选择账号')
|
|
|
- return
|
|
|
- }
|
|
|
- minting.value = true
|
|
|
- try {
|
|
|
- console.log(table.value.getSelectionRows())
|
|
|
- await http.post('/web3/mint', {
|
|
|
- accountId: table.value.getSelectionRows()[0].id,
|
|
|
- network: network.value
|
|
|
- })
|
|
|
- ElMessage.success('成功')
|
|
|
- minting.value = false
|
|
|
- } catch (error) {
|
|
|
- minting.value = false
|
|
|
- ElMessage.error(error.message)
|
|
|
- }
|
|
|
}
|
|
|
</script>
|