Skip to content

Commit

Permalink
fix: address feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
samsiegart committed Jul 14, 2022
1 parent 4510bd9 commit fff0237
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 91 deletions.
5 changes: 1 addition & 4 deletions docs/env.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,7 @@ Lifetime: until we decide not to support Prometheus for metrics export

Affects: solo

Until dApps are converted to connect to the smart-wallet UI directly,
this allows them to continue to connect to `/wallet-bridge.html` and such
on the solo and have these endpoints serviced by `/wallet/bridge.html`
and such in a wallet UI.
This enables a proxy so that the solo bridge interface (/wallet-bridge.html) is backed by the smart wallet (/wallet/bridge.html). Dapps designed for the solo bridge can enable this until they connect to the smart wallet directly.

```
BRIDGE_TARGET=http://localhost:3001 make BASE_PORT=8002 scenario3-run
Expand Down
3 changes: 3 additions & 0 deletions packages/solo/public/wallet-bridge.html
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@
});
};

// This ensures the message wont be posted until after the iframe's
// "onLoad" event fires so we can rely on the consistent ordering of
// events in the WalletConnection component.
window.addEventListener('load', () => {
// Start the flow of messages.
if (window.parent !== window) {
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet/ui/public/bridge.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</head>

<body style="overflow-x: hidden; overflow-y: scroll">
<noscript>Without JavaScript enabled, this page cannot function.</noscript>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>

Expand Down
33 changes: 12 additions & 21 deletions packages/wallet/ui/src/bridge.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ const BridgeProtocol = /** @type {const} */ ({
opened: 'walletBridgeOpened',
});

const checkParentWindow = () => {
const me = window;
const { parent } = window;
if (me === parent) {
throw Error('window.parent === parent!!!');
}
};

/**
* Install a dApp "connection" where messages posted from
* the dApp are forwarded to localStorage and vice versa.
Expand All @@ -25,6 +33,8 @@ const BridgeProtocol = /** @type {const} */ ({
* }} io
*/
const installDappConnection = ({ addEventListener, parentPost, t0 }) => {
checkParentWindow();

/** @type { string } */
let origin;

Expand All @@ -44,18 +54,12 @@ const installDappConnection = ({ addEventListener, parentPost, t0 }) => {
const { stringify, parse } = JSON;

addEventListener('message', ev => {
// console.debug('bridge: handling message:', ev.data);
if (
!ev.data ||
typeof ev.data.type !== 'string' ||
!ev.data.type.startsWith('CTP_')
) {
if (!ev.data?.type?.startsWith('CTP_')) {
return;
}

if (origin === undefined) {
// First-come, first-serve.
// console.debug('bridge: setting origin to', origin);
origin = ev.origin;
}
if (setItem) {
Expand All @@ -71,7 +75,6 @@ const installDappConnection = ({ addEventListener, parentPost, t0 }) => {
/** @param {typeof window.localStorage} storage */
connectStorage: storage => {
addEventListener('storage', ev => {
// console.debug('from storage', origin, ev.key, ev.newValue);
if (!ev.key || !ev.newValue) {
return;
}
Expand Down Expand Up @@ -101,21 +104,9 @@ const installDappConnection = ({ addEventListener, parentPost, t0 }) => {
});
};

const sameParent = () => {
const me = window;
const { parent } = window;
if (me === parent) {
// eslint-disable-next-line no-debugger
debugger;
throw Error('window.parent === parent!!!');
}
return false;
};

const conn = installDappConnection({
addEventListener: window.addEventListener,
parentPost: (payload, origin) =>
!sameParent() && window.parent.postMessage(payload, origin),
parentPost: (payload, origin) => window.parent.postMessage(payload, origin),
t0: Date.now(),
});

Expand Down
71 changes: 6 additions & 65 deletions packages/wallet/ui/src/components/WalletConnection.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import { makeReactAgoricWalletConnection } from '@agoric/wallet-connection/react.js';
import React, { useState, useEffect } from 'react';
import { E } from '@endo/eventual-send';
import { makeCapTP } from '@endo/captp';
import { observeIterator } from '@agoric/notifier';
import { makeStyles } from '@mui/styles';

import { withApplicationContext } from '../contexts/Application.jsx';
import { makeBackendFromWalletBridge } from '../util/WalletBackendAdapter.js';
import { makeFixedWebSocketConnector } from '../util/fixed-websocket-connector.js';
import { bridgeStorageMessages } from '../util/BridgeStorage.js';

const useStyles = makeStyles(_ => ({
hidden: {
Expand All @@ -31,13 +31,13 @@ const WalletConnection = ({
}) => {
const classes = useStyles();
/**
* ISSUE: where to get the full types for these?
* TODO: where to get the full types for these?
*
* @typedef {{
* getAdminBootstrap: (string, unknown) => WalletBridge
* getAdminBootstrap: (accessToken: any, makeConnector?: any) => WalletBridge
* }} WalletConnection
* @typedef {{
* getScopedBridge: (petname: unknown, origin: unknown) => unknown
* getScopedBridge: (suggestedDappPetname: unknown, dappOrigin?: unknown, makeConnector?: unknown) => unknown
* }} WalletBridge
*/
const [wc, setWC] = useState(/** @type {WalletConnection|null} */ (null));
Expand Down Expand Up @@ -79,70 +79,11 @@ const WalletConnection = ({
},
}).catch(rethrowIfNotCancelled);

/** @type {Map<string,[ReturnType<typeof makeCapTP>, number]>} */
const dappToConn = new Map();
const {
localStorage: storage,
addEventListener,
removeEventListener,
} = window; // WARNING: ambient

function handleStorageMessage(key, newValue) {
const keyParts = JSON.parse(key);
assert(Array.isArray(keyParts));
const [tag, origin, epoch, _ix] = /** @type {unknown[]} */ (keyParts);
const payload = JSON.parse(newValue);
if (tag !== 'out' || !payload || typeof payload.type !== 'string') {
return;
}

// console.debug('handleStorageMessage', payload);
const obj = {
...payload,
dappOrigin: origin,
};
const dappKey = JSON.stringify([origin, epoch]);
/** @type {ReturnType<typeof makeCapTP>} */
let conn;
/** @type {number} */
let ix;
if (dappToConn.has(dappKey)) {
[conn, ix] = dappToConn.get(dappKey) || assert.fail();
} else {
/** @param {unknown} payloadOut */
const send = payloadOut => {
console.debug('WalletConnect: message -> storage', payloadOut);
storage.setItem(
JSON.stringify(['in', origin, epoch, ix]),
JSON.stringify(payloadOut),
);
ix += 1; // ISSUE: overflow?
};
const makeBoot = () => E(bridge).getScopedBridge(origin, origin);
// console.debug('new capTP connection', { origin, epoch });
conn = makeCapTP(`from ${origin} at ${epoch}`, send, makeBoot);
ix = 0;
}
dappToConn.set(dappKey, [conn, ix + 1]);
console.debug('WalletConnect: storage -> dispatch', obj);
conn.dispatch(obj);
storage.removeItem(key);
}
addEventListener('storage', ev => {
const { key, newValue } = ev;
// removeItem causes an event where newValue is null
if (key && newValue) {
handleStorageMessage(key, newValue);
}
});
const cleanupStorageBridge = bridgeStorageMessages(bridge);

return () => {
cancelled = true;
removeEventListener('storage', handleStorageMessage);
for (const [conn, _ix] of dappToConn.values()) {
// @ts-expect-error capTP abort has wrong type?
conn.abort(Error('wallet connection cancelled'));
}
cleanupStorageBridge();
disconnect();
cancel();
};
Expand Down
65 changes: 65 additions & 0 deletions packages/wallet/ui/src/util/BridgeStorage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { makeCapTP } from '@endo/captp';
import { E } from '@endo/eventual-send';

export const bridgeStorageMessages = bridge => {
/** @type {Map<string,[ReturnType<typeof makeCapTP>, number]>} */
const dappToConn = new Map();

const handleStorageMessage = (key, newValue) => {
const keyParts = JSON.parse(key);
assert(Array.isArray(keyParts));
const [tag, origin, epoch, _ix] = /** @type {unknown[]} */ (keyParts);
const payload = JSON.parse(newValue);
if (tag !== 'out' || !payload || typeof payload.type !== 'string') {
return;
}

const obj = {
...payload,
dappOrigin: origin,
};
const dappKey = JSON.stringify([origin, epoch]);
/** @type {ReturnType<typeof makeCapTP>} */
let conn;
/** @type {number} */
let ix;
if (dappToConn.has(dappKey)) {
[conn, ix] = dappToConn.get(dappKey) || assert.fail();
} else {
/** @param {unknown} payloadOut */
const send = payloadOut => {
console.debug('WalletConnect: message -> storage', payloadOut);
window.localStorage.setItem(
JSON.stringify(['in', origin, epoch, ix]),
JSON.stringify(payloadOut),
);
ix += 1; // ISSUE: overflow?
};
const makeBoot = () => E(bridge).getScopedBridge(origin, origin);
console.debug('new capTP connection', { origin, epoch });
conn = makeCapTP(`from ${origin} at ${epoch}`, send, makeBoot);
ix = 0;
}
dappToConn.set(dappKey, [conn, ix + 1]);
console.debug('WalletConnect: storage -> dispatch', obj);
conn.dispatch(obj);
window.localStorage.removeItem(key);
};

const storageListener = ev => {
const { key, newValue } = ev;
// removeItem causes an event where newValue is null
if (key && newValue) {
handleStorageMessage(key, newValue);
}
};
window.addEventListener('storage', storageListener);

return () => {
window.removeEventListener('storage', storageListener);
for (const [conn, _ix] of dappToConn.values()) {
// @ts-expect-error capTP abort has wrong type?
conn.abort(Error('wallet connection cancelled'));
}
};
};

0 comments on commit fff0237

Please sign in to comment.