Skip to content

Commit

Permalink
feat: socket reconnection optimization (#994)
Browse files Browse the repository at this point in the history
* feat: prevent duplicate events

* feat: fully reuse existing channel

* fix: unit tests

* feat: cleanup

* feat: cleanup

* feat: cleanup

* feat: wip

* feat: connection management and cleanup events

* feat: cleanup

* feat: cleanup

* feat: prevent e2e locally

* feat: cleanup

* fix: tests

* fix: unit tests

* fix: unit tests

* fix: unit tests

* fix: unit tests

* fix: unit tests

* fix: unit tests

* fix: unit test

* fix: unit tests

* fix: unit tests

* fix: cleanup

* feat: cleanup

* feat: add missing sdkVersion to originatorInfo

* feat: add utility to debug universal links

* feat: cleanup

* feat: working nodejs and wagmi

* feat: working state

* feat: debug logs

* fix: authorized not reset after terminate
  • Loading branch information
abretonc7s authored Aug 28, 2024
1 parent 9e9f1ac commit 5af7cbf
Show file tree
Hide file tree
Showing 72 changed files with 1,611 additions and 1,506 deletions.
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ typings/
.vscode/settings.json
packages/deve2e/sdk-comm
packages/examples/test1/ios/test1.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
packages/examples/nodejs/.sdk-comm

# yarn v3 (w/o zero-install)
# See: https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
Expand All @@ -114,5 +113,7 @@ packages/examples/nodejs/.sdk-comm
packages/devnext/next.webpack.config.json
packages/devreact/webpack.config.json
.tool-versions
packages/examples/nodejs/.MMSDK_cached_address
packages/examples/nodejs/.MMSDK_cached_chainId
**/.MMSDK_cached_address
**/.MMSDK_cached_chainId
**/.sdk-comm
**/package-lock.json
2 changes: 1 addition & 1 deletion packages/deve2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"watch": "yarn start --watch"
},
"dependencies": {
"@metamask/providers": "^10.2.1",
"@metamask/providers": "16.1.0",
"@metamask/sdk": "workspace:^",
"@metamask/sdk-communication-layer": "workspace:^",
"bowser": "^2.11.0",
Expand Down
5 changes: 4 additions & 1 deletion packages/devnext/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
"@metamask/sdk-react": "workspace:^",
"@metamask/sdk-react-ui": "workspace:^",
"@metamask/sdk-ui": "workspace:^",
"@tanstack/query-core": "^5.52.2",
"@tanstack/react-query": "^5.52.2",
"@types/node": "18.15.3",
"@types/react": "^18.2.37",
"@types/react-dom": "^18.2.15",
Expand All @@ -54,7 +56,8 @@
"siwe": "^2.3.2",
"typescript": "5.0.2",
"utf-8-validate": "^5.0.2",
"viem": "^1.8.1",
"viem": "^2.20.1",
"wagmi": "^2.12.7",
"web3": "^4.1.1"
},
"devDependencies": {
Expand Down
226 changes: 226 additions & 0 deletions packages/devnext/public/deep.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Input Debug Universal Link</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
padding: 20px;
max-width: 800px;
margin: 0 auto;
}

pre {
background-color: #f4f4f4;
padding: 10px;
border-radius: 5px;
overflow-x: auto;
white-space: pre-wrap;
word-wrap: break-word;
}

table {
border-collapse: collapse;
width: 100%;
margin-bottom: 20px;
table-layout: fixed;
}

th,
td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}

th {
background-color: #f2f2f2;
}

.param-column {
width: 30%;
word-break: break-word;
}

.value-column {
width: 70%;
word-break: break-word;
}

.param-control {
display: flex;
align-items: center;
margin-bottom: 5px;
}

.param-control input[type="checkbox"] {
margin-right: 10px;
}

button,
input[type="text"] {
margin-top: 10px;
padding: 5px 10px;
}

input[type="text"] {
width: 100%;
box-sizing: border-box;
}

#decodedInfo {
max-height: 300px;
overflow-y: auto;
}
</style>
</head>

<body>
<h1>Custom Input Universal Link Debug</h1>
<p>Paste your own deep link or use the default:</p>
<input type="text" id="customLink" placeholder="Paste your deep link here">
<button id="useCustomLink">Use Custom Link</button>
<button id="useDefaultLink">Use Default Link</button>

<h2>Modify Parameters:</h2>
<div id="paramControls"></div>
<button id="updateLink">Update Link</button>
<button id="testLink">Test Updated Link</button>

<h2>Current Link:</h2>
<pre id="currentLink"></pre>

<h2>Query Parameters:</h2>
<table id="paramsTable">
<tr>
<th class="param-column">Parameter</th>
<th class="value-column">Value</th>
</tr>
</table>

<h2>Decoded originatorInfo:</h2>
<pre id="decodedInfo"></pre>

<script>
const defaultLink = "https://metamask.app.link/connect?channelId=4e8f280b-3c3b-41f4-9cbe-9967f67f1ee8&v=2&comm=socket&pubkey=0248e0e610fd2ce7a1058a57434a65114520d2f0a6dcafa587b7ceebe5ac439359&t=q&originatorInfo=eyJ1cmwiOiJodHRwczovL2RldmRhcHAuc2l0ZWVkLm5ldCIsInRpdGxlIjoiRGV2TmV4dCIsImljb24iOiJodHRwczovL2RldmRhcHAuc2l0ZWVkLm5ldC9mYXZpY29uLmljbyIsInNjaGVtZSI6IiIsImFwaVZlcnNpb24iOiIwLjI3LjAiLCJkYXBwSWQiOiJkZXZkYXBwLnNpdGVlZC5uZXQiLCJwbGF0Zm9ybSI6IndlYi1kZXNrdG9wIiwic291cmNlIjoiZGlyZWN0In0=";
let currentLink = defaultLink;
let originalParams = new URLSearchParams(new URL(defaultLink).search);

function decodeOriginatorInfo(encodedInfo) {
try {
return JSON.parse(atob(decodeURIComponent(encodedInfo)));
} catch (e) {
return "Error decoding originatorInfo: " + e.message;
}
}

function displayLinkInfo(link) {
const url = new URL(link);
const currentLinkElement = document.getElementById('currentLink');
const paramsTable = document.getElementById('paramsTable');
const decodedInfo = document.getElementById('decodedInfo');

currentLinkElement.textContent = link;

// Clear existing rows
while (paramsTable.rows.length > 1) {
paramsTable.deleteRow(1);
}

// Display query parameters
url.searchParams.forEach((value, key) => {
const row = paramsTable.insertRow();
const cell1 = row.insertCell(0);
const cell2 = row.insertCell(1);
cell1.className = 'param-column';
cell2.className = 'value-column';
cell1.textContent = key;
cell2.textContent = value;
});

// Decode and display originatorInfo
const originatorInfo = url.searchParams.get('originatorInfo');
if (originatorInfo) {
decodedInfo.textContent = JSON.stringify(decodeOriginatorInfo(originatorInfo), null, 2);
} else {
decodedInfo.textContent = "No originatorInfo found in the URL.";
}
}

function createParamControls(link) {
const url = new URL(link);
const controlsContainer = document.getElementById('paramControls');
controlsContainer.innerHTML = ''; // Clear existing controls

originalParams = new URLSearchParams(url.search);

originalParams.forEach((value, key) => {
const controlDiv = document.createElement('div');
controlDiv.className = 'param-control';

const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.id = `check-${key}`;
checkbox.checked = true;

const label = document.createElement('label');
label.htmlFor = `check-${key}`;
label.textContent = key;

controlDiv.appendChild(checkbox);
controlDiv.appendChild(label);
controlsContainer.appendChild(controlDiv);
});
}

function updateLink() {
const url = new URL(currentLink);
const newUrl = new URL(url.origin + url.pathname);

originalParams.forEach((value, key) => {
const checkbox = document.getElementById(`check-${key}`);
if (checkbox && checkbox.checked) {
newUrl.searchParams.append(key, value);
}
});

currentLink = newUrl.toString();
displayLinkInfo(currentLink);
}

document.getElementById('useCustomLink').addEventListener('click', function () {
const customLinkInput = document.getElementById('customLink');
if (customLinkInput.value) {
currentLink = customLinkInput.value;
createParamControls(currentLink);
displayLinkInfo(currentLink);
} else {
alert('Please enter a custom link first.');
}
});

document.getElementById('useDefaultLink').addEventListener('click', function () {
currentLink = defaultLink;
document.getElementById('customLink').value = '';
createParamControls(currentLink);
displayLinkInfo(currentLink);
});

document.getElementById('updateLink').addEventListener('click', updateLink);

document.getElementById('testLink').addEventListener('click', function () {
console.log('Testing link:', currentLink);
window.location.href = currentLink;
});

// Initialize the page
createParamControls(currentLink);
displayLinkInfo(currentLink);
</script>
</body>

</html>
3 changes: 2 additions & 1 deletion packages/sdk-communication-layer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"publish:preview": "yarn npm publish --tag preview",
"prepack": "../../scripts/prepack.sh",
"reset": "yarn clean && rimraf ./node_modules/",
"test": "jest",
"test": "jest --testPathIgnorePatterns \"/e2e/\"",
"test:e2e": "jest --testPathPattern \"/e2e/\"",
"test:coverage": "jest --coverage",
"test:ci": "jest --coverage --passWithNoTests --setupFilesAfterEnv ./jest-preload.js --testPathIgnorePatterns \"/e2e/\"",
"test:dev": "jest",
Expand Down
3 changes: 2 additions & 1 deletion packages/sdk-communication-layer/src/RemoteCommunication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
} from './types/StorageManager';
import { WalletInfo } from './types/WalletInfo';
import { logger } from './utils/logger';
import { Channel } from './types/Channel';

type MetaMaskMobile = 'metamask-mobile';

Expand Down Expand Up @@ -268,7 +269,7 @@ export class RemoteCommunication extends EventEmitter2 {
return channelConfig;
}

async generateChannelIdConnect() {
async generateChannelIdConnect(): Promise<Channel> {
return generateChannelIdConnect(this.state);
}

Expand Down
8 changes: 5 additions & 3 deletions packages/sdk-communication-layer/src/SocketService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,12 @@ describe('SocketService', () => {
expect(keyCheck).toHaveBeenCalledWith(socketService);
});

it('should create a new channel', () => {
it('should create a new channel', async () => {
const mockChannel = { id: 'CHANNEL_ID', members: [] };
(createChannel as jest.Mock).mockReturnValue(mockChannel);
const channel = socketService.createChannel();
(createChannel as jest.Mock).mockReturnValue(Promise.resolve(mockChannel));

const channel = await socketService.createChannel();

expect(channel).toStrictEqual(mockChannel);
});
});
Loading

0 comments on commit 5af7cbf

Please sign in to comment.