Skip to content

Commit

Permalink
fix: notifications + provider types and add provider dropdown (#10)
Browse files Browse the repository at this point in the history
* fix: provider types and add provider dropdown

* fix: null check for newProvider.disconnect

* fix: provider setup
  • Loading branch information
shanejonas authored Oct 24, 2024
1 parent 514d338 commit 82f389f
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 56 deletions.
95 changes: 44 additions & 51 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
import React, { useEffect, useState } from 'react';

import './App.css';
// import makeProvider from './providers/MockMultichainProvider';
import MetaMaskMultichainProvider from './providers/MetaMaskMultichainProvider';

const provider = new MetaMaskMultichainProvider();
import makeProvider from './providers/MockMultichainProvider';
import type { Provider } from './providers/Provider';

function App() {
const [createSessionResult, setCreateSessionResult] = useState<any>(null);
const [providerType, setProviderType] = useState<string>('metamask');
const [provider, setProvider] = useState<Provider>();
const [getSessionResult, setGetSessionResult] = useState<any>(null);
const [revokeSessionResult, setRevokeSessionResult] = useState<any>(null);
const [selectedMethods, setSelectedMethods] = useState<
Expand All @@ -27,59 +28,41 @@ function App() {
const [walletNotifyResults, setWalletNotifyResults] = useState<any>(null);
const [walletSessionChangedResults, setWalletSessionChangedResults] =
useState<any>(null);
const [currentSession, setCurrentSession] = useState<any>(null);
const [extensionId, setExtensionId] = useState<string>('');
const [invokeMethodRequests, setInvokeMethodRequests] = useState<
Record<string, string>
>({});

// Use useEffect to handle provider initialization and cleanup
useEffect(() => {
console.log('extensionId', extensionId);
if (extensionId) {
provider.connect(extensionId);
let newProvider: Provider;
if (providerType === 'mock') {
newProvider = makeProvider(() => createSessionResult);
} else {
newProvider = new MetaMaskMultichainProvider();
}
return () => {
provider.disconnect();
};
}, [extensionId]);

useEffect(() => {
const interval = setInterval(() => {
if (!currentSession) {
return;
}
setWalletSessionChangedResults({
jsonrpc: '2.0',
method: 'wallet_sessionChanged',
params: currentSession,
});
}, 1000);
return () => clearInterval(interval);
}, [currentSession]);
setProvider(newProvider);
}, [providerType, createSessionResult]);

// setup provider
useEffect(() => {
const interval = setInterval(() => {
if (!currentSession) {
return;
}
setWalletNotifyResults({
jsonrpc: '2.0',
method: 'wallet_notify',
params: {
'eip155:1': {
method: 'eth_subscription',
params: {
subscription: '0xfoo',
result: {
blockNumber: '0x1',
},
},
},
},
if (extensionId && provider) {
provider.connect(extensionId);
provider.onNotification((notification: any) => {
if (notification.method === 'wallet_notify') {
setWalletNotifyResults(notification);
} else if (notification.method === 'wallet_sessionChanged') {
setWalletSessionChangedResults(notification);
}
});
}, 1000);
return () => clearInterval(interval);
}, [currentSession]);
}
return () => {
if (provider) {
provider.disconnect();
}
};
}, [extensionId, provider]);

// Update the handleResetState function
const handleResetState = () => {
Expand All @@ -95,7 +78,6 @@ function App() {
'eip155:1337': false,
'eip155:1': false,
});
setCurrentSession(null);
};

const handleCreateSession = async () => {
Expand All @@ -118,20 +100,19 @@ function App() {
};
}

const result = await provider.request({
const result = await provider?.request({
method: 'wallet_createSession',
params: { requiredScopes },
});
setCreateSessionResult(result);
setCurrentSession(result);
} catch (error) {
console.error('Error creating session:', error);
}
};

const handleGetSession = async () => {
try {
const result = await provider.request({
const result = await provider?.request({
method: 'wallet_getSession',
params: [],
});
Expand All @@ -143,7 +124,7 @@ function App() {

const handleRevokeSession = async () => {
try {
const result = await provider.request({
const result = await provider?.request({
method: 'wallet_revokeSession',
params: [],
});
Expand All @@ -159,7 +140,7 @@ function App() {
const handleInvokeMethod = async (scope: string, method: string) => {
try {
const requestObject = JSON.parse(invokeMethodRequests[scope] ?? '{}');
const result = await provider.request(requestObject);
const result = await provider?.request(requestObject);
setInvokeMethodResults((prev) => ({
...prev,
[scope]: { ...prev[scope], [method]: result },
Expand All @@ -176,6 +157,18 @@ function App() {
return (
<div className="App">
<h1>MetaMask MultiChain API Test Dapp</h1>
<div>
<label>
Provider:
<select
value={providerType}
onChange={(evt) => setProviderType(evt.target.value)}
>
<option value="metamask">MetaMask Provider</option>
<option value="mock">Mock Provider</option>
</select>
</label>
</div>
<div>
<label>
Extension ID:
Expand Down
13 changes: 10 additions & 3 deletions src/providers/MetaMaskMultichainProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { Provider } from './Provider';

type NotificationCallback = (notification: any) => void;

class MetaMaskMultichainProvider {
class MetaMaskMultichainProvider implements Provider {
#port: chrome.runtime.Port | null;

#requestMap: Map<
Expand All @@ -26,7 +28,6 @@ class MetaMaskMultichainProvider {

// eslint-disable-next-line
this.#port = chrome.runtime.connect(extensionId);
console.log('this.#port', this.#port);
this.#port.onMessage.addListener(this.#handleMessage.bind(this));
this.#port.onDisconnect.addListener(() => {
this.#port = null;
Expand All @@ -40,6 +41,7 @@ class MetaMaskMultichainProvider {
this.#port = null;
}
this.#requestMap.clear();
this.removeAllNotificationListeners();
}

async request({
Expand Down Expand Up @@ -79,7 +81,6 @@ class MetaMaskMultichainProvider {
}

#handleMessage(message: any): void {
console.log('got message back', message);
const { data } = message;
if (data.id && this.#requestMap.has(data.id)) {
const { resolve, reject } = this.#requestMap.get(data.id) ?? {};
Expand All @@ -106,6 +107,12 @@ class MetaMaskMultichainProvider {
this.#notificationCallbacks.delete(callback);
}

removeAllNotificationListeners() {
this.#notificationCallbacks.forEach(
this.removeNotificationListener.bind(this),
);
}

#notifyCallbacks(notification: any): void {
this.#notificationCallbacks.forEach((callback) => {
try {
Expand Down
49 changes: 47 additions & 2 deletions src/providers/MockMultichainProvider.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,47 @@
const makeProvider = (getSession: () => any) => {
import type { Provider } from './Provider';

const callbacks: ((message: any) => void)[] = [];

const makeProvider = (getSession: () => any): Provider => {
const notify = (message: any) => {
callbacks.forEach((callback) => {
callback(message);
});
};

// Function to simulate MetaMask provider
const provider = {
const provider: Provider = {
connect: (_: string) => {
// do nothing
},
disconnect: () => {
// do nothing
},
request: async ({ method, params }: { method: string; params: any }) => {
console.log(`Calling ${method} with params:`, params);
// Simulate responses based on method
switch (method) {
case 'wallet_createSession':
notify({
jsonrpc: '2.0',
method: 'wallet_notify',
params: {
'eip155:1': {
method: 'eth_subscription',
params: {
subscription: '0xfoo',
result: {
blockNumber: '0x1',
},
},
},
},
});
notify({
jsonrpc: '2.0',
method: 'wallet_sessionChanged',
params: params.requiredScopes,
});
return {
sessionScopes: params.requiredScopes,
};
Expand All @@ -19,6 +55,15 @@ const makeProvider = (getSession: () => any) => {
throw new Error('Method not implemented');
}
},
onNotification: (callback: (message: any) => void) => {
callbacks.push(callback);
},
removeAllNotificationListeners: () => {
// no op
},
removeNotificationListener(_: (notification: any) => void): void {
throw new Error('Function not implemented.');
},
};
return provider;
};
Expand Down
8 changes: 8 additions & 0 deletions src/providers/Provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type Provider = {
connect(extensionId?: string): void;
disconnect(): void;
request(params: { method: string; params: any }): Promise<any>;
onNotification(callback: (notification: any) => void): void;
removeNotificationListener(callback: (notification: any) => void): void;
removeAllNotificationListeners(): void;
};

0 comments on commit 82f389f

Please sign in to comment.