Skip to content
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

feat(wallet-connection): Connect dapp directly to wallet UI #5750

Merged
merged 17 commits into from
Jul 14, 2022

Conversation

samsiegart
Copy link
Contributor

@samsiegart samsiegart commented Jul 12, 2022

refs: #3995

We can now add the useLocalStorage={true} property to the wallet connection component to get the dapp to talk directly to the wallet UI in local storage, that way the dapp doesn't directly depend on an ag-solo connection.

@samsiegart samsiegart changed the title Sam bridge working Working AMM Swap Jul 12, 2022
@samsiegart samsiegart force-pushed the sam-bridge-working branch 3 times, most recently from 0580cac to 16d853c Compare July 13, 2022 05:04
@samsiegart samsiegart changed the title Working AMM Swap feat(wallet-connection): Connect dapp directly to wallet UI Jul 13, 2022
@samsiegart samsiegart requested a review from dckc July 13, 2022 05:10
@samsiegart samsiegart marked this pull request as ready for review July 13, 2022 05:10
window.parent.postMessage({ type: 'walletBridgeLoaded' }, '*');
}
retryWebSocket();
window.addEventListener('load', () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ensures the message wont be posted until after the iframe's "onLoad" event fires. This is the way it works with the react bridge.jsx, so we can rely on the consistent ordering of events in the WalletConnection component.

@dckc dckc requested a review from turadg July 13, 2022 05:22
Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems pretty good, but given the low test coverage for this stuff, I'd like to give @turadg a chance to look it over

docs/env.md Outdated
Comment on lines 129 to 132
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't clear to me from reading this what the purpose was (the difference between the two paths or how it worked.)

Consider:

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

window.parent.postMessage({ type: 'walletBridgeLoaded' }, '*');
}
retryWebSocket();
window.addEventListener('load', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your PR comment would make a good code comment.

Suggested change
window.addEventListener('load', () => {
// 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', () => {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, please!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

packages/solo/src/web.js Show resolved Hide resolved
packages/wallet/ui/config-overrides/index.js Show resolved Hide resolved
</head>

<body style="overflow-x: hidden; overflow-y: scroll">
<noscript>Without JavaScript enabled, this page cannot function.</noscript>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that statement is true invariantly. minor but let's tell the user something about the state.

Suggested change
<noscript>Without JavaScript enabled, this page cannot function.</noscript>
<noscript>ERROR: JavaScript not available.</noscript>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Better to be explicit and tell them how to fix it?

I have a hard time blaming the user when the page requires JavaScript. I was trying to concisely say something like "Please excuse the requirement for you to put yourself at risk by enabling JavaScript in your browser. This page uses JavaScript for more than progressive enhancement. Providing similar functionality without JavaScript is not practical."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just used the same message as index.html. Do people still use the web without javascript? At the very least they're trying to use a dapp so I don't think we need to be apologetic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... so I don't think we need to be apologetic.

No, we don't need to, but...

Do people still use the web without javascript?

Yes. In fact, I bet you do it regularly, with curl and the like.

);
} else {
console.debug('bridge: SKIP document.requestStorageAccess');
conn.connectStorage(window.localStorage);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could this fail?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't think so. It only calls addEventListener; I don't see how that could fail.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been bitten by localStorage permission problems, but I suppose if you've got the object you won't find out about any errors until mutating. And even then you could have failures later with hitting quotas, the permission being revoked, etc. So fine to handle those cases later.

@@ -28,7 +30,17 @@ const WalletConnection = ({
connectionConfig,
}) => {
const classes = useStyles();
const [wc, setWC] = useState(null);
/**
* ISSUE: where to get the full types for these?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. for WalletConnection at this line to the bottom of AgoricWalletConnection.js,

/** @typedef {InstanceType<AgoricWalletConnection>['walletConnection']} FarWalletConnection */

I suggest "Far" to distinguish from the other "WalletConnection" strings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't get this working

localStorage: storage,
addEventListener,
removeEventListener,
} = window; // WARNING: ambient
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider using window. at each access so the ambience is apparent at the callsite

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather go the other direction: pass in these parts of window explicitly rather than using window ambiently.

OTOH, window.addEventListener is quite likely more conventional and easier to read, and since the current code doesn't follow ocap discipline, maybe making it easier to read is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to window.

removeEventListener,
} = window; // WARNING: ambient

function handleStorageMessage(key, newValue) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a lot of logic in here that's not UI concern. consider extracting some to a utility function. that could be unit tested.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

concur

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled this into a separate function. I don't know how much of this code is just placeholder/PoC until after we have the smart wallet bridge fully working, so I won't bother writing tests yet.

Comment on lines +6 to +12
// eslint-disable-next-line no-restricted-properties
const { pow: mathPow } = Math;
// eslint-disable-next-line no-restricted-properties
Math.pow = (base, exp) =>
typeof base === 'bigint' && typeof exp === 'bigint'
? base ** exp
: mathPow(base, exp);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we not have a shim that can be called to do this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Umm... This is such a shim, no?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean one that can be imported here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure this is a one-off. It's kind of a kludge, really.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a new kludge, btw. It comes from...

Math.pow = (base, exp) =>
typeof base === 'bigint' && typeof exp === 'bigint'
? base ** exp
: mathPow(base, exp);

Git says that's from @samsiegart Oct 2021 01a9118 but I suspect it predates that too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Where is it used? I can't find any instances. If there are any consider making them bigint-safe there instead of modifying Math.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know where it's used. Maybe some 3rd party library?

I sure wish we had made a test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this code has been here longer than I have, will leave this convo open but won't block this PR on it.

@samsiegart samsiegart requested review from turadg and dckc July 14, 2022 03:11
Copy link
Member

@dckc dckc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's at least a step in the right direction.

Copy link
Member

@turadg turadg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@samsiegart samsiegart added the automerge:squash Automatically squash merge label Jul 14, 2022
@mergify mergify bot merged commit 1dd584b into master Jul 14, 2022
@mergify mergify bot deleted the sam-bridge-working branch July 14, 2022 17:55
turadg pushed a commit that referenced this pull request Jul 14, 2022
* chore(web-components): revert casting experiment

* feat(solo): transitional reverse-proxy wallet-bridge

 - document SOLO_BRIDGE_TARGET

* feat(wallet/api): E(walletAdmin).getScopedBridge()

* build(wallet/ui): deps += react-app-rewire-multiple-entry

* feat(wallet/ui): localStorage bridge functional in AMM (WIP)

it worked up to making an offer; the trade didn't complete...
not sure why

 - much wrestling with webpack magic in config-overrides
   to add wallet/bridge.html entry point
 - move lockdown() call from .html to .jsx
   - HandledPromise identity discontinuity musings

* chore(wallet/ui): prune bridge.html to essentials

* doc(casting): package name typo in README

* refactor(wallet/ui): take advantage of ev type

 - JSON.stringify is more familiar

* chore: sameParent is evidently superfluous

* chore: delay storage listener until user requests

* chore: gate storage connection on requestStorageAccess

* chore: sameParent check before postMessage. noop???

* feat: use correct bridge url

* feat: amm swap working

* chore: prettier

* fix: address feedback

* chore: rebase

Co-authored-by: Dan Connolly <connolly@agoric.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge:squash Automatically squash merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants