Skip to content

feat: display syncing data with birthday block #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/demo-wallet/src/App/components/AddAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function AddAccount() {
const handleMetamaskImport = async (e: FormEvent) => {
e.preventDefault();
const viewKey = (await invokeSnap({ method: 'getViewingKey' })) as string;
console.log(viewKey);

await addNewAccountFromUfvk(state, dispatch, viewKey, birthdayHeight);
toast.success('Account imported successfully', {
position: 'top-center',
Expand Down
5 changes: 3 additions & 2 deletions packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/MetaMask/template-snap-monorepo.git"
},
"source": {
"shasum": "5kk+7sR8jY1fw3MjTH9dRIPkwAHHo0pFgfCseq+3rko=",
"shasum": "K0ntTcddlgOu8VTqTscszF/wJHveOkwBujyr7uvVf7g=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand All @@ -27,7 +27,8 @@
{
"coinType": 133
}
]
],
"snap_manageState": {}
},
"platformVersion": "6.17.1",
"manifestVersion": "0.1"
Expand Down
32 changes: 22 additions & 10 deletions packages/snap/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import { getViewingKey } from './rpc/getViewingKey';
import { InitOutput } from '@webzjs/webz-keys';
import { initialiseWasm } from './utils/initialiseWasm';
import { OnRpcRequestHandler, OnUserInputHandler, UserInputEventType } from '@metamask/snaps-sdk';
import { setBirthdayBlock, SetBirthdayBlockParams } from './rpc/setBirthdayBlock';
import {
OnRpcRequestHandler,
OnUserInputHandler,
UserInputEventType,
} from '@metamask/snaps-sdk';
import { setBirthdayBlock } from './rpc/setBirthdayBlock';
import { getSnapState } from './rpc/getSnapState';
import { SetBirthdayBlockParams, SnapState } from './types';
import { setSnapState } from './rpc/setSnapState';

let wasm: InitOutput;

Expand All @@ -24,24 +31,29 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
case 'getViewingKey':
return await getViewingKey();
case 'setBirthdayBlock':
const params = request.params as SetBirthdayBlockParams;
return await setBirthdayBlock(params);
const setBirthdayBlockParams = request.params as SetBirthdayBlockParams;
return await setBirthdayBlock(setBirthdayBlockParams);
case 'getSnapStete':
return await getSnapState();
case 'setSnapStete':
const setSnapStateParams = request.params as unknown as SnapState;
return await setSnapState(setSnapStateParams);
default:
throw new Error('Method not found.');
}
};

export const onUserInput: OnUserInputHandler = async ({ id, event, context }) => {
export const onUserInput: OnUserInputHandler = async ({ id, event }) => {
if (event.type === UserInputEventType.FormSubmitEvent) {
switch (event.name) {
case "birthday-block-form":
case 'birthday-block-form':
await snap.request({
method: "snap_resolveInterface",
method: 'snap_resolveInterface',
params: {
id,
value: event.value
}
})
value: event.value,
},
});

default:
break;
Expand Down
12 changes: 12 additions & 0 deletions packages/snap/src/rpc/getSnapState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Json } from '@metamask/snaps-sdk';

export async function getSnapState(): Promise<Json> {
const state = (await snap.request({
method: 'snap_manageState',
params: {
operation: 'get',
},
})) as unknown as Json;

return state;
}
31 changes: 21 additions & 10 deletions packages/snap/src/rpc/setBirthdayBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/** @jsxImportSource @metamask/snaps-sdk */
import {
Form,
Box,
Expand All @@ -6,17 +7,17 @@ import {
Button,
Text,
Bold,
Divider
Divider,
} from '@metamask/snaps-sdk/jsx';
import { setSyncBlockHeight } from '../utils/setSyncBlockHeight';

type BirthdayBlockForm = { customBirthdayBlock: string | null };

export type SetBirthdayBlockParams = { latestBlock?: number };
type SetBirthdayBlockParams = { latestBlock: number };

export async function setBirthdayBlock({
latestBlock,
}: SetBirthdayBlockParams): Promise<string | null> {

}: SetBirthdayBlockParams): Promise<number | null> {
const interfaceId = await snap.request({
method: 'snap_createInterface',
params: {
Expand All @@ -29,12 +30,13 @@ export async function setBirthdayBlock({
seed you can enter optional birthday block of that Wallet.
</Text>
<Divider />
<Text>
Syncing
proccess will start from that block.
</Text>
<Text>Syncing proccess will start from that block.</Text>
<Divider />
{!!latestBlock && <Text>Latest block: <Bold>{latestBlock.toString()}</Bold></Text>}
{!!latestBlock && (
<Text>
Latest block: <Bold>{latestBlock.toString()}</Bold>
</Text>
)}
<Input
min={0}
step={1}
Expand All @@ -58,6 +60,15 @@ export async function setBirthdayBlock({
},
})) as BirthdayBlockForm;

const webWalletSyncStartBlock = setSyncBlockHeight(customBirthdayBlock, latestBlock);

await snap.request({
method: 'snap_manageState',
params: {
operation: 'update',
newState: { webWalletSyncStartBlock },
},
});

return customBirthdayBlock;
return webWalletSyncStartBlock;
}
14 changes: 14 additions & 0 deletions packages/snap/src/rpc/setSnapState.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Json } from '@metamask/snaps-sdk';
import { SnapState } from 'src/types';

export async function setSnapState(newSnapState: SnapState): Promise<Json> {
const state = (await snap.request({
method: 'snap_manageState',
params: {
operation: 'update',
newState: newSnapState,
},
})) as unknown as Json;

return state;
}
7 changes: 7 additions & 0 deletions packages/snap/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Json } from "@metamask/snaps-sdk";

export type SetBirthdayBlockParams = { latestBlock: number };

export interface SnapState extends Record<string, Json> {
webWalletSyncStartBlock: string;
}
15 changes: 15 additions & 0 deletions packages/snap/src/utils/setSyncBlockHeight.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//NU5 (Network Upgrade 5) (Block 1,687,104, May 31, 2022)
const NU5_ACTIVATION = 1687104;

export function setSyncBlockHeight(
userInputCreationBlock: string | null,
latestBlock: number,
) {
if(userInputCreationBlock === null) return latestBlock;

const customBirthdayBlock = Number(userInputCreationBlock);

const latestAcceptableSyncBlock = NU5_ACTIVATION;

return customBirthdayBlock>latestAcceptableSyncBlock ? customBirthdayBlock : latestAcceptableSyncBlock
}
13 changes: 1 addition & 12 deletions packages/web-wallet/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import { useInterval } from 'usehooks-ts';
import { Outlet, useLocation } from 'react-router-dom';
import { Outlet } from 'react-router-dom';
import { RESCAN_INTERVAL } from './config/constants';
import { useEffect } from 'react';
import { useWebZjsActions } from './hooks';
import Layout from './components/Layout/Layout';

function App() {
const { triggerRescan } = useWebZjsActions();
const location = useLocation();

useEffect(() => {
// Add custom background to home page
if (location.pathname === '/') {
document.body.classList.add('home-page', 'home-page-bg');
} else {
document.body.classList.remove('home-page', 'home-page-bg');
}
}, [location.pathname]);

useInterval(() => {
triggerRescan();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { FC } from 'react';
import { WebZjsState } from 'src/context/WebzjsContext';

export const BlockHeightCard: FC<{
state: WebZjsState;
syncedFrom?: string;
}> = ({ state, syncedFrom }) => {
return (
<div className="grow shrink min-w-[317px] basis-0 p-6 bg-white rounded-xl border border-[#afafaf] flex-col justify-start items-start gap-2 inline-flex">
{state.syncInProgress ? (
<div className="self-stretch flex items-center gap-2">
<svg
className="animate-spin h-4 w-4 text-[#595959]"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
<span className="text-[#595959] text-sm font-semibold font-inter leading-[21px]">
Sync in progress...
</span>
</div>
) : null}
<div className="self-stretch text-[#595959] text-sm font-semibold font-inter leading-[21px]">
Chain Height
</div>
<div className="self-stretch justify-start items-center gap-2 inline-flex">
<div className="text-black text-2xl font-medium font-['Inter'] leading-9">
{state.chainHeight ? '' + state.chainHeight : '?'}
</div>
</div>
<div className="self-stretch text-[#595959] text-sm font-semibold font-inter leading-[21px]">
Synced Height
</div>
<div className="self-stretch justify-start items-center gap-2 inline-flex">
<div className="text-black text-2xl font-medium font-['Inter'] leading-9">
{state.summary?.fully_scanned_height
? state.summary?.fully_scanned_height
: '?'}
</div>
</div>
{syncedFrom && (
<>
<div className="self-stretch text-[#595959] text-sm font-semibold font-inter leading-[21px]">
Sync Start Block
</div>
<div className="self-stretch justify-start items-center gap-2 inline-flex">
<div className="text-black text-2xl font-medium font-['Inter'] leading-9">
{syncedFrom}
</div>
</div>
</>
)}
</div>
);
};
2 changes: 1 addition & 1 deletion packages/web-wallet/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const Header = (): React.JSX.Element => {
const isHomePage = location.pathname === '/';

return (
<header className="font-inter h-[60px] fixed top-0 left-0 w-full px-16 flex grow items-center justify-between bg-transparent py-3 border-b border-neutral-200">
<header className="font-inter h-[60px] w-full px-16 flex items-center justify-between bg-transparent py-3 border-b border-neutral-200">
<Link to={'/'}>
<div className="flex items-center">
<img
Expand Down
10 changes: 10 additions & 0 deletions packages/web-wallet/src/context/MetamaskContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import { createContext, useContext, useEffect, useState } from 'react';

import type { Snap } from '../types';
import { getSnapsProvider } from '../utils';
import { SnapState } from 'src/hooks/snaps/useGetSnapState';

type MetaMaskContextType = {
provider: MetaMaskInpageProvider | null;
installedSnap: Snap | null;
error: Error | null;
snapState: SnapState | null;
setSnapState: (SnapState: SnapState) => void;
setInstalledSnap: (snap: Snap | null) => void;
setError: (error: Error) => void;
};
Expand All @@ -17,6 +20,8 @@ const MetaMaskContext = createContext<MetaMaskContextType>({
provider: null,
installedSnap: null,
error: null,
snapState: null,
setSnapState: () => {},
setInstalledSnap: () => {},
setError: () => {},
});
Expand All @@ -29,9 +34,12 @@ const MetaMaskContext = createContext<MetaMaskContextType>({
* @returns JSX.
*/
export const MetaMaskProvider = ({ children }: { children: ReactNode }) => {
// const { getSnapState } = useGetSnapState();

const [provider, setProvider] = useState<MetaMaskInpageProvider | null>(null);
const [installedSnap, setInstalledSnap] = useState<Snap | null>(null);
const [error, setError] = useState<Error | null>(null);
const [snapState, setSnapState] = useState<SnapState | null>(null);

useEffect(() => {
getSnapsProvider().then(setProvider).catch(console.error);
Expand All @@ -56,6 +64,8 @@ export const MetaMaskProvider = ({ children }: { children: ReactNode }) => {
value={{
provider,
error,
snapState,
setSnapState,
setError,
installedSnap,
setInstalledSnap,
Expand Down
8 changes: 4 additions & 4 deletions packages/web-wallet/src/context/WebzjsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { MAINNET_LIGHTWALLETD_PROXY } from '../config/constants';
import { Snap } from '../types';
import toast, { Toaster } from 'react-hot-toast';

interface State {
export interface WebZjsState {
webWallet: WebWallet | null;
installedSnap: Snap | null;
error: Error | null | string;
Expand All @@ -37,7 +37,7 @@ type Action =
| { type: 'set-sync-in-progress'; payload: boolean }
| { type: 'set-loading'; payload: boolean };

const initialState: State = {
const initialState: WebZjsState = {
webWallet: null,
installedSnap: null,
error: null,
Expand All @@ -48,7 +48,7 @@ const initialState: State = {
loading: true,
};

function reducer(state: State, action: Action): State {
function reducer(state: WebZjsState, action: Action): WebZjsState {
switch (action.type) {
case 'set-web-wallet':
return { ...state, webWallet: action.payload };
Expand All @@ -70,7 +70,7 @@ function reducer(state: State, action: Action): State {
}

interface WebZjsContextType {
state: State;
state: WebZjsState;
dispatch: React.Dispatch<Action>;
}

Expand Down
Loading