Initial commit
This commit is contained in:
commit
252aa995cd
33490
assets/abis/BEMultiSigWallet.json
Normal file
33490
assets/abis/BEMultiSigWallet.json
Normal file
File diff suppressed because one or more lines are too long
7
assets/scripts/bootstrap.bundle.min.js
vendored
Normal file
7
assets/scripts/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
166
assets/scripts/confirm.js
Normal file
166
assets/scripts/confirm.js
Normal file
@ -0,0 +1,166 @@
|
||||
const { createApp } = Vue;
|
||||
const API_BASE = "http://localhost:3002";
|
||||
const TASK_INFO_URL = "/workflow/task";
|
||||
|
||||
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");
|
||||
// 显示加载动画
|
||||
function showLoading() {
|
||||
// add style display: block
|
||||
loader.style.display = "block";
|
||||
}
|
||||
// 隐藏加载动画
|
||||
function hideLoading() {
|
||||
// add style display: none
|
||||
loader.style.display = "none";
|
||||
}
|
||||
async function loadJson(url) {
|
||||
return fetch(url).then((response) => response.json());
|
||||
}
|
||||
|
||||
function toHexChainId(chainId) {
|
||||
return "0x" + chainId.toString(16);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const fetchTaskInfo = function (taskId) {
|
||||
const url = `${API_BASE}${TASK_INFO_URL}/${taskId}`;
|
||||
return fetch(url)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch task info");
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.errcode) {
|
||||
throw new Error(data.errmsg);
|
||||
}
|
||||
return data.data;
|
||||
});
|
||||
};
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
id: "",
|
||||
task: {},
|
||||
subTasks: [],
|
||||
address: "",
|
||||
types: {},
|
||||
walletConnect: false,
|
||||
contract: null,
|
||||
wallet: null,
|
||||
disabled: false,
|
||||
chainId: 0,
|
||||
};
|
||||
},
|
||||
async created() {
|
||||
let params = getAllParameter();
|
||||
this.id = params.id;
|
||||
console.log("taskid: ", this.id);
|
||||
let result = await fetchTaskInfo(this.id);
|
||||
this.task = result.chainTask;
|
||||
this.subTasks = result.requestTasks;
|
||||
this.address = result.address;
|
||||
this.types = result.types;
|
||||
console.log(result);
|
||||
await this.initWallet();
|
||||
},
|
||||
methods: {
|
||||
showType(type) {
|
||||
return this.types[type];
|
||||
},
|
||||
async initInstance(user, address, jsonUrl) {
|
||||
let json = await loadJson(jsonUrl);
|
||||
return new this.web3.eth.Contract(json.abi, address, { from: user });
|
||||
},
|
||||
async initWallet() {
|
||||
let provider = await connectMetaMask();
|
||||
this.web3 = new Web3(provider);
|
||||
let accounts = await this.web3.eth.getAccounts();
|
||||
if (accounts.length > 0) {
|
||||
this.account = accounts[0];
|
||||
}
|
||||
this.contract = await this.initInstance(
|
||||
this.account,
|
||||
this.address,
|
||||
"assets/abis/BEMultiSigWallet.json"
|
||||
);
|
||||
this.chainId = await this.web3.eth.getChainId();
|
||||
this.walletConnect = true;
|
||||
console.log(this.chainId, this.account);
|
||||
console.log(this.contract);
|
||||
},
|
||||
async confirmTask() {
|
||||
let ids = this.subTasks.map((o) => o.scheduleId);
|
||||
console.log("confirm task", ids);
|
||||
showLoading();
|
||||
try {
|
||||
let gas = await this.contract.methods
|
||||
.confirmTransaction(ids)
|
||||
.estimateGas();
|
||||
gas = gas | 0;
|
||||
await this.contract.methods.confirmTransaction(ids).send({ gas });
|
||||
} catch (err) {
|
||||
console.log("error confirm task", err);
|
||||
}
|
||||
hideLoading();
|
||||
},
|
||||
async rejectTask() {
|
||||
console.log("reject task");
|
||||
let ids = this.subTasks.map((o) => o.scheduleId);
|
||||
showLoading();
|
||||
try {
|
||||
let gas = await this.contract.methods
|
||||
.revokeConfirmation(ids)
|
||||
.estimateGas();
|
||||
gas = gas | 0;
|
||||
await this.contract.methods.revokeConfirmation(ids).send({ gas });
|
||||
} catch (err) {
|
||||
console.log("error confirm task", err);
|
||||
}
|
||||
hideLoading();
|
||||
},
|
||||
async 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,
|
||||
]);
|
||||
},
|
||||
},
|
||||
}).mount("#app");
|
2
assets/scripts/jcchain.js
Normal file
2
assets/scripts/jcchain.js
Normal file
File diff suppressed because one or more lines are too long
20
assets/scripts/lib.js
Normal file
20
assets/scripts/lib.js
Normal file
@ -0,0 +1,20 @@
|
||||
function getParameter(t) {
|
||||
var e = window.location.search,
|
||||
i = new RegExp(t + "=([^&?]*)", "ig");
|
||||
return e.match(i) ? e.match(i)[0].substr(t.length + 1) : null
|
||||
}
|
||||
|
||||
function getAllParameter() {
|
||||
var pairs = window.location.search.substring(1).split("&");
|
||||
var obj = {};
|
||||
for (let i in pairs ) {
|
||||
if ( pairs[i] === "" ) continue;
|
||||
let pair = pairs[i].split("=");
|
||||
obj[ decodeURIComponent( pair[0] ) ] = decodeURIComponent( pair[1] );
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
function isMobile() {
|
||||
return /(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent);
|
||||
}
|
12
assets/scripts/redirect.js
Normal file
12
assets/scripts/redirect.js
Normal file
@ -0,0 +1,12 @@
|
||||
(function() {
|
||||
var params = getAllParameter()
|
||||
console.log(params)
|
||||
console.log(location)
|
||||
var next = `confirm.html?id=${params.id}`
|
||||
if (isMobile()) {
|
||||
var url = `https://metamask.app.link/dapp/${location.host}/ct/${next}`
|
||||
document.getElementById('href').setAttribute('href', url)
|
||||
} else {
|
||||
// location.href = url
|
||||
}
|
||||
})()
|
10
assets/scripts/vconsole.min.js
vendored
Normal file
10
assets/scripts/vconsole.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
16159
assets/scripts/vue.global.js
Normal file
16159
assets/scripts/vue.global.js
Normal file
File diff suppressed because it is too large
Load Diff
3
assets/scripts/web3.min.js
vendored
Normal file
3
assets/scripts/web3.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
105
confirm.html
Normal file
105
confirm.html
Normal file
@ -0,0 +1,105 @@
|
||||
<!DOCTYPE HTML>
|
||||
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<title>crypto test</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
|
||||
<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">
|
||||
<style>
|
||||
.small-address{
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app" class="container">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
{{task.name}} - <small>{{task.starterName}}</small>
|
||||
</h1>
|
||||
<p>{{task.desc}}</p>
|
||||
<p>本次申请包含<b>{{subTasks.length}}</b>个链操作任务</p>
|
||||
</div>
|
||||
<div class="hstack gap-3 mb-1">
|
||||
<template v-if="!walletConnect">
|
||||
<button type="button" class="btn btn-info" @click="initWallet">连接钱包</button>
|
||||
<div class="vr"></div>
|
||||
</template>
|
||||
<button type="button" class="btn btn-primary" :disabled="disabled" @click="confirmTask">确认</button>
|
||||
<div class="vr"></div>
|
||||
<button type="button" class="btn btn-danger" :disabled="disabled">拒绝</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="accordion" id="accordionExample">
|
||||
<div class="accordion-item" v-for="(item, i) in subTasks" >
|
||||
<h2 class="accordion-header" id="heading{{i}}">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{i}}" aria-expanded="true" aria-controls="collapse{{i}}">
|
||||
任务{{item.index}}: {{item.reqDatas.length}}个子任务
|
||||
</button>
|
||||
</h2>
|
||||
<div id="collapse{{i}}" class="accordion-collapse collapse show" aria-labelledby="heading{{i}}" data-bs-parent="#accordionExample">
|
||||
<div class="accordion-body">
|
||||
<div v-for="sub in item.reqDatas" class="border-bottom">
|
||||
<p><label class="text-secondary">类型:</label> {{showType(sub.type)}}</p>
|
||||
<p class="small-address"><label class="text-secondary">合约地址: </label>{{sub.address}}</p>
|
||||
<p class="small-address"><label class="text-secondary">目标钱包: </label>{{sub.to}}</p>
|
||||
<p v-if="sub.amount"><label class="text-secondary">数量: </label>{{sub.amount}}</p>
|
||||
<p v-if="sub.tokenId"><label class="text-secondary">NFT ID: </label>{{sub.tokenId}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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="assets/scripts/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||
<script src="assets/scripts/vconsole.min.js"></script>
|
||||
<script src="assets/scripts/web3.min.js"></script>
|
||||
<script src="assets/scripts/lib.js"></script>
|
||||
<script src="assets/scripts/vue.global.js"></script>
|
||||
<script src="assets/scripts/confirm.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
60
redirect.html
Normal file
60
redirect.html
Normal file
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE HTML>
|
||||
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<head>
|
||||
<title>crypto test</title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="renderer" content="webkit">
|
||||
|
||||
<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">
|
||||
<style>
|
||||
body {
|
||||
|
||||
}
|
||||
.main-btn {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 8rem;
|
||||
}
|
||||
.main-btn a {
|
||||
text-decoration: none;
|
||||
padding: 0.5rem 1rem;
|
||||
margin: 0.25rem 0.125rem;
|
||||
cursor: pointer;
|
||||
background-color: #198754;
|
||||
vertical-align: middle;
|
||||
border: 1px solid #198754;
|
||||
border-radius: 5px;
|
||||
transition: color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
font-size: 1.0rem;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="main-btn"><a href="" id="href">点击跳转至操作页面</a></div>
|
||||
</div>
|
||||
<script src="assets/scripts/lib.js"></script>
|
||||
<script src="assets/scripts/redirect.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user