增加任务确认页面
This commit is contained in:
parent
205cc035bb
commit
53875dd014
@ -24,6 +24,7 @@
|
||||
"@fastify/formbody": "^7.4.0",
|
||||
"@fastify/helmet": "^10.1.0",
|
||||
"@fastify/jwt": "^6.7.1",
|
||||
"@fastify/static": "^6.10.0",
|
||||
"@fastify/view": "^7.4.1",
|
||||
"@metamask/eth-sig-util": "^4.0.1",
|
||||
"@typegoose/auto-increment": "^0.4.1",
|
||||
@ -33,6 +34,7 @@
|
||||
"bson": "^4.0.4",
|
||||
"deepmerge": "^4.2.2",
|
||||
"dotenv": "^16.0.3",
|
||||
"ejs": "^3.1.9",
|
||||
"ethereumjs-util": "^7.1.5",
|
||||
"fast-rbac": "^1.3.0",
|
||||
"fast-xml-parser": "^4.1.3",
|
||||
|
37643
public/abis/BEMultiSigWallet.json
Normal file
37643
public/abis/BEMultiSigWallet.json
Normal file
File diff suppressed because one or more lines are too long
BIN
public/imgs/source.jpg
Executable file
BIN
public/imgs/source.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 166 KiB |
7
public/scripts/bootstrap.bundle.min.js
vendored
Normal file
7
public/scripts/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/scripts/jcchain.js
Normal file
2
public/scripts/jcchain.js
Normal file
File diff suppressed because one or more lines are too long
256
public/scripts/main.js
Normal file
256
public/scripts/main.js
Normal file
@ -0,0 +1,256 @@
|
||||
window.wallet = window.wallet = {}
|
||||
const DEFAULT_CHAIN_DATA = {
|
||||
name: 'Matic Testnet RPC',
|
||||
type: 'Testnet',
|
||||
rpc: 'https://rpc-mumbai.maticvigil.com',
|
||||
id: 80001,
|
||||
symbol: 'MATIC',
|
||||
explorerurl: 'https://mumbai.polygonscan.com/',
|
||||
}
|
||||
|
||||
const loader = document.getElementById('loader')
|
||||
|
||||
const WALLET_ADDRESS = ''
|
||||
function toHexChainId(chainId) {
|
||||
return '0x' + chainId.toString(16)
|
||||
}
|
||||
// 显示加载动画
|
||||
function showLoading() {
|
||||
// add style display: block
|
||||
loader.style.display = 'block'
|
||||
}
|
||||
// 隐藏加载动画
|
||||
function hideLoading() {
|
||||
// add style display: none
|
||||
loader.style.display = 'none'
|
||||
}
|
||||
// 加载json
|
||||
async function loadJson(url) {
|
||||
return fetch(url).then(response => response.json())
|
||||
}
|
||||
|
||||
async function initInstance(user, address, jsonUrl) {
|
||||
let json = await loadJson(jsonUrl)
|
||||
return new wallet.web3.eth.Contract(json.abi, address, { from: user })
|
||||
}
|
||||
|
||||
async function initWallet() {
|
||||
wallet.contract = await initInstance(wallet.account, wallet.walletAddress, '/public/abis/BEMultiSigWallet.json')
|
||||
}
|
||||
|
||||
async function confirmTask() {
|
||||
console.log('confirm task')
|
||||
let ids = wallet.scheduleList
|
||||
showLoading()
|
||||
try {
|
||||
let gas = await wallet.contract.methods.confirmTransactionBatch(ids).estimateGas()
|
||||
gas = gas | 0
|
||||
await wallet.contract.methods.confirmTransactionBatch(ids).send({ gas })
|
||||
} catch (err) {
|
||||
console.log('error confirm task', err)
|
||||
}
|
||||
hideLoading()
|
||||
}
|
||||
|
||||
async function rejectTask() {
|
||||
console.log('reject task')
|
||||
let ids = wallet.scheduleList
|
||||
showLoading()
|
||||
try {
|
||||
let gas = await wallet.contract.methods.revokeConfirmationBatch(ids).estimateGas()
|
||||
gas = gas | 0
|
||||
await wallet.contract.methods.revokeConfirmationBatch(ids).send({ gas })
|
||||
} catch (err) {
|
||||
console.log('error confirm task', err)
|
||||
}
|
||||
hideLoading()
|
||||
}
|
||||
|
||||
function makeBatchRequest(calls, callFrom) {
|
||||
let batch = new wallet.web3.BatchRequest()
|
||||
let promises = calls.map(call => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = call.request({ from: callFrom }, (error, data) => {
|
||||
if (error) {
|
||||
reject(error)
|
||||
} else {
|
||||
resolve(data)
|
||||
}
|
||||
})
|
||||
batch.add(request)
|
||||
})
|
||||
})
|
||||
|
||||
batch.execute()
|
||||
|
||||
return Promise.all(promises)
|
||||
}
|
||||
/**
|
||||
* 查询定时
|
||||
* @param {bytes32} id beginSchedule返回的id
|
||||
* @returns
|
||||
*/
|
||||
async function querySchedule(id) {
|
||||
let instance = wallet.contract
|
||||
return makeBatchRequest([
|
||||
instance.methods.isOperation(id).call,
|
||||
instance.methods.isOperationPending(id).call,
|
||||
instance.methods.isOperationReady(id).call,
|
||||
instance.methods.isOperationDone(id).call,
|
||||
instance.methods.isConfirmed(id).call,
|
||||
instance.methods.getTimestamp(id).call,
|
||||
])
|
||||
}
|
||||
|
||||
function initUIEvent() {
|
||||
var btnConfirm = document.getElementById('btn-confirm')
|
||||
btnConfirm.addEventListener('click', confirmTask, false)
|
||||
var btnReject = document.getElementById('btn-reject')
|
||||
btnReject.addEventListener('click', rejectTask, false)
|
||||
}
|
||||
|
||||
async function connectMetaMask() {
|
||||
let provider = null
|
||||
if (typeof window.ethereum !== 'undefined') {
|
||||
provider = window.ethereum
|
||||
try {
|
||||
await provider.request({ method: 'eth_requestAccounts' })
|
||||
} catch (error) {
|
||||
if (error.code === -32002) {
|
||||
throw new Error('MeatMask not login, Open MeatMask and login first')
|
||||
} else {
|
||||
throw new Error('User Rejected')
|
||||
}
|
||||
}
|
||||
} else if (window.web3) {
|
||||
provider = window.web3.currentProvider
|
||||
} else if (window.celo) {
|
||||
provider = window.celo
|
||||
} else {
|
||||
throw new Error('No Web3 Provider found')
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
async function confirmNeededChain() {
|
||||
const chainId = wallet.chainId
|
||||
if (chainId !== DEFAULT_CHAIN_DATA.id) {
|
||||
await switchEthereumChain()
|
||||
}
|
||||
}
|
||||
|
||||
async function switchEthereumChain() {
|
||||
let data = DEFAULT_CHAIN_DATA
|
||||
let hexChainId = toHexChainId(data.id)
|
||||
try {
|
||||
await wallet.provider.request({
|
||||
method: 'wallet_addEthereumChain',
|
||||
params: [
|
||||
{
|
||||
chainId: hexChainId,
|
||||
chainName: data.name,
|
||||
nativeCurrency: {
|
||||
name: data.symbol,
|
||||
symbol: data.symbol,
|
||||
decimals: data.decimals || 18,
|
||||
},
|
||||
blockExplorerUrls: [data.explorerurl],
|
||||
rpcUrls: [data.rpc],
|
||||
},
|
||||
],
|
||||
})
|
||||
console.log('add chain success')
|
||||
} catch (addError) {
|
||||
console.error('add chain error: ', addError)
|
||||
}
|
||||
// try {
|
||||
// await wallet.provider.request({
|
||||
// method: 'wallet_switchEthereumChain',
|
||||
// params: [{ chainId: hexChainId }],
|
||||
// })
|
||||
// console.log('switch chain success')
|
||||
// } catch (e) {
|
||||
// console.log('switch chain error: ', e)
|
||||
// if (e.code === 4902 || e.message.indexOf('Unrecognized chain ID') >= 0) {
|
||||
// try {
|
||||
// await wallet.provider.request({
|
||||
// method: 'wallet_addEthereumChain',
|
||||
// params: [
|
||||
// {
|
||||
// chainId: hexChainId,
|
||||
// chainName: data.name,
|
||||
// nativeCurrency: {
|
||||
// name: data.symbol,
|
||||
// symbol: data.symbol,
|
||||
// decimals: data.decimals || 18,
|
||||
// },
|
||||
// blockExplorerUrls: [data.explorerurl],
|
||||
// rpcUrls: [data.rpc],
|
||||
// },
|
||||
// ],
|
||||
// })
|
||||
// console.log('add chain success')
|
||||
// } catch (addError) {
|
||||
// console.error('add chain error: ', addError)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
function subscribeToEvents(provider) {
|
||||
provider.on('accountsChanged', async accounts => {
|
||||
if (accounts && accounts.length > 0) {
|
||||
if (wallet.account !== accounts[0]) {
|
||||
console.log('account change', wallet.account, accounts[0])
|
||||
wallet.account = accounts
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Subscribe to chainId change
|
||||
provider.on('chainChanged', async chainId => {
|
||||
const chainIdNum = parseInt(chainId)
|
||||
console.log('chainChanged', chainId, chainIdNum)
|
||||
if (wallet.chainId !== chainIdNum) {
|
||||
wallet.chainId = chainIdNum
|
||||
await confirmNeededChain()
|
||||
}
|
||||
})
|
||||
|
||||
// Subscribe to session disconnection
|
||||
provider.on('disconnect', err => {
|
||||
console.log('disconnect', err)
|
||||
})
|
||||
}
|
||||
|
||||
function loadTaskData() {
|
||||
wallet.walletAddress = document.getElementById('wallet-address').value
|
||||
let nodes = document.getElementsByClassName('scheduleId')
|
||||
wallet.scheduleList = []
|
||||
for (let node of nodes) {
|
||||
wallet.scheduleList.push(node.value)
|
||||
}
|
||||
console.log(wallet.scheduleList)
|
||||
}
|
||||
|
||||
;(async function () {
|
||||
loadTaskData()
|
||||
initUIEvent()
|
||||
|
||||
let self = window.wallet
|
||||
self.provider = await connectMetaMask()
|
||||
self.web3 = new Web3(self.provider)
|
||||
let accounts = await self.web3.eth.getAccounts()
|
||||
if (accounts.length > 0) {
|
||||
self.account = accounts[0]
|
||||
}
|
||||
self.chainId = await self.web3.eth.getChainId()
|
||||
console.log('chainId: ', self.chainId, 'account: ', self.account)
|
||||
await confirmNeededChain()
|
||||
subscribeToEvents(self.provider)
|
||||
await initWallet()
|
||||
setTimeout(async () => {
|
||||
let result = await querySchedule('0xa5c35368cd44dbe805a4595d6813ed3afefa1bf667209dc8d63f99cdec117f58')
|
||||
console.log(result)
|
||||
}, 5000)
|
||||
})()
|
3
public/scripts/web3.min.js
vendored
Normal file
3
public/scripts/web3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
112
public/styles/main.css
Normal file
112
public/styles/main.css
Normal file
@ -0,0 +1,112 @@
|
||||
body {
|
||||
background-color: rgb(249, 249, 249);
|
||||
}
|
||||
.action-bar {
|
||||
display: flex;
|
||||
width: 50%;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.loader {
|
||||
background: #00000035;
|
||||
background: radial-gradient(#222, #000);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.loader-inner {
|
||||
bottom: 0;
|
||||
height: 60px;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.loader-line-wrap {
|
||||
animation: spin 2000ms cubic-bezier(0.175, 0.885, 0.32, 1.275) infinite;
|
||||
box-sizing: border-box;
|
||||
height: 50px;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transform-origin: 50% 100%;
|
||||
width: 100px;
|
||||
}
|
||||
.loader-line {
|
||||
border: 4px solid transparent;
|
||||
border-radius: 100%;
|
||||
box-sizing: border-box;
|
||||
height: 100px;
|
||||
left: 0;
|
||||
margin: 0 auto;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 100px;
|
||||
}
|
||||
.loader-line-wrap:nth-child(1) {
|
||||
animation-delay: -50ms;
|
||||
}
|
||||
.loader-line-wrap:nth-child(2) {
|
||||
animation-delay: -100ms;
|
||||
}
|
||||
.loader-line-wrap:nth-child(3) {
|
||||
animation-delay: -150ms;
|
||||
}
|
||||
.loader-line-wrap:nth-child(4) {
|
||||
animation-delay: -200ms;
|
||||
}
|
||||
.loader-line-wrap:nth-child(5) {
|
||||
animation-delay: -250ms;
|
||||
}
|
||||
|
||||
.loader-line-wrap:nth-child(1) .loader-line {
|
||||
border-color: hsl(0, 80%, 60%);
|
||||
height: 90px;
|
||||
width: 90px;
|
||||
top: 7px;
|
||||
}
|
||||
.loader-line-wrap:nth-child(2) .loader-line {
|
||||
border-color: hsl(60, 80%, 60%);
|
||||
height: 76px;
|
||||
width: 76px;
|
||||
top: 14px;
|
||||
}
|
||||
.loader-line-wrap:nth-child(3) .loader-line {
|
||||
border-color: hsl(120, 80%, 60%);
|
||||
height: 62px;
|
||||
width: 62px;
|
||||
top: 21px;
|
||||
}
|
||||
.loader-line-wrap:nth-child(4) .loader-line {
|
||||
border-color: hsl(180, 80%, 60%);
|
||||
height: 48px;
|
||||
width: 48px;
|
||||
top: 28px;
|
||||
}
|
||||
.loader-line-wrap:nth-child(5) .loader-line {
|
||||
border-color: hsl(240, 80%, 60%);
|
||||
height: 34px;
|
||||
width: 34px;
|
||||
top: 35px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0%,
|
||||
15% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -8,6 +8,7 @@ import { RouterMap } from 'decorators/router'
|
||||
import { mongoose } from '@typegoose/typegoose'
|
||||
import logger from 'logger/logger'
|
||||
import BlocknumSchedule from 'schedule/blocknum.schedule'
|
||||
import path from 'path'
|
||||
|
||||
const zReqParserPlugin = require('plugins/zReqParser')
|
||||
|
||||
@ -33,6 +34,16 @@ export class ApiServer {
|
||||
this.server.register(zReqParserPlugin)
|
||||
this.server.register(helmet, { hidePoweredBy: false })
|
||||
this.server.register(zTokenParserPlugin)
|
||||
this.server.register(require('@fastify/view'), {
|
||||
engine: {
|
||||
ejs: require('ejs'),
|
||||
},
|
||||
})
|
||||
this.server.register(require('@fastify/static'), {
|
||||
root: path.join(__dirname, '../public'),
|
||||
prefix: '/public/', // optional: default '/'
|
||||
constraints: {}, // optional: default {}
|
||||
})
|
||||
|
||||
this.server.register(apiAuthPlugin, {
|
||||
secret: process.env.API_TOKEN_SECRET,
|
||||
|
@ -2,6 +2,7 @@ import { Contract } from 'web3-eth-contract'
|
||||
import Web3 from 'web3'
|
||||
import { Account } from 'web3-core'
|
||||
import { ZERO_BYTES32 } from 'common/Constants'
|
||||
import { generateRandomBytes32 } from 'utils/wallet.util'
|
||||
const abi = require('abis/BEMultiSigWallet.json').abi
|
||||
|
||||
/**
|
||||
@ -126,4 +127,20 @@ export class WalletReactor {
|
||||
batch.execute()
|
||||
return Promise.all(promises)
|
||||
}
|
||||
|
||||
async updateRequired(num: number) {
|
||||
let contractAddress = [process.env.CHAIN_WALLET_ADDRESS]
|
||||
let values = ['0']
|
||||
let abi = await this.contract.methods.changeRequirement(num + '').encodeABI()
|
||||
let salt = generateRandomBytes32()
|
||||
let operation: any = this.genOperation({
|
||||
targets: contractAddress,
|
||||
values,
|
||||
datas: [abi],
|
||||
predecessor: ZERO_BYTES32,
|
||||
salt,
|
||||
})
|
||||
operation = await this.beginSchedule(operation, 60)
|
||||
return operation
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import fastify = require('fastify')
|
||||
|
||||
export const ROLE_ANON = 'anon'
|
||||
class BaseController {
|
||||
aotoRoute(req: fastify.FastifyRequest, res) {}
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
import BaseController from 'common/base.controller'
|
||||
import BaseController, { ROLE_ANON } from 'common/base.controller'
|
||||
import { ZError } from 'common/ZError'
|
||||
import { role, router } from 'decorators/router'
|
||||
import { getSignature, decrypt } from '@wecom/crypto'
|
||||
import { excelToJson } from 'utils/excel.util'
|
||||
import { XMLParser } from 'fast-xml-parser'
|
||||
import { TaskQueue } from 'queue/task.queue'
|
||||
import { TaskStatus, WechatWorkService } from 'service/wechatwork.service'
|
||||
import { TaskStatus } from 'service/wechatwork.service'
|
||||
import { RequestTask } from 'models/RequestTask'
|
||||
import { BlockChain } from 'chain/BlockChain'
|
||||
import { ChainTask } from 'models/ChainTask'
|
||||
|
||||
class WorkFlowController extends BaseController {
|
||||
@role('anon')
|
||||
@role(ROLE_ANON)
|
||||
@router('get /workflow/notify')
|
||||
async wxNotifyCheck(req, res) {
|
||||
const token = process.env.WX_TOKEN
|
||||
@ -24,7 +24,7 @@ class WorkFlowController extends BaseController {
|
||||
res.send(message)
|
||||
}
|
||||
|
||||
@role('anon')
|
||||
@role(ROLE_ANON)
|
||||
@router('post /workflow/notify')
|
||||
async flowNotify(req, res) {
|
||||
let { msg_signature, timestamp, nonce, xml } = req.params
|
||||
@ -47,7 +47,48 @@ class WorkFlowController extends BaseController {
|
||||
res.send('success')
|
||||
}
|
||||
|
||||
@role('anon')
|
||||
@role(ROLE_ANON)
|
||||
@router('get /workflow/confirm/:id')
|
||||
async confirmPage(req, res) {
|
||||
const { id } = req.params
|
||||
if (!id) {
|
||||
return res.view('/templates/confirm_err_page.ejs', { msg: '参数错误' })
|
||||
}
|
||||
const chainTask = await ChainTask.findById(id)
|
||||
if (!chainTask) {
|
||||
return res.view('/templates/confirm_err_page.ejs', { msg: '任务未找到' })
|
||||
}
|
||||
let requestTasks = await RequestTask.find({ chainTaskId: id })
|
||||
if (requestTasks.length === 0) {
|
||||
return res.view('/templates/confirm_err_page.ejs', { msg: '链请求任务未找到' })
|
||||
}
|
||||
let address = process.env.CHAIN_WALLET_ADDRESS
|
||||
return res.view('/templates/confirm_page.ejs', { id: id, subtasks: requestTasks, mainTask: chainTask, address })
|
||||
}
|
||||
|
||||
@role(ROLE_ANON)
|
||||
@router('get /workflow/update_required')
|
||||
async updateRequired(req, res) {
|
||||
let result = await new BlockChain().walletReactor.updateRequired(1)
|
||||
return result
|
||||
}
|
||||
|
||||
@role(ROLE_ANON)
|
||||
@router('post /workflow/update_required')
|
||||
async execUpdateRequired(req, res) {
|
||||
let data = {
|
||||
scheduleId: '0xa5c35368cd44dbe805a4595d6813ed3afefa1bf667209dc8d63f99cdec117f58',
|
||||
targets: ['0xc195196351566d2c4e13563C4492fB0BdB7894Fb'],
|
||||
values: ['0'],
|
||||
datas: ['0xba51a6df0000000000000000000000000000000000000000000000000000000000000001'],
|
||||
predecessor: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
||||
salt: '0x39383830353131363736333036',
|
||||
}
|
||||
let result = await new BlockChain().walletReactor.executeSchedule(data)
|
||||
return result
|
||||
}
|
||||
|
||||
@role(ROLE_ANON)
|
||||
@router('get /workflow/test')
|
||||
async test(req, res) {
|
||||
// let file_path = '/Users/zhl/Documents/workspace/tools/excel2json/test.xlsx'
|
||||
|
@ -11,8 +11,9 @@ export enum TaskStatus {
|
||||
NOTSTART = 0,
|
||||
PEDING = 1,
|
||||
SUCCESS = 2,
|
||||
PART_ERROR = 8,
|
||||
ERROR = 9,
|
||||
TX_ALL_CONFIRM = 3,
|
||||
TX_PART_ERROR = 8,
|
||||
TX_ERROR = 9,
|
||||
}
|
||||
|
||||
@dbconn()
|
||||
@ -80,17 +81,17 @@ export class ChainTaskClass extends BaseModule {
|
||||
record.successCount = sCount
|
||||
record.errorCount = errCount
|
||||
if (sCount === record.tasks.length) {
|
||||
record.status = TaskStatus.SUCCESS
|
||||
record.status = TaskStatus.TX_ALL_CONFIRM
|
||||
record.allEnd = true
|
||||
} else {
|
||||
record.allEnd = false
|
||||
if (record.status === TaskStatus.NOTSTART && sCount > 0) {
|
||||
record.status = TaskStatus.PEDING
|
||||
} else if (errCount === record.tasks.length) {
|
||||
record.status = TaskStatus.ERROR
|
||||
record.status = TaskStatus.TX_ERROR
|
||||
record.allEnd = true
|
||||
} else if (errCount + sCount === record.tasks.length) {
|
||||
record.status = TaskStatus.PART_ERROR
|
||||
record.status = TaskStatus.TX_PART_ERROR
|
||||
record.allEnd = true
|
||||
}
|
||||
}
|
||||
|
2
templates/confirm_err_page.ejs
Normal file
2
templates/confirm_err_page.ejs
Normal file
@ -0,0 +1,2 @@
|
||||
<h1>Error</h1>
|
||||
<p><%= msg %></p>
|
105
templates/confirm_page.ejs
Normal file
105
templates/confirm_page.ejs
Normal file
@ -0,0 +1,105 @@
|
||||
|
||||
<!DOCTYPE HTML>
|
||||
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<title>任务详情</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src * self https://cdn.jsdelivr.net/; style-src * self 'unsafe-inline'; script-src * self https://cdn.jsdelivr.net/ 'unsafe-eval'; img-src * self 'unsafe-inline' data: w3.org/svg/2000; connect-src self * 'unsafe-inline';">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Cache-control" content="no-cache">
|
||||
<meta http-equiv="Cache" content="no-cache">
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no, minimal-ui" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="full-screen" content="true" />
|
||||
<meta name="screen-orientation" content="portrait" />
|
||||
<meta name="x5-fullscreen" content="true" />
|
||||
<meta name="360-fullscreen" content="true" />
|
||||
<meta name="apple-mobile-web-app-title" content="WJTX">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||
<link href="/public/styles/main.css" rel="stylesheet" >
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>确认任务 <i><%= mainTask.name %></i></h1>
|
||||
<h3><%= mainTask.desc %></h3>
|
||||
<input type="hidden" id="wallet-address" value="<%= address %>">
|
||||
<div class="action-bar">
|
||||
<button class="btn btn-primary" id="btn-confirm">通过</button>
|
||||
<button class="btn btn-danger" id="btn-reject">拒绝</button>
|
||||
</div>
|
||||
<p>本次申请包含<b><%= subtasks.length %></b>个链操作任务</p>
|
||||
<%for(var i=0;i<subtasks.length;i++){%>
|
||||
<input type="hidden" class="scheduleId" value="<%=subtasks[i].scheduleId %>">
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header" id="heading<%=subtasks[i].index %>">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse<%=subtasks[i].index %>" aria-expanded="true" aria-controls="collapse<%=subtasks[i].index %>">
|
||||
任务<%=subtasks[i].index %>: <%=subtasks[i].reqDatas.length %>个子任务
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse<%=subtasks[i].index %>" class="accordion-collapse collapse show" aria-labelledby="heading<%=subtasks[i].index %>" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<%for(var j=0;j<subtasks[i].reqDatas.length;j++){%>
|
||||
<% switch (subtasks[i].reqDatas[j].type) {
|
||||
case '1' : %>
|
||||
<p>Mint Ft</p>
|
||||
<% break;
|
||||
|
||||
case '2' : %>
|
||||
<p>Mint NFT</p>
|
||||
<% break;
|
||||
|
||||
case '3' : %>
|
||||
<p>FT 转账</p>
|
||||
<% break;
|
||||
case '2' : %>
|
||||
<p>NFT 转账</p>
|
||||
<% break;
|
||||
} %>
|
||||
<p>合约地址: <%=subtasks[i].reqDatas[j].address%></p>
|
||||
<p>目标钱包: <%=subtasks[i].reqDatas[j].to%></p>
|
||||
<%if (subtasks[i].reqDatas[j].amount) {%> <p>数量: <%=subtasks[i].reqDatas[j].amount %></p> <%}%>
|
||||
<%if (subtasks[i].reqDatas[j].tokenId) {%> <p>数量: <%=subtasks[i].reqDatas[j].tokenId %></p><%}%>
|
||||
<%}%>
|
||||
<%}%>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="loader" id="loader">
|
||||
<div class="loader-inner">
|
||||
<div class="loader-line-wrap">
|
||||
<div class="loader-line"></div>
|
||||
</div>
|
||||
<div class="loader-line-wrap">
|
||||
<div class="loader-line"></div>
|
||||
</div>
|
||||
<div class="loader-line-wrap">
|
||||
<div class="loader-line"></div>
|
||||
</div>
|
||||
<div class="loader-line-wrap">
|
||||
<div class="loader-line"></div>
|
||||
</div>
|
||||
<div class="loader-line-wrap">
|
||||
<div class="loader-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/public/scripts/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="/public/scripts/web3.min.js"></script>
|
||||
<script src="/public/scripts/main.js" async type = "module"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
106
yarn.lock
106
yarn.lock
@ -245,6 +245,11 @@
|
||||
"@ethersproject/properties" "^5.7.0"
|
||||
"@ethersproject/strings" "^5.7.0"
|
||||
|
||||
"@fastify/accept-negotiator@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz#c1c66b3b771c09742a54dd5bc87c582f6b0630ff"
|
||||
integrity sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==
|
||||
|
||||
"@fastify/ajv-compiler@^3.5.0":
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz#459bff00fefbf86c96ec30e62e933d2379e46670"
|
||||
@ -306,6 +311,30 @@
|
||||
fastify-plugin "^4.0.0"
|
||||
steed "^1.1.3"
|
||||
|
||||
"@fastify/send@^2.0.0":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/send/-/send-2.0.1.tgz#db10d1401883b4aef41669fcf2ddb4e1bb4630df"
|
||||
integrity sha512-8jdouu0o5d0FMq1+zCKeKXc1tmOQ5tTGYdQP3MpyF9+WWrZT1KCBdh6hvoEYxOm3oJG/akdE9BpehLiJgYRvGw==
|
||||
dependencies:
|
||||
"@lukeed/ms" "^2.0.1"
|
||||
escape-html "~1.0.3"
|
||||
fast-decode-uri-component "^1.0.1"
|
||||
http-errors "2.0.0"
|
||||
mime "^3.0.0"
|
||||
|
||||
"@fastify/static@^6.10.0":
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/static/-/static-6.10.0.tgz#cdb6a5ddcc3ea8691c79aad0c846bc986a4bc721"
|
||||
integrity sha512-TGruNm6ZabkQz2oRNoarPnY2BvS9i9DNf8Nn1aDcZp+WjOQRPCq0Wy2ko78yGB5JHytdCWoHpprc128QtLl8hw==
|
||||
dependencies:
|
||||
"@fastify/accept-negotiator" "^1.0.0"
|
||||
"@fastify/send" "^2.0.0"
|
||||
content-disposition "^0.5.3"
|
||||
fastify-plugin "^4.0.0"
|
||||
glob "^8.0.1"
|
||||
p-limit "^3.1.0"
|
||||
readable-stream "^4.0.0"
|
||||
|
||||
"@fastify/view@^7.4.1":
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@fastify/view/-/view-7.4.1.tgz#265daba48386a5d3f69dfc446af468d72e0a8757"
|
||||
@ -346,7 +375,7 @@
|
||||
"@jridgewell/resolve-uri" "^3.0.3"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||
|
||||
"@lukeed/ms@^2.0.0":
|
||||
"@lukeed/ms@^2.0.0", "@lukeed/ms@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@lukeed/ms/-/ms-2.0.1.tgz#3c2bbc258affd9cc0e0cc7828477383c73afa6ee"
|
||||
integrity sha512-Xs/4RZltsAL7pkvaNStUQt7netTkyxrS0K+RILcVr3TRMS/ToOg4I6uNfhB9SlGsnWBym4U+EaXq0f0cEMNkHA==
|
||||
@ -827,6 +856,11 @@ async-limiter@~1.0.0:
|
||||
resolved "https://registry.npmmirror.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
||||
|
||||
async@^3.2.3:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
|
||||
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
|
||||
|
||||
asynckit@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||
@ -966,6 +1000,13 @@ brace-expansion@^1.1.7:
|
||||
balanced-match "^1.0.0"
|
||||
concat-map "0.0.1"
|
||||
|
||||
brace-expansion@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
|
||||
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
|
||||
dependencies:
|
||||
balanced-match "^1.0.0"
|
||||
|
||||
braces@^3.0.2, braces@~3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
|
||||
@ -1173,7 +1214,7 @@ chalk@^2.0.0, chalk@^2.3.0:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^4.0.0:
|
||||
chalk@^4.0.0, chalk@^4.0.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
@ -1283,7 +1324,7 @@ concat-map@0.0.1:
|
||||
resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
|
||||
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
|
||||
|
||||
content-disposition@0.5.4:
|
||||
content-disposition@0.5.4, content-disposition@^0.5.3:
|
||||
version "0.5.4"
|
||||
resolved "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
|
||||
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
|
||||
@ -1590,6 +1631,13 @@ ee-first@1.1.1:
|
||||
resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
|
||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||
|
||||
ejs@^3.1.9:
|
||||
version "3.1.9"
|
||||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
|
||||
integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
|
||||
dependencies:
|
||||
jake "^10.8.5"
|
||||
|
||||
elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.npmmirror.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
||||
@ -2240,6 +2288,13 @@ file-entry-cache@^6.0.1:
|
||||
dependencies:
|
||||
flat-cache "^3.0.4"
|
||||
|
||||
filelist@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
|
||||
integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
|
||||
dependencies:
|
||||
minimatch "^5.0.1"
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
@ -2434,6 +2489,17 @@ glob@^7.1.1, glob@^7.1.3:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^8.0.1:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
|
||||
integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^5.0.1"
|
||||
once "^1.3.0"
|
||||
|
||||
global@~4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.npmmirror.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
|
||||
@ -2882,6 +2948,16 @@ isstream@~0.1.2:
|
||||
resolved "https://registry.npmmirror.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
|
||||
|
||||
jake@^10.8.5:
|
||||
version "10.8.5"
|
||||
resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
|
||||
integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==
|
||||
dependencies:
|
||||
async "^3.2.3"
|
||||
chalk "^4.0.2"
|
||||
filelist "^1.0.1"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
js-sha3@0.8.0, js-sha3@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmmirror.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
@ -3130,6 +3206,11 @@ mime@1.6.0:
|
||||
resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
|
||||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
mime@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7"
|
||||
integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
|
||||
|
||||
mimic-response@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
@ -3164,6 +3245,13 @@ minimatch@^3.0.4, minimatch@^3.1.1:
|
||||
dependencies:
|
||||
brace-expansion "^1.1.7"
|
||||
|
||||
minimatch@^5.0.1:
|
||||
version "5.1.6"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
|
||||
integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimist@^1.2.0, minimist@^1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
||||
@ -3480,6 +3568,13 @@ p-cancelable@^3.0.0:
|
||||
resolved "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050"
|
||||
integrity sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==
|
||||
|
||||
p-limit@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
|
||||
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
|
||||
dependencies:
|
||||
yocto-queue "^0.1.0"
|
||||
|
||||
parent-module@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||
@ -5097,3 +5192,8 @@ yn@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmmirror.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
Loading…
x
Reference in New Issue
Block a user