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

EIP-1193: standard provider API #6170

Merged
merged 7 commits into from
Feb 20, 2019
Merged

EIP-1193: standard provider API #6170

merged 7 commits into from
Feb 20, 2019

Conversation

bitpshr
Copy link
Contributor

@bitpshr bitpshr commented Feb 15, 2019

This pull request adds a new inpage adapter that exposes a provider API that conforms to EIP-1193. The exposure of this API is done so in a way that coexists with the legacy API without additional global namespace pollution.

The key difference between the legacy provider API and the standard provider API is the send method; formerly, this method was a synchronous method that was rarely used in production dapps. In the new API, send becomes asynchronous and replaces sendAsync, which goes away entirely. So to summarize, a single asynchronous send method and five emitted events are all that make up the standard provider API. So, per @danfinlay's suggestion, we can gracefully let these two APIs initially coexist:

  • We already emit most of the events on the legacy provider.
  • We can introspect the arguments passed to send to determine legacy vs. standard.
  • The new web3 1193-compatible version will work with this approach.
  • The legacy provider API works unchanged.

This means the following provider APIs all work with this PR:

// Legacy sync
ethereum.send({ method: 'net_version', params: [] });
// Legacy async
ethereum.sendAsync({ method: 'net_version', params: [] }, console.log);
// Standard async
await ethereum.send('net_version');

@bitpshr bitpshr force-pushed the eip-1193-new branch 2 times, most recently from bb46ee1 to 9d142bd Compare February 15, 2019 14:46
@metamaskbot
Copy link
Collaborator

Builds ready [5fc1d66]: mascara, chrome, firefox, edge, opera

danfinlay
danfinlay previously approved these changes Feb 15, 2019
Copy link
Contributor

@danfinlay danfinlay left a comment

Choose a reason for hiding this comment

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

Looks good, safe, and useful!

I'm happy to merge it before I continue my branch kinda refactoring how the provider communicates with the background, so I can just handle the conflicts with this PR fresh in my head.

Some small comments but nothing that can't be cleaned up or refined later imo, no blockers.

module.exports = function createBlockTracker (args, platform) {
const blockTracker = new BlockTracker(args)
blockTracker.on('latest', () => {
platform && platform.sendMessage && platform.sendMessage({ action: 'ethereum-ping-success' })
Copy link
Contributor

Choose a reason for hiding this comment

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

Oh cool, so this means dapps can now receive push events on new blocks?

})
} catch (error) {
// Per EIP-1193, send should never throw, only reject its Promise. Here
// we swallow thrown errors, which is safe since we handle them above.
Copy link
Contributor

Choose a reason for hiding this comment

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

This still makes me nervous. resolve() is safe to call twice, so as a casual reader, I would gain confidence seeing a reject() here too in case it ever isn't handled correctly.

Copy link
Contributor

Choose a reason for hiding this comment

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

I too feel like this should reject the promise, esp. if it's part of the contract that sendAsync doesn't throw sync errors.

'November 2nd, 2018. Dapps should now call provider.enable() in order to view and use ' +
'accounts. Please see https://bit.ly/2QQHXvF for complete information and up-to-date ' +
'example code.')

Copy link
Contributor

Choose a reason for hiding this comment

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

This will make certain twitter users happy ;)

Copy link
Contributor

@whymarrh whymarrh left a comment

Choose a reason for hiding this comment

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

I'm excited for this! I've left a few small comments.

platform && platform.sendMessage && platform.sendMessage({ action: 'ethereum-ping-success' })
})
blockTracker.on('error', () => {
platform && platform.sendMessage && platform.sendMessage({ action: 'ethereum-ping-error' })
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use an if block here? We might be getting away with too many of these single-line expressions. 😄

Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly for on('latest', ...) above

Copy link
Contributor Author

Choose a reason for hiding this comment

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

But such terse syntax! Fixed :)

}

_onClose () {
(this._isConnected === undefined || this._isConnected) && this._provider.emit('close', {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use an if block here as well?

})
} catch (error) {
// Per EIP-1193, send should never throw, only reject its Promise. Here
// we swallow thrown errors, which is safe since we handle them above.
Copy link
Contributor

Choose a reason for hiding this comment

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

I too feel like this should reject the promise, esp. if it's part of the contract that sendAsync doesn't throw sync errors.

export default function createStandardProvider (provider) {
const standardProvider = new StandardProvider(provider)
const sendLegacy = provider.send
provider.send = (a, b) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we be a bit more specific with the argument names here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since these can be completely different types of variables depending on what API you're targeting (standard or legacy), good names were tough. I improved them as best I could though, I think we can do better than a and b, you're right :)

const standardProvider = new StandardProvider(provider)
const sendLegacy = provider.send
provider.send = (a, b) => {
if (typeof payload === 'string' && !b || Array.isArray(b)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Where does payload come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great save, should be a, or what is now methodOrPayload.

@metamaskbot
Copy link
Collaborator

Builds ready [9f1d7ef]: mascara, chrome, firefox, edge, opera

Copy link
Contributor

@danfinlay danfinlay left a comment

Choose a reason for hiding this comment

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

This looks good. Thank you!

@danfinlay danfinlay dismissed whymarrh’s stale review February 20, 2019 00:41

Requested changes were made.

@danfinlay danfinlay merged commit 2f7d449 into develop Feb 20, 2019
@danfinlay danfinlay deleted the eip-1193-new branch February 20, 2019 00:42

return new Promise((resolve, reject) => {
try {
this._provider.sendAsync({ method, params, beta: true }, (error, response) => {

Choose a reason for hiding this comment

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

If may, why use beta: true and not the classic jsonrpc: '2.0' ?
I think this the cause of this issue : MM 6.2.1 sends invalid JSONRPC requests to private network

@kumavis
Copy link
Member

kumavis commented Apr 16, 2019

Historical note: this reintroduced always-on block tracker

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants