Re-adding signing capabilities and adding better UI
This commit is contained in:
@ -14,7 +14,7 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<body data-theme="wintry" data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
54
src/lib/Signer.svelte
Normal file
54
src/lib/Signer.svelte
Normal file
@ -0,0 +1,54 @@
|
||||
<script lang="ts">
|
||||
import { signMessageWithPKP } from "$lib/services/signMessage";
|
||||
import { walletState, messageToSign, messageSignature } from "$lib/stores.js";
|
||||
|
||||
let currentPKP;
|
||||
let sessionSigs;
|
||||
let message;
|
||||
let signature;
|
||||
|
||||
walletState.subscribe((value) => {
|
||||
currentPKP = value.pkps[0];
|
||||
sessionSigs = value.sessionSigs;
|
||||
});
|
||||
|
||||
messageToSign.subscribe((value) => {
|
||||
message = value;
|
||||
});
|
||||
|
||||
messageSignature.subscribe((value) => {
|
||||
signature = value;
|
||||
});
|
||||
|
||||
async function signMessage() {
|
||||
const result = await signMessageWithPKP(sessionSigs, currentPKP, message);
|
||||
if (result.error) {
|
||||
console.error(result.error);
|
||||
} else {
|
||||
messageSignature.set(result.messageSignature);
|
||||
}
|
||||
}
|
||||
|
||||
function declineSign() {
|
||||
messageSignature.set(null);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col items-center justify-center h-full space-y-4">
|
||||
<p class="text-lg break-words">{JSON.stringify(message)}</p>
|
||||
{#if signature}
|
||||
<p class="text-sm font-bold break-words">
|
||||
Signature: {JSON.stringify(signature)}
|
||||
</p>
|
||||
{/if}
|
||||
<div
|
||||
class="absolute bottom-0 flex items-center justify-center w-full pb-4 space-x-4"
|
||||
>
|
||||
<button on:click={declineSign} class="btn variant-filled-error"
|
||||
>Decline</button
|
||||
>
|
||||
<button on:click={signMessage} class="btn variant-filled-success"
|
||||
>Sign</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
120
src/lib/SignerOLD.svelte
Normal file
120
src/lib/SignerOLD.svelte
Normal file
@ -0,0 +1,120 @@
|
||||
<!-- SignVerifyMessage.svelte -->
|
||||
<script lang="ts">
|
||||
import { ethers } from "ethers";
|
||||
import { onMount } from "svelte";
|
||||
import { signRequest, signedMessages } from "./stores.js";
|
||||
|
||||
let messageToSign = {};
|
||||
let currentPKP;
|
||||
let sessionSigs;
|
||||
let status = "";
|
||||
let litNodeClient;
|
||||
let messageSignature;
|
||||
|
||||
onMount(async () => {
|
||||
litNodeClient = new LitNodeClient({ litNetwork: "serrano" });
|
||||
await litNodeClient.connect();
|
||||
|
||||
const sessionSigsLocalStorage = localStorage.getItem("google-session");
|
||||
const currentPKPLocalStorage = localStorage.getItem("current-pkp");
|
||||
if (sessionSigsLocalStorage && currentPKPLocalStorage) {
|
||||
sessionSigs = JSON.parse(sessionSigsLocalStorage);
|
||||
currentPKP = JSON.parse(currentPKPLocalStorage);
|
||||
}
|
||||
});
|
||||
|
||||
async function signMessageWithPKP() {
|
||||
const userConfirmed = window.confirm(
|
||||
"Do you want to sign the following message?\n\n" +
|
||||
JSON.stringify(messageToSign, null, 2)
|
||||
);
|
||||
|
||||
if (!userConfirmed) {
|
||||
status = "User did not allow to sign the message.";
|
||||
dispatch("status", status);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// Create a specific JSON object
|
||||
const jsonString = JSON.stringify(messageToSign);
|
||||
|
||||
// Convert the JSON string to an array of character codes
|
||||
const toSign = ethers.getBytes(ethers.hashMessage(jsonString));
|
||||
|
||||
const litActionCode = `
|
||||
const go = async () => {
|
||||
const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName });
|
||||
};
|
||||
go();
|
||||
`;
|
||||
|
||||
// Sign message
|
||||
const results = await litNodeClient.executeJs({
|
||||
code: litActionCode,
|
||||
sessionSigs: sessionSigs,
|
||||
jsParams: {
|
||||
toSign: toSign,
|
||||
publicKey: currentPKP.publicKey,
|
||||
sigName: "sig1",
|
||||
},
|
||||
});
|
||||
|
||||
// Get signature
|
||||
const result = results.signatures["sig1"];
|
||||
messageSignature = ethers.Signature.from({
|
||||
r: "0x" + result.r,
|
||||
s: "0x" + result.s,
|
||||
v: result.recid,
|
||||
});
|
||||
|
||||
signedMessages.update((messages) => [
|
||||
...messages,
|
||||
{ json: messageToSign, signature: messageSignature },
|
||||
]);
|
||||
|
||||
// verify();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
async function verify() {
|
||||
const response = await fetch("/api/verify", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
messageToSign,
|
||||
messageSignature,
|
||||
currentPKP,
|
||||
}),
|
||||
});
|
||||
if (!response.ok) {
|
||||
alert("verify failed");
|
||||
} else {
|
||||
let json = await response.json();
|
||||
alert(json.verified ? "Signature valid" : "! Signature NOT valid !");
|
||||
}
|
||||
}
|
||||
|
||||
signRequest.subscribe(({ json }) => {
|
||||
if (messageToSign && Object.keys(json).length > 0) {
|
||||
signRequest.set({ json: {} });
|
||||
messageToSign = json;
|
||||
signMessageWithPKP(json);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if status}
|
||||
<div class="mt-4 text-center">
|
||||
<p>Status: {status}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if messageSignature}
|
||||
<div class="mt-4 text-center">
|
||||
<p>Signature</p>
|
||||
<pre>{JSON.stringify(messageSignature)}</pre>
|
||||
</div>
|
||||
<button on:click={verify}>Verify</button><br />
|
||||
{/if}
|
@ -3,13 +3,20 @@
|
||||
import walletMachine from "./machines/walletMachine";
|
||||
import { onMount } from "svelte";
|
||||
import Icon from "@iconify/svelte";
|
||||
import { walletState } from "./stores";
|
||||
import { messageToSign } from "./stores";
|
||||
|
||||
import {
|
||||
signInWithGoogle,
|
||||
startSignIn as startSignInService,
|
||||
} from "./services/signInWithGoogle";
|
||||
import Signer from "./Signer.svelte";
|
||||
import { getDrawerStore } from "@skeletonlabs/skeleton";
|
||||
|
||||
const { state, send } = useMachine(walletMachine);
|
||||
const drawerStore = getDrawerStore();
|
||||
|
||||
$: walletState.set($state.context);
|
||||
|
||||
$: {
|
||||
if ($state.context.pkps && $state.context.sessionSigs) {
|
||||
@ -33,9 +40,16 @@
|
||||
startSignInService.set(true);
|
||||
await signInWithGoogle();
|
||||
}
|
||||
|
||||
function clearSession() {
|
||||
send("LOGOUT");
|
||||
}
|
||||
|
||||
function signRequest() {
|
||||
const settings = { position: "bottom", id: "signMessage" };
|
||||
drawerStore.open(settings);
|
||||
messageToSign.set({ hello: "test" });
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if $state.matches("sessionAvailable") || $state.matches("creatingSession") || $state.matches("signIn")}
|
||||
@ -43,7 +57,7 @@
|
||||
<div class="w-1/3">
|
||||
<button
|
||||
on:click={startSignIn}
|
||||
class="w-full py-2 text-white bg-blue-500 rounded hover:bg-blue-700 flex items-center justify-center"
|
||||
class="flex items-center justify-center w-full py-2 text-white bg-blue-500 rounded hover:bg-blue-700"
|
||||
>
|
||||
<span class="mr-2"><Icon icon="flat-color-icons:google" /></span>
|
||||
<span>Sign in with Google</span>
|
||||
@ -51,9 +65,9 @@
|
||||
</div>
|
||||
{:else if $state.context.pkps}
|
||||
<div
|
||||
class="fixed bottom-0 left-0 right-0 p-3 bg-white bg-opacity-75 rounded-t-lg shadow-md flex flex-col items-center space-y-4"
|
||||
class="fixed bottom-0 left-0 right-0 flex flex-col items-center p-3 space-y-4 bg-white bg-opacity-75 rounded-t-lg shadow-md"
|
||||
>
|
||||
<div class="w-full flex items-center justify-between space-x-4">
|
||||
<div class="flex items-center justify-between w-full space-x-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<div>
|
||||
<p class="text-sm">
|
||||
@ -66,23 +80,23 @@
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
on:click={clearSession}
|
||||
class="py-1 px-2 text-white bg-red-500 rounded hover:bg-red-700"
|
||||
<button on:click={signRequest} type="button" class="btn variant-filled"
|
||||
>SignRequest</button
|
||||
>
|
||||
<button type="button" class="btn variant-filled" on:click={clearSession}
|
||||
>Logout</button
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{:else if $state.matches("sessionExpired")}
|
||||
<div class="bg-white p-10">
|
||||
<div class="p-10 bg-white">
|
||||
<p>Error creating session. Please try again.</p>
|
||||
<pre>{JSON.stringify($state.context.error, null, 2)}</pre>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="bg-white p-10 rounded-full">
|
||||
<div class="bg-white rounded-full p-5 animate-spin">
|
||||
<div class="p-10 bg-white rounded-full">
|
||||
<div class="p-5 bg-white rounded-full animate-spin">
|
||||
<Icon icon="la:spinner" width="100" height="100" />
|
||||
</div>
|
||||
</div>
|
||||
|
41
src/lib/services/signMessage.ts
Normal file
41
src/lib/services/signMessage.ts
Normal file
@ -0,0 +1,41 @@
|
||||
// $lib/services/signMessage.ts
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export async function signMessageWithPKP(sessionSigs, currentPKP, messageToSign) {
|
||||
try {
|
||||
const jsonString = JSON.stringify(messageToSign);
|
||||
const toSign = ethers.getBytes(ethers.hashMessage(jsonString));
|
||||
|
||||
const litActionCode = `
|
||||
const go = async () => {
|
||||
const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName });
|
||||
};
|
||||
go();
|
||||
`;
|
||||
|
||||
const litNodeClient = new LitNodeClient({ litNetwork: "serrano" });
|
||||
await litNodeClient.connect();
|
||||
|
||||
const results = await litNodeClient.executeJs({
|
||||
code: litActionCode,
|
||||
sessionSigs: sessionSigs,
|
||||
jsParams: {
|
||||
toSign: toSign,
|
||||
publicKey: currentPKP.publicKey,
|
||||
sigName: "sig1",
|
||||
},
|
||||
});
|
||||
|
||||
const result = results.signatures["sig1"];
|
||||
const messageSignature = ethers.Signature.from({
|
||||
r: "0x" + result.r,
|
||||
s: "0x" + result.s,
|
||||
v: result.recid,
|
||||
});
|
||||
|
||||
return { messageSignature };
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return { error: err };
|
||||
}
|
||||
}
|
@ -1,11 +1,13 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export const signRequest = writable({json: {}});
|
||||
|
||||
export const signedMessages = writable([])
|
||||
|
||||
export const redirectStore = writable(false);
|
||||
|
||||
export const walletState = writable(null);
|
||||
|
||||
export const messageToSign = writable(null);
|
||||
|
||||
export const messageSignature = writable(null);
|
||||
|
||||
export const googleSession = writable({
|
||||
activeSession: false
|
||||
});
|
@ -8,6 +8,13 @@
|
||||
import { initChainProvider } from "$lib/setupChainProvider";
|
||||
import { googleSession } from "$lib/stores.js";
|
||||
import Wallet from "$lib/Wallet.svelte";
|
||||
import { Drawer, initializeStores } from "@skeletonlabs/skeleton";
|
||||
import { getDrawerStore } from "@skeletonlabs/skeleton";
|
||||
import Signer from "$lib/Signer.svelte";
|
||||
|
||||
initializeStores();
|
||||
|
||||
const drawerStore = getDrawerStore();
|
||||
|
||||
let activeSession = false;
|
||||
|
||||
@ -28,8 +35,16 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer>
|
||||
{#if $drawerStore.id === "signMessage"}
|
||||
<Signer messageToSign={{ hello: "me" }} />
|
||||
{:else}
|
||||
<!-- (fallback contents) -->
|
||||
{/if}</Drawer
|
||||
>
|
||||
|
||||
<div
|
||||
class="flex items-center justify-center h-screen bg-cover bg-center"
|
||||
class="flex items-center justify-center h-screen bg-center bg-cover"
|
||||
style="background-image: url('lake.jpeg');"
|
||||
>
|
||||
<QueryClientProvider client={data.queryClient}>
|
||||
|
Reference in New Issue
Block a user