Initial commit
This commit is contained in:
commit
18d1a02e98
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules/
|
2
dist/bundle.js
vendored
Normal file
2
dist/bundle.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1291
dist/bundle.js.LICENSE.txt
vendored
Normal file
1291
dist/bundle.js.LICENSE.txt
vendored
Normal file
File diff suppressed because it is too large
Load Diff
21
index.html
Normal file
21
index.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>ZKL Roadhog Demo</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<script src="dist/bundle.js"></script>
|
||||||
|
<script>
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
document.getElementById('metamask').disabled = false;
|
||||||
|
document.getElementById('phantom').disabled = false;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<h1>ZKL Roadhog Demo</h1>
|
||||||
|
<button disabled id="metamask" onclick="signIn('ethereum')">sign in with metamask</button>
|
||||||
|
<button disabled id="phantom" onclick="signIn('solana')">sign in with solana</button>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
35
index.js
Normal file
35
index.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { signIn as _signIn } from './roadhog.js';
|
||||||
|
|
||||||
|
const endpoint = "http://localhost:3000";
|
||||||
|
|
||||||
|
async function signIn(type) {
|
||||||
|
try {
|
||||||
|
const result = await _signIn(type);
|
||||||
|
if (result.success) {
|
||||||
|
console.log('Successfully signed in with', type);
|
||||||
|
const b = document.createElement('button');
|
||||||
|
b.innerText = 'Click to access protected';
|
||||||
|
b.setAttribute('onclick', 'fetchProtected()');
|
||||||
|
document.body.appendChild(b);
|
||||||
|
} else {
|
||||||
|
console.error('Sign-in failed:', result.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Sign-in error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signOff() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
window.signIn = signIn;
|
||||||
|
window.signOff = signOff;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
console.log('start');
|
||||||
|
if (!window.ethereum) console.log('ethereum not detected');
|
||||||
|
if (!window.solana) console.log('solana not detected');
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('load', init);
|
13
package.json
Normal file
13
package.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"terser-webpack-plugin": "^5.3.10",
|
||||||
|
"webpack": "^5.95.0",
|
||||||
|
"webpack-cli": "^5.1.4"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@solana/web3.js": "^1.95.3",
|
||||||
|
"bs58": "^6.0.0",
|
||||||
|
"ethers": "^6.13.3",
|
||||||
|
"siwe": "^2.3.2"
|
||||||
|
}
|
||||||
|
}
|
1502
pnpm-lock.yaml
generated
Normal file
1502
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
88
roadhog.js
Normal file
88
roadhog.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import { SiweMessage } from 'siwe';
|
||||||
|
import { BrowserProvider, JsonRpcSigner } from 'ethers';
|
||||||
|
import * as solanaWeb3 from '@solana/web3.js';
|
||||||
|
import bs58 from 'bs58';
|
||||||
|
|
||||||
|
const endpoint = 'http://localhost:3000';
|
||||||
|
|
||||||
|
export async function signIn(type) {
|
||||||
|
let address, signature, message;
|
||||||
|
|
||||||
|
if (type === 'ethereum') {
|
||||||
|
await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||||
|
const provider = new BrowserProvider(window.ethereum);
|
||||||
|
const signer = await provider.getSigner();
|
||||||
|
address = await signer.getAddress();
|
||||||
|
|
||||||
|
const nonceResponse = await fetch(`${endpoint}/auth/nonce?address=${address}&type=ethereum`);
|
||||||
|
const { nonce } = await nonceResponse.json();
|
||||||
|
|
||||||
|
const siweMessage = new SiweMessage({
|
||||||
|
domain: window.location.host,
|
||||||
|
address: address,
|
||||||
|
statement: 'Sign in with Ethereum to zk-Lokomotive.',
|
||||||
|
uri: window.location.origin,
|
||||||
|
version: '1',
|
||||||
|
chainId: 1,
|
||||||
|
nonce: nonce
|
||||||
|
});
|
||||||
|
|
||||||
|
message = siweMessage.prepareMessage();
|
||||||
|
signature = await signer.signMessage(message);
|
||||||
|
} else if (type === 'solana') {
|
||||||
|
const provider = window.solana;
|
||||||
|
await provider.connect();
|
||||||
|
address = provider.publicKey.toString();
|
||||||
|
|
||||||
|
const nonceResponse = await fetch(`${endpoint}/auth/nonce?address=${address}&type=solana`);
|
||||||
|
const { nonce } = await nonceResponse.json();
|
||||||
|
|
||||||
|
const encodedMessage = new TextEncoder().encode(nonce);
|
||||||
|
const signatureBytes = await provider.signMessage(encodedMessage, 'utf8');
|
||||||
|
signature = bs58.encode(signatureBytes.signature);
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid type');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${endpoint}/auth/verify`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ type, message, address, signature }),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error((await response.json()).error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { auth_token } = await response.json();
|
||||||
|
|
||||||
|
localStorage.setItem('auth_token', auth_token);
|
||||||
|
localStorage.setItem('auth_type', type);
|
||||||
|
|
||||||
|
return { success: true, auth_token };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Authentication error:', error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function signOff() {
|
||||||
|
const auth_token = localStorage.getItem('auth_token');
|
||||||
|
const response = await fetch(`${endpoint}/auth/signoff`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': `Bearer ${auth_token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error((await response.json()).error);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem('auth_token');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
25
webpack.config.js
Normal file
25
webpack.config.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
const path = require("path");
|
||||||
|
const webpack = require("webpack");
|
||||||
|
const TerserPlugin = require("terser-webpack-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: "./index.js",
|
||||||
|
output: {
|
||||||
|
filename: "bundle.js",
|
||||||
|
path: path.resolve(__dirname, "dist")
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new TerserPlugin({
|
||||||
|
terserOptions: {
|
||||||
|
keep_fnames: ['signIn', 'fetchProtected'],
|
||||||
|
mangle: {
|
||||||
|
reserved: [
|
||||||
|
"signIn",
|
||||||
|
"fetchProtected"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
mode: "development"
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user