Initial interface draft skeleton
This commit is contained in:
parent
07b680ed59
commit
f36f560edd
110
js/index.html
Normal file
110
js/index.html
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>gas3</title>
|
||||||
|
<script defer src="node_modules/alpinejs/dist/cdn.min.js"></script>
|
||||||
|
<script src="node_modules/openpgp/dist/openpgp.min.js"></script>
|
||||||
|
<script src="node_modules/MimeJS/src/base64.js"></script>
|
||||||
|
<script src="node_modules/MimeJS/dist/mime-js.min.js"></script>
|
||||||
|
<script src="run.js"></script>
|
||||||
|
<script src="booteth/wallet.js"></script>
|
||||||
|
<script src="booteth/session.js"></script>
|
||||||
|
<script src="booteth/eip1193.js"></script>
|
||||||
|
<script src="registry.js"></script>
|
||||||
|
<script src="rpc.js"></script>
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
|
<script>
|
||||||
|
wallet_fallback = sessionwallet_create;
|
||||||
|
wallet_detect(ethers);
|
||||||
|
</script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="style.css"></link>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body x-data='{
|
||||||
|
needKey: false,
|
||||||
|
error: "",
|
||||||
|
lock: true,
|
||||||
|
menuSelected: "main",
|
||||||
|
}
|
||||||
|
' >
|
||||||
|
|
||||||
|
<ul id="menu"
|
||||||
|
x-data='{
|
||||||
|
keyTitle: "unlock key",
|
||||||
|
}'>
|
||||||
|
<li><a id="menuKey" href="javascript:void(null);" x-text='keyTitle' @click='menuSelected="main"' ></a>
|
||||||
|
<li><a href="javascript:void(null);" id="menuStatus" @click='menuSelected="state"'>status</a>
|
||||||
|
<div id="main"
|
||||||
|
x-show='needKey && menuSelected == "main"'
|
||||||
|
@messagestatechange.window='
|
||||||
|
if (checkState(STATE.LOCAL_KEY_PROBE, $event.detail.state)) {
|
||||||
|
needKey = true;
|
||||||
|
}
|
||||||
|
if (checkState(STATE.LOCAL_KEY_REQUEST | STATE.LOCAL_KEY_UNLOCK, $event.detail.state)) {
|
||||||
|
error = undefined;
|
||||||
|
}
|
||||||
|
if (checkState(STATE.LOCAL_KEY_REJECT, $event.detail.state)) {
|
||||||
|
error = $event.detail.s;
|
||||||
|
}
|
||||||
|
if (checkState(STATE.LOCAL_KEY_UNLOCK, $event.detail.state)) {
|
||||||
|
needKey = false;
|
||||||
|
lock = false;
|
||||||
|
}
|
||||||
|
'
|
||||||
|
>
|
||||||
|
<form onSubmit='return false;'>
|
||||||
|
<input type="password" name="passphrase" id="passphrase" /><br/>
|
||||||
|
<button id="passphraseUnlock"
|
||||||
|
x-data='{
|
||||||
|
keyTitle: "enter new pin",
|
||||||
|
}
|
||||||
|
'
|
||||||
|
x-text='keyTitle'
|
||||||
|
@messagestatechange.window='
|
||||||
|
if (checkState(STATE.LOCAL_KEY, $event.detail.state)) {
|
||||||
|
keyTitle = "unlock key with pin";
|
||||||
|
}
|
||||||
|
'
|
||||||
|
x-on:click='stateChange("unlock key request", STATE.LOCAL_KEY_REQUEST);'
|
||||||
|
></button>
|
||||||
|
<form>
|
||||||
|
<br/><span id="keyError" class="error"
|
||||||
|
x-text='error'
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="state"
|
||||||
|
x-show='menuSelected == "state"'
|
||||||
|
x-data='{
|
||||||
|
stateKey: "locked",
|
||||||
|
stateEvm: "disconnected",
|
||||||
|
stateBalance: "0",
|
||||||
|
stateAddress: "unknown",
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
|
@messagestatechange.window='
|
||||||
|
if (checkState(STATE.LOCAL_KEY_UNLOCK, $event.detail.state)) {
|
||||||
|
stateKey = "unlocked";
|
||||||
|
}
|
||||||
|
if (checkState(STATE.RPC_CONNECT, $event.detail.state)) {
|
||||||
|
stateEvm = "connected";
|
||||||
|
}
|
||||||
|
console.debug("state", $event.detail.s);
|
||||||
|
if (checkState(STATE.RPC_PING, $event.detail.state)) {
|
||||||
|
stateBalance = $event.detail.s["balance"];
|
||||||
|
stateAddress = $event.detail.s["address"];
|
||||||
|
stateChange("reset ping", 0, STATE.RPC_PING);
|
||||||
|
}
|
||||||
|
'
|
||||||
|
>
|
||||||
|
<dl>
|
||||||
|
<dt>Key</dt>
|
||||||
|
<dd x-text='stateKey'>
|
||||||
|
<dt>Address</dt>
|
||||||
|
<dd x-text='stateAddress'>
|
||||||
|
<dt>Balance</dt>
|
||||||
|
<dd x-text='stateBalance'>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
67
js/main.js
Normal file
67
js/main.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import { ethers } from "./node_modules/ethers/dist/ethers.min.js";
|
||||||
|
register_ethers(ethers);
|
||||||
|
|
||||||
|
var connect = connect_manual_session;
|
||||||
|
|
||||||
|
async function checkKey() {
|
||||||
|
const r = localStorage.getItem(KEY_STORAGEKEY);
|
||||||
|
if (r) {
|
||||||
|
stateChange('found existing key', STATE.LOCAL_KEY, STATE.LOCAL_KEY_PROBE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// router
|
||||||
|
var initRouter = async (e) => {
|
||||||
|
if (checkState(STATE.LOCAL_KEY_REQUEST, e.detail.state)) {
|
||||||
|
stateChange('process key unlock request', 0, STATE.LOCAL_KEY_REQUEST | STATE.LOCAL_KEY_PROBE | STATE.LOCAL_KEY_REJECT);
|
||||||
|
checkKey();
|
||||||
|
const v = document.getElementById('passphrase').value;
|
||||||
|
const r = await connect(v);
|
||||||
|
if (r) {
|
||||||
|
stateChange('key request finished', STATE.LOCAL_KEY_UNLOCK, );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async function connect_manual_session(pw) {
|
||||||
|
let provider = undefined;
|
||||||
|
let wallet = undefined;
|
||||||
|
try {
|
||||||
|
provider = await provider_connect('default');
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
if (!provider) {
|
||||||
|
stateChange('no provider', STATE.RPC_GONE, STATE.LOCAL_KEY_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
wallet = await wallet_connect('default', SESSION_ID, pw);
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
if (!wallet) {
|
||||||
|
stateChange('wallet unlock fail', STATE.LOCAL_KEY_REJECT, STATE.LOCAL_KEY_REQUEST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
checkRpc();
|
||||||
|
scanRegistry(g_registry);
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkRpc() {
|
||||||
|
pingRpc();
|
||||||
|
setTimeout(checkRpc, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', () => {
|
||||||
|
window.addEventListener('messagestatechange', initRouter);
|
||||||
|
if (DEBUG) {
|
||||||
|
window.addEventListener('messagestatechange', (e) => {
|
||||||
|
console.debug('state change:', e.detail.s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
stateChange('init key', STATE.LOCAL_KEY_PROBE);
|
||||||
|
});
|
||||||
|
|
13
js/package.json
Normal file
13
js/package.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "gas3",
|
||||||
|
"version": "0.0.1-alpha.1",
|
||||||
|
"dependencies": {
|
||||||
|
"alpinejs": "3.10.3",
|
||||||
|
"openpgp": "5.5.0",
|
||||||
|
"jssha": "3.2.0",
|
||||||
|
"MimeJS": "git://git.defalsify.org/mime-js.git#lash/plain-part",
|
||||||
|
"jsqr": "^1.4.0",
|
||||||
|
"qrcode": "1.5.3",
|
||||||
|
"ethers": "6.5.1"
|
||||||
|
}
|
||||||
|
}
|
23
js/registry.js
Normal file
23
js/registry.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
const registryInterface = [{"inputs":[{"internalType":"bytes32[]","name":"_identifiers","type":"bytes32[]"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"_key","type":"bytes32"},{"indexed":false,"internalType":"address","name":"_address","type":"address"}],"name":"AddressKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"_newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"bytes32","name":"_identifier","type":"bytes32"}],"name":"addressOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"identifier","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"identifierCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_identifier","type":"bytes32"},{"internalType":"address","name":"_address","type":"address"}],"name":"set","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}];
|
||||||
|
|
||||||
|
async function scanRegistry(registry) {
|
||||||
|
const o = new ethers.Contract(registry, registryInterface, current_provider);
|
||||||
|
let i = 0;
|
||||||
|
let r = {};
|
||||||
|
while (true) {
|
||||||
|
let identifier;
|
||||||
|
try {
|
||||||
|
identifier = await o.identifier(i);
|
||||||
|
} catch(e) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!identifier) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const v = await o.addressOf(identifier);
|
||||||
|
r[identifier] = v;
|
||||||
|
console.debug('found ' + identifier + '=' + v + ' in registry ' + registry);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
16
js/rpc.js
Normal file
16
js/rpc.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
async function pingRpc() {
|
||||||
|
const address = await current_signer.getAddress();
|
||||||
|
const balance = await current_provider.getBalance(address);
|
||||||
|
let set = STATE.RPC_PING;
|
||||||
|
let rst = 0;
|
||||||
|
let r = {
|
||||||
|
balance: balance,
|
||||||
|
address: address,
|
||||||
|
}
|
||||||
|
if (balance < g_minBalance) {
|
||||||
|
set |= STATE.EVM_BALANCE_LOW;
|
||||||
|
} else {
|
||||||
|
rst = STATE.EVM_BALANCE_LOW;
|
||||||
|
}
|
||||||
|
stateChange(r, set, rst);
|
||||||
|
}
|
69
js/run.js
Normal file
69
js/run.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
var STATE = {
|
||||||
|
LOCAL_KEY: 1 << 0,
|
||||||
|
LOCAL_KEY_PROBE: 1 << 1,
|
||||||
|
LOCAL_KEY_REQUEST: 1 << 2,
|
||||||
|
LOCAL_KEY_REJECT: 1 << 3,
|
||||||
|
LOCAL_KEY_UNLOCK: 1 << 4,
|
||||||
|
LOCAL_KEY_LOCK: 1 << 5,
|
||||||
|
RPC_CONNECT: 1 << 6,
|
||||||
|
RPC_CHANGE: 1 << 7,
|
||||||
|
RPC_GONE: 1 << 8,
|
||||||
|
RPC_PING: 1 << 9,
|
||||||
|
EVM_BALANCE_LOW: 1 << 10,
|
||||||
|
};
|
||||||
|
const STATE_KEYS = Object.keys(STATE);
|
||||||
|
var g_state = 0;
|
||||||
|
var g_chainId = 1337;
|
||||||
|
var g_balance = 0;
|
||||||
|
var g_minBalance = 10000000;
|
||||||
|
var g_registry = '0xb708175e3f6cd850643aaf7b32212afad50e2549';
|
||||||
|
|
||||||
|
const DEBUG = true;
|
||||||
|
const SESSION_ID = 'gasgasgas';
|
||||||
|
const KEY_STORAGEKEY = 'sessionKey_' + SESSION_ID;
|
||||||
|
|
||||||
|
const qp = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
|
// borrowed from forro
|
||||||
|
async function stateChange(s, set_states, rst_states) {
|
||||||
|
if (!set_states) {
|
||||||
|
set_states = [];
|
||||||
|
} else if (!Array.isArray(set_states)) {
|
||||||
|
set_states = [set_states];
|
||||||
|
}
|
||||||
|
if (!rst_states) {
|
||||||
|
rst_states = [];
|
||||||
|
} else if (!Array.isArray(rst_states)) {
|
||||||
|
rst_states = [rst_states];
|
||||||
|
}
|
||||||
|
let new_state = g_state;
|
||||||
|
for (let i = 0; i < set_states.length; i++) {
|
||||||
|
let state = parseInt(set_states[i]);
|
||||||
|
new_state |= state;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < rst_states.length; i++) {
|
||||||
|
let state = parseInt(set_states[i]);
|
||||||
|
new_state = new_state & (0xffffffff & ~rst_states[i]);
|
||||||
|
}
|
||||||
|
old_state = g_state;
|
||||||
|
g_state = new_state;
|
||||||
|
|
||||||
|
const ev = new CustomEvent('messagestatechange', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: false,
|
||||||
|
composed: true,
|
||||||
|
detail: {
|
||||||
|
s: s,
|
||||||
|
old_state: old_state,
|
||||||
|
state: new_state,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
window.dispatchEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkState(bit_check, bit_field) {
|
||||||
|
if (bit_field != 0 && !bit_field) {
|
||||||
|
bit_field = g_state;
|
||||||
|
}
|
||||||
|
return (bit_check & bit_field) > 0;
|
||||||
|
}
|
5
js/style.css
Normal file
5
js/style.css
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
body {}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user